From 740c6ffea7c4794ae82d2a37c600ca0304d5c0d9 Mon Sep 17 00:00:00 2001 From: Jomo Date: Fri, 3 May 2024 11:06:18 +0800 Subject: [PATCH 01/60] =?UTF-8?q?fix(mm):=E4=BC=98=E5=8C=96extract?= =?UTF-8?q?=E4=B8=AD=E5=AF=B9before=E5=92=8Cafter=20VMA=E6=98=A0=E5=B0=84?= =?UTF-8?q?=E6=83=85=E5=86=B5=E7=9A=84=E5=88=A4=E6=96=AD=20(#802)=20(#803)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/mm/ucontext.rs | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 8d030e2b6..1e909ec9a 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -1132,22 +1132,28 @@ impl LockedVMA { // 重新设置before、after这两个VMA里面的物理页的anon_vma let mut page_manager_guard = page_manager_lock_irqsave(); if let Some(before) = before.clone() { - let virt_iter = before.lock().region.iter_pages(); - for frame in virt_iter { - let paddr = utable.translate(frame.virt_address()).unwrap().0; - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(before.clone()); - page.remove_vma(self); + let before_guard = before.lock(); + if before_guard.mapped { + let virt_iter = before_guard.region.iter_pages(); + for frame in virt_iter { + let paddr = utable.translate(frame.virt_address()).unwrap().0; + let page = page_manager_guard.get_mut(&paddr); + page.insert_vma(before.clone()); + page.remove_vma(self); + } } } if let Some(after) = after.clone() { - let virt_iter = after.lock().region.iter_pages(); - for frame in virt_iter { - let paddr = utable.translate(frame.virt_address()).unwrap().0; - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(after.clone()); - page.remove_vma(self); + let after_guard = after.lock(); + if after_guard.mapped { + let virt_iter = after_guard.region.iter_pages(); + for frame in virt_iter { + let paddr = utable.translate(frame.virt_address()).unwrap().0; + let page = page_manager_guard.get_mut(&paddr); + page.insert_vma(after.clone()); + page.remove_vma(self); + } } } From 62da39bb0c5fd54355b2e60c8ac466d882bd9bcf Mon Sep 17 00:00:00 2001 From: Jomo Date: Fri, 3 May 2024 11:30:19 +0800 Subject: [PATCH 02/60] =?UTF-8?q?feat(mm):=E6=B7=BB=E5=8A=A0MAP=5FFIXED?= =?UTF-8?q?=E5=A4=84=E7=90=86=20(#804)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(mm):优化extract中对before和after VMA映射情况的判断 (#802) * 添加mmap对MAP_FIXED的处理 (#706) --- kernel/src/mm/ucontext.rs | 92 ++++++++++++++++++++++----------------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 1e909ec9a..18d8bda4b 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -353,8 +353,7 @@ impl InnerAddressSpace { // 找到未使用的区域 let region = match addr { Some(vaddr) => { - self.mappings - .find_free_at(self.mmap_min, vaddr, page_count.bytes(), map_flags)? + self.find_free_at(self.mmap_min, vaddr, page_count.bytes(), map_flags)? } None => self .mappings @@ -693,6 +692,56 @@ impl InnerAddressSpace { return self.set_brk(new_brk); } + + pub fn find_free_at( + &mut self, + min_vaddr: VirtAddr, + vaddr: VirtAddr, + size: usize, + flags: MapFlags, + ) -> Result { + // 如果没有指定地址,那么就在当前进程的地址空间中寻找一个空闲的虚拟内存范围。 + if vaddr == VirtAddr::new(0) { + return self + .mappings + .find_free(min_vaddr, size) + .ok_or(SystemError::ENOMEM); + } + + // 如果指定了地址,那么就检查指定的地址是否可用。 + let requested = VirtRegion::new(vaddr, size); + + if requested.end() >= MMArch::USER_END_VADDR || !vaddr.check_aligned(MMArch::PAGE_SIZE) { + return Err(SystemError::EINVAL); + } + + let intersect_vma = self.mappings.conflicts(requested).next(); + if let Some(vma) = intersect_vma { + if flags.contains(MapFlags::MAP_FIXED_NOREPLACE) { + // 如果指定了 MAP_FIXED_NOREPLACE 标志,由于所指定的地址无法成功建立映射,则放弃映射,不对地址做修正 + return Err(SystemError::EEXIST); + } + + if flags.contains(MapFlags::MAP_FIXED) { + // 对已有的VMA进行覆盖 + let intersect_region = vma.lock().region.intersect(&requested).unwrap(); + self.munmap( + VirtPageFrame::new(intersect_region.start), + PageFrameCount::from_bytes(intersect_region.size).unwrap(), + )?; + return Ok(requested); + } + + // 如果没有指定MAP_FIXED标志,那么就对地址做修正 + let requested = self + .mappings + .find_free(min_vaddr, size) + .ok_or(SystemError::ENOMEM)?; + return Ok(requested); + } + + return Ok(requested); + } } impl Drop for InnerAddressSpace { @@ -844,45 +893,6 @@ impl UserMappings { return Some(region); } - pub fn find_free_at( - &self, - min_vaddr: VirtAddr, - vaddr: VirtAddr, - size: usize, - flags: MapFlags, - ) -> Result { - // 如果没有指定地址,那么就在当前进程的地址空间中寻找一个空闲的虚拟内存范围。 - if vaddr == VirtAddr::new(0) { - return self.find_free(min_vaddr, size).ok_or(SystemError::ENOMEM); - } - - // 如果指定了地址,那么就检查指定的地址是否可用。 - - let requested = VirtRegion::new(vaddr, size); - - if requested.end() >= MMArch::USER_END_VADDR || !vaddr.check_aligned(MMArch::PAGE_SIZE) { - return Err(SystemError::EINVAL); - } - - if let Some(_x) = self.conflicts(requested).next() { - if flags.contains(MapFlags::MAP_FIXED_NOREPLACE) { - // 如果指定了 MAP_FIXED_NOREPLACE 标志,由于所指定的地址无法成功建立映射,则放弃映射,不对地址做修正 - return Err(SystemError::EEXIST); - } - - if flags.contains(MapFlags::MAP_FIXED) { - // todo: 支持MAP_FIXED标志对已有的VMA进行覆盖 - return Err(SystemError::ENOSYS); - } - - // 如果没有指定MAP_FIXED标志,那么就对地址做修正 - let requested = self.find_free(min_vaddr, size).ok_or(SystemError::ENOMEM)?; - return Ok(requested); - } - - return Ok(requested); - } - /// 在当前进程的地址空间中,保留一个指定大小的区域,使得该区域不在空洞中。 /// 该函数会修改vm_holes中的空洞信息。 /// From 35ae1f6574c5828c2e7d41271730a98bbecf0227 Mon Sep 17 00:00:00 2001 From: Jomo Date: Fri, 3 May 2024 11:32:30 +0800 Subject: [PATCH 03/60] =?UTF-8?q?feat(mm):=E6=B7=BB=E5=8A=A0MAP=5FFIXED?= =?UTF-8?q?=E5=A4=84=E7=90=86=20(#805)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加mmap对MAP_FIXED的处理 (#706) From 9bf53ec0c1c2b756c03590d359fe41503149b1a4 Mon Sep 17 00:00:00 2001 From: Chiichen Date: Fri, 3 May 2024 11:49:09 +0800 Subject: [PATCH 04/60] feat(elf): [WIP] Dynamic link support (#806) * feat(elf): [WIP] Dynamic link support * chore: revert changes on Cargo.toml --- build-scripts/.gitignore | 1 + kernel/src/libs/elf.rs | 384 ++++++++++++++++++------- kernel/src/process/exec.rs | 5 + user/apps/test_glibc/.gitignore | 3 + user/apps/test_glibc/Cargo.toml | 10 + user/apps/test_glibc/LICENSE | 201 +++++++++++++ user/apps/test_glibc/Makefile | 56 ++++ user/apps/test_glibc/README.md | 14 + user/apps/test_glibc/src/main.rs | 3 + user/dadk/config/glibc-2.39.9.dadk | 27 ++ user/dadk/config/test_glibc-0.1.0.dadk | 23 ++ 11 files changed, 628 insertions(+), 99 deletions(-) create mode 100644 build-scripts/.gitignore create mode 100644 user/apps/test_glibc/.gitignore create mode 100644 user/apps/test_glibc/Cargo.toml create mode 100644 user/apps/test_glibc/LICENSE create mode 100644 user/apps/test_glibc/Makefile create mode 100644 user/apps/test_glibc/README.md create mode 100644 user/apps/test_glibc/src/main.rs create mode 100644 user/dadk/config/glibc-2.39.9.dadk create mode 100644 user/dadk/config/test_glibc-0.1.0.dadk diff --git a/build-scripts/.gitignore b/build-scripts/.gitignore new file mode 100644 index 000000000..1de565933 --- /dev/null +++ b/build-scripts/.gitignore @@ -0,0 +1 @@ +target \ No newline at end of file diff --git a/kernel/src/libs/elf.rs b/kernel/src/libs/elf.rs index 417366b5b..71bb91e37 100644 --- a/kernel/src/libs/elf.rs +++ b/kernel/src/libs/elf.rs @@ -7,18 +7,17 @@ use core::{ use alloc::vec::Vec; use elf::{ - abi::{PT_GNU_PROPERTY, PT_INTERP}, + abi::{ET_DYN, ET_EXEC, PT_GNU_PROPERTY, PT_INTERP, PT_LOAD}, endian::AnyEndian, file::FileHeader, - segment::ProgramHeader, + segment::{ProgramHeader, SegmentTable}, }; use system_error::SystemError; use crate::{ arch::{CurrentElfArch, MMArch}, driver::base::block::SeekFrom, - filesystem::vfs::file::File, - kerror, + kdebug, kerror, libs::align::page_align_up, mm::{ allocator::page_frame::{PageFrameCount, VirtPageFrame}, @@ -28,7 +27,9 @@ use crate::{ }, process::{ abi::AtType, - exec::{BinaryLoader, BinaryLoaderResult, ExecError, ExecLoadMode, ExecParam}, + exec::{ + BinaryLoader, BinaryLoaderResult, ExecError, ExecLoadMode, ExecParam, ExecParamFlags, + }, ProcessFlags, ProcessManager, }, syscall::user_access::{clear_user, copy_to_user}, @@ -121,9 +122,9 @@ impl ElfLoader { end: VirtAddr, prot_flags: ProtFlags, ) -> Result<(), ExecError> { - let start = self.elf_page_start(start); - let end = self.elf_page_align_up(end); - // kdebug!("set_elf_brk: start={:?}, end={:?}", start, end); + let start = Self::elf_page_start(start); + let end = Self::elf_page_align_up(end); + if end > start { let r = user_vm_guard.map_anonymous( start, @@ -145,15 +146,15 @@ impl ElfLoader { } /// 计算addr在ELF PAGE内的偏移 - fn elf_page_offset(&self, addr: VirtAddr) -> usize { + fn elf_page_offset(addr: VirtAddr) -> usize { addr.data() & (CurrentElfArch::ELF_PAGE_SIZE - 1) } - fn elf_page_start(&self, addr: VirtAddr) -> VirtAddr { + fn elf_page_start(addr: VirtAddr) -> VirtAddr { VirtAddr::new(addr.data() & (!(CurrentElfArch::ELF_PAGE_SIZE - 1))) } - fn elf_page_align_up(&self, addr: VirtAddr) -> VirtAddr { + fn elf_page_align_up(addr: VirtAddr) -> VirtAddr { VirtAddr::new( (addr.data() + CurrentElfArch::ELF_PAGE_SIZE - 1) & (!(CurrentElfArch::ELF_PAGE_SIZE - 1)), @@ -161,7 +162,7 @@ impl ElfLoader { } /// 根据ELF的p_flags生成对应的ProtFlags - fn make_prot(&self, p_flags: u32, _has_interpreter: bool, _is_interpreter: bool) -> ProtFlags { + fn make_prot(p_flags: u32, _has_interpreter: bool, _is_interpreter: bool) -> ProtFlags { let mut prot = ProtFlags::empty(); if p_flags & elf::abi::PF_R != 0 { prot |= ProtFlags::PROT_READ; @@ -198,7 +199,6 @@ impl ElfLoader { /// - `Ok((VirtAddr, bool))`:如果成功加载,则bool值为true,否则为false. VirtAddr为加载的地址 #[allow(clippy::too_many_arguments)] fn load_elf_segment( - &self, user_vm_guard: &mut RwLockWriteGuard<'_, InnerAddressSpace>, param: &mut ExecParam, phent: &ProgramHeader, @@ -210,11 +210,11 @@ impl ElfLoader { // kdebug!("load_elf_segment: addr_to_map={:?}", addr_to_map); // 映射位置的偏移量(页内偏移) - let beginning_page_offset = self.elf_page_offset(addr_to_map); - addr_to_map = self.elf_page_start(addr_to_map); + let beginning_page_offset = Self::elf_page_offset(addr_to_map); + addr_to_map = Self::elf_page_start(addr_to_map); // 计算要映射的内存的大小 let map_size = phent.p_filesz as usize + beginning_page_offset; - let map_size = self.elf_page_align_up(VirtAddr::new(map_size)).data(); + let map_size = Self::elf_page_align_up(VirtAddr::new(map_size)).data(); // 当前段在文件中的大小 let seg_in_file_size = phent.p_filesz as usize; // 当前段在文件中的偏移量 @@ -253,7 +253,9 @@ impl ElfLoader { // So we first map the 'big' image - and unmap the remainder at // the end. (which unmap is needed for ELF images with holes.) if total_size != 0 { - let total_size = self.elf_page_align_up(VirtAddr::new(total_size)).data(); + let total_size = Self::elf_page_align_up(VirtAddr::new(total_size)).data(); + + // kdebug!("total_size={}", total_size); map_addr = user_vm_guard .map_anonymous(addr_to_map, total_size, tmp_prot, *map_flags, false, true) @@ -269,7 +271,7 @@ impl ElfLoader { )?; // 加载文件到内存 - self.do_load_file( + Self::do_load_file( map_addr + beginning_page_offset, seg_in_file_size, file_offset, @@ -294,7 +296,7 @@ impl ElfLoader { // ); // 加载文件到内存 - self.do_load_file( + Self::do_load_file( map_addr + beginning_page_offset, seg_in_file_size, file_offset, @@ -313,6 +315,154 @@ impl ElfLoader { return Ok((map_addr, true)); } + /// 加载elf动态链接器 + /// + /// ## 参数 + /// + /// - `interp_elf_ex`:动态链接器 + /// - `load_bias`偏移量 + /// + /// ## TODO + /// + /// 添加一个Arch state抽象,描述架构相关的elf state(参考 https://code.dragonos.org.cn/xref/linux-6.1.9/fs/binfmt_elf.c#592) + fn load_elf_interp( + interp_elf_ex: &mut ExecParam, + load_bias: usize, + ) -> Result { + kdebug!("loading elf interp"); + let mut head_buf = [0u8; 512]; + interp_elf_ex + .file_mut() + .lseek(SeekFrom::SeekSet(0)) + .map_err(|_| ExecError::NotSupported)?; + let _bytes = interp_elf_ex + .file_mut() + .read(512, &mut head_buf) + .map_err(|_| ExecError::NotSupported)?; + let interp_hdr = + Self::parse_ehdr(head_buf.as_ref()).map_err(|_| ExecError::NotExecutable)?; + if interp_hdr.e_type != ET_EXEC && interp_hdr.e_type != ET_DYN { + return Err(ExecError::NotExecutable); + } + let mut phdr_buf = Vec::new(); + let phdr_table = Self::parse_segments(interp_elf_ex, &interp_hdr, &mut phdr_buf) + .map_err(|_| ExecError::ParseError)? + .ok_or(ExecError::ParseError)?; + //TODO 架构相关检查 https://code.dragonos.org.cn/xref/linux-6.1.9/fs/binfmt_elf.c#610 + let mut total_size = Self::total_mapping_size(&phdr_table); + + if total_size == 0 { + return Err(ExecError::InvalidParemeter); + } + + let mut load_addr_set = false; + let mut load_addr: VirtAddr = VirtAddr::new(0); + let mut elf_bss: VirtAddr = VirtAddr::new(0); + let mut last_bss: VirtAddr = VirtAddr::new(0); + let mut bss_prot: Option = None; + for section in phdr_table { + kdebug!("loading {:?}", section); + if section.p_type == PT_LOAD { + let mut elf_type = MapFlags::MAP_PRIVATE; + let elf_prot = Self::make_prot(section.p_flags, true, true); + let vaddr = TryInto::::try_into(section.p_vaddr).unwrap(); + if interp_hdr.e_type == ET_EXEC || load_addr_set { + elf_type.insert(MapFlags::MAP_FIXED) //TODO 应当为MapFlags::MAP_FIXED,暂时未支持 + } + load_addr += vaddr; + if load_bias != 0 && interp_hdr.e_type == ET_DYN { + load_addr -= vaddr; + } + let map_addr = Self::load_elf_segment( + &mut interp_elf_ex.vm().clone().write(), + interp_elf_ex, + §ion, + load_addr, + &elf_prot, + &elf_type, + total_size, + ) + .map_err(|e| { + kerror!("Failed to load elf interpreter :{:?}", e); + return ExecError::InvalidParemeter; + })?; + if !map_addr.1 { + return Err(ExecError::BadAddress(Some(map_addr.0))); + } + let map_addr = map_addr.0; + total_size = 0; + if !load_addr_set && interp_hdr.e_type == ET_DYN { + load_addr = + VirtAddr::new(map_addr - Self::elf_page_start(VirtAddr::new(vaddr))); + load_addr_set = true; + } + let addr = load_addr + TryInto::::try_into(section.p_vaddr).unwrap(); + if addr >= MMArch::USER_END_VADDR + || section.p_filesz > section.p_memsz + || TryInto::::try_into(section.p_memsz).unwrap() + > MMArch::USER_END_VADDR.data() + || MMArch::USER_END_VADDR - TryInto::::try_into(section.p_memsz).unwrap() + < addr + { + return Err(ExecError::OutOfMemory); + } + + let addr = load_addr + + TryInto::::try_into(section.p_vaddr + section.p_filesz).unwrap(); + if addr > elf_bss { + elf_bss = addr; + } + + let addr = load_addr + + TryInto::::try_into(section.p_vaddr + section.p_memsz).unwrap(); + if addr > last_bss { + last_bss = addr; + bss_prot = Some(elf_prot); + } + } + } + Self::pad_zero(elf_bss).map_err(|_| return ExecError::BadAddress(Some(elf_bss)))?; + elf_bss = Self::elf_page_align_up(elf_bss); + last_bss = Self::elf_page_align_up(last_bss); + if last_bss > elf_bss { + if bss_prot.is_none() { + return Err(ExecError::InvalidParemeter); + } + let mut bss_prot = bss_prot.unwrap(); + if bss_prot.contains(ProtFlags::PROT_EXEC) { + bss_prot = ProtFlags::PROT_EXEC; + } else { + bss_prot = ProtFlags::PROT_NONE; + } + interp_elf_ex + .vm() + .clone() + .write() + .map_anonymous( + elf_bss, + last_bss - elf_bss, + bss_prot, + MapFlags::MAP_ANONYMOUS | MapFlags::MAP_FIXED_NOREPLACE, + false, + true, + ) + .map_err(|e| match e { + SystemError::EINVAL => ExecError::InvalidParemeter, + SystemError::ENOMEM => ExecError::OutOfMemory, + _ => return ExecError::InvalidParemeter, + })?; + } + if load_addr + TryInto::::try_into(interp_hdr.e_entry).unwrap() + > MMArch::USER_END_VADDR + { + return Err(ExecError::BadAddress(Some( + load_addr + TryInto::::try_into(interp_hdr.e_entry).unwrap(), + ))); + } + kdebug!("sucessfully load elf interp"); + return Ok(BinaryLoaderResult::new(load_addr)); + } + /// 加载ELF文件到用户空间 /// /// ## 参数 @@ -322,7 +472,6 @@ impl ElfLoader { /// - `offset_in_file`:在文件内的偏移量 /// - `param`:执行参数 fn do_load_file( - &self, mut vaddr: VirtAddr, size: usize, offset_in_file: usize, @@ -354,8 +503,8 @@ impl ElfLoader { } /// 我们需要显式的把数据段之后剩余的内存页都清零。 - fn pad_zero(&self, elf_bss: VirtAddr) -> Result<(), SystemError> { - let nbyte = self.elf_page_offset(elf_bss); + fn pad_zero(elf_bss: VirtAddr) -> Result<(), SystemError> { + let nbyte = Self::elf_page_offset(elf_bss); if nbyte > 0 { let nbyte = CurrentElfArch::ELF_PAGE_SIZE - nbyte; unsafe { clear_user(elf_bss, nbyte).map_err(|_| SystemError::EFAULT) }?; @@ -363,6 +512,37 @@ impl ElfLoader { return Ok(()); } + /// 参考https://code.dragonos.org.cn/xref/linux-6.1.9/fs/binfmt_elf.c#1158,获取要加载的total_size + fn total_mapping_size(ehdr_table: &SegmentTable<'_, AnyEndian>) -> usize { + let mut has_load = false; + let mut min_address = VirtAddr::new(usize::MAX); + let mut max_address = VirtAddr::new(0usize); + let loadable_sections = ehdr_table + .into_iter() + .filter(|seg| seg.p_type == elf::abi::PT_LOAD); + for seg_to_load in loadable_sections { + min_address = min( + min_address, + Self::elf_page_start(VirtAddr::new(seg_to_load.p_vaddr.try_into().unwrap())), + ); + max_address = max( + max_address, + VirtAddr::new( + (seg_to_load.p_vaddr + seg_to_load.p_memsz) + .try_into() + .unwrap(), + ), + ); + has_load = true; + } + let total_size = if has_load { + max_address - min_address + } else { + 0 + }; + return total_size; + } + /// 创建auxv /// /// ## 参数 @@ -542,7 +722,7 @@ impl BinaryLoader for ElfLoader { .map_err(|_| ExecError::ParseError)? .ok_or(ExecError::ParseError)?; let mut _gnu_property_data: Option = None; - let interpreter: Option = None; + let mut interpreter: Option = None; for seg in phdr_table { if seg.p_type == PT_GNU_PROPERTY { _gnu_property_data = Some(seg); @@ -558,22 +738,44 @@ impl BinaryLoader for ElfLoader { if seg.p_filesz > 4096 || seg.p_filesz < 2 { return Err(ExecError::NotExecutable); } - - let interpreter_ptr = unsafe { - core::slice::from_raw_parts( - seg.p_offset as *const u8, + kdebug!("seg:{:?}", seg); + let mut buffer = Vec::new(); + buffer.resize(seg.p_filesz.try_into().unwrap(), 0); + let r = param + .file_mut() + .pread( + seg.p_offset.try_into().unwrap(), seg.p_filesz.try_into().unwrap(), + buffer.as_mut_slice(), ) - }; - let _interpreter_path = core::str::from_utf8(interpreter_ptr).map_err(|e| { + .map_err(|e| { + kerror!("Failed to load interpreter :{:?}", e); + return ExecError::NotSupported; + })?; + if r != seg.p_filesz.try_into().unwrap() { + kerror!("Failed to load interpreter "); + return Err(ExecError::NotSupported); + } + let interpreter_path = core::str::from_utf8( + &buffer[0..TryInto::::try_into(seg.p_filesz).unwrap() - 1], // + ) + .map_err(|e| { ExecError::Other(format!( "Failed to parse the path of dynamic linker with error {}", e )) })?; - - //TODO 加入对动态链接器的加载,参照 https://code.dragonos.org.cn/xref/linux-6.1.9/fs/binfmt_elf.c#890 + kdebug!("opening interpreter at :{}", interpreter_path); + interpreter = Some( + ExecParam::new(&interpreter_path, param.vm().clone(), ExecParamFlags::EXEC) + .map_err(|e| { + kerror!("Failed to load interpreter :{:?}", e); + return ExecError::NotSupported; + })?, + ); } + //TODO 缺少一部分逻辑 https://code.dragonos.org.cn/xref/linux-6.1.9/fs/binfmt_elf.c#931 + if interpreter.is_some() { /* Some simple consistency checks for the interpreter */ // 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/fs/binfmt_elf.c#950 @@ -595,39 +797,14 @@ impl BinaryLoader for ElfLoader { // program header的虚拟地址 let mut phdr_vaddr: Option = None; let mut _reloc_func_desc = 0usize; - // 参考https://code.dragonos.org.cn/xref/linux-6.1.9/fs/binfmt_elf.c#1158,获取要加载的total_size - let mut has_load = false; - let mut min_address = VirtAddr::new(usize::MAX); - let mut max_address = VirtAddr::new(0usize); - let loadable_sections = phdr_table - .into_iter() - .filter(|seg| seg.p_type == elf::abi::PT_LOAD); - for seg_to_load in loadable_sections { - min_address = min( - min_address, - self.elf_page_start(VirtAddr::new(seg_to_load.p_vaddr.try_into().unwrap())), - ); - max_address = max( - max_address, - VirtAddr::new( - (seg_to_load.p_vaddr + seg_to_load.p_memsz) - .try_into() - .unwrap(), - ), - ); - has_load = true; - } - let total_size = if has_load { - max_address - min_address - } else { - 0 - }; + let total_size = Self::total_mapping_size(&phdr_table); let loadable_sections = phdr_table .into_iter() .filter(|seg| seg.p_type == elf::abi::PT_LOAD); + for seg_to_load in loadable_sections { - // kdebug!("seg_to_load = {:?}", seg_to_load); + kdebug!("seg_to_load = {:?}", seg_to_load); if unlikely(elf_brk > elf_bss) { // kdebug!( // "to set brk, elf_brk = {:?}, elf_bss = {:?}", @@ -640,7 +817,7 @@ impl BinaryLoader for ElfLoader { elf_brk + load_bias, bss_prot_flags, )?; - let nbyte = self.elf_page_offset(elf_bss); + let nbyte = Self::elf_page_offset(elf_bss); if nbyte > 0 { let nbyte = min(CurrentElfArch::ELF_PAGE_SIZE - nbyte, elf_brk - elf_bss); unsafe { @@ -652,7 +829,7 @@ impl BinaryLoader for ElfLoader { } // 生成ProtFlags. - let elf_prot_flags = self.make_prot(seg_to_load.p_flags, interpreter.is_some(), false); + let elf_prot_flags = Self::make_prot(seg_to_load.p_flags, interpreter.is_some(), false); let mut elf_map_flags = MapFlags::MAP_PRIVATE; @@ -681,36 +858,31 @@ impl BinaryLoader for ElfLoader { load_bias = 0; } } - load_bias = self - .elf_page_start(VirtAddr::new( - load_bias - TryInto::::try_into(seg_to_load.p_vaddr).unwrap(), - )) - .data(); + load_bias = Self::elf_page_start(VirtAddr::new( + load_bias - TryInto::::try_into(seg_to_load.p_vaddr).unwrap(), + )) + .data(); if total_size == 0 { return Err(ExecError::InvalidParemeter); } } // 加载这个段到用户空间 - // kdebug!("to load elf segment"); - let e = self - .load_elf_segment( - &mut user_vm, - param, - &seg_to_load, - vaddr + load_bias, - &elf_prot_flags, - &elf_map_flags, - total_size, - ) - .map_err(|e| { - kerror!("load_elf_segment failed: {:?}", e); - match e { - SystemError::EFAULT => ExecError::BadAddress(None), - SystemError::ENOMEM => ExecError::OutOfMemory, - _ => ExecError::Other(format!("load_elf_segment failed: {:?}", e)), - } - })?; + + let e = Self::load_elf_segment( + &mut user_vm, + param, + &seg_to_load, + vaddr + load_bias, + &elf_prot_flags, + &elf_map_flags, + total_size, + ) + .map_err(|e| match e { + SystemError::EFAULT => ExecError::BadAddress(None), + SystemError::ENOMEM => ExecError::OutOfMemory, + _ => ExecError::Other(format!("load_elf_segment failed: {:?}", e)), + })?; // 如果地址不对,那么就报错 if !e.1 { @@ -722,12 +894,10 @@ impl BinaryLoader for ElfLoader { if elf_type == ElfType::DSO { // todo: 在这里增加对load_bias和reloc_func_desc的更新代码 load_bias += e.0.data() - - self - .elf_page_start(VirtAddr::new( - load_bias - + TryInto::::try_into(seg_to_load.p_vaddr).unwrap(), - )) - .data(); + - Self::elf_page_start(VirtAddr::new( + load_bias + TryInto::::try_into(seg_to_load.p_vaddr).unwrap(), + )) + .data(); _reloc_func_desc = load_bias; } } @@ -761,7 +931,7 @@ impl BinaryLoader for ElfLoader { // 如果程序段要加载的目标地址不在用户空间内,或者是其他不合法的情况,那么就报错 if !p_vaddr.check_user() || seg_to_load.p_filesz > seg_to_load.p_memsz - || self.elf_page_align_up(p_vaddr + seg_to_load.p_memsz as usize) + || Self::elf_page_align_up(p_vaddr + seg_to_load.p_memsz as usize) >= MMArch::USER_END_VADDR { // kdebug!("ERR: p_vaddr={p_vaddr:?}"); @@ -769,7 +939,7 @@ impl BinaryLoader for ElfLoader { } // end vaddr of this segment(code+data+bss) - let seg_end_vaddr_f = self.elf_page_align_up(VirtAddr::new( + let seg_end_vaddr_f = Self::elf_page_align_up(VirtAddr::new( (seg_to_load.p_vaddr + seg_to_load.p_filesz) as usize, )); @@ -807,7 +977,7 @@ impl BinaryLoader for ElfLoader { end_code = end_code.map(|v| v + load_bias); start_data = start_data.map(|v| v + load_bias); end_data = end_data.map(|v| v + load_bias); - + let mut interp_load_addr: Option = None; // kdebug!( // "to set brk: elf_bss: {:?}, elf_brk: {:?}, bss_prot_flags: {:?}", // elf_bss, @@ -816,17 +986,33 @@ impl BinaryLoader for ElfLoader { // ); self.set_elf_brk(&mut user_vm, elf_bss, elf_brk, bss_prot_flags)?; - if likely(elf_bss != elf_brk) && unlikely(self.pad_zero(elf_bss).is_err()) { + if likely(elf_bss != elf_brk) && unlikely(Self::pad_zero(elf_bss).is_err()) { // kdebug!("elf_bss = {elf_bss:?}, elf_brk = {elf_brk:?}"); return Err(ExecError::BadAddress(Some(elf_bss))); } - if interpreter.is_some() { - // TODO 添加对动态加载器的处理 + drop(user_vm); + if let Some(mut interpreter) = interpreter { // 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/fs/binfmt_elf.c#1249 + let elf_entry = Self::load_elf_interp(&mut interpreter, load_bias)?.entry_point(); + interp_load_addr = Some(elf_entry); + _reloc_func_desc = elf_entry.data(); + //参考 https://code.dragonos.org.cn/xref/linux-6.1.9/fs/binfmt_elf.c#1269 + //TODO allow_write_access(interpreter); + ProcessManager::current_pcb() + .fd_table() + .write() + .alloc_fd(interpreter.file(), None) + .map(|fd| fd as usize) + .map_err(|_| ExecError::InvalidParemeter)?; } // kdebug!("to create auxv"); - - self.create_auxv(param, program_entrypoint, phdr_vaddr, &ehdr)?; + let mut user_vm = binding.write(); + self.create_auxv( + param, + interp_load_addr.unwrap_or(program_entrypoint), + phdr_vaddr, + &ehdr, + )?; // kdebug!("auxv create ok"); user_vm.start_code = start_code.unwrap_or(VirtAddr::new(0)); diff --git a/kernel/src/process/exec.rs b/kernel/src/process/exec.rs index 91b7ac27d..3233b06fe 100644 --- a/kernel/src/process/exec.rs +++ b/kernel/src/process/exec.rs @@ -157,6 +157,11 @@ impl ExecParam { pub fn file_mut(&mut self) -> &mut File { &mut self.file } + + /// 获取File的所有权,用于将动态链接器加入文件描述符表中 + pub fn file(self) -> File { + self.file + } } /// ## 加载二进制文件 diff --git a/user/apps/test_glibc/.gitignore b/user/apps/test_glibc/.gitignore new file mode 100644 index 000000000..1ac354611 --- /dev/null +++ b/user/apps/test_glibc/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +/install/ \ No newline at end of file diff --git a/user/apps/test_glibc/Cargo.toml b/user/apps/test_glibc/Cargo.toml new file mode 100644 index 000000000..f59f35209 --- /dev/null +++ b/user/apps/test_glibc/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "glibc-hello-world" +version = "0.1.0" +edition = "2021" +description = "glibc dynamic link test program" +authors = [ "chiichen " ] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] \ No newline at end of file diff --git a/user/apps/test_glibc/LICENSE b/user/apps/test_glibc/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/user/apps/test_glibc/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/user/apps/test_glibc/Makefile b/user/apps/test_glibc/Makefile new file mode 100644 index 000000000..cd7ea39fb --- /dev/null +++ b/user/apps/test_glibc/Makefile @@ -0,0 +1,56 @@ +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" +RUSTFLAGS+="" + +ifdef DADK_CURRENT_BUILD_DIR +# 如果是在dadk中编译,那么安装到dadk的安装目录中 + INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) +else +# 如果是在本地编译,那么安装到当前目录下的install目录中 + INSTALL_DIR = ./install +endif + +ifeq ($(ARCH), x86_64) + export RUST_TARGET=x86_64-unknown-linux-gnu +else ifeq ($(ARCH), riscv64) + export RUST_TARGET=riscv64gc-unknown-linux-gnu +else +# 默认为x86_86,用于本地编译 + export RUST_TARGET=x86_64-unknown-linux-musl +endif + +run: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) + +build: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) + +clean: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) + +test: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) + +doc: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET) + +fmt: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt + +fmt-check: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check + +run-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release + +build-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release + +clean-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release + +test-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release + +.PHONY: install +install: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test_glibc/README.md b/user/apps/test_glibc/README.md new file mode 100644 index 000000000..74b00a908 --- /dev/null +++ b/user/apps/test_glibc/README.md @@ -0,0 +1,14 @@ +# DragonOS Rust-Application Template + +您可以使用此模板来创建DragonOS应用程序。 + +## 使用方法 + +1. 使用DragonOS的tools目录下的`bootstrap.sh`脚本初始化环境 +2. 在终端输入`cargo install cargo-generate` +3. 在终端输入`cargo generate --git https://github.com/DragonOS-Community/Rust-App-Template`即可创建项目 +如果您的网络较慢,请使用镜像站`cargo generate --git https://git.mirrors.dragonos.org/DragonOS-Community/Rust-App-Template` +4. 使用`cargo run`来运行项目 +5. 在DragonOS的`user/dadk/config`目录下,使用`dadk new`命令,创建编译配置,安装到DragonOS的`/`目录下。 +(在dadk的编译命令选项处,请使用Makefile里面的`make install`配置进行编译、安装) +6. 编译DragonOS即可安装 diff --git a/user/apps/test_glibc/src/main.rs b/user/apps/test_glibc/src/main.rs new file mode 100644 index 000000000..aec1f5653 --- /dev/null +++ b/user/apps/test_glibc/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world DragonOS-musl"); +} diff --git a/user/dadk/config/glibc-2.39.9.dadk b/user/dadk/config/glibc-2.39.9.dadk new file mode 100644 index 000000000..456be2f84 --- /dev/null +++ b/user/dadk/config/glibc-2.39.9.dadk @@ -0,0 +1,27 @@ +{ + "name": "glibc", + "version": "2.39.9", + "description": "glibc libc", + "rust_target": null, + "task_type": { + "BuildFromSource": { + "Archive": { + "url": "https://github.com/DragonOS-Community/glibc-mirror/archive/refs/tags/v2.39.9-beta.3.tar.gz" + } + } + }, + "depends": [], + "build": { + "build_command": "bash dragonos.sh" + }, + "install": { + "in_dragonos_path": "/" + }, + "clean": { + "clean_command": "cd build & make clean" + }, + "envs": [], + "build_once": true, + + "install_once": true +} \ No newline at end of file diff --git a/user/dadk/config/test_glibc-0.1.0.dadk b/user/dadk/config/test_glibc-0.1.0.dadk new file mode 100644 index 000000000..58be8fff3 --- /dev/null +++ b/user/dadk/config/test_glibc-0.1.0.dadk @@ -0,0 +1,23 @@ +{ + "name": "glibc-hello-world", + "version": "0.1.0", + "description": "用于测试glibc功能的测试程序", + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test_glibc" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/bin" + }, + "clean": { + "clean_command": "make clean" + }, + "envs": [] +} From 80ca935a8d9e86ba4f2a5ed6935e648d2bbedf34 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Fri, 24 May 2024 03:40:49 +0800 Subject: [PATCH 05/60] 20240524 3:40 --- kernel/src/arch/riscv64/mm/mod.rs | 6 +- kernel/src/arch/x86_64/kvm/vmx/ept.rs | 4 +- kernel/src/arch/x86_64/kvm/vmx/mmu.rs | 4 +- kernel/src/arch/x86_64/mm/mod.rs | 10 +- kernel/src/driver/net/dma.rs | 4 +- kernel/src/driver/video/mod.rs | 4 +- kernel/src/driver/virtio/virtio_impl.rs | 4 +- kernel/src/filesystem/vfs/file.rs | 54 ++++++- kernel/src/filesystem/vfs/mod.rs | 11 +- kernel/src/filesystem/vfs/mount.rs | 1 + kernel/src/ipc/shm.rs | 2 +- kernel/src/ipc/syscall.rs | 8 +- kernel/src/mm/c_adapter.rs | 4 +- kernel/src/mm/fault.rs | 194 ++++++++++++++++++++---- kernel/src/mm/kernel_mapper.rs | 4 +- kernel/src/mm/mmio_buddy.rs | 6 +- kernel/src/mm/mod.rs | 2 +- kernel/src/mm/no_init.rs | 8 +- kernel/src/mm/page.rs | 90 +++++++---- kernel/src/mm/ucontext.rs | 53 ++++--- kernel/src/virt/kvm/host_mem.rs | 4 +- 21 files changed, 366 insertions(+), 111 deletions(-) diff --git a/kernel/src/arch/riscv64/mm/mod.rs b/kernel/src/arch/riscv64/mm/mod.rs index 76bfbed04..c157ba556 100644 --- a/kernel/src/arch/riscv64/mm/mod.rs +++ b/kernel/src/arch/riscv64/mm/mod.rs @@ -12,7 +12,7 @@ use crate::{ page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage, PhysPageFrame}, }, kernel_mapper::KernelMapper, - page::{PageEntry, PageFlags, PAGE_1G_SHIFT}, + page::{PageEntry, EntryFlags, PAGE_1G_SHIFT}, ucontext::UserMapper, MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, }, @@ -270,8 +270,8 @@ impl VirtAddr { } /// 获取内核地址默认的页面标志 -pub unsafe fn kernel_page_flags(_virt: VirtAddr) -> PageFlags { - PageFlags::from_data(RiscV64MMArch::ENTRY_FLAG_DEFAULT_PAGE) +pub unsafe fn kernel_page_flags(_virt: VirtAddr) -> EntryFlags { + EntryFlags::from_data(RiscV64MMArch::ENTRY_FLAG_DEFAULT_PAGE) .set_user(false) .set_execute(true) } diff --git a/kernel/src/arch/x86_64/kvm/vmx/ept.rs b/kernel/src/arch/x86_64/kvm/vmx/ept.rs index 032231902..838c1a159 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/ept.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/ept.rs @@ -1,7 +1,7 @@ use crate::arch::mm::LockedFrameAllocator; use crate::arch::mm::PageMapper; use crate::arch::MMArch; -use crate::mm::page::PageFlags; +use crate::mm::page::EntryFlags; use crate::mm::{PageTableKind, PhysAddr, VirtAddr}; use crate::smp::core::smp_get_processor_id; use crate::smp::cpu::AtomicProcessorId; @@ -92,7 +92,7 @@ impl EptMapper { &mut self, gpa: u64, hpa: u64, - flags: PageFlags, + flags: EntryFlags, ) -> Result<(), SystemError> { if self.readonly { return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); diff --git a/kernel/src/arch/x86_64/kvm/vmx/mmu.rs b/kernel/src/arch/x86_64/kvm/vmx/mmu.rs index 2c03c2383..c93be63c0 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/mmu.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/mmu.rs @@ -2,7 +2,7 @@ use crate::{ arch::kvm::vmx::ept::EptMapper, kdebug, libs::mutex::Mutex, - mm::{page::PageFlags, syscall::ProtFlags}, + mm::{page::EntryFlags, syscall::ProtFlags}, virt::kvm::host_mem::{__gfn_to_pfn, kvm_vcpu_gfn_to_memslot, PAGE_MASK, PAGE_SHIFT}, }; use bitfield_struct::bitfield; @@ -218,7 +218,7 @@ pub fn __direct_map( } // 把gpa映射到hpa let mut ept_mapper = EptMapper::lock(); - let page_flags = PageFlags::from_prot_flags(ProtFlags::from_bits_truncate(0x7_u64), false); + let page_flags = EntryFlags::from_prot_flags(ProtFlags::from_bits_truncate(0x7_u64), false); unsafe { assert!(ept_mapper.walk(gpa, pfn << PAGE_SHIFT, page_flags).is_ok()); } diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index bfc491eff..18c210a8e 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -27,7 +27,7 @@ use crate::{ }; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{PageEntry, PageFlags, PAGE_1G_SHIFT}; +use crate::mm::page::{PageEntry, EntryFlags, PAGE_1G_SHIFT}; use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr}; use crate::{kdebug, kinfo, kwarn}; use system_error::SystemError; @@ -651,17 +651,17 @@ impl FrameAllocator for LockedFrameAllocator { } /// 获取内核地址默认的页面标志 -pub unsafe fn kernel_page_flags(virt: VirtAddr) -> PageFlags { +pub unsafe fn kernel_page_flags(virt: VirtAddr) -> EntryFlags { let info: X86_64MMBootstrapInfo = BOOTSTRAP_MM_INFO.unwrap(); if virt.data() >= info.kernel_code_start && virt.data() < info.kernel_code_end { // Remap kernel code execute - return PageFlags::new().set_execute(true).set_write(true); + return EntryFlags::new().set_execute(true).set_write(true); } else if virt.data() >= info.kernel_data_end && virt.data() < info.kernel_rodata_end { // Remap kernel rodata read only - return PageFlags::new().set_execute(true); + return EntryFlags::new().set_execute(true); } else { - return PageFlags::new().set_write(true).set_execute(true); + return EntryFlags::new().set_write(true).set_execute(true); } } diff --git a/kernel/src/driver/net/dma.rs b/kernel/src/driver/net/dma.rs index 11fcf6229..f8c06b74e 100644 --- a/kernel/src/driver/net/dma.rs +++ b/kernel/src/driver/net/dma.rs @@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags; use crate::arch::MMArch; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, PageFlags}; +use crate::mm::page::{page_manager_lock_irqsave, EntryFlags}; use crate::mm::{ allocator::page_frame::{ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, @@ -25,7 +25,7 @@ pub fn dma_alloc(pages: usize) -> (usize, NonNull) { // 清空这块区域,防止出现脏数据 core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); - let dma_flags: PageFlags = PageFlags::mmio_flags(); + let dma_flags: EntryFlags = EntryFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let kernel_mapper = kernel_mapper.as_mut().unwrap(); diff --git a/kernel/src/driver/video/mod.rs b/kernel/src/driver/video/mod.rs index 6a8c406d9..88618578d 100644 --- a/kernel/src/driver/video/mod.rs +++ b/kernel/src/driver/video/mod.rs @@ -11,7 +11,7 @@ use crate::{ spinlock::SpinLock, }, mm::{ - allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::PageFlags, + allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::EntryFlags, MemoryManagementArch, }, time::timer::{Timer, TimerFunction}, @@ -95,7 +95,7 @@ impl VideoRefreshManager { let count = PageFrameCount::new( page_align_up(frame_buffer_info_guard.buf_size()) / MMArch::PAGE_SIZE, ); - let page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); + let page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); let mut kernel_mapper = KernelMapper::lock(); let mut kernel_mapper = kernel_mapper.as_mut(); diff --git a/kernel/src/driver/virtio/virtio_impl.rs b/kernel/src/driver/virtio/virtio_impl.rs index 92aa07cc1..c0820ab4c 100644 --- a/kernel/src/driver/virtio/virtio_impl.rs +++ b/kernel/src/driver/virtio/virtio_impl.rs @@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags; use crate::arch::MMArch; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, PageFlags}; +use crate::mm::page::{page_manager_lock_irqsave, EntryFlags}; use crate::mm::{ allocator::page_frame::{ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, @@ -32,7 +32,7 @@ unsafe impl Hal for HalImpl { // 清空这块区域,防止出现脏数据 core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); - let dma_flags: PageFlags = PageFlags::mmio_flags(); + let dma_flags: EntryFlags = EntryFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let kernel_mapper = kernel_mapper.as_mut().unwrap(); diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index c867d4ca7..e71f975dc 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -5,6 +5,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use hashbrown::HashMap; use system_error::SystemError; use crate::{ @@ -12,10 +13,11 @@ use crate::{ base::{block::SeekFrom, device::DevicePrivateData}, tty::tty_device::TtyFilePrivateData, }, - filesystem::procfs::ProcfsFilePrivateData, + filesystem::{fat::fs::LockedFATInode, procfs::ProcfsFilePrivateData}, ipc::pipe::{LockedPipeInode, PipeFsPrivateData}, kerror, libs::{rwlock::RwLock, spinlock::SpinLock}, + mm::page::Page, net::{ event_poll::{EPollItem, EPollPrivateData, EventPoll}, socket::SocketInode, @@ -23,7 +25,7 @@ use crate::{ process::ProcessManager, }; -use super::{Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; +use super::{mount::MountFSInode, Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; /// 文件私有信息的枚举类型 #[derive(Debug, Clone)] @@ -118,6 +120,54 @@ impl FileMode { return self.bits() & FileMode::O_ACCMODE.bits(); } } + +pub struct PageCache { + inode_ref: Weak, + map: HashMap>, +} + +impl PageCache { + pub fn new(inode_ref: Weak) -> PageCache { + Self { + inode_ref, + map: HashMap::new(), + } + } + + pub fn set_page(&mut self, offset: usize, page: Arc) { + self.map.insert(offset, page); + } + + pub fn get_page(&self, offset: usize) -> Option> { + self.map.get(&offset).map(|page| page.clone()) + } + + // pub fn get_pages(&self, start_pgoff: usize, end_pgoff: usize) -> Vec> { + // let mut vec = Vec::new(); + // for pgoff in start_pgoff..=end_pgoff { + // if let Some(page) = self.map.get(&pgoff) { + // vec.push(page.clone()); + // } + // } + // vec + // } +} + +pub trait PageCacheOperations: IndexNode { + fn write_page(&self, page: Page); + fn read_ahead(&self); +} + +impl PageCacheOperations for LockedFATInode { + fn write_page(&self, page: Page) { + todo!() + } + + fn read_ahead(&self) { + todo!() + } +} + /// @brief 抽象文件结构体 #[derive(Debug)] pub struct File { diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 482d4a884..dbd248618 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -23,7 +23,12 @@ use crate::{ time::PosixTimeSpec, }; -use self::{core::generate_inode_id, file::FileMode, syscall::ModeType, utils::DName}; +use self::{ + core::generate_inode_id, + file::{FileMode, PageCache}, + syscall::ModeType, + utils::DName, +}; pub use self::{core::ROOT_INODE, file::FilePrivateData, mount::MountFS}; /// vfs容许的最大的路径名称长度 @@ -548,6 +553,10 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { fn parent(&self) -> Result, SystemError> { return self.find(".."); } + + fn page_cache(&self) -> Option { + None + } } impl DowncastArc for dyn IndexNode { diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index 7d7515833..6f381bc40 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -296,6 +296,7 @@ impl IndexNode for MountFSInode { #[inline] fn fs(&self) -> Arc { + kdebug!("{}", self.inner_inode.fs().name()); return self.mount_fs.clone(); } diff --git a/kernel/src/ipc/shm.rs b/kernel/src/ipc/shm.rs index 6e4b7224d..62cb51981 100644 --- a/kernel/src/ipc/shm.rs +++ b/kernel/src/ipc/shm.rs @@ -164,7 +164,7 @@ impl ShmManager { let mut page_manager_guard = page_manager_lock_irqsave(); let mut cur_phys = PhysPageFrame::new(phys_page.0); for _ in 0..page_count.data() { - let mut page = Page::new(true); + let mut page = Page::new(true, cur_phys); page.set_shm_id(shm_id); let paddr = cur_phys.phys_address(); page_manager_guard.insert(paddr, page); diff --git a/kernel/src/ipc/syscall.rs b/kernel/src/ipc/syscall.rs index 995dcfbd5..1d9420db1 100644 --- a/kernel/src/ipc/syscall.rs +++ b/kernel/src/ipc/syscall.rs @@ -20,7 +20,7 @@ use crate::{ libs::spinlock::SpinLock, mm::{ allocator::page_frame::{PageFrameCount, PhysPageFrame, VirtPageFrame}, - page::{page_manager_lock_irqsave, PageFlags, PageFlushAll}, + page::{page_manager_lock_irqsave, EntryFlags, PageFlushAll}, syscall::ProtFlags, ucontext::{AddressSpace, VMA}, VirtAddr, VmFlags, @@ -324,8 +324,8 @@ impl Syscall { .ok_or(SystemError::EINVAL)?; let vm_flags = VmFlags::from(shmflg); let destination = VirtPageFrame::new(region.start()); - let page_flags: PageFlags = - PageFlags::from_prot_flags(ProtFlags::from(vm_flags), true); + let page_flags: EntryFlags = + EntryFlags::from_prot_flags(ProtFlags::from(vm_flags), true); let flusher: PageFlushAll = PageFlushAll::new(); // 将共享内存映射到对应虚拟区域 @@ -358,7 +358,7 @@ impl Syscall { // 验证用户虚拟内存区域是否有效 let _ = UserBufferReader::new(vaddr.data() as *const u8, size, true)?; - // 必须在取消映射前获取到PageFlags + // 必须在取消映射前获取到EntryFlags let page_flags = address_write_guard .user_mapper .utable diff --git a/kernel/src/mm/c_adapter.rs b/kernel/src/mm/c_adapter.rs index 35ac24de9..d519c96ce 100644 --- a/kernel/src/mm/c_adapter.rs +++ b/kernel/src/mm/c_adapter.rs @@ -15,7 +15,7 @@ use crate::{ use super::{ allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, mmio_buddy::mmio_pool, - no_init::pseudo_map_phys, page::PageFlags, MemoryManagementArch, PhysAddr, VirtAddr, + no_init::pseudo_map_phys, page::EntryFlags, MemoryManagementArch, PhysAddr, VirtAddr, }; lazy_static! { @@ -40,7 +40,7 @@ pub unsafe extern "C" fn rs_map_phys(vaddr: usize, paddr: usize, size: usize, fl let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); // kdebug!("rs_map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); - let mut page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); + let mut page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); if flags & PAGE_U_S as usize != 0 { page_flags = page_flags.set_user(true); } diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 71e0623ae..d26fa492a 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -1,11 +1,11 @@ -use core::{alloc::Layout, intrinsics::unlikely, panic}; +use core::{alloc::Layout, cmp::min, intrinsics::unlikely, panic}; use alloc::sync::Arc; use crate::{ arch::{mm::PageMapper, MMArch}, mm::{ - page::{page_manager_lock_irqsave, PageFlags}, + page::{page_manager_lock_irqsave, EntryFlags}, ucontext::LockedVMA, VirtAddr, VmFaultReason, VmFlags, }, @@ -14,21 +14,26 @@ use crate::{ use crate::mm::MemoryManagementArch; +use super::{ + page::{self, PageFlags}, + phys_2_virt, +}; + bitflags! { pub struct FaultFlags: u64{ - const FAULT_FLAG_WRITE = 1 << 0; - const FAULT_FLAG_MKWRITE = 1 << 1; - const FAULT_FLAG_ALLOW_RETRY = 1 << 2; - const FAULT_FLAG_RETRY_NOWAIT = 1 << 3; - const FAULT_FLAG_KILLABLE = 1 << 4; - const FAULT_FLAG_TRIED = 1 << 5; - const FAULT_FLAG_USER = 1 << 6; - const FAULT_FLAG_REMOTE = 1 << 7; - const FAULT_FLAG_INSTRUCTION = 1 << 8; - const FAULT_FLAG_INTERRUPTIBLE =1 << 9; - const FAULT_FLAG_UNSHARE = 1 << 10; - const FAULT_FLAG_ORIG_PTE_VALID = 1 << 11; - const FAULT_FLAG_VMA_LOCK = 1 << 12; + const FAULT_FLAG_WRITE = 1 << 0; + const FAULT_FLAG_MKWRITE = 1 << 1; + const FAULT_FLAG_ALLOW_RETRY = 1 << 2; + const FAULT_FLAG_RETRY_NOWAIT = 1 << 3; + const FAULT_FLAG_KILLABLE = 1 << 4; + const FAULT_FLAG_TRIED = 1 << 5; + const FAULT_FLAG_USER = 1 << 6; + const FAULT_FLAG_REMOTE = 1 << 7; + const FAULT_FLAG_INSTRUCTION = 1 << 8; + const FAULT_FLAG_INTERRUPTIBLE =1 << 9; + const FAULT_FLAG_UNSHARE = 1 << 10; + const FAULT_FLAG_ORIG_PTE_VALID = 1 << 11; + const FAULT_FLAG_VMA_LOCK = 1 << 12; } } @@ -36,9 +41,14 @@ bitflags! { /// 包含了页面错误处理的相关信息,例如出错的地址、VMA等 #[derive(Debug)] pub struct PageFaultMessage { + /// 产生缺页的VMA结构体 vma: Arc, + /// 缺页地址 address: VirtAddr, + /// 异常处理标志 flags: FaultFlags, + /// 缺页的文件页在文件中的偏移量 + file_pgoff: usize, } impl PageFaultMessage { @@ -47,6 +57,8 @@ impl PageFaultMessage { vma: vma.clone(), address, flags, + file_pgoff: ((address - vma.lock().region().start()) >> MMArch::PAGE_SHIFT) + + vma.lock().file_page_offset().unwrap(), } } @@ -58,8 +70,8 @@ impl PageFaultMessage { #[inline(always)] #[allow(dead_code)] - pub fn address(&self) -> VirtAddr { - self.address + pub fn address(&self) -> &VirtAddr { + &self.address } #[inline(always)] @@ -70,8 +82,8 @@ impl PageFaultMessage { #[inline(always)] #[allow(dead_code)] - pub fn flags(&self) -> FaultFlags { - self.flags + pub fn flags(&self) -> &FaultFlags { + &self.flags } } @@ -81,6 +93,7 @@ impl Clone for PageFaultMessage { vma: self.vma.clone(), address: self.address, flags: self.flags, + file_pgoff: self.file_pgoff, } } } @@ -189,7 +202,7 @@ impl PageFaultHandler { if !entry.write() { ret = Self::do_wp_page(pfm.clone(), mapper); } else { - entry.set_flags(PageFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); + entry.set_flags(EntryFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); } } } else if vma.is_anonymous() { @@ -248,14 +261,22 @@ impl PageFaultHandler { /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] pub unsafe fn do_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - panic!( - "do_fault has not yet been implemented, - fault message: {:?}, - pid: {}\n", - pfm, - crate::process::ProcessManager::current_pid().data() - ); + // panic!( + // "do_fault has not yet been implemented, + // fault message: {:?}, + // pid: {}\n", + // pfm, + // crate::process::ProcessManager::current_pid().data() + // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_fault + + if !pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) { + return Self::do_read_fault(pfm, mapper); + } else if !pfm.vma().lock().vm_flags().contains(VmFlags::VM_SHARED) { + return Self::do_cow_fault(pfm, mapper); + } else { + return Self::do_shared_fault(pfm, mapper); + } } /// 处理私有文件映射的写时复制 @@ -295,7 +316,12 @@ impl PageFaultHandler { pfm, crate::process::ProcessManager::current_pid().data() ); + // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_read_fault + let mut ret = VmFaultReason::empty(); + ret = Self::do_fault_around(pfm, mapper); + ret = Self::filemap_fault(pfm, mapper); + return ret; } /// 处理对共享文件映射区写入引起的缺页 @@ -405,4 +431,118 @@ impl PageFaultHandler { VmFaultReason::VM_FAULT_OOM } } + + pub unsafe fn do_fault_around(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + mapper + .allocate_table(*pfm.address(), 0) + .expect("failed to allocate pte table"); + let vma = pfm.vma(); + let vma_guard = vma.lock(); + let vma_region = vma_guard.region(); + // 缺页在VMA中的偏移量 + let vm_pgoff = (*pfm.address() - vma_region.start()) >> MMArch::PAGE_SHIFT; + // 缺页在PTE中的偏移量 + let pte_pgoff = (pfm.address().data() >> MMArch::PAGE_SHIFT) & (1 << MMArch::PAGE_SIZE); + + let vma_pages_count = (vma_region.end() - vma_region.start()) >> MMArch::PAGE_SHIFT; + + // 开始位置不能超出当前pte和vma头部的最小值 + let from_pte = pte_pgoff - min(vm_pgoff, pte_pgoff); + + let fault_around_page_number = 16; + + // pte结束位置不能超过: + // 1.最大预读上限(默认16) + // 2.最大pte(512) + // 3.vma结束位置(pte_pgoff + (vma_pages_count - vm_pgoff)计算出vma结束页号对当前pte开头的偏移) + let to_pte = min( + from_pte + fault_around_page_number, + min( + 1 << MMArch::PAGE_SHIFT, + pte_pgoff + (vma_pages_count - vm_pgoff), + ), + ); + + // 预先分配pte页表(如果不存在) + if mapper.get_table(*pfm.address(), 0).is_none() { + if mapper.allocate_table(*pfm.address(), 0).is_none() { + return VmFaultReason::VM_FAULT_OOM; + } + }; + + // from_pte - pte_pgoff得出预读起始pte相对缺失页的偏移,加上pfm.file_pgoff(缺失页在文件中的偏移)得出起始页在文件中的偏移,结束pte同理 + Self::filemap_map_pages( + pfm.clone(), + mapper, + pfm.file_pgoff + (from_pte - pte_pgoff), + pfm.file_pgoff + (to_pte - pte_pgoff), + ); + + VmFaultReason::empty() + } + + pub unsafe fn filemap_map_pages( + pfm: PageFaultMessage, + mapper: &mut PageMapper, + start_pgoff: usize, + end_pgoff: usize, + ) -> VmFaultReason { + let vma = pfm.vma(); + let vma_guard = vma.lock(); + let file = vma_guard.vm_file().expect("no vm_file in vma"); + let page_cache = file.inode().page_cache().unwrap(); + + // 起始页地址 + let addr = vma_guard.region().start + + ((start_pgoff + - vma_guard + .file_page_offset() + .expect("file_page_offset is none")) + << MMArch::PAGE_SHIFT); + // let pages = page_cache.get_pages(start_pgoff, end_pgoff); + // let uptodate_pages = pages + // .iter() + // .filter(|page| page.flags().contains(PageFlags::PG_UPTODATE)); + for pgoff in start_pgoff..=end_pgoff { + if let Some(page) = page_cache.get_page(pgoff) { + if page.flags().contains(PageFlags::PG_UPTODATE) { + let phys = page.phys_frame().phys_address(); + let virt = phys_2_virt(phys.data()); + + let address = + VirtAddr::new(addr.data() + ((pgoff - start_pgoff) << MMArch::PAGE_SHIFT)); + mapper.map(address, vma_guard.flags()).unwrap().flush(); + let frame = virt as *mut u8; + let new_frame = + phys_2_virt(mapper.translate(address).unwrap().0.data()) as *mut u8; + frame.copy_from_nonoverlapping(new_frame, MMArch::PAGE_SIZE); + } + } + } + VmFaultReason::empty() + } + + pub unsafe fn filemap_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + let vma = pfm.vma(); + let vma_guard = vma.lock(); + let file = vma_guard.vm_file().expect("no vm_file in vma"); + let page_cache = file.inode().page_cache().unwrap(); + + if let Some(page) = page_cache.get_page(pfm.file_pgoff) { + // TODO 异步从磁盘中预读页面进PageCache + let address = vma_guard.region().start + + (pfm.file_pgoff + - vma_guard + .file_page_offset() + .expect("file_page_offset is none") + << MMArch::PAGE_SHIFT); + mapper.map(address, vma_guard.flags()).unwrap().flush(); + let frame = phys_2_virt(page.phys_frame().phys_address().data()) as *mut u8; + let new_frame = phys_2_virt(mapper.translate(address).unwrap().0.data()) as *mut u8; + frame.copy_from_nonoverlapping(new_frame, MMArch::PAGE_SIZE); + } else { + // TODO 同步预读 + } + VmFaultReason::VM_FAULT_COMPLETED + } } diff --git a/kernel/src/mm/kernel_mapper.rs b/kernel/src/mm/kernel_mapper.rs index 02436189d..e98fc6fa2 100644 --- a/kernel/src/mm/kernel_mapper.rs +++ b/kernel/src/mm/kernel_mapper.rs @@ -1,6 +1,6 @@ use system_error::SystemError; -use super::{page::PageFlags, PageTableKind, PhysAddr, VirtAddr}; +use super::{page::EntryFlags, PageTableKind, PhysAddr, VirtAddr}; use crate::{ arch::{ mm::{LockedFrameAllocator, PageMapper}, @@ -104,7 +104,7 @@ impl KernelMapper { mut vaddr: VirtAddr, mut paddr: PhysAddr, size: usize, - flags: PageFlags, + flags: EntryFlags, flush: bool, ) -> Result<(), SystemError> { if self.readonly { diff --git a/kernel/src/mm/mmio_buddy.rs b/kernel/src/mm/mmio_buddy.rs index 0c5005721..be9bbaac8 100644 --- a/kernel/src/mm/mmio_buddy.rs +++ b/kernel/src/mm/mmio_buddy.rs @@ -14,7 +14,7 @@ use core::mem::MaybeUninit; use core::sync::atomic::{AtomicBool, Ordering}; use system_error::SystemError; -use super::page::{PageFlags, PAGE_4K_SIZE}; +use super::page::{EntryFlags, PAGE_4K_SIZE}; use super::{PhysAddr, VirtAddr}; // 最大的伙伴块的幂 @@ -551,7 +551,7 @@ impl MmioBuddyMemPool { unsafe { let x: Option<( PhysAddr, - PageFlags, + EntryFlags, crate::mm::page::PageFlush, )> = kernel_mapper .as_mut() @@ -676,7 +676,7 @@ impl MMIOSpaceGuard { return Err(SystemError::EINVAL); } - let flags = PageFlags::mmio_flags(); + let flags = EntryFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let r = kernel_mapper.map_phys_with_size(self.vaddr, paddr, length, flags, true); diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index 49e61fcd1..bfd051933 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -610,7 +610,7 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { /// 创建页表项 /// - /// 这是一个低阶api,用于根据物理地址以及指定好的pageflags,创建页表项 + /// 这是一个低阶api,用于根据物理地址以及指定好的EntryFlags,创建页表项 /// /// ## 参数 /// diff --git a/kernel/src/mm/no_init.rs b/kernel/src/mm/no_init.rs index 855a86767..fdb8d4d66 100644 --- a/kernel/src/mm/no_init.rs +++ b/kernel/src/mm/no_init.rs @@ -19,7 +19,7 @@ use core::marker::PhantomData; use super::{ allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage}, - page::PageFlags, + page::EntryFlags, PageTableKind, VirtAddr, }; @@ -141,7 +141,7 @@ impl FrameAllocator for PseudoAllocator { /// 并且,内核引导文件必须以4K页为粒度,填写了前100M的内存映射关系。(具体以本文件开头的注释为准) #[inline(never)] pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { - let flags: PageFlags = PageFlags::new().set_write(true); + let flags: EntryFlags = EntryFlags::new().set_write(true); pseudo_map_phys_with_flags(vaddr, paddr, count, flags); } @@ -150,7 +150,7 @@ pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrame /// with READ_ONLY and EXECUTE flags. #[inline(never)] pub unsafe fn pseudo_map_phys_ro(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { - let flags: PageFlags = PageFlags::new().set_write(false).set_execute(true); + let flags: EntryFlags = EntryFlags::new().set_write(false).set_execute(true); pseudo_map_phys_with_flags(vaddr, paddr, count, flags); } @@ -160,7 +160,7 @@ pub unsafe fn pseudo_map_phys_with_flags( vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount, - flags: PageFlags, + flags: EntryFlags, ) { assert!(vaddr.check_aligned(MMArch::PAGE_SIZE)); assert!(paddr.check_aligned(MMArch::PAGE_SIZE)); diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 0bc5d5b51..456bd9ca6 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -18,7 +18,7 @@ use crate::{ }; use super::{ - allocator::page_frame::{FrameAllocator, PageFrameCount}, + allocator::page_frame::{FrameAllocator, PageFrameCount, PhysPageFrame}, syscall::ProtFlags, ucontext::LockedVMA, MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, @@ -86,6 +86,27 @@ impl PageManager { } } +bitflags! { + pub struct PageFlags: u64 { + const PG_LOCKED = 1 << 0; + const PG_WRITEBACK = 1 << 1; + const PG_REFERENCED = 1 << 2; + const PG_UPTODATE = 1 << 3; + const PG_DIRTY = 1 << 4; + const PG_LRU = 1 << 5; + const PG_HEAD = 1 << 6; + const PG_WAITERS = 1 << 7; + const PG_ACTIVE = 1 << 8; + const PG_WORKINGSET = 1 << 9; + const PG_ERROR = 1 << 10; + const PG_SLAB = 1 << 11; + const PG_RESERVED = 1 << 14; + const PG_PRIVATE = 1 << 15; + const PG_RECLAIM = 1 << 18; + const PG_SWAPBACKED = 1 << 19; + } +} + /// 物理页面信息 pub struct Page { /// 映射计数 @@ -98,10 +119,14 @@ pub struct Page { shm_id: Option, /// 映射到当前page的VMA anon_vma: HashSet>, + /// 标志 + flags: PageFlags, + /// 页所在的物理页帧号 + phys_frame: PhysPageFrame, } impl Page { - pub fn new(shared: bool) -> Self { + pub fn new(shared: bool, phys_frame: PhysPageFrame) -> Self { let dealloc_when_zero = !shared; Self { map_count: 0, @@ -109,6 +134,8 @@ impl Page { free_when_zero: dealloc_when_zero, shm_id: None, anon_vma: HashSet::new(), + flags: PageFlags::empty(), + phys_frame, } } @@ -154,6 +181,16 @@ impl Page { pub fn map_count(&self) -> usize { self.map_count } + + #[inline(always)] + pub fn flags(&self) -> &PageFlags { + &self.flags + } + + #[inline(always)] + pub fn phys_frame(&self) -> &PhysPageFrame { + &self.phys_frame + } } #[derive(Debug)] @@ -330,7 +367,7 @@ impl PageTable { } else { let phys = allocator.allocate_one()?; let mut anon_vma_guard = page_manager_lock_irqsave(); - anon_vma_guard.insert(phys, Page::new(false)); + anon_vma_guard.insert(phys, Page::new(false, PhysPageFrame::new(phys))); let old_phys = entry.address().unwrap(); let frame = MMArch::phys_2_virt(phys).unwrap().data() as *mut u8; frame.copy_from_nonoverlapping( @@ -372,7 +409,7 @@ impl Debug for PageEntry { impl PageEntry { #[inline(always)] - pub fn new(paddr: PhysAddr, flags: PageFlags) -> Self { + pub fn new(paddr: PhysAddr, flags: EntryFlags) -> Self { Self { data: MMArch::make_entry(paddr, flags.data()), phantom: PhantomData, @@ -420,12 +457,12 @@ impl PageEntry { } #[inline(always)] - pub fn flags(&self) -> PageFlags { - unsafe { PageFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } + pub fn flags(&self) -> EntryFlags { + unsafe { EntryFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } } #[inline(always)] - pub fn set_flags(&mut self, flags: PageFlags) { + pub fn set_flags(&mut self, flags: EntryFlags) { self.data = (self.data & !Arch::ENTRY_FLAGS_MASK) | flags.data(); } @@ -453,13 +490,13 @@ impl PageEntry { /// 页表项的标志位 #[derive(Copy, Clone, Hash)] -pub struct PageFlags { +pub struct EntryFlags { data: usize, phantom: PhantomData, } #[allow(dead_code)] -impl PageFlags { +impl EntryFlags { #[inline(always)] pub fn new() -> Self { let mut r = unsafe { @@ -480,14 +517,14 @@ impl PageFlags { return r; } - /// 根据ProtFlags生成PageFlags + /// 根据ProtFlags生成EntryFlags /// /// ## 参数 /// /// - prot_flags: 页的保护标志 /// - user: 用户空间是否可访问 - pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> PageFlags { - let flags: PageFlags = PageFlags::new() + pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> EntryFlags { + let flags: EntryFlags = EntryFlags::new() .set_user(user) .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); @@ -757,9 +794,9 @@ impl PageFlags { } } -impl fmt::Debug for PageFlags { +impl fmt::Debug for EntryFlags { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PageFlags") + f.debug_struct("EntryFlags") .field("bits", &format_args!("{:#0x}", self.data)) .field("present", &self.present()) .field("has_write", &self.has_write()) @@ -855,7 +892,7 @@ impl PageMapper { pub unsafe fn map( &mut self, virt: VirtAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { compiler_fence(Ordering::SeqCst); let phys: PhysAddr = self.frame_allocator.allocate_one()?; @@ -864,7 +901,7 @@ impl PageMapper { let mut page_manager_guard: SpinLockGuard<'static, PageManager> = page_manager_lock_irqsave(); if !page_manager_guard.contains(&phys) { - page_manager_guard.insert(phys, Page::new(false)) + page_manager_guard.insert(phys, Page::new(false, PhysPageFrame::new(phys))) } return self.map_phys(virt, phys, flags); @@ -875,7 +912,7 @@ impl PageMapper { &mut self, virt: VirtAddr, phys: PhysAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { // 验证虚拟地址和物理地址是否对齐 if !(virt.check_aligned(Arch::PAGE_SIZE) && phys.check_aligned(Arch::PAGE_SIZE)) { @@ -916,8 +953,8 @@ impl PageMapper { // 清空这个页帧 MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); // 设置页表项的flags - let flags: PageFlags = - PageFlags::new_page_table(virt.kind() == PageTableKind::User); + let flags: EntryFlags = + EntryFlags::new_page_table(virt.kind() == PageTableKind::User); // 把新分配的页表映射到当前页表 table.set_entry(i, PageEntry::new(frame, flags)); @@ -933,7 +970,7 @@ impl PageMapper { pub unsafe fn map_huge_page( &mut self, virt: VirtAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { // 验证虚拟地址是否对齐 if !(virt.check_aligned(Arch::PAGE_SIZE)) { @@ -999,7 +1036,8 @@ impl PageMapper { MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); // 设置页表项的flags - let flags: PageFlags = PageFlags::new_page_table(virt.kind() == PageTableKind::User); + let flags: EntryFlags = + EntryFlags::new_page_table(virt.kind() == PageTableKind::User); table.set_entry(i, PageEntry::new(frame, flags)); table.next_level_table(i) @@ -1101,7 +1139,7 @@ impl PageMapper { pub unsafe fn map_linearly( &mut self, phys: PhysAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option<(VirtAddr, PageFlush)> { let virt: VirtAddr = Arch::phys_2_virt(phys)?; return self.map_phys(virt, phys, flags).map(|flush| (virt, flush)); @@ -1121,7 +1159,7 @@ impl PageMapper { pub unsafe fn remap( &mut self, virt: VirtAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { return self .visit(virt, |p1, i| { @@ -1143,7 +1181,7 @@ impl PageMapper { /// ## 返回值 /// /// 如果查找成功,返回物理地址和页表项的flags,否则返回None - pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, PageFlags)> { + pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, EntryFlags)> { let entry: PageEntry = self.visit(virt, |p1, i| unsafe { p1.entry(i) })??; let paddr = entry.address().ok()?; let flags = entry.flags(); @@ -1182,7 +1220,7 @@ impl PageMapper { &mut self, virt: VirtAddr, unmap_parents: bool, - ) -> Option<(PhysAddr, PageFlags, PageFlush)> { + ) -> Option<(PhysAddr, EntryFlags, PageFlush)> { if !virt.check_aligned(Arch::PAGE_SIZE) { kerror!("Try to unmap unaligned page: virt={:?}", virt); return None; @@ -1230,7 +1268,7 @@ unsafe fn unmap_phys_inner( table: &PageTable, unmap_parents: bool, allocator: &mut impl FrameAllocator, -) -> Option<(PhysAddr, PageFlags)> { +) -> Option<(PhysAddr, EntryFlags)> { // 获取页表项的索引 let i = table.index_of(vaddr)?; diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 8d030e2b6..4dd350082 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -20,6 +20,7 @@ use system_error::SystemError; use crate::{ arch::{mm::PageMapper, CurrentIrqArch, MMArch}, exception::InterruptArch, + filesystem::vfs::file::File, libs::{ align::page_align_up, rwlock::RwLock, @@ -34,7 +35,7 @@ use super::{ allocator::page_frame::{ deallocate_page_frames, PageFrameCount, PhysPageFrame, VirtPageFrame, VirtPageFrameIter, }, - page::{Flusher, InactiveFlusher, PageFlags, PageFlushAll}, + page::{EntryFlags, Flusher, InactiveFlusher, PageFlushAll}, syscall::{MadvFlags, MapFlags, MremapFlags, ProtFlags}, MemoryManagementArch, PageTableKind, VirtAddr, VirtRegion, VmFlags, }; @@ -333,7 +334,7 @@ impl InnerAddressSpace { F: FnOnce( VirtPageFrame, PageFrameCount, - PageFlags, + EntryFlags, &mut PageMapper, &mut dyn Flusher, ) -> Result, SystemError>, @@ -380,7 +381,7 @@ impl InnerAddressSpace { self.mappings.insert_vma(map_func( page, page_count, - PageFlags::from_prot_flags(prot_flags, true), + EntryFlags::from_prot_flags(prot_flags, true), &mut self.user_mapper.utable, flusher, )?); @@ -556,7 +557,7 @@ impl InnerAddressSpace { return Err(SystemError::EACCES); } - let new_flags: PageFlags = r_guard + let new_flags: EntryFlags = r_guard .flags() .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); @@ -1022,7 +1023,7 @@ impl LockedVMA { /// pub fn remap( &self, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { @@ -1229,13 +1230,17 @@ pub struct VMA { /// 虚拟内存区域标志 vm_flags: VmFlags, /// VMA内的页帧的标志 - flags: PageFlags, + flags: EntryFlags, /// VMA内的页帧是否已经映射到页表 mapped: bool, /// VMA所属的用户地址空间 user_address_space: Option>, self_ref: Weak, + vm_file: Option>, + /// VMA映射的文件部分相对于整个文件的偏移页数 + file_pgoff: Option, + provider: Provider, } @@ -1258,7 +1263,7 @@ impl VMA { pub fn new( region: VirtRegion, vm_flags: VmFlags, - flags: PageFlags, + flags: EntryFlags, mapped: bool, ) -> Self { VMA { @@ -1269,6 +1274,8 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, + file_pgoff: None, + vm_file: None, } } @@ -1280,6 +1287,10 @@ impl VMA { return &self.vm_flags; } + pub fn vm_file(&self) -> Option> { + return self.vm_file.clone(); + } + pub fn set_vm_flags(&mut self, vm_flags: VmFlags) { self.vm_flags = vm_flags; } @@ -1306,6 +1317,8 @@ impl VMA { user_address_space: self.user_address_space.clone(), self_ref: self.self_ref.clone(), provider: Provider::Allocated, + file_pgoff: self.file_pgoff, + vm_file: self.vm_file.clone(), }; } @@ -1318,14 +1331,21 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, + file_pgoff: self.file_pgoff, + vm_file: self.vm_file.clone(), }; } #[inline(always)] - pub fn flags(&self) -> PageFlags { + pub fn flags(&self) -> EntryFlags { return self.flags; } + #[inline(always)] + pub fn file_page_offset(&self) -> Option { + return self.file_pgoff; + } + pub fn pages(&self) -> VirtPageFrameIter { return VirtPageFrameIter::new( VirtPageFrame::new(self.region.start()), @@ -1335,7 +1355,7 @@ impl VMA { pub fn remap( &mut self, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { @@ -1388,7 +1408,7 @@ impl VMA { destination: VirtPageFrame, count: PageFrameCount, vm_flags: VmFlags, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result, SystemError> { @@ -1410,15 +1430,12 @@ impl VMA { cur_dest = cur_dest.next(); } - let r: Arc = LockedVMA::new(VMA { - region: VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), + let r: Arc = LockedVMA::new(VMA::new( + VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, flags, - mapped: true, - user_address_space: None, - self_ref: Weak::default(), - provider: Provider::Allocated, - }); + true, + )); // 将VMA加入到anon_vma中 let mut page_manager_guard = page_manager_lock_irqsave(); @@ -1446,7 +1463,7 @@ impl VMA { destination: VirtPageFrame, page_count: PageFrameCount, vm_flags: VmFlags, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result, SystemError> { diff --git a/kernel/src/virt/kvm/host_mem.rs b/kernel/src/virt/kvm/host_mem.rs index 75780f0a5..e4622a792 100644 --- a/kernel/src/virt/kvm/host_mem.rs +++ b/kernel/src/virt/kvm/host_mem.rs @@ -3,7 +3,7 @@ use system_error::SystemError; use super::{vcpu::Vcpu, vm}; use crate::{ kdebug, - mm::{kernel_mapper::KernelMapper, page::PageFlags, VirtAddr}, + mm::{kernel_mapper::KernelMapper, page::EntryFlags, VirtAddr}, }; /* @@ -154,7 +154,7 @@ fn hva_to_pfn(addr: u64, _atomic: bool, _writable: &mut bool) -> Result> PAGE_SHIFT); } unsafe { - mapper.map(hva, PageFlags::mmio_flags()); + mapper.map(hva, EntryFlags::mmio_flags()); } let (hpa, _) = mapper.translate(hva).unwrap(); return Ok(hpa.data() as u64 >> PAGE_SHIFT); From d1d0aca49e5d7f57e05f071a05303829a6a41894 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Mon, 27 May 2024 00:10:41 +0800 Subject: [PATCH 06/60] 20240527 0010 --- kernel/src/arch/riscv64/mm/mod.rs | 2 +- kernel/src/arch/x86_64/mm/mod.rs | 2 +- kernel/src/filesystem/vfs/file.rs | 19 ++----- kernel/src/mm/fault.rs | 84 ++++++++++++++++++++----------- kernel/src/mm/page.rs | 6 +++ 5 files changed, 69 insertions(+), 44 deletions(-) diff --git a/kernel/src/arch/riscv64/mm/mod.rs b/kernel/src/arch/riscv64/mm/mod.rs index c157ba556..a335dee51 100644 --- a/kernel/src/arch/riscv64/mm/mod.rs +++ b/kernel/src/arch/riscv64/mm/mod.rs @@ -12,7 +12,7 @@ use crate::{ page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage, PhysPageFrame}, }, kernel_mapper::KernelMapper, - page::{PageEntry, EntryFlags, PAGE_1G_SHIFT}, + page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}, ucontext::UserMapper, MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, }, diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index 18c210a8e..b6c2b47c0 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -27,7 +27,7 @@ use crate::{ }; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{PageEntry, EntryFlags, PAGE_1G_SHIFT}; +use crate::mm::page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}; use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr}; use crate::{kdebug, kinfo, kwarn}; use system_error::SystemError; diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index e71f975dc..81a134cbb 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -13,7 +13,7 @@ use crate::{ base::{block::SeekFrom, device::DevicePrivateData}, tty::tty_device::TtyFilePrivateData, }, - filesystem::{fat::fs::LockedFATInode, procfs::ProcfsFilePrivateData}, + filesystem::procfs::ProcfsFilePrivateData, ipc::pipe::{LockedPipeInode, PipeFsPrivateData}, kerror, libs::{rwlock::RwLock, spinlock::SpinLock}, @@ -25,7 +25,7 @@ use crate::{ process::ProcessManager, }; -use super::{mount::MountFSInode, Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; +use super::{Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; /// 文件私有信息的枚举类型 #[derive(Debug, Clone)] @@ -121,6 +121,7 @@ impl FileMode { } } +#[allow(dead_code)] pub struct PageCache { inode_ref: Weak, map: HashMap>, @@ -134,12 +135,12 @@ impl PageCache { } } - pub fn set_page(&mut self, offset: usize, page: Arc) { + pub fn add_page(&mut self, offset: usize, page: Arc) { self.map.insert(offset, page); } pub fn get_page(&self, offset: usize) -> Option> { - self.map.get(&offset).map(|page| page.clone()) + self.map.get(&offset).cloned() } // pub fn get_pages(&self, start_pgoff: usize, end_pgoff: usize) -> Vec> { @@ -158,16 +159,6 @@ pub trait PageCacheOperations: IndexNode { fn read_ahead(&self); } -impl PageCacheOperations for LockedFATInode { - fn write_page(&self, page: Page) { - todo!() - } - - fn read_ahead(&self) { - todo!() - } -} - /// @brief 抽象文件结构体 #[derive(Debug)] pub struct File { diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index d26fa492a..63116c04f 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -1,6 +1,6 @@ use core::{alloc::Layout, cmp::min, intrinsics::unlikely, panic}; -use alloc::sync::Arc; +use alloc::{sync::Arc, vec::Vec}; use crate::{ arch::{mm::PageMapper, MMArch}, @@ -15,7 +15,8 @@ use crate::{ use crate::mm::MemoryManagementArch; use super::{ - page::{self, PageFlags}, + allocator::page_frame::{FrameAllocator, PhysPageFrame}, + page::{Page, PageFlags}, phys_2_virt, }; @@ -309,19 +310,20 @@ impl PageFaultHandler { /// - VmFaultReason: 页面错误处理信息标志 #[allow(dead_code, unused_variables)] pub unsafe fn do_read_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - panic!( - "do_read_fault has not yet been implemented, - fault message: {:?}, - pid: {}\n", - pfm, - crate::process::ProcessManager::current_pid().data() - ); + // panic!( + // "do_read_fault has not yet been implemented, + // fault message: {:?}, + // pid: {}\n", + // pfm, + // crate::process::ProcessManager::current_pid().data() + // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_read_fault - let mut ret = VmFaultReason::empty(); - ret = Self::do_fault_around(pfm, mapper); - ret = Self::filemap_fault(pfm, mapper); - return ret; + let ret = Self::do_fault_around(pfm.clone(), mapper); + if !ret.is_empty() { + return ret; + } + return Self::filemap_fault(pfm.clone(), mapper); } /// 处理对共享文件映射区写入引起的缺页 @@ -433,20 +435,24 @@ impl PageFaultHandler { } pub unsafe fn do_fault_around(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - mapper - .allocate_table(*pfm.address(), 0) - .expect("failed to allocate pte table"); + if mapper.get_table(*pfm.address(), 0).is_none() { + mapper + .allocate_table(*pfm.address(), 0) + .expect("failed to allocate pte table"); + } let vma = pfm.vma(); let vma_guard = vma.lock(); let vma_region = vma_guard.region(); // 缺页在VMA中的偏移量 let vm_pgoff = (*pfm.address() - vma_region.start()) >> MMArch::PAGE_SHIFT; + // 缺页在PTE中的偏移量 - let pte_pgoff = (pfm.address().data() >> MMArch::PAGE_SHIFT) & (1 << MMArch::PAGE_SIZE); + let pte_pgoff = + (pfm.address().data() >> MMArch::PAGE_SHIFT) & (1 << MMArch::PAGE_ENTRY_SHIFT); let vma_pages_count = (vma_region.end() - vma_region.start()) >> MMArch::PAGE_SHIFT; - // 开始位置不能超出当前pte和vma头部的最小值 + // 开始位置不能超出当前pte和vma头部 let from_pte = pte_pgoff - min(vm_pgoff, pte_pgoff); let fault_around_page_number = 16; @@ -464,11 +470,11 @@ impl PageFaultHandler { ); // 预先分配pte页表(如果不存在) - if mapper.get_table(*pfm.address(), 0).is_none() { - if mapper.allocate_table(*pfm.address(), 0).is_none() { - return VmFaultReason::VM_FAULT_OOM; - } - }; + if mapper.get_table(*pfm.address(), 0).is_none() + && mapper.allocate_table(*pfm.address(), 0).is_none() + { + return VmFaultReason::VM_FAULT_OOM; + } // from_pte - pte_pgoff得出预读起始pte相对缺失页的偏移,加上pfm.file_pgoff(缺失页在文件中的偏移)得出起始页在文件中的偏移,结束pte同理 Self::filemap_map_pages( @@ -515,7 +521,7 @@ impl PageFaultHandler { let frame = virt as *mut u8; let new_frame = phys_2_virt(mapper.translate(address).unwrap().0.data()) as *mut u8; - frame.copy_from_nonoverlapping(new_frame, MMArch::PAGE_SIZE); + new_frame.copy_from_nonoverlapping(frame, MMArch::PAGE_SIZE); } } } @@ -526,22 +532,44 @@ impl PageFaultHandler { let vma = pfm.vma(); let vma_guard = vma.lock(); let file = vma_guard.vm_file().expect("no vm_file in vma"); - let page_cache = file.inode().page_cache().unwrap(); + let mut page_cache = file.inode().page_cache().unwrap(); if let Some(page) = page_cache.get_page(pfm.file_pgoff) { // TODO 异步从磁盘中预读页面进PageCache let address = vma_guard.region().start - + (pfm.file_pgoff + + ((pfm.file_pgoff - vma_guard .file_page_offset() - .expect("file_page_offset is none") + .expect("file_page_offset is none")) << MMArch::PAGE_SHIFT); mapper.map(address, vma_guard.flags()).unwrap().flush(); let frame = phys_2_virt(page.phys_frame().phys_address().data()) as *mut u8; let new_frame = phys_2_virt(mapper.translate(address).unwrap().0.data()) as *mut u8; - frame.copy_from_nonoverlapping(new_frame, MMArch::PAGE_SIZE); + new_frame.copy_from_nonoverlapping(frame, MMArch::PAGE_SIZE); } else { // TODO 同步预读 + let mut buf: Vec = vec![0; MMArch::PAGE_SIZE]; + file.pread( + pfm.file_pgoff * MMArch::PAGE_SIZE, + MMArch::PAGE_SIZE, + &mut buf[..], + ) + .unwrap(); + let allocator = mapper.allocator_mut(); + + // 分配一个物理页面作为加入PageCache的新页 + let new_cache_page = allocator.allocate_one().unwrap(); + (phys_2_virt(new_cache_page.data()) as *mut u8) + .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); + page_cache.add_page( + pfm.file_pgoff, + Arc::new(Page::new(false, PhysPageFrame::new(new_cache_page))), + ); + + // 分配空白页并映射到缺页地址 + mapper.map(pfm.address, vma_guard.flags()).unwrap().flush(); + let new_frame = phys_2_virt(mapper.translate(pfm.address).unwrap().0.data()); + (new_frame as *mut u8).copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); } VmFaultReason::VM_FAULT_COMPLETED } diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 456bd9ca6..96cf958bb 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -495,6 +495,12 @@ pub struct EntryFlags { phantom: PhantomData, } +impl Default for EntryFlags { + fn default() -> Self { + Self::new() + } +} + #[allow(dead_code)] impl EntryFlags { #[inline(always)] From 33e9f0b34f9dc35a47757137a29605e51052a26e Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Mon, 27 May 2024 23:57:51 +0800 Subject: [PATCH 07/60] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dmmap=E6=9C=AA=E5=BB=B6?= =?UTF-8?q?=E8=BF=9F=E5=88=86=E9=85=8D=E5=86=85=E5=AD=98=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/x86_64/interrupt/trap.rs | 2 +- kernel/src/mm/page.rs | 5 ++++ kernel/src/mm/syscall.rs | 2 +- kernel/src/mm/ucontext.rs | 30 +++++++++++------------- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/kernel/src/arch/x86_64/interrupt/trap.rs b/kernel/src/arch/x86_64/interrupt/trap.rs index ecc03f8d3..9cd31fa9b 100644 --- a/kernel/src/arch/x86_64/interrupt/trap.rs +++ b/kernel/src/arch/x86_64/interrupt/trap.rs @@ -401,7 +401,7 @@ unsafe extern "C" fn do_page_fault(regs: &'static TrapFrame, error_code: u64) { // panic!("Page Fault"); CurrentIrqArch::interrupt_disable(); let address = x86::controlregs::cr2(); - // crate::info!( + // log::info!( // "fault address: {:#x}, error_code: {:#b}, pid: {}\n", // address, // error_code, diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index eb75c3eec..117708111 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -861,6 +861,11 @@ impl PageMapper { let phys: PhysAddr = self.frame_allocator.allocate_one()?; compiler_fence(Ordering::SeqCst); + unsafe { + let vaddr = MMArch::phys_2_virt(phys).unwrap(); + MMArch::write_bytes(vaddr, 0, MMArch::PAGE_SIZE); + } + let mut page_manager_guard: SpinLockGuard<'static, PageManager> = page_manager_lock_irqsave(); if !page_manager_guard.contains(&phys) { diff --git a/kernel/src/mm/syscall.rs b/kernel/src/mm/syscall.rs index 16bca4d33..beac5cc23 100644 --- a/kernel/src/mm/syscall.rs +++ b/kernel/src/mm/syscall.rs @@ -329,7 +329,7 @@ impl Syscall { prot_flags, map_flags, true, - true, + false, )?; return Ok(start_page.virt_address().data()); } diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 1919ddeef..f5d484bc4 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -1116,7 +1116,7 @@ impl LockedVMA { let before: Option> = guard.region.before(®ion).map(|virt_region| { let mut vma: VMA = unsafe { guard.clone() }; vma.region = virt_region; - + vma.mapped = false; let vma: Arc = LockedVMA::new(vma); vma }); @@ -1124,7 +1124,7 @@ impl LockedVMA { let after: Option> = guard.region.after(®ion).map(|virt_region| { let mut vma: VMA = unsafe { guard.clone() }; vma.region = virt_region; - + vma.mapped = false; let vma: Arc = LockedVMA::new(vma); vma }); @@ -1134,20 +1134,24 @@ impl LockedVMA { if let Some(before) = before.clone() { let virt_iter = before.lock().region.iter_pages(); for frame in virt_iter { - let paddr = utable.translate(frame.virt_address()).unwrap().0; - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(before.clone()); - page.remove_vma(self); + if let Some((paddr, _)) = utable.translate(frame.virt_address()) { + let page = page_manager_guard.get_mut(&paddr); + page.insert_vma(before.clone()); + page.remove_vma(self); + before.lock().mapped = true; + } } } if let Some(after) = after.clone() { let virt_iter = after.lock().region.iter_pages(); for frame in virt_iter { - let paddr = utable.translate(frame.virt_address()).unwrap().0; - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(after.clone()); - page.remove_vma(self); + if let Some((paddr, _)) = utable.translate(frame.virt_address()) { + let page = page_manager_guard.get_mut(&paddr); + page.insert_vma(after.clone()); + page.remove_vma(self); + after.lock().mapped = true; + } } } @@ -1490,12 +1494,6 @@ impl VMA { // 将VMA加入到anon_vma let page = page_manager_guard.get_mut(&paddr); page.insert_vma(r.clone()); - - // 清空内存 - unsafe { - let vaddr = MMArch::phys_2_virt(paddr).unwrap(); - MMArch::write_bytes(vaddr, 0, MMArch::PAGE_SIZE); - } } // debug!("VMA::zeroed: done"); return Ok(r); From 9261cb79f08d12bbe4b3b5bf29c84625db059c13 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 28 May 2024 17:40:01 +0800 Subject: [PATCH 08/60] Revert "Merge branch 'patch-add-file-mapping' into patch-fix-mmap" This reverts commit 8eb687c60b43831d7e9614bca0af41e8f2175ae8, reversing changes made to 33e9f0b34f9dc35a47757137a29605e51052a26e. --- kernel/src/arch/riscv64/mm/mod.rs | 6 +- kernel/src/arch/x86_64/kvm/vmx/ept.rs | 4 +- kernel/src/arch/x86_64/kvm/vmx/mmu.rs | 4 +- kernel/src/arch/x86_64/mm/mod.rs | 10 +- kernel/src/driver/net/dma.rs | 4 +- kernel/src/driver/video/mod.rs | 4 +- kernel/src/driver/virtio/virtio_impl.rs | 4 +- kernel/src/filesystem/vfs/file.rs | 41 ---- kernel/src/filesystem/vfs/mod.rs | 11 +- kernel/src/ipc/shm.rs | 2 +- kernel/src/ipc/syscall.rs | 8 +- kernel/src/mm/c_adapter.rs | 4 +- kernel/src/mm/fault.rs | 238 ++++-------------------- kernel/src/mm/kernel_mapper.rs | 4 +- kernel/src/mm/mmio_buddy.rs | 6 +- kernel/src/mm/mod.rs | 2 +- kernel/src/mm/no_init.rs | 8 +- kernel/src/mm/page.rs | 96 +++------- kernel/src/mm/ucontext.rs | 53 ++---- kernel/src/virt/kvm/host_mem.rs | 4 +- 20 files changed, 117 insertions(+), 396 deletions(-) diff --git a/kernel/src/arch/riscv64/mm/mod.rs b/kernel/src/arch/riscv64/mm/mod.rs index a335dee51..76bfbed04 100644 --- a/kernel/src/arch/riscv64/mm/mod.rs +++ b/kernel/src/arch/riscv64/mm/mod.rs @@ -12,7 +12,7 @@ use crate::{ page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage, PhysPageFrame}, }, kernel_mapper::KernelMapper, - page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}, + page::{PageEntry, PageFlags, PAGE_1G_SHIFT}, ucontext::UserMapper, MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, }, @@ -270,8 +270,8 @@ impl VirtAddr { } /// 获取内核地址默认的页面标志 -pub unsafe fn kernel_page_flags(_virt: VirtAddr) -> EntryFlags { - EntryFlags::from_data(RiscV64MMArch::ENTRY_FLAG_DEFAULT_PAGE) +pub unsafe fn kernel_page_flags(_virt: VirtAddr) -> PageFlags { + PageFlags::from_data(RiscV64MMArch::ENTRY_FLAG_DEFAULT_PAGE) .set_user(false) .set_execute(true) } diff --git a/kernel/src/arch/x86_64/kvm/vmx/ept.rs b/kernel/src/arch/x86_64/kvm/vmx/ept.rs index 838c1a159..032231902 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/ept.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/ept.rs @@ -1,7 +1,7 @@ use crate::arch::mm::LockedFrameAllocator; use crate::arch::mm::PageMapper; use crate::arch::MMArch; -use crate::mm::page::EntryFlags; +use crate::mm::page::PageFlags; use crate::mm::{PageTableKind, PhysAddr, VirtAddr}; use crate::smp::core::smp_get_processor_id; use crate::smp::cpu::AtomicProcessorId; @@ -92,7 +92,7 @@ impl EptMapper { &mut self, gpa: u64, hpa: u64, - flags: EntryFlags, + flags: PageFlags, ) -> Result<(), SystemError> { if self.readonly { return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); diff --git a/kernel/src/arch/x86_64/kvm/vmx/mmu.rs b/kernel/src/arch/x86_64/kvm/vmx/mmu.rs index c05ef9bb5..e28b3f03d 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/mmu.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/mmu.rs @@ -1,7 +1,7 @@ use crate::{ arch::kvm::vmx::ept::EptMapper, libs::mutex::Mutex, - mm::{page::EntryFlags, syscall::ProtFlags}, + mm::{page::PageFlags, syscall::ProtFlags}, virt::kvm::host_mem::{__gfn_to_pfn, kvm_vcpu_gfn_to_memslot, PAGE_MASK, PAGE_SHIFT}, }; use bitfield_struct::bitfield; @@ -218,7 +218,7 @@ pub fn __direct_map( } // 把gpa映射到hpa let mut ept_mapper = EptMapper::lock(); - let page_flags = EntryFlags::from_prot_flags(ProtFlags::from_bits_truncate(0x7_u64), false); + let page_flags = PageFlags::from_prot_flags(ProtFlags::from_bits_truncate(0x7_u64), false); unsafe { assert!(ept_mapper.walk(gpa, pfn << PAGE_SHIFT, page_flags).is_ok()); } diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index 841ae4335..6eaf2dd5a 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -28,7 +28,7 @@ use crate::{ }; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}; +use crate::mm::page::{PageEntry, PageFlags, PAGE_1G_SHIFT}; use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr}; use system_error::SystemError; @@ -650,17 +650,17 @@ impl FrameAllocator for LockedFrameAllocator { } /// 获取内核地址默认的页面标志 -pub unsafe fn kernel_page_flags(virt: VirtAddr) -> EntryFlags { +pub unsafe fn kernel_page_flags(virt: VirtAddr) -> PageFlags { let info: X86_64MMBootstrapInfo = BOOTSTRAP_MM_INFO.unwrap(); if virt.data() >= info.kernel_code_start && virt.data() < info.kernel_code_end { // Remap kernel code execute - return EntryFlags::new().set_execute(true).set_write(true); + return PageFlags::new().set_execute(true).set_write(true); } else if virt.data() >= info.kernel_data_end && virt.data() < info.kernel_rodata_end { // Remap kernel rodata read only - return EntryFlags::new().set_execute(true); + return PageFlags::new().set_execute(true); } else { - return EntryFlags::new().set_write(true).set_execute(true); + return PageFlags::new().set_write(true).set_execute(true); } } diff --git a/kernel/src/driver/net/dma.rs b/kernel/src/driver/net/dma.rs index f8c06b74e..11fcf6229 100644 --- a/kernel/src/driver/net/dma.rs +++ b/kernel/src/driver/net/dma.rs @@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags; use crate::arch::MMArch; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, EntryFlags}; +use crate::mm::page::{page_manager_lock_irqsave, PageFlags}; use crate::mm::{ allocator::page_frame::{ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, @@ -25,7 +25,7 @@ pub fn dma_alloc(pages: usize) -> (usize, NonNull) { // 清空这块区域,防止出现脏数据 core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); - let dma_flags: EntryFlags = EntryFlags::mmio_flags(); + let dma_flags: PageFlags = PageFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let kernel_mapper = kernel_mapper.as_mut().unwrap(); diff --git a/kernel/src/driver/video/mod.rs b/kernel/src/driver/video/mod.rs index e9a705140..282e672d3 100644 --- a/kernel/src/driver/video/mod.rs +++ b/kernel/src/driver/video/mod.rs @@ -10,7 +10,7 @@ use crate::{ spinlock::SpinLock, }, mm::{ - allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::EntryFlags, + allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::PageFlags, MemoryManagementArch, }, time::timer::{Timer, TimerFunction}, @@ -95,7 +95,7 @@ impl VideoRefreshManager { let count = PageFrameCount::new( page_align_up(frame_buffer_info_guard.buf_size()) / MMArch::PAGE_SIZE, ); - let page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); + let page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); let mut kernel_mapper = KernelMapper::lock(); let mut kernel_mapper = kernel_mapper.as_mut(); diff --git a/kernel/src/driver/virtio/virtio_impl.rs b/kernel/src/driver/virtio/virtio_impl.rs index 0166b138b..256b40eb2 100644 --- a/kernel/src/driver/virtio/virtio_impl.rs +++ b/kernel/src/driver/virtio/virtio_impl.rs @@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags; use crate::arch::MMArch; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, EntryFlags}; +use crate::mm::page::{page_manager_lock_irqsave, PageFlags}; use crate::mm::{ allocator::page_frame::{ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, @@ -32,7 +32,7 @@ unsafe impl Hal for HalImpl { // 清空这块区域,防止出现脏数据 core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); - let dma_flags: EntryFlags = EntryFlags::mmio_flags(); + let dma_flags: PageFlags = PageFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let kernel_mapper = kernel_mapper.as_mut().unwrap(); diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index c76460767..2271065dd 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -5,7 +5,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use hashbrown::HashMap; use log::error; use system_error::SystemError; @@ -17,7 +16,6 @@ use crate::{ filesystem::procfs::ProcfsFilePrivateData, ipc::pipe::{LockedPipeInode, PipeFsPrivateData}, libs::{rwlock::RwLock, spinlock::SpinLock}, - mm::page::Page, net::{ event_poll::{EPollItem, EPollPrivateData, EventPoll}, socket::SocketInode, @@ -120,45 +118,6 @@ impl FileMode { return self.bits() & FileMode::O_ACCMODE.bits(); } } - -#[allow(dead_code)] -pub struct PageCache { - inode_ref: Weak, - map: HashMap>, -} - -impl PageCache { - pub fn new(inode_ref: Weak) -> PageCache { - Self { - inode_ref, - map: HashMap::new(), - } - } - - pub fn add_page(&mut self, offset: usize, page: Arc) { - self.map.insert(offset, page); - } - - pub fn get_page(&self, offset: usize) -> Option> { - self.map.get(&offset).cloned() - } - - // pub fn get_pages(&self, start_pgoff: usize, end_pgoff: usize) -> Vec> { - // let mut vec = Vec::new(); - // for pgoff in start_pgoff..=end_pgoff { - // if let Some(page) = self.map.get(&pgoff) { - // vec.push(page.clone()); - // } - // } - // vec - // } -} - -pub trait PageCacheOperations: IndexNode { - fn write_page(&self, page: Page); - fn read_ahead(&self); -} - /// @brief 抽象文件结构体 #[derive(Debug)] pub struct File { diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 09c04f072..7bdbe47bc 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -23,12 +23,7 @@ use crate::{ time::PosixTimeSpec, }; -use self::{ - core::generate_inode_id, - file::{FileMode, PageCache}, - syscall::ModeType, - utils::DName, -}; +use self::{core::generate_inode_id, file::FileMode, syscall::ModeType, utils::DName}; pub use self::{core::ROOT_INODE, file::FilePrivateData, mount::MountFS}; /// vfs容许的最大的路径名称长度 @@ -553,10 +548,6 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { fn parent(&self) -> Result, SystemError> { return self.find(".."); } - - fn page_cache(&self) -> Option { - None - } } impl DowncastArc for dyn IndexNode { diff --git a/kernel/src/ipc/shm.rs b/kernel/src/ipc/shm.rs index f3f87eb74..13c9f1b06 100644 --- a/kernel/src/ipc/shm.rs +++ b/kernel/src/ipc/shm.rs @@ -165,7 +165,7 @@ impl ShmManager { let mut page_manager_guard = page_manager_lock_irqsave(); let mut cur_phys = PhysPageFrame::new(phys_page.0); for _ in 0..page_count.data() { - let mut page = Page::new(true, cur_phys); + let mut page = Page::new(true); page.set_shm_id(shm_id); let paddr = cur_phys.phys_address(); page_manager_guard.insert(paddr, page); diff --git a/kernel/src/ipc/syscall.rs b/kernel/src/ipc/syscall.rs index 6cc525bba..37a9cc9c6 100644 --- a/kernel/src/ipc/syscall.rs +++ b/kernel/src/ipc/syscall.rs @@ -20,7 +20,7 @@ use crate::{ libs::spinlock::SpinLock, mm::{ allocator::page_frame::{PageFrameCount, PhysPageFrame, VirtPageFrame}, - page::{page_manager_lock_irqsave, EntryFlags, PageFlushAll}, + page::{page_manager_lock_irqsave, PageFlags, PageFlushAll}, syscall::ProtFlags, ucontext::{AddressSpace, VMA}, VirtAddr, VmFlags, @@ -324,8 +324,8 @@ impl Syscall { .ok_or(SystemError::EINVAL)?; let vm_flags = VmFlags::from(shmflg); let destination = VirtPageFrame::new(region.start()); - let page_flags: EntryFlags = - EntryFlags::from_prot_flags(ProtFlags::from(vm_flags), true); + let page_flags: PageFlags = + PageFlags::from_prot_flags(ProtFlags::from(vm_flags), true); let flusher: PageFlushAll = PageFlushAll::new(); // 将共享内存映射到对应虚拟区域 @@ -358,7 +358,7 @@ impl Syscall { // 验证用户虚拟内存区域是否有效 let _ = UserBufferReader::new(vaddr.data() as *const u8, size, true)?; - // 必须在取消映射前获取到EntryFlags + // 必须在取消映射前获取到PageFlags let page_flags = address_write_guard .user_mapper .utable diff --git a/kernel/src/mm/c_adapter.rs b/kernel/src/mm/c_adapter.rs index 1c824595d..bd949fa5c 100644 --- a/kernel/src/mm/c_adapter.rs +++ b/kernel/src/mm/c_adapter.rs @@ -15,7 +15,7 @@ use crate::{ use super::{ allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, mmio_buddy::mmio_pool, - no_init::pseudo_map_phys, page::EntryFlags, MemoryManagementArch, PhysAddr, VirtAddr, + no_init::pseudo_map_phys, page::PageFlags, MemoryManagementArch, PhysAddr, VirtAddr, }; lazy_static! { @@ -40,7 +40,7 @@ pub unsafe extern "C" fn rs_map_phys(vaddr: usize, paddr: usize, size: usize, fl let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); // debug!("rs_map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); - let mut page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); + let mut page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); if flags & PAGE_U_S as usize != 0 { page_flags = page_flags.set_user(true); } diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 63116c04f..71e0623ae 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -1,11 +1,11 @@ -use core::{alloc::Layout, cmp::min, intrinsics::unlikely, panic}; +use core::{alloc::Layout, intrinsics::unlikely, panic}; -use alloc::{sync::Arc, vec::Vec}; +use alloc::sync::Arc; use crate::{ arch::{mm::PageMapper, MMArch}, mm::{ - page::{page_manager_lock_irqsave, EntryFlags}, + page::{page_manager_lock_irqsave, PageFlags}, ucontext::LockedVMA, VirtAddr, VmFaultReason, VmFlags, }, @@ -14,27 +14,21 @@ use crate::{ use crate::mm::MemoryManagementArch; -use super::{ - allocator::page_frame::{FrameAllocator, PhysPageFrame}, - page::{Page, PageFlags}, - phys_2_virt, -}; - bitflags! { pub struct FaultFlags: u64{ - const FAULT_FLAG_WRITE = 1 << 0; - const FAULT_FLAG_MKWRITE = 1 << 1; - const FAULT_FLAG_ALLOW_RETRY = 1 << 2; - const FAULT_FLAG_RETRY_NOWAIT = 1 << 3; - const FAULT_FLAG_KILLABLE = 1 << 4; - const FAULT_FLAG_TRIED = 1 << 5; - const FAULT_FLAG_USER = 1 << 6; - const FAULT_FLAG_REMOTE = 1 << 7; - const FAULT_FLAG_INSTRUCTION = 1 << 8; - const FAULT_FLAG_INTERRUPTIBLE =1 << 9; - const FAULT_FLAG_UNSHARE = 1 << 10; - const FAULT_FLAG_ORIG_PTE_VALID = 1 << 11; - const FAULT_FLAG_VMA_LOCK = 1 << 12; + const FAULT_FLAG_WRITE = 1 << 0; + const FAULT_FLAG_MKWRITE = 1 << 1; + const FAULT_FLAG_ALLOW_RETRY = 1 << 2; + const FAULT_FLAG_RETRY_NOWAIT = 1 << 3; + const FAULT_FLAG_KILLABLE = 1 << 4; + const FAULT_FLAG_TRIED = 1 << 5; + const FAULT_FLAG_USER = 1 << 6; + const FAULT_FLAG_REMOTE = 1 << 7; + const FAULT_FLAG_INSTRUCTION = 1 << 8; + const FAULT_FLAG_INTERRUPTIBLE =1 << 9; + const FAULT_FLAG_UNSHARE = 1 << 10; + const FAULT_FLAG_ORIG_PTE_VALID = 1 << 11; + const FAULT_FLAG_VMA_LOCK = 1 << 12; } } @@ -42,14 +36,9 @@ bitflags! { /// 包含了页面错误处理的相关信息,例如出错的地址、VMA等 #[derive(Debug)] pub struct PageFaultMessage { - /// 产生缺页的VMA结构体 vma: Arc, - /// 缺页地址 address: VirtAddr, - /// 异常处理标志 flags: FaultFlags, - /// 缺页的文件页在文件中的偏移量 - file_pgoff: usize, } impl PageFaultMessage { @@ -58,8 +47,6 @@ impl PageFaultMessage { vma: vma.clone(), address, flags, - file_pgoff: ((address - vma.lock().region().start()) >> MMArch::PAGE_SHIFT) - + vma.lock().file_page_offset().unwrap(), } } @@ -71,8 +58,8 @@ impl PageFaultMessage { #[inline(always)] #[allow(dead_code)] - pub fn address(&self) -> &VirtAddr { - &self.address + pub fn address(&self) -> VirtAddr { + self.address } #[inline(always)] @@ -83,8 +70,8 @@ impl PageFaultMessage { #[inline(always)] #[allow(dead_code)] - pub fn flags(&self) -> &FaultFlags { - &self.flags + pub fn flags(&self) -> FaultFlags { + self.flags } } @@ -94,7 +81,6 @@ impl Clone for PageFaultMessage { vma: self.vma.clone(), address: self.address, flags: self.flags, - file_pgoff: self.file_pgoff, } } } @@ -203,7 +189,7 @@ impl PageFaultHandler { if !entry.write() { ret = Self::do_wp_page(pfm.clone(), mapper); } else { - entry.set_flags(EntryFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); + entry.set_flags(PageFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); } } } else if vma.is_anonymous() { @@ -262,22 +248,14 @@ impl PageFaultHandler { /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] pub unsafe fn do_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - // panic!( - // "do_fault has not yet been implemented, - // fault message: {:?}, - // pid: {}\n", - // pfm, - // crate::process::ProcessManager::current_pid().data() - // ); + panic!( + "do_fault has not yet been implemented, + fault message: {:?}, + pid: {}\n", + pfm, + crate::process::ProcessManager::current_pid().data() + ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_fault - - if !pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) { - return Self::do_read_fault(pfm, mapper); - } else if !pfm.vma().lock().vm_flags().contains(VmFlags::VM_SHARED) { - return Self::do_cow_fault(pfm, mapper); - } else { - return Self::do_shared_fault(pfm, mapper); - } } /// 处理私有文件映射的写时复制 @@ -310,20 +288,14 @@ impl PageFaultHandler { /// - VmFaultReason: 页面错误处理信息标志 #[allow(dead_code, unused_variables)] pub unsafe fn do_read_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - // panic!( - // "do_read_fault has not yet been implemented, - // fault message: {:?}, - // pid: {}\n", - // pfm, - // crate::process::ProcessManager::current_pid().data() - // ); - + panic!( + "do_read_fault has not yet been implemented, + fault message: {:?}, + pid: {}\n", + pfm, + crate::process::ProcessManager::current_pid().data() + ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_read_fault - let ret = Self::do_fault_around(pfm.clone(), mapper); - if !ret.is_empty() { - return ret; - } - return Self::filemap_fault(pfm.clone(), mapper); } /// 处理对共享文件映射区写入引起的缺页 @@ -433,144 +405,4 @@ impl PageFaultHandler { VmFaultReason::VM_FAULT_OOM } } - - pub unsafe fn do_fault_around(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - if mapper.get_table(*pfm.address(), 0).is_none() { - mapper - .allocate_table(*pfm.address(), 0) - .expect("failed to allocate pte table"); - } - let vma = pfm.vma(); - let vma_guard = vma.lock(); - let vma_region = vma_guard.region(); - // 缺页在VMA中的偏移量 - let vm_pgoff = (*pfm.address() - vma_region.start()) >> MMArch::PAGE_SHIFT; - - // 缺页在PTE中的偏移量 - let pte_pgoff = - (pfm.address().data() >> MMArch::PAGE_SHIFT) & (1 << MMArch::PAGE_ENTRY_SHIFT); - - let vma_pages_count = (vma_region.end() - vma_region.start()) >> MMArch::PAGE_SHIFT; - - // 开始位置不能超出当前pte和vma头部 - let from_pte = pte_pgoff - min(vm_pgoff, pte_pgoff); - - let fault_around_page_number = 16; - - // pte结束位置不能超过: - // 1.最大预读上限(默认16) - // 2.最大pte(512) - // 3.vma结束位置(pte_pgoff + (vma_pages_count - vm_pgoff)计算出vma结束页号对当前pte开头的偏移) - let to_pte = min( - from_pte + fault_around_page_number, - min( - 1 << MMArch::PAGE_SHIFT, - pte_pgoff + (vma_pages_count - vm_pgoff), - ), - ); - - // 预先分配pte页表(如果不存在) - if mapper.get_table(*pfm.address(), 0).is_none() - && mapper.allocate_table(*pfm.address(), 0).is_none() - { - return VmFaultReason::VM_FAULT_OOM; - } - - // from_pte - pte_pgoff得出预读起始pte相对缺失页的偏移,加上pfm.file_pgoff(缺失页在文件中的偏移)得出起始页在文件中的偏移,结束pte同理 - Self::filemap_map_pages( - pfm.clone(), - mapper, - pfm.file_pgoff + (from_pte - pte_pgoff), - pfm.file_pgoff + (to_pte - pte_pgoff), - ); - - VmFaultReason::empty() - } - - pub unsafe fn filemap_map_pages( - pfm: PageFaultMessage, - mapper: &mut PageMapper, - start_pgoff: usize, - end_pgoff: usize, - ) -> VmFaultReason { - let vma = pfm.vma(); - let vma_guard = vma.lock(); - let file = vma_guard.vm_file().expect("no vm_file in vma"); - let page_cache = file.inode().page_cache().unwrap(); - - // 起始页地址 - let addr = vma_guard.region().start - + ((start_pgoff - - vma_guard - .file_page_offset() - .expect("file_page_offset is none")) - << MMArch::PAGE_SHIFT); - // let pages = page_cache.get_pages(start_pgoff, end_pgoff); - // let uptodate_pages = pages - // .iter() - // .filter(|page| page.flags().contains(PageFlags::PG_UPTODATE)); - for pgoff in start_pgoff..=end_pgoff { - if let Some(page) = page_cache.get_page(pgoff) { - if page.flags().contains(PageFlags::PG_UPTODATE) { - let phys = page.phys_frame().phys_address(); - let virt = phys_2_virt(phys.data()); - - let address = - VirtAddr::new(addr.data() + ((pgoff - start_pgoff) << MMArch::PAGE_SHIFT)); - mapper.map(address, vma_guard.flags()).unwrap().flush(); - let frame = virt as *mut u8; - let new_frame = - phys_2_virt(mapper.translate(address).unwrap().0.data()) as *mut u8; - new_frame.copy_from_nonoverlapping(frame, MMArch::PAGE_SIZE); - } - } - } - VmFaultReason::empty() - } - - pub unsafe fn filemap_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - let vma = pfm.vma(); - let vma_guard = vma.lock(); - let file = vma_guard.vm_file().expect("no vm_file in vma"); - let mut page_cache = file.inode().page_cache().unwrap(); - - if let Some(page) = page_cache.get_page(pfm.file_pgoff) { - // TODO 异步从磁盘中预读页面进PageCache - let address = vma_guard.region().start - + ((pfm.file_pgoff - - vma_guard - .file_page_offset() - .expect("file_page_offset is none")) - << MMArch::PAGE_SHIFT); - mapper.map(address, vma_guard.flags()).unwrap().flush(); - let frame = phys_2_virt(page.phys_frame().phys_address().data()) as *mut u8; - let new_frame = phys_2_virt(mapper.translate(address).unwrap().0.data()) as *mut u8; - new_frame.copy_from_nonoverlapping(frame, MMArch::PAGE_SIZE); - } else { - // TODO 同步预读 - let mut buf: Vec = vec![0; MMArch::PAGE_SIZE]; - file.pread( - pfm.file_pgoff * MMArch::PAGE_SIZE, - MMArch::PAGE_SIZE, - &mut buf[..], - ) - .unwrap(); - let allocator = mapper.allocator_mut(); - - // 分配一个物理页面作为加入PageCache的新页 - let new_cache_page = allocator.allocate_one().unwrap(); - (phys_2_virt(new_cache_page.data()) as *mut u8) - .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); - page_cache.add_page( - pfm.file_pgoff, - Arc::new(Page::new(false, PhysPageFrame::new(new_cache_page))), - ); - - // 分配空白页并映射到缺页地址 - mapper.map(pfm.address, vma_guard.flags()).unwrap().flush(); - let new_frame = phys_2_virt(mapper.translate(pfm.address).unwrap().0.data()); - (new_frame as *mut u8).copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); - } - VmFaultReason::VM_FAULT_COMPLETED - } } diff --git a/kernel/src/mm/kernel_mapper.rs b/kernel/src/mm/kernel_mapper.rs index 487d15378..682fad602 100644 --- a/kernel/src/mm/kernel_mapper.rs +++ b/kernel/src/mm/kernel_mapper.rs @@ -1,6 +1,6 @@ use system_error::SystemError; -use super::{page::EntryFlags, PageTableKind, PhysAddr, VirtAddr}; +use super::{page::PageFlags, PageTableKind, PhysAddr, VirtAddr}; use crate::{ arch::{ mm::{LockedFrameAllocator, PageMapper}, @@ -104,7 +104,7 @@ impl KernelMapper { mut vaddr: VirtAddr, mut paddr: PhysAddr, size: usize, - flags: EntryFlags, + flags: PageFlags, flush: bool, ) -> Result<(), SystemError> { if self.readonly { diff --git a/kernel/src/mm/mmio_buddy.rs b/kernel/src/mm/mmio_buddy.rs index b15eda9a1..99e845d3f 100644 --- a/kernel/src/mm/mmio_buddy.rs +++ b/kernel/src/mm/mmio_buddy.rs @@ -12,7 +12,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use log::{debug, error, info, warn}; use system_error::SystemError; -use super::page::{EntryFlags, PAGE_4K_SIZE}; +use super::page::{PageFlags, PAGE_4K_SIZE}; use super::{PhysAddr, VirtAddr}; // 最大的伙伴块的幂 @@ -549,7 +549,7 @@ impl MmioBuddyMemPool { unsafe { let x: Option<( PhysAddr, - EntryFlags, + PageFlags, crate::mm::page::PageFlush, )> = kernel_mapper .as_mut() @@ -674,7 +674,7 @@ impl MMIOSpaceGuard { return Err(SystemError::EINVAL); } - let flags = EntryFlags::mmio_flags(); + let flags = PageFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let r = kernel_mapper.map_phys_with_size(self.vaddr, paddr, length, flags, true); diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index bfd051933..49e61fcd1 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -610,7 +610,7 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { /// 创建页表项 /// - /// 这是一个低阶api,用于根据物理地址以及指定好的EntryFlags,创建页表项 + /// 这是一个低阶api,用于根据物理地址以及指定好的pageflags,创建页表项 /// /// ## 参数 /// diff --git a/kernel/src/mm/no_init.rs b/kernel/src/mm/no_init.rs index fdb8d4d66..855a86767 100644 --- a/kernel/src/mm/no_init.rs +++ b/kernel/src/mm/no_init.rs @@ -19,7 +19,7 @@ use core::marker::PhantomData; use super::{ allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage}, - page::EntryFlags, + page::PageFlags, PageTableKind, VirtAddr, }; @@ -141,7 +141,7 @@ impl FrameAllocator for PseudoAllocator { /// 并且,内核引导文件必须以4K页为粒度,填写了前100M的内存映射关系。(具体以本文件开头的注释为准) #[inline(never)] pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { - let flags: EntryFlags = EntryFlags::new().set_write(true); + let flags: PageFlags = PageFlags::new().set_write(true); pseudo_map_phys_with_flags(vaddr, paddr, count, flags); } @@ -150,7 +150,7 @@ pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrame /// with READ_ONLY and EXECUTE flags. #[inline(never)] pub unsafe fn pseudo_map_phys_ro(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { - let flags: EntryFlags = EntryFlags::new().set_write(false).set_execute(true); + let flags: PageFlags = PageFlags::new().set_write(false).set_execute(true); pseudo_map_phys_with_flags(vaddr, paddr, count, flags); } @@ -160,7 +160,7 @@ pub unsafe fn pseudo_map_phys_with_flags( vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount, - flags: EntryFlags, + flags: PageFlags, ) { assert!(vaddr.check_aligned(MMArch::PAGE_SIZE)); assert!(paddr.check_aligned(MMArch::PAGE_SIZE)); diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index b359c129a..117708111 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -18,7 +18,7 @@ use crate::{ }; use super::{ - allocator::page_frame::{FrameAllocator, PageFrameCount, PhysPageFrame}, + allocator::page_frame::{FrameAllocator, PageFrameCount}, syscall::ProtFlags, ucontext::LockedVMA, MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, @@ -86,27 +86,6 @@ impl PageManager { } } -bitflags! { - pub struct PageFlags: u64 { - const PG_LOCKED = 1 << 0; - const PG_WRITEBACK = 1 << 1; - const PG_REFERENCED = 1 << 2; - const PG_UPTODATE = 1 << 3; - const PG_DIRTY = 1 << 4; - const PG_LRU = 1 << 5; - const PG_HEAD = 1 << 6; - const PG_WAITERS = 1 << 7; - const PG_ACTIVE = 1 << 8; - const PG_WORKINGSET = 1 << 9; - const PG_ERROR = 1 << 10; - const PG_SLAB = 1 << 11; - const PG_RESERVED = 1 << 14; - const PG_PRIVATE = 1 << 15; - const PG_RECLAIM = 1 << 18; - const PG_SWAPBACKED = 1 << 19; - } -} - /// 物理页面信息 pub struct Page { /// 映射计数 @@ -119,14 +98,10 @@ pub struct Page { shm_id: Option, /// 映射到当前page的VMA anon_vma: HashSet>, - /// 标志 - flags: PageFlags, - /// 页所在的物理页帧号 - phys_frame: PhysPageFrame, } impl Page { - pub fn new(shared: bool, phys_frame: PhysPageFrame) -> Self { + pub fn new(shared: bool) -> Self { let dealloc_when_zero = !shared; Self { map_count: 0, @@ -134,8 +109,6 @@ impl Page { free_when_zero: dealloc_when_zero, shm_id: None, anon_vma: HashSet::new(), - flags: PageFlags::empty(), - phys_frame, } } @@ -181,16 +154,6 @@ impl Page { pub fn map_count(&self) -> usize { self.map_count } - - #[inline(always)] - pub fn flags(&self) -> &PageFlags { - &self.flags - } - - #[inline(always)] - pub fn phys_frame(&self) -> &PhysPageFrame { - &self.phys_frame - } } #[derive(Debug)] @@ -367,7 +330,7 @@ impl PageTable { } else { let phys = allocator.allocate_one()?; let mut anon_vma_guard = page_manager_lock_irqsave(); - anon_vma_guard.insert(phys, Page::new(false, PhysPageFrame::new(phys))); + anon_vma_guard.insert(phys, Page::new(false)); let old_phys = entry.address().unwrap(); let frame = MMArch::phys_2_virt(phys).unwrap().data() as *mut u8; frame.copy_from_nonoverlapping( @@ -409,7 +372,7 @@ impl Debug for PageEntry { impl PageEntry { #[inline(always)] - pub fn new(paddr: PhysAddr, flags: EntryFlags) -> Self { + pub fn new(paddr: PhysAddr, flags: PageFlags) -> Self { Self { data: MMArch::make_entry(paddr, flags.data()), phantom: PhantomData, @@ -457,12 +420,12 @@ impl PageEntry { } #[inline(always)] - pub fn flags(&self) -> EntryFlags { - unsafe { EntryFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } + pub fn flags(&self) -> PageFlags { + unsafe { PageFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } } #[inline(always)] - pub fn set_flags(&mut self, flags: EntryFlags) { + pub fn set_flags(&mut self, flags: PageFlags) { self.data = (self.data & !Arch::ENTRY_FLAGS_MASK) | flags.data(); } @@ -490,19 +453,13 @@ impl PageEntry { /// 页表项的标志位 #[derive(Copy, Clone, Hash)] -pub struct EntryFlags { +pub struct PageFlags { data: usize, phantom: PhantomData, } -impl Default for EntryFlags { - fn default() -> Self { - Self::new() - } -} - #[allow(dead_code)] -impl EntryFlags { +impl PageFlags { #[inline(always)] pub fn new() -> Self { let mut r = unsafe { @@ -523,14 +480,14 @@ impl EntryFlags { return r; } - /// 根据ProtFlags生成EntryFlags + /// 根据ProtFlags生成PageFlags /// /// ## 参数 /// /// - prot_flags: 页的保护标志 /// - user: 用户空间是否可访问 - pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> EntryFlags { - let flags: EntryFlags = EntryFlags::new() + pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> PageFlags { + let flags: PageFlags = PageFlags::new() .set_user(user) .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); @@ -800,9 +757,9 @@ impl EntryFlags { } } -impl fmt::Debug for EntryFlags { +impl fmt::Debug for PageFlags { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EntryFlags") + f.debug_struct("PageFlags") .field("bits", &format_args!("{:#0x}", self.data)) .field("present", &self.present()) .field("has_write", &self.has_write()) @@ -898,7 +855,7 @@ impl PageMapper { pub unsafe fn map( &mut self, virt: VirtAddr, - flags: EntryFlags, + flags: PageFlags, ) -> Option> { compiler_fence(Ordering::SeqCst); let phys: PhysAddr = self.frame_allocator.allocate_one()?; @@ -912,7 +869,7 @@ impl PageMapper { let mut page_manager_guard: SpinLockGuard<'static, PageManager> = page_manager_lock_irqsave(); if !page_manager_guard.contains(&phys) { - page_manager_guard.insert(phys, Page::new(false, PhysPageFrame::new(phys))) + page_manager_guard.insert(phys, Page::new(false)) } return self.map_phys(virt, phys, flags); @@ -923,7 +880,7 @@ impl PageMapper { &mut self, virt: VirtAddr, phys: PhysAddr, - flags: EntryFlags, + flags: PageFlags, ) -> Option> { // 验证虚拟地址和物理地址是否对齐 if !(virt.check_aligned(Arch::PAGE_SIZE) && phys.check_aligned(Arch::PAGE_SIZE)) { @@ -963,8 +920,8 @@ impl PageMapper { // 清空这个页帧 MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); // 设置页表项的flags - let flags: EntryFlags = - EntryFlags::new_page_table(virt.kind() == PageTableKind::User); + let flags: PageFlags = + PageFlags::new_page_table(virt.kind() == PageTableKind::User); // 把新分配的页表映射到当前页表 table.set_entry(i, PageEntry::new(frame, flags)); @@ -980,7 +937,7 @@ impl PageMapper { pub unsafe fn map_huge_page( &mut self, virt: VirtAddr, - flags: EntryFlags, + flags: PageFlags, ) -> Option> { // 验证虚拟地址是否对齐 if !(virt.check_aligned(Arch::PAGE_SIZE)) { @@ -1046,8 +1003,7 @@ impl PageMapper { MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); // 设置页表项的flags - let flags: EntryFlags = - EntryFlags::new_page_table(virt.kind() == PageTableKind::User); + let flags: PageFlags = PageFlags::new_page_table(virt.kind() == PageTableKind::User); table.set_entry(i, PageEntry::new(frame, flags)); table.next_level_table(i) @@ -1149,7 +1105,7 @@ impl PageMapper { pub unsafe fn map_linearly( &mut self, phys: PhysAddr, - flags: EntryFlags, + flags: PageFlags, ) -> Option<(VirtAddr, PageFlush)> { let virt: VirtAddr = Arch::phys_2_virt(phys)?; return self.map_phys(virt, phys, flags).map(|flush| (virt, flush)); @@ -1169,7 +1125,7 @@ impl PageMapper { pub unsafe fn remap( &mut self, virt: VirtAddr, - flags: EntryFlags, + flags: PageFlags, ) -> Option> { return self .visit(virt, |p1, i| { @@ -1191,7 +1147,7 @@ impl PageMapper { /// ## 返回值 /// /// 如果查找成功,返回物理地址和页表项的flags,否则返回None - pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, EntryFlags)> { + pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, PageFlags)> { let entry: PageEntry = self.visit(virt, |p1, i| unsafe { p1.entry(i) })??; let paddr = entry.address().ok()?; let flags = entry.flags(); @@ -1230,7 +1186,7 @@ impl PageMapper { &mut self, virt: VirtAddr, unmap_parents: bool, - ) -> Option<(PhysAddr, EntryFlags, PageFlush)> { + ) -> Option<(PhysAddr, PageFlags, PageFlush)> { if !virt.check_aligned(Arch::PAGE_SIZE) { error!("Try to unmap unaligned page: virt={:?}", virt); return None; @@ -1278,7 +1234,7 @@ unsafe fn unmap_phys_inner( table: &PageTable, unmap_parents: bool, allocator: &mut impl FrameAllocator, -) -> Option<(PhysAddr, EntryFlags)> { +) -> Option<(PhysAddr, PageFlags)> { // 获取页表项的索引 let i = table.index_of(vaddr)?; diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 791cd7f10..f5d484bc4 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -20,7 +20,6 @@ use system_error::SystemError; use crate::{ arch::{mm::PageMapper, CurrentIrqArch, MMArch}, exception::InterruptArch, - filesystem::vfs::file::File, libs::{ align::page_align_up, rwlock::RwLock, @@ -35,7 +34,7 @@ use super::{ allocator::page_frame::{ deallocate_page_frames, PageFrameCount, PhysPageFrame, VirtPageFrame, VirtPageFrameIter, }, - page::{EntryFlags, Flusher, InactiveFlusher, PageFlushAll}, + page::{Flusher, InactiveFlusher, PageFlags, PageFlushAll}, syscall::{MadvFlags, MapFlags, MremapFlags, ProtFlags}, MemoryManagementArch, PageTableKind, VirtAddr, VirtRegion, VmFlags, }; @@ -334,7 +333,7 @@ impl InnerAddressSpace { F: FnOnce( VirtPageFrame, PageFrameCount, - EntryFlags, + PageFlags, &mut PageMapper, &mut dyn Flusher, ) -> Result, SystemError>, @@ -381,7 +380,7 @@ impl InnerAddressSpace { self.mappings.insert_vma(map_func( page, page_count, - EntryFlags::from_prot_flags(prot_flags, true), + PageFlags::from_prot_flags(prot_flags, true), &mut self.user_mapper.utable, flusher, )?); @@ -557,7 +556,7 @@ impl InnerAddressSpace { return Err(SystemError::EACCES); } - let new_flags: EntryFlags = r_guard + let new_flags: PageFlags = r_guard .flags() .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); @@ -1023,7 +1022,7 @@ impl LockedVMA { /// pub fn remap( &self, - flags: EntryFlags, + flags: PageFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { @@ -1234,17 +1233,13 @@ pub struct VMA { /// 虚拟内存区域标志 vm_flags: VmFlags, /// VMA内的页帧的标志 - flags: EntryFlags, + flags: PageFlags, /// VMA内的页帧是否已经映射到页表 mapped: bool, /// VMA所属的用户地址空间 user_address_space: Option>, self_ref: Weak, - vm_file: Option>, - /// VMA映射的文件部分相对于整个文件的偏移页数 - file_pgoff: Option, - provider: Provider, } @@ -1267,7 +1262,7 @@ impl VMA { pub fn new( region: VirtRegion, vm_flags: VmFlags, - flags: EntryFlags, + flags: PageFlags, mapped: bool, ) -> Self { VMA { @@ -1278,8 +1273,6 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, - file_pgoff: None, - vm_file: None, } } @@ -1291,10 +1284,6 @@ impl VMA { return &self.vm_flags; } - pub fn vm_file(&self) -> Option> { - return self.vm_file.clone(); - } - pub fn set_vm_flags(&mut self, vm_flags: VmFlags) { self.vm_flags = vm_flags; } @@ -1321,8 +1310,6 @@ impl VMA { user_address_space: self.user_address_space.clone(), self_ref: self.self_ref.clone(), provider: Provider::Allocated, - file_pgoff: self.file_pgoff, - vm_file: self.vm_file.clone(), }; } @@ -1335,21 +1322,14 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, - file_pgoff: self.file_pgoff, - vm_file: self.vm_file.clone(), }; } #[inline(always)] - pub fn flags(&self) -> EntryFlags { + pub fn flags(&self) -> PageFlags { return self.flags; } - #[inline(always)] - pub fn file_page_offset(&self) -> Option { - return self.file_pgoff; - } - pub fn pages(&self) -> VirtPageFrameIter { return VirtPageFrameIter::new( VirtPageFrame::new(self.region.start()), @@ -1359,7 +1339,7 @@ impl VMA { pub fn remap( &mut self, - flags: EntryFlags, + flags: PageFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { @@ -1412,7 +1392,7 @@ impl VMA { destination: VirtPageFrame, count: PageFrameCount, vm_flags: VmFlags, - flags: EntryFlags, + flags: PageFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result, SystemError> { @@ -1434,12 +1414,15 @@ impl VMA { cur_dest = cur_dest.next(); } - let r: Arc = LockedVMA::new(VMA::new( - VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), + let r: Arc = LockedVMA::new(VMA { + region: VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, flags, - true, - )); + mapped: true, + user_address_space: None, + self_ref: Weak::default(), + provider: Provider::Allocated, + }); // 将VMA加入到anon_vma中 let mut page_manager_guard = page_manager_lock_irqsave(); @@ -1467,7 +1450,7 @@ impl VMA { destination: VirtPageFrame, page_count: PageFrameCount, vm_flags: VmFlags, - flags: EntryFlags, + flags: PageFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result, SystemError> { diff --git a/kernel/src/virt/kvm/host_mem.rs b/kernel/src/virt/kvm/host_mem.rs index 95291b146..f34a1e83b 100644 --- a/kernel/src/virt/kvm/host_mem.rs +++ b/kernel/src/virt/kvm/host_mem.rs @@ -2,7 +2,7 @@ use log::debug; use system_error::SystemError; use super::{vcpu::Vcpu, vm}; -use crate::mm::{kernel_mapper::KernelMapper, page::EntryFlags, VirtAddr}; +use crate::mm::{kernel_mapper::KernelMapper, page::PageFlags, VirtAddr}; /* * Address types: @@ -152,7 +152,7 @@ fn hva_to_pfn(addr: u64, _atomic: bool, _writable: &mut bool) -> Result> PAGE_SHIFT); } unsafe { - mapper.map(hva, EntryFlags::mmio_flags()); + mapper.map(hva, PageFlags::mmio_flags()); } let (hpa, _) = mapper.translate(hva).unwrap(); return Ok(hpa.data() as u64 >> PAGE_SHIFT); From aff3316a2ea4e593dc656976189ac97df6da2cb4 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 28 May 2024 18:00:13 +0800 Subject: [PATCH 09/60] 20240528 1800 --- kernel/src/mm/fault.rs | 44 +++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 63116c04f..de92d81c6 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -1,9 +1,15 @@ -use core::{alloc::Layout, cmp::min, intrinsics::unlikely, panic}; +use core::{ + alloc::Layout, + cmp::{max, min}, + intrinsics::unlikely, + panic, +}; use alloc::{sync::Arc, vec::Vec}; use crate::{ arch::{mm::PageMapper, MMArch}, + libs::align::align_down, mm::{ page::{page_manager_lock_irqsave, EntryFlags}, ucontext::LockedVMA, @@ -49,17 +55,22 @@ pub struct PageFaultMessage { /// 异常处理标志 flags: FaultFlags, /// 缺页的文件页在文件中的偏移量 - file_pgoff: usize, + file_pgoff: Option, } impl PageFaultMessage { pub fn new(vma: Arc, address: VirtAddr, flags: FaultFlags) -> Self { + let guard = vma.lock(); + let file_pgoff = if let Some(file_page_offset) = guard.file_page_offset() { + Some(((address - guard.region().start()) >> MMArch::PAGE_SHIFT) + file_page_offset) + } else { + None + }; Self { vma: vma.clone(), address, flags, - file_pgoff: ((address - vma.lock().region().start()) >> MMArch::PAGE_SHIFT) - + vma.lock().file_page_offset().unwrap(), + file_pgoff, } } @@ -450,13 +461,19 @@ impl PageFaultHandler { let pte_pgoff = (pfm.address().data() >> MMArch::PAGE_SHIFT) & (1 << MMArch::PAGE_ENTRY_SHIFT); - let vma_pages_count = (vma_region.end() - vma_region.start()) >> MMArch::PAGE_SHIFT; + // 缺页在文件中的偏移量 + let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); - // 开始位置不能超出当前pte和vma头部 - let from_pte = pte_pgoff - min(vm_pgoff, pte_pgoff); + let vma_pages_count = (vma_region.end() - vma_region.start()) >> MMArch::PAGE_SHIFT; let fault_around_page_number = 16; + // 开始位置不能超出当前pte和vma头部 + let from_pte = max( + align_down(pte_pgoff, fault_around_page_number), + pte_pgoff - min(vm_pgoff, pte_pgoff), + ); + // pte结束位置不能超过: // 1.最大预读上限(默认16) // 2.最大pte(512) @@ -480,8 +497,8 @@ impl PageFaultHandler { Self::filemap_map_pages( pfm.clone(), mapper, - pfm.file_pgoff + (from_pte - pte_pgoff), - pfm.file_pgoff + (to_pte - pte_pgoff), + file_pgoff + (from_pte - pte_pgoff), + file_pgoff + (to_pte - pte_pgoff), ); VmFaultReason::empty() @@ -533,11 +550,12 @@ impl PageFaultHandler { let vma_guard = vma.lock(); let file = vma_guard.vm_file().expect("no vm_file in vma"); let mut page_cache = file.inode().page_cache().unwrap(); + let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); - if let Some(page) = page_cache.get_page(pfm.file_pgoff) { + if let Some(page) = page_cache.get_page(file_pgoff) { // TODO 异步从磁盘中预读页面进PageCache let address = vma_guard.region().start - + ((pfm.file_pgoff + + ((file_pgoff - vma_guard .file_page_offset() .expect("file_page_offset is none")) @@ -550,7 +568,7 @@ impl PageFaultHandler { // TODO 同步预读 let mut buf: Vec = vec![0; MMArch::PAGE_SIZE]; file.pread( - pfm.file_pgoff * MMArch::PAGE_SIZE, + file_pgoff * MMArch::PAGE_SIZE, MMArch::PAGE_SIZE, &mut buf[..], ) @@ -562,7 +580,7 @@ impl PageFaultHandler { (phys_2_virt(new_cache_page.data()) as *mut u8) .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); page_cache.add_page( - pfm.file_pgoff, + file_pgoff, Arc::new(Page::new(false, PhysPageFrame::new(new_cache_page))), ); From 653180e131f387278d6f036cb11c1694bb7a6af2 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 28 May 2024 19:42:27 +0800 Subject: [PATCH 10/60] Revert "Revert "Merge branch 'patch-add-file-mapping' into patch-fix-mmap"" This reverts commit 9261cb79f08d12bbe4b3b5bf29c84625db059c13. --- kernel/src/arch/riscv64/mm/mod.rs | 6 +- kernel/src/arch/x86_64/kvm/vmx/ept.rs | 4 +- kernel/src/arch/x86_64/kvm/vmx/mmu.rs | 4 +- kernel/src/arch/x86_64/mm/mod.rs | 10 +- kernel/src/driver/net/dma.rs | 4 +- kernel/src/driver/video/mod.rs | 4 +- kernel/src/driver/virtio/virtio_impl.rs | 4 +- kernel/src/filesystem/vfs/file.rs | 41 ++++ kernel/src/filesystem/vfs/mod.rs | 11 +- kernel/src/ipc/shm.rs | 2 +- kernel/src/ipc/syscall.rs | 8 +- kernel/src/mm/c_adapter.rs | 4 +- kernel/src/mm/fault.rs | 238 ++++++++++++++++++++---- kernel/src/mm/kernel_mapper.rs | 4 +- kernel/src/mm/mmio_buddy.rs | 6 +- kernel/src/mm/mod.rs | 2 +- kernel/src/mm/no_init.rs | 8 +- kernel/src/mm/page.rs | 96 +++++++--- kernel/src/mm/ucontext.rs | 53 ++++-- kernel/src/virt/kvm/host_mem.rs | 4 +- 20 files changed, 396 insertions(+), 117 deletions(-) diff --git a/kernel/src/arch/riscv64/mm/mod.rs b/kernel/src/arch/riscv64/mm/mod.rs index 76bfbed04..a335dee51 100644 --- a/kernel/src/arch/riscv64/mm/mod.rs +++ b/kernel/src/arch/riscv64/mm/mod.rs @@ -12,7 +12,7 @@ use crate::{ page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage, PhysPageFrame}, }, kernel_mapper::KernelMapper, - page::{PageEntry, PageFlags, PAGE_1G_SHIFT}, + page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}, ucontext::UserMapper, MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, }, @@ -270,8 +270,8 @@ impl VirtAddr { } /// 获取内核地址默认的页面标志 -pub unsafe fn kernel_page_flags(_virt: VirtAddr) -> PageFlags { - PageFlags::from_data(RiscV64MMArch::ENTRY_FLAG_DEFAULT_PAGE) +pub unsafe fn kernel_page_flags(_virt: VirtAddr) -> EntryFlags { + EntryFlags::from_data(RiscV64MMArch::ENTRY_FLAG_DEFAULT_PAGE) .set_user(false) .set_execute(true) } diff --git a/kernel/src/arch/x86_64/kvm/vmx/ept.rs b/kernel/src/arch/x86_64/kvm/vmx/ept.rs index 032231902..838c1a159 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/ept.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/ept.rs @@ -1,7 +1,7 @@ use crate::arch::mm::LockedFrameAllocator; use crate::arch::mm::PageMapper; use crate::arch::MMArch; -use crate::mm::page::PageFlags; +use crate::mm::page::EntryFlags; use crate::mm::{PageTableKind, PhysAddr, VirtAddr}; use crate::smp::core::smp_get_processor_id; use crate::smp::cpu::AtomicProcessorId; @@ -92,7 +92,7 @@ impl EptMapper { &mut self, gpa: u64, hpa: u64, - flags: PageFlags, + flags: EntryFlags, ) -> Result<(), SystemError> { if self.readonly { return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); diff --git a/kernel/src/arch/x86_64/kvm/vmx/mmu.rs b/kernel/src/arch/x86_64/kvm/vmx/mmu.rs index e28b3f03d..c05ef9bb5 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/mmu.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/mmu.rs @@ -1,7 +1,7 @@ use crate::{ arch::kvm::vmx::ept::EptMapper, libs::mutex::Mutex, - mm::{page::PageFlags, syscall::ProtFlags}, + mm::{page::EntryFlags, syscall::ProtFlags}, virt::kvm::host_mem::{__gfn_to_pfn, kvm_vcpu_gfn_to_memslot, PAGE_MASK, PAGE_SHIFT}, }; use bitfield_struct::bitfield; @@ -218,7 +218,7 @@ pub fn __direct_map( } // 把gpa映射到hpa let mut ept_mapper = EptMapper::lock(); - let page_flags = PageFlags::from_prot_flags(ProtFlags::from_bits_truncate(0x7_u64), false); + let page_flags = EntryFlags::from_prot_flags(ProtFlags::from_bits_truncate(0x7_u64), false); unsafe { assert!(ept_mapper.walk(gpa, pfn << PAGE_SHIFT, page_flags).is_ok()); } diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index 6eaf2dd5a..841ae4335 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -28,7 +28,7 @@ use crate::{ }; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{PageEntry, PageFlags, PAGE_1G_SHIFT}; +use crate::mm::page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}; use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr}; use system_error::SystemError; @@ -650,17 +650,17 @@ impl FrameAllocator for LockedFrameAllocator { } /// 获取内核地址默认的页面标志 -pub unsafe fn kernel_page_flags(virt: VirtAddr) -> PageFlags { +pub unsafe fn kernel_page_flags(virt: VirtAddr) -> EntryFlags { let info: X86_64MMBootstrapInfo = BOOTSTRAP_MM_INFO.unwrap(); if virt.data() >= info.kernel_code_start && virt.data() < info.kernel_code_end { // Remap kernel code execute - return PageFlags::new().set_execute(true).set_write(true); + return EntryFlags::new().set_execute(true).set_write(true); } else if virt.data() >= info.kernel_data_end && virt.data() < info.kernel_rodata_end { // Remap kernel rodata read only - return PageFlags::new().set_execute(true); + return EntryFlags::new().set_execute(true); } else { - return PageFlags::new().set_write(true).set_execute(true); + return EntryFlags::new().set_write(true).set_execute(true); } } diff --git a/kernel/src/driver/net/dma.rs b/kernel/src/driver/net/dma.rs index 11fcf6229..f8c06b74e 100644 --- a/kernel/src/driver/net/dma.rs +++ b/kernel/src/driver/net/dma.rs @@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags; use crate::arch::MMArch; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, PageFlags}; +use crate::mm::page::{page_manager_lock_irqsave, EntryFlags}; use crate::mm::{ allocator::page_frame::{ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, @@ -25,7 +25,7 @@ pub fn dma_alloc(pages: usize) -> (usize, NonNull) { // 清空这块区域,防止出现脏数据 core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); - let dma_flags: PageFlags = PageFlags::mmio_flags(); + let dma_flags: EntryFlags = EntryFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let kernel_mapper = kernel_mapper.as_mut().unwrap(); diff --git a/kernel/src/driver/video/mod.rs b/kernel/src/driver/video/mod.rs index 282e672d3..e9a705140 100644 --- a/kernel/src/driver/video/mod.rs +++ b/kernel/src/driver/video/mod.rs @@ -10,7 +10,7 @@ use crate::{ spinlock::SpinLock, }, mm::{ - allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::PageFlags, + allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::EntryFlags, MemoryManagementArch, }, time::timer::{Timer, TimerFunction}, @@ -95,7 +95,7 @@ impl VideoRefreshManager { let count = PageFrameCount::new( page_align_up(frame_buffer_info_guard.buf_size()) / MMArch::PAGE_SIZE, ); - let page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); + let page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); let mut kernel_mapper = KernelMapper::lock(); let mut kernel_mapper = kernel_mapper.as_mut(); diff --git a/kernel/src/driver/virtio/virtio_impl.rs b/kernel/src/driver/virtio/virtio_impl.rs index 256b40eb2..0166b138b 100644 --- a/kernel/src/driver/virtio/virtio_impl.rs +++ b/kernel/src/driver/virtio/virtio_impl.rs @@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags; use crate::arch::MMArch; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, PageFlags}; +use crate::mm::page::{page_manager_lock_irqsave, EntryFlags}; use crate::mm::{ allocator::page_frame::{ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, @@ -32,7 +32,7 @@ unsafe impl Hal for HalImpl { // 清空这块区域,防止出现脏数据 core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); - let dma_flags: PageFlags = PageFlags::mmio_flags(); + let dma_flags: EntryFlags = EntryFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let kernel_mapper = kernel_mapper.as_mut().unwrap(); diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 2271065dd..c76460767 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -5,6 +5,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use hashbrown::HashMap; use log::error; use system_error::SystemError; @@ -16,6 +17,7 @@ use crate::{ filesystem::procfs::ProcfsFilePrivateData, ipc::pipe::{LockedPipeInode, PipeFsPrivateData}, libs::{rwlock::RwLock, spinlock::SpinLock}, + mm::page::Page, net::{ event_poll::{EPollItem, EPollPrivateData, EventPoll}, socket::SocketInode, @@ -118,6 +120,45 @@ impl FileMode { return self.bits() & FileMode::O_ACCMODE.bits(); } } + +#[allow(dead_code)] +pub struct PageCache { + inode_ref: Weak, + map: HashMap>, +} + +impl PageCache { + pub fn new(inode_ref: Weak) -> PageCache { + Self { + inode_ref, + map: HashMap::new(), + } + } + + pub fn add_page(&mut self, offset: usize, page: Arc) { + self.map.insert(offset, page); + } + + pub fn get_page(&self, offset: usize) -> Option> { + self.map.get(&offset).cloned() + } + + // pub fn get_pages(&self, start_pgoff: usize, end_pgoff: usize) -> Vec> { + // let mut vec = Vec::new(); + // for pgoff in start_pgoff..=end_pgoff { + // if let Some(page) = self.map.get(&pgoff) { + // vec.push(page.clone()); + // } + // } + // vec + // } +} + +pub trait PageCacheOperations: IndexNode { + fn write_page(&self, page: Page); + fn read_ahead(&self); +} + /// @brief 抽象文件结构体 #[derive(Debug)] pub struct File { diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 7bdbe47bc..09c04f072 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -23,7 +23,12 @@ use crate::{ time::PosixTimeSpec, }; -use self::{core::generate_inode_id, file::FileMode, syscall::ModeType, utils::DName}; +use self::{ + core::generate_inode_id, + file::{FileMode, PageCache}, + syscall::ModeType, + utils::DName, +}; pub use self::{core::ROOT_INODE, file::FilePrivateData, mount::MountFS}; /// vfs容许的最大的路径名称长度 @@ -548,6 +553,10 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { fn parent(&self) -> Result, SystemError> { return self.find(".."); } + + fn page_cache(&self) -> Option { + None + } } impl DowncastArc for dyn IndexNode { diff --git a/kernel/src/ipc/shm.rs b/kernel/src/ipc/shm.rs index 13c9f1b06..f3f87eb74 100644 --- a/kernel/src/ipc/shm.rs +++ b/kernel/src/ipc/shm.rs @@ -165,7 +165,7 @@ impl ShmManager { let mut page_manager_guard = page_manager_lock_irqsave(); let mut cur_phys = PhysPageFrame::new(phys_page.0); for _ in 0..page_count.data() { - let mut page = Page::new(true); + let mut page = Page::new(true, cur_phys); page.set_shm_id(shm_id); let paddr = cur_phys.phys_address(); page_manager_guard.insert(paddr, page); diff --git a/kernel/src/ipc/syscall.rs b/kernel/src/ipc/syscall.rs index 37a9cc9c6..6cc525bba 100644 --- a/kernel/src/ipc/syscall.rs +++ b/kernel/src/ipc/syscall.rs @@ -20,7 +20,7 @@ use crate::{ libs::spinlock::SpinLock, mm::{ allocator::page_frame::{PageFrameCount, PhysPageFrame, VirtPageFrame}, - page::{page_manager_lock_irqsave, PageFlags, PageFlushAll}, + page::{page_manager_lock_irqsave, EntryFlags, PageFlushAll}, syscall::ProtFlags, ucontext::{AddressSpace, VMA}, VirtAddr, VmFlags, @@ -324,8 +324,8 @@ impl Syscall { .ok_or(SystemError::EINVAL)?; let vm_flags = VmFlags::from(shmflg); let destination = VirtPageFrame::new(region.start()); - let page_flags: PageFlags = - PageFlags::from_prot_flags(ProtFlags::from(vm_flags), true); + let page_flags: EntryFlags = + EntryFlags::from_prot_flags(ProtFlags::from(vm_flags), true); let flusher: PageFlushAll = PageFlushAll::new(); // 将共享内存映射到对应虚拟区域 @@ -358,7 +358,7 @@ impl Syscall { // 验证用户虚拟内存区域是否有效 let _ = UserBufferReader::new(vaddr.data() as *const u8, size, true)?; - // 必须在取消映射前获取到PageFlags + // 必须在取消映射前获取到EntryFlags let page_flags = address_write_guard .user_mapper .utable diff --git a/kernel/src/mm/c_adapter.rs b/kernel/src/mm/c_adapter.rs index bd949fa5c..1c824595d 100644 --- a/kernel/src/mm/c_adapter.rs +++ b/kernel/src/mm/c_adapter.rs @@ -15,7 +15,7 @@ use crate::{ use super::{ allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, mmio_buddy::mmio_pool, - no_init::pseudo_map_phys, page::PageFlags, MemoryManagementArch, PhysAddr, VirtAddr, + no_init::pseudo_map_phys, page::EntryFlags, MemoryManagementArch, PhysAddr, VirtAddr, }; lazy_static! { @@ -40,7 +40,7 @@ pub unsafe extern "C" fn rs_map_phys(vaddr: usize, paddr: usize, size: usize, fl let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); // debug!("rs_map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); - let mut page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); + let mut page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); if flags & PAGE_U_S as usize != 0 { page_flags = page_flags.set_user(true); } diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 71e0623ae..63116c04f 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -1,11 +1,11 @@ -use core::{alloc::Layout, intrinsics::unlikely, panic}; +use core::{alloc::Layout, cmp::min, intrinsics::unlikely, panic}; -use alloc::sync::Arc; +use alloc::{sync::Arc, vec::Vec}; use crate::{ arch::{mm::PageMapper, MMArch}, mm::{ - page::{page_manager_lock_irqsave, PageFlags}, + page::{page_manager_lock_irqsave, EntryFlags}, ucontext::LockedVMA, VirtAddr, VmFaultReason, VmFlags, }, @@ -14,21 +14,27 @@ use crate::{ use crate::mm::MemoryManagementArch; +use super::{ + allocator::page_frame::{FrameAllocator, PhysPageFrame}, + page::{Page, PageFlags}, + phys_2_virt, +}; + bitflags! { pub struct FaultFlags: u64{ - const FAULT_FLAG_WRITE = 1 << 0; - const FAULT_FLAG_MKWRITE = 1 << 1; - const FAULT_FLAG_ALLOW_RETRY = 1 << 2; - const FAULT_FLAG_RETRY_NOWAIT = 1 << 3; - const FAULT_FLAG_KILLABLE = 1 << 4; - const FAULT_FLAG_TRIED = 1 << 5; - const FAULT_FLAG_USER = 1 << 6; - const FAULT_FLAG_REMOTE = 1 << 7; - const FAULT_FLAG_INSTRUCTION = 1 << 8; - const FAULT_FLAG_INTERRUPTIBLE =1 << 9; - const FAULT_FLAG_UNSHARE = 1 << 10; - const FAULT_FLAG_ORIG_PTE_VALID = 1 << 11; - const FAULT_FLAG_VMA_LOCK = 1 << 12; + const FAULT_FLAG_WRITE = 1 << 0; + const FAULT_FLAG_MKWRITE = 1 << 1; + const FAULT_FLAG_ALLOW_RETRY = 1 << 2; + const FAULT_FLAG_RETRY_NOWAIT = 1 << 3; + const FAULT_FLAG_KILLABLE = 1 << 4; + const FAULT_FLAG_TRIED = 1 << 5; + const FAULT_FLAG_USER = 1 << 6; + const FAULT_FLAG_REMOTE = 1 << 7; + const FAULT_FLAG_INSTRUCTION = 1 << 8; + const FAULT_FLAG_INTERRUPTIBLE =1 << 9; + const FAULT_FLAG_UNSHARE = 1 << 10; + const FAULT_FLAG_ORIG_PTE_VALID = 1 << 11; + const FAULT_FLAG_VMA_LOCK = 1 << 12; } } @@ -36,9 +42,14 @@ bitflags! { /// 包含了页面错误处理的相关信息,例如出错的地址、VMA等 #[derive(Debug)] pub struct PageFaultMessage { + /// 产生缺页的VMA结构体 vma: Arc, + /// 缺页地址 address: VirtAddr, + /// 异常处理标志 flags: FaultFlags, + /// 缺页的文件页在文件中的偏移量 + file_pgoff: usize, } impl PageFaultMessage { @@ -47,6 +58,8 @@ impl PageFaultMessage { vma: vma.clone(), address, flags, + file_pgoff: ((address - vma.lock().region().start()) >> MMArch::PAGE_SHIFT) + + vma.lock().file_page_offset().unwrap(), } } @@ -58,8 +71,8 @@ impl PageFaultMessage { #[inline(always)] #[allow(dead_code)] - pub fn address(&self) -> VirtAddr { - self.address + pub fn address(&self) -> &VirtAddr { + &self.address } #[inline(always)] @@ -70,8 +83,8 @@ impl PageFaultMessage { #[inline(always)] #[allow(dead_code)] - pub fn flags(&self) -> FaultFlags { - self.flags + pub fn flags(&self) -> &FaultFlags { + &self.flags } } @@ -81,6 +94,7 @@ impl Clone for PageFaultMessage { vma: self.vma.clone(), address: self.address, flags: self.flags, + file_pgoff: self.file_pgoff, } } } @@ -189,7 +203,7 @@ impl PageFaultHandler { if !entry.write() { ret = Self::do_wp_page(pfm.clone(), mapper); } else { - entry.set_flags(PageFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); + entry.set_flags(EntryFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); } } } else if vma.is_anonymous() { @@ -248,14 +262,22 @@ impl PageFaultHandler { /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] pub unsafe fn do_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - panic!( - "do_fault has not yet been implemented, - fault message: {:?}, - pid: {}\n", - pfm, - crate::process::ProcessManager::current_pid().data() - ); + // panic!( + // "do_fault has not yet been implemented, + // fault message: {:?}, + // pid: {}\n", + // pfm, + // crate::process::ProcessManager::current_pid().data() + // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_fault + + if !pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) { + return Self::do_read_fault(pfm, mapper); + } else if !pfm.vma().lock().vm_flags().contains(VmFlags::VM_SHARED) { + return Self::do_cow_fault(pfm, mapper); + } else { + return Self::do_shared_fault(pfm, mapper); + } } /// 处理私有文件映射的写时复制 @@ -288,14 +310,20 @@ impl PageFaultHandler { /// - VmFaultReason: 页面错误处理信息标志 #[allow(dead_code, unused_variables)] pub unsafe fn do_read_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - panic!( - "do_read_fault has not yet been implemented, - fault message: {:?}, - pid: {}\n", - pfm, - crate::process::ProcessManager::current_pid().data() - ); + // panic!( + // "do_read_fault has not yet been implemented, + // fault message: {:?}, + // pid: {}\n", + // pfm, + // crate::process::ProcessManager::current_pid().data() + // ); + // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_read_fault + let ret = Self::do_fault_around(pfm.clone(), mapper); + if !ret.is_empty() { + return ret; + } + return Self::filemap_fault(pfm.clone(), mapper); } /// 处理对共享文件映射区写入引起的缺页 @@ -405,4 +433,144 @@ impl PageFaultHandler { VmFaultReason::VM_FAULT_OOM } } + + pub unsafe fn do_fault_around(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + if mapper.get_table(*pfm.address(), 0).is_none() { + mapper + .allocate_table(*pfm.address(), 0) + .expect("failed to allocate pte table"); + } + let vma = pfm.vma(); + let vma_guard = vma.lock(); + let vma_region = vma_guard.region(); + // 缺页在VMA中的偏移量 + let vm_pgoff = (*pfm.address() - vma_region.start()) >> MMArch::PAGE_SHIFT; + + // 缺页在PTE中的偏移量 + let pte_pgoff = + (pfm.address().data() >> MMArch::PAGE_SHIFT) & (1 << MMArch::PAGE_ENTRY_SHIFT); + + let vma_pages_count = (vma_region.end() - vma_region.start()) >> MMArch::PAGE_SHIFT; + + // 开始位置不能超出当前pte和vma头部 + let from_pte = pte_pgoff - min(vm_pgoff, pte_pgoff); + + let fault_around_page_number = 16; + + // pte结束位置不能超过: + // 1.最大预读上限(默认16) + // 2.最大pte(512) + // 3.vma结束位置(pte_pgoff + (vma_pages_count - vm_pgoff)计算出vma结束页号对当前pte开头的偏移) + let to_pte = min( + from_pte + fault_around_page_number, + min( + 1 << MMArch::PAGE_SHIFT, + pte_pgoff + (vma_pages_count - vm_pgoff), + ), + ); + + // 预先分配pte页表(如果不存在) + if mapper.get_table(*pfm.address(), 0).is_none() + && mapper.allocate_table(*pfm.address(), 0).is_none() + { + return VmFaultReason::VM_FAULT_OOM; + } + + // from_pte - pte_pgoff得出预读起始pte相对缺失页的偏移,加上pfm.file_pgoff(缺失页在文件中的偏移)得出起始页在文件中的偏移,结束pte同理 + Self::filemap_map_pages( + pfm.clone(), + mapper, + pfm.file_pgoff + (from_pte - pte_pgoff), + pfm.file_pgoff + (to_pte - pte_pgoff), + ); + + VmFaultReason::empty() + } + + pub unsafe fn filemap_map_pages( + pfm: PageFaultMessage, + mapper: &mut PageMapper, + start_pgoff: usize, + end_pgoff: usize, + ) -> VmFaultReason { + let vma = pfm.vma(); + let vma_guard = vma.lock(); + let file = vma_guard.vm_file().expect("no vm_file in vma"); + let page_cache = file.inode().page_cache().unwrap(); + + // 起始页地址 + let addr = vma_guard.region().start + + ((start_pgoff + - vma_guard + .file_page_offset() + .expect("file_page_offset is none")) + << MMArch::PAGE_SHIFT); + // let pages = page_cache.get_pages(start_pgoff, end_pgoff); + // let uptodate_pages = pages + // .iter() + // .filter(|page| page.flags().contains(PageFlags::PG_UPTODATE)); + for pgoff in start_pgoff..=end_pgoff { + if let Some(page) = page_cache.get_page(pgoff) { + if page.flags().contains(PageFlags::PG_UPTODATE) { + let phys = page.phys_frame().phys_address(); + let virt = phys_2_virt(phys.data()); + + let address = + VirtAddr::new(addr.data() + ((pgoff - start_pgoff) << MMArch::PAGE_SHIFT)); + mapper.map(address, vma_guard.flags()).unwrap().flush(); + let frame = virt as *mut u8; + let new_frame = + phys_2_virt(mapper.translate(address).unwrap().0.data()) as *mut u8; + new_frame.copy_from_nonoverlapping(frame, MMArch::PAGE_SIZE); + } + } + } + VmFaultReason::empty() + } + + pub unsafe fn filemap_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + let vma = pfm.vma(); + let vma_guard = vma.lock(); + let file = vma_guard.vm_file().expect("no vm_file in vma"); + let mut page_cache = file.inode().page_cache().unwrap(); + + if let Some(page) = page_cache.get_page(pfm.file_pgoff) { + // TODO 异步从磁盘中预读页面进PageCache + let address = vma_guard.region().start + + ((pfm.file_pgoff + - vma_guard + .file_page_offset() + .expect("file_page_offset is none")) + << MMArch::PAGE_SHIFT); + mapper.map(address, vma_guard.flags()).unwrap().flush(); + let frame = phys_2_virt(page.phys_frame().phys_address().data()) as *mut u8; + let new_frame = phys_2_virt(mapper.translate(address).unwrap().0.data()) as *mut u8; + new_frame.copy_from_nonoverlapping(frame, MMArch::PAGE_SIZE); + } else { + // TODO 同步预读 + let mut buf: Vec = vec![0; MMArch::PAGE_SIZE]; + file.pread( + pfm.file_pgoff * MMArch::PAGE_SIZE, + MMArch::PAGE_SIZE, + &mut buf[..], + ) + .unwrap(); + let allocator = mapper.allocator_mut(); + + // 分配一个物理页面作为加入PageCache的新页 + let new_cache_page = allocator.allocate_one().unwrap(); + (phys_2_virt(new_cache_page.data()) as *mut u8) + .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); + page_cache.add_page( + pfm.file_pgoff, + Arc::new(Page::new(false, PhysPageFrame::new(new_cache_page))), + ); + + // 分配空白页并映射到缺页地址 + mapper.map(pfm.address, vma_guard.flags()).unwrap().flush(); + let new_frame = phys_2_virt(mapper.translate(pfm.address).unwrap().0.data()); + (new_frame as *mut u8).copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); + } + VmFaultReason::VM_FAULT_COMPLETED + } } diff --git a/kernel/src/mm/kernel_mapper.rs b/kernel/src/mm/kernel_mapper.rs index 682fad602..487d15378 100644 --- a/kernel/src/mm/kernel_mapper.rs +++ b/kernel/src/mm/kernel_mapper.rs @@ -1,6 +1,6 @@ use system_error::SystemError; -use super::{page::PageFlags, PageTableKind, PhysAddr, VirtAddr}; +use super::{page::EntryFlags, PageTableKind, PhysAddr, VirtAddr}; use crate::{ arch::{ mm::{LockedFrameAllocator, PageMapper}, @@ -104,7 +104,7 @@ impl KernelMapper { mut vaddr: VirtAddr, mut paddr: PhysAddr, size: usize, - flags: PageFlags, + flags: EntryFlags, flush: bool, ) -> Result<(), SystemError> { if self.readonly { diff --git a/kernel/src/mm/mmio_buddy.rs b/kernel/src/mm/mmio_buddy.rs index 99e845d3f..b15eda9a1 100644 --- a/kernel/src/mm/mmio_buddy.rs +++ b/kernel/src/mm/mmio_buddy.rs @@ -12,7 +12,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use log::{debug, error, info, warn}; use system_error::SystemError; -use super::page::{PageFlags, PAGE_4K_SIZE}; +use super::page::{EntryFlags, PAGE_4K_SIZE}; use super::{PhysAddr, VirtAddr}; // 最大的伙伴块的幂 @@ -549,7 +549,7 @@ impl MmioBuddyMemPool { unsafe { let x: Option<( PhysAddr, - PageFlags, + EntryFlags, crate::mm::page::PageFlush, )> = kernel_mapper .as_mut() @@ -674,7 +674,7 @@ impl MMIOSpaceGuard { return Err(SystemError::EINVAL); } - let flags = PageFlags::mmio_flags(); + let flags = EntryFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let r = kernel_mapper.map_phys_with_size(self.vaddr, paddr, length, flags, true); diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index 49e61fcd1..bfd051933 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -610,7 +610,7 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { /// 创建页表项 /// - /// 这是一个低阶api,用于根据物理地址以及指定好的pageflags,创建页表项 + /// 这是一个低阶api,用于根据物理地址以及指定好的EntryFlags,创建页表项 /// /// ## 参数 /// diff --git a/kernel/src/mm/no_init.rs b/kernel/src/mm/no_init.rs index 855a86767..fdb8d4d66 100644 --- a/kernel/src/mm/no_init.rs +++ b/kernel/src/mm/no_init.rs @@ -19,7 +19,7 @@ use core::marker::PhantomData; use super::{ allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage}, - page::PageFlags, + page::EntryFlags, PageTableKind, VirtAddr, }; @@ -141,7 +141,7 @@ impl FrameAllocator for PseudoAllocator { /// 并且,内核引导文件必须以4K页为粒度,填写了前100M的内存映射关系。(具体以本文件开头的注释为准) #[inline(never)] pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { - let flags: PageFlags = PageFlags::new().set_write(true); + let flags: EntryFlags = EntryFlags::new().set_write(true); pseudo_map_phys_with_flags(vaddr, paddr, count, flags); } @@ -150,7 +150,7 @@ pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrame /// with READ_ONLY and EXECUTE flags. #[inline(never)] pub unsafe fn pseudo_map_phys_ro(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { - let flags: PageFlags = PageFlags::new().set_write(false).set_execute(true); + let flags: EntryFlags = EntryFlags::new().set_write(false).set_execute(true); pseudo_map_phys_with_flags(vaddr, paddr, count, flags); } @@ -160,7 +160,7 @@ pub unsafe fn pseudo_map_phys_with_flags( vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount, - flags: PageFlags, + flags: EntryFlags, ) { assert!(vaddr.check_aligned(MMArch::PAGE_SIZE)); assert!(paddr.check_aligned(MMArch::PAGE_SIZE)); diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 117708111..b359c129a 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -18,7 +18,7 @@ use crate::{ }; use super::{ - allocator::page_frame::{FrameAllocator, PageFrameCount}, + allocator::page_frame::{FrameAllocator, PageFrameCount, PhysPageFrame}, syscall::ProtFlags, ucontext::LockedVMA, MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, @@ -86,6 +86,27 @@ impl PageManager { } } +bitflags! { + pub struct PageFlags: u64 { + const PG_LOCKED = 1 << 0; + const PG_WRITEBACK = 1 << 1; + const PG_REFERENCED = 1 << 2; + const PG_UPTODATE = 1 << 3; + const PG_DIRTY = 1 << 4; + const PG_LRU = 1 << 5; + const PG_HEAD = 1 << 6; + const PG_WAITERS = 1 << 7; + const PG_ACTIVE = 1 << 8; + const PG_WORKINGSET = 1 << 9; + const PG_ERROR = 1 << 10; + const PG_SLAB = 1 << 11; + const PG_RESERVED = 1 << 14; + const PG_PRIVATE = 1 << 15; + const PG_RECLAIM = 1 << 18; + const PG_SWAPBACKED = 1 << 19; + } +} + /// 物理页面信息 pub struct Page { /// 映射计数 @@ -98,10 +119,14 @@ pub struct Page { shm_id: Option, /// 映射到当前page的VMA anon_vma: HashSet>, + /// 标志 + flags: PageFlags, + /// 页所在的物理页帧号 + phys_frame: PhysPageFrame, } impl Page { - pub fn new(shared: bool) -> Self { + pub fn new(shared: bool, phys_frame: PhysPageFrame) -> Self { let dealloc_when_zero = !shared; Self { map_count: 0, @@ -109,6 +134,8 @@ impl Page { free_when_zero: dealloc_when_zero, shm_id: None, anon_vma: HashSet::new(), + flags: PageFlags::empty(), + phys_frame, } } @@ -154,6 +181,16 @@ impl Page { pub fn map_count(&self) -> usize { self.map_count } + + #[inline(always)] + pub fn flags(&self) -> &PageFlags { + &self.flags + } + + #[inline(always)] + pub fn phys_frame(&self) -> &PhysPageFrame { + &self.phys_frame + } } #[derive(Debug)] @@ -330,7 +367,7 @@ impl PageTable { } else { let phys = allocator.allocate_one()?; let mut anon_vma_guard = page_manager_lock_irqsave(); - anon_vma_guard.insert(phys, Page::new(false)); + anon_vma_guard.insert(phys, Page::new(false, PhysPageFrame::new(phys))); let old_phys = entry.address().unwrap(); let frame = MMArch::phys_2_virt(phys).unwrap().data() as *mut u8; frame.copy_from_nonoverlapping( @@ -372,7 +409,7 @@ impl Debug for PageEntry { impl PageEntry { #[inline(always)] - pub fn new(paddr: PhysAddr, flags: PageFlags) -> Self { + pub fn new(paddr: PhysAddr, flags: EntryFlags) -> Self { Self { data: MMArch::make_entry(paddr, flags.data()), phantom: PhantomData, @@ -420,12 +457,12 @@ impl PageEntry { } #[inline(always)] - pub fn flags(&self) -> PageFlags { - unsafe { PageFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } + pub fn flags(&self) -> EntryFlags { + unsafe { EntryFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } } #[inline(always)] - pub fn set_flags(&mut self, flags: PageFlags) { + pub fn set_flags(&mut self, flags: EntryFlags) { self.data = (self.data & !Arch::ENTRY_FLAGS_MASK) | flags.data(); } @@ -453,13 +490,19 @@ impl PageEntry { /// 页表项的标志位 #[derive(Copy, Clone, Hash)] -pub struct PageFlags { +pub struct EntryFlags { data: usize, phantom: PhantomData, } +impl Default for EntryFlags { + fn default() -> Self { + Self::new() + } +} + #[allow(dead_code)] -impl PageFlags { +impl EntryFlags { #[inline(always)] pub fn new() -> Self { let mut r = unsafe { @@ -480,14 +523,14 @@ impl PageFlags { return r; } - /// 根据ProtFlags生成PageFlags + /// 根据ProtFlags生成EntryFlags /// /// ## 参数 /// /// - prot_flags: 页的保护标志 /// - user: 用户空间是否可访问 - pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> PageFlags { - let flags: PageFlags = PageFlags::new() + pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> EntryFlags { + let flags: EntryFlags = EntryFlags::new() .set_user(user) .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); @@ -757,9 +800,9 @@ impl PageFlags { } } -impl fmt::Debug for PageFlags { +impl fmt::Debug for EntryFlags { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PageFlags") + f.debug_struct("EntryFlags") .field("bits", &format_args!("{:#0x}", self.data)) .field("present", &self.present()) .field("has_write", &self.has_write()) @@ -855,7 +898,7 @@ impl PageMapper { pub unsafe fn map( &mut self, virt: VirtAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { compiler_fence(Ordering::SeqCst); let phys: PhysAddr = self.frame_allocator.allocate_one()?; @@ -869,7 +912,7 @@ impl PageMapper { let mut page_manager_guard: SpinLockGuard<'static, PageManager> = page_manager_lock_irqsave(); if !page_manager_guard.contains(&phys) { - page_manager_guard.insert(phys, Page::new(false)) + page_manager_guard.insert(phys, Page::new(false, PhysPageFrame::new(phys))) } return self.map_phys(virt, phys, flags); @@ -880,7 +923,7 @@ impl PageMapper { &mut self, virt: VirtAddr, phys: PhysAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { // 验证虚拟地址和物理地址是否对齐 if !(virt.check_aligned(Arch::PAGE_SIZE) && phys.check_aligned(Arch::PAGE_SIZE)) { @@ -920,8 +963,8 @@ impl PageMapper { // 清空这个页帧 MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); // 设置页表项的flags - let flags: PageFlags = - PageFlags::new_page_table(virt.kind() == PageTableKind::User); + let flags: EntryFlags = + EntryFlags::new_page_table(virt.kind() == PageTableKind::User); // 把新分配的页表映射到当前页表 table.set_entry(i, PageEntry::new(frame, flags)); @@ -937,7 +980,7 @@ impl PageMapper { pub unsafe fn map_huge_page( &mut self, virt: VirtAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { // 验证虚拟地址是否对齐 if !(virt.check_aligned(Arch::PAGE_SIZE)) { @@ -1003,7 +1046,8 @@ impl PageMapper { MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); // 设置页表项的flags - let flags: PageFlags = PageFlags::new_page_table(virt.kind() == PageTableKind::User); + let flags: EntryFlags = + EntryFlags::new_page_table(virt.kind() == PageTableKind::User); table.set_entry(i, PageEntry::new(frame, flags)); table.next_level_table(i) @@ -1105,7 +1149,7 @@ impl PageMapper { pub unsafe fn map_linearly( &mut self, phys: PhysAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option<(VirtAddr, PageFlush)> { let virt: VirtAddr = Arch::phys_2_virt(phys)?; return self.map_phys(virt, phys, flags).map(|flush| (virt, flush)); @@ -1125,7 +1169,7 @@ impl PageMapper { pub unsafe fn remap( &mut self, virt: VirtAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { return self .visit(virt, |p1, i| { @@ -1147,7 +1191,7 @@ impl PageMapper { /// ## 返回值 /// /// 如果查找成功,返回物理地址和页表项的flags,否则返回None - pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, PageFlags)> { + pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, EntryFlags)> { let entry: PageEntry = self.visit(virt, |p1, i| unsafe { p1.entry(i) })??; let paddr = entry.address().ok()?; let flags = entry.flags(); @@ -1186,7 +1230,7 @@ impl PageMapper { &mut self, virt: VirtAddr, unmap_parents: bool, - ) -> Option<(PhysAddr, PageFlags, PageFlush)> { + ) -> Option<(PhysAddr, EntryFlags, PageFlush)> { if !virt.check_aligned(Arch::PAGE_SIZE) { error!("Try to unmap unaligned page: virt={:?}", virt); return None; @@ -1234,7 +1278,7 @@ unsafe fn unmap_phys_inner( table: &PageTable, unmap_parents: bool, allocator: &mut impl FrameAllocator, -) -> Option<(PhysAddr, PageFlags)> { +) -> Option<(PhysAddr, EntryFlags)> { // 获取页表项的索引 let i = table.index_of(vaddr)?; diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index f5d484bc4..791cd7f10 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -20,6 +20,7 @@ use system_error::SystemError; use crate::{ arch::{mm::PageMapper, CurrentIrqArch, MMArch}, exception::InterruptArch, + filesystem::vfs::file::File, libs::{ align::page_align_up, rwlock::RwLock, @@ -34,7 +35,7 @@ use super::{ allocator::page_frame::{ deallocate_page_frames, PageFrameCount, PhysPageFrame, VirtPageFrame, VirtPageFrameIter, }, - page::{Flusher, InactiveFlusher, PageFlags, PageFlushAll}, + page::{EntryFlags, Flusher, InactiveFlusher, PageFlushAll}, syscall::{MadvFlags, MapFlags, MremapFlags, ProtFlags}, MemoryManagementArch, PageTableKind, VirtAddr, VirtRegion, VmFlags, }; @@ -333,7 +334,7 @@ impl InnerAddressSpace { F: FnOnce( VirtPageFrame, PageFrameCount, - PageFlags, + EntryFlags, &mut PageMapper, &mut dyn Flusher, ) -> Result, SystemError>, @@ -380,7 +381,7 @@ impl InnerAddressSpace { self.mappings.insert_vma(map_func( page, page_count, - PageFlags::from_prot_flags(prot_flags, true), + EntryFlags::from_prot_flags(prot_flags, true), &mut self.user_mapper.utable, flusher, )?); @@ -556,7 +557,7 @@ impl InnerAddressSpace { return Err(SystemError::EACCES); } - let new_flags: PageFlags = r_guard + let new_flags: EntryFlags = r_guard .flags() .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); @@ -1022,7 +1023,7 @@ impl LockedVMA { /// pub fn remap( &self, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { @@ -1233,13 +1234,17 @@ pub struct VMA { /// 虚拟内存区域标志 vm_flags: VmFlags, /// VMA内的页帧的标志 - flags: PageFlags, + flags: EntryFlags, /// VMA内的页帧是否已经映射到页表 mapped: bool, /// VMA所属的用户地址空间 user_address_space: Option>, self_ref: Weak, + vm_file: Option>, + /// VMA映射的文件部分相对于整个文件的偏移页数 + file_pgoff: Option, + provider: Provider, } @@ -1262,7 +1267,7 @@ impl VMA { pub fn new( region: VirtRegion, vm_flags: VmFlags, - flags: PageFlags, + flags: EntryFlags, mapped: bool, ) -> Self { VMA { @@ -1273,6 +1278,8 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, + file_pgoff: None, + vm_file: None, } } @@ -1284,6 +1291,10 @@ impl VMA { return &self.vm_flags; } + pub fn vm_file(&self) -> Option> { + return self.vm_file.clone(); + } + pub fn set_vm_flags(&mut self, vm_flags: VmFlags) { self.vm_flags = vm_flags; } @@ -1310,6 +1321,8 @@ impl VMA { user_address_space: self.user_address_space.clone(), self_ref: self.self_ref.clone(), provider: Provider::Allocated, + file_pgoff: self.file_pgoff, + vm_file: self.vm_file.clone(), }; } @@ -1322,14 +1335,21 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, + file_pgoff: self.file_pgoff, + vm_file: self.vm_file.clone(), }; } #[inline(always)] - pub fn flags(&self) -> PageFlags { + pub fn flags(&self) -> EntryFlags { return self.flags; } + #[inline(always)] + pub fn file_page_offset(&self) -> Option { + return self.file_pgoff; + } + pub fn pages(&self) -> VirtPageFrameIter { return VirtPageFrameIter::new( VirtPageFrame::new(self.region.start()), @@ -1339,7 +1359,7 @@ impl VMA { pub fn remap( &mut self, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { @@ -1392,7 +1412,7 @@ impl VMA { destination: VirtPageFrame, count: PageFrameCount, vm_flags: VmFlags, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result, SystemError> { @@ -1414,15 +1434,12 @@ impl VMA { cur_dest = cur_dest.next(); } - let r: Arc = LockedVMA::new(VMA { - region: VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), + let r: Arc = LockedVMA::new(VMA::new( + VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, flags, - mapped: true, - user_address_space: None, - self_ref: Weak::default(), - provider: Provider::Allocated, - }); + true, + )); // 将VMA加入到anon_vma中 let mut page_manager_guard = page_manager_lock_irqsave(); @@ -1450,7 +1467,7 @@ impl VMA { destination: VirtPageFrame, page_count: PageFrameCount, vm_flags: VmFlags, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result, SystemError> { diff --git a/kernel/src/virt/kvm/host_mem.rs b/kernel/src/virt/kvm/host_mem.rs index f34a1e83b..95291b146 100644 --- a/kernel/src/virt/kvm/host_mem.rs +++ b/kernel/src/virt/kvm/host_mem.rs @@ -2,7 +2,7 @@ use log::debug; use system_error::SystemError; use super::{vcpu::Vcpu, vm}; -use crate::mm::{kernel_mapper::KernelMapper, page::PageFlags, VirtAddr}; +use crate::mm::{kernel_mapper::KernelMapper, page::EntryFlags, VirtAddr}; /* * Address types: @@ -152,7 +152,7 @@ fn hva_to_pfn(addr: u64, _atomic: bool, _writable: &mut bool) -> Result> PAGE_SHIFT); } unsafe { - mapper.map(hva, PageFlags::mmio_flags()); + mapper.map(hva, EntryFlags::mmio_flags()); } let (hpa, _) = mapper.translate(hva).unwrap(); return Ok(hpa.data() as u64 >> PAGE_SHIFT); From 4e9f5770d3fcd5dfff95b1e8a0ca163df2f024bf Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Wed, 29 May 2024 03:47:52 +0800 Subject: [PATCH 11/60] update-20240529-0347 --- kernel/src/arch/x86_64/pci/pci.rs | 2 +- kernel/src/mm/fault.rs | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/kernel/src/arch/x86_64/pci/pci.rs b/kernel/src/arch/x86_64/pci/pci.rs index 521fa9f06..427d6c88b 100644 --- a/kernel/src/arch/x86_64/pci/pci.rs +++ b/kernel/src/arch/x86_64/pci/pci.rs @@ -11,7 +11,7 @@ use crate::init::initcall::INITCALL_SUBSYS; use crate::mm::PhysAddr; use acpi::mcfg::Mcfg; -use log::{error, warn}; +use log::warn; use system_error::SystemError; use unified_init::macros::unified_init; diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index de92d81c6..4ffbab39c 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -61,11 +61,9 @@ pub struct PageFaultMessage { impl PageFaultMessage { pub fn new(vma: Arc, address: VirtAddr, flags: FaultFlags) -> Self { let guard = vma.lock(); - let file_pgoff = if let Some(file_page_offset) = guard.file_page_offset() { - Some(((address - guard.region().start()) >> MMArch::PAGE_SHIFT) + file_page_offset) - } else { - None - }; + let file_pgoff = guard.file_page_offset().map(|file_page_offset| { + ((address - guard.region().start()) >> MMArch::PAGE_SHIFT) + file_page_offset + }); Self { vma: vma.clone(), address, From 95c37e409a34f9b3f56fc1a6cab816742a75ba56 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 4 Jun 2024 02:33:44 +0800 Subject: [PATCH 12/60] update 20240604 0233 --- kernel/src/mm/fault.rs | 18 ++++- kernel/src/mm/syscall.rs | 49 +++++++++---- kernel/src/mm/ucontext.rs | 140 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 184 insertions(+), 23 deletions(-) diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 4ffbab39c..b9b95890e 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -197,6 +197,9 @@ impl PageFaultHandler { pfm: PageFaultMessage, mapper: &mut PageMapper, ) -> VmFaultReason { + if pfm.address.data() == 0x10000 { + log::info!("handle_pte_fault: {:?}", pfm); + } let address = pfm.address_aligned_down(); let flags = pfm.flags; let vma = pfm.vma.clone(); @@ -271,6 +274,7 @@ impl PageFaultHandler { /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] pub unsafe fn do_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + log::info!("do_fault"); // panic!( // "do_fault has not yet been implemented, // fault message: {:?}, @@ -328,6 +332,7 @@ impl PageFaultHandler { // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_read_fault + log::info!("do_read_fault"); let ret = Self::do_fault_around(pfm.clone(), mapper); if !ret.is_empty() { return ret; @@ -510,7 +515,11 @@ impl PageFaultHandler { ) -> VmFaultReason { let vma = pfm.vma(); let vma_guard = vma.lock(); - let file = vma_guard.vm_file().expect("no vm_file in vma"); + let file = vma_guard + .vm_file() + .expect("no vm_file in vma") + .upgrade() + .expect("struct file not exist"); let page_cache = file.inode().page_cache().unwrap(); // 起始页地址 @@ -544,9 +553,14 @@ impl PageFaultHandler { } pub unsafe fn filemap_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + log::info!("filemap_fault"); let vma = pfm.vma(); let vma_guard = vma.lock(); - let file = vma_guard.vm_file().expect("no vm_file in vma"); + let file = vma_guard + .vm_file() + .expect("no vm_file in vma") + .upgrade() + .expect("struct file not exist"); let mut page_cache = file.inode().page_cache().unwrap(); let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); diff --git a/kernel/src/mm/syscall.rs b/kernel/src/mm/syscall.rs index beac5cc23..10409a717 100644 --- a/kernel/src/mm/syscall.rs +++ b/kernel/src/mm/syscall.rs @@ -296,8 +296,8 @@ impl Syscall { len: usize, prot_flags: usize, map_flags: usize, - _fd: i32, - _offset: usize, + fd: i32, + offset: usize, ) -> Result { let map_flags = MapFlags::from_bits_truncate(map_flags as u64); let prot_flags = ProtFlags::from_bits_truncate(prot_flags as u64); @@ -312,10 +312,10 @@ impl Syscall { return Err(SystemError::EINVAL); } // 暂时不支持除匿名页以外的映射 - if !map_flags.contains(MapFlags::MAP_ANONYMOUS) { - error!("mmap: not support file mapping"); - return Err(SystemError::ENOSYS); - } + // if !map_flags.contains(MapFlags::MAP_ANONYMOUS) { + // error!("mmap: not support file mapping"); + // return Err(SystemError::ENOSYS); + // } // 暂时不支持巨页映射 if map_flags.contains(MapFlags::MAP_HUGETLB) { @@ -323,14 +323,35 @@ impl Syscall { return Err(SystemError::ENOSYS); } let current_address_space = AddressSpace::current()?; - let start_page = current_address_space.write().map_anonymous( - start_vaddr, - len, - prot_flags, - map_flags, - true, - false, - )?; + let start_page = if map_flags.contains(MapFlags::MAP_ANONYMOUS) { + current_address_space.write().map_anonymous( + start_vaddr, + len, + prot_flags, + map_flags, + true, + false, + )? + } else { + current_address_space.write().file_mapping( + start_vaddr, + len, + prot_flags, + map_flags, + fd, + offset, + true, + false, + )? + }; + // let start_page = current_address_space.write().map_anonymous( + // start_vaddr, + // len, + // prot_flags, + // map_flags, + // true, + // false, + // )?; return Ok(start_page.virt_address().data()); } diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 791cd7f10..d437e8074 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -290,7 +290,7 @@ impl InnerAddressSpace { prot_flags, map_flags, move |page, count, flags, mapper, flusher| { - VMA::zeroed(page, count, vm_flags, flags, mapper, flusher) + VMA::zeroed(page, count, vm_flags, flags, mapper, flusher, None, None) }, )? } else { @@ -304,6 +304,8 @@ impl InnerAddressSpace { VirtRegion::new(page.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, flags, + None, + None, false, ))) }, @@ -313,6 +315,122 @@ impl InnerAddressSpace { return Ok(start_page); } + /// 进行文件页映射 + /// + /// ## 参数 + /// + /// - `start_vaddr`:映射的起始地址 + /// - `len`:映射的长度 + /// - `prot_flags`:保护标志 + /// - `map_flags`:映射标志 + /// - `round_to_min`:是否将`start_vaddr`对齐到`mmap_min`,如果为`true`,则当`start_vaddr`不为0时,会对齐到`mmap_min`,否则仅向下对齐到页边界 + /// - `allocate_at_once`:是否立即分配物理空间 + /// + /// ## 返回 + /// + /// 返回映射的起始虚拟页帧 + pub fn file_mapping( + &mut self, + start_vaddr: VirtAddr, + len: usize, + prot_flags: ProtFlags, + map_flags: MapFlags, + fd: i32, + offset: usize, + round_to_min: bool, + allocate_at_once: bool, + ) -> Result { + log::info!("file_mapping"); + let allocate_at_once = if MMArch::PAGE_FAULT_ENABLED { + allocate_at_once + } else { + true + }; + // 用于对齐hint的函数 + let round_hint_to_min = |hint: VirtAddr| { + // 先把hint向下对齐到页边界 + let addr = hint.data() & (!MMArch::PAGE_OFFSET_MASK); + // debug!("map_anonymous: hint = {:?}, addr = {addr:#x}", hint); + // 如果hint不是0,且hint小于DEFAULT_MMAP_MIN_ADDR,则对齐到DEFAULT_MMAP_MIN_ADDR + if (addr != 0) && round_to_min && (addr < DEFAULT_MMAP_MIN_ADDR) { + Some(VirtAddr::new(page_align_up(DEFAULT_MMAP_MIN_ADDR))) + } else if addr == 0 { + None + } else { + Some(VirtAddr::new(addr)) + } + }; + // debug!("map_anonymous: start_vaddr = {:?}", start_vaddr); + // debug!("map_anonymous: len(no align) = {}", len); + + let len = page_align_up(len); + + let vm_flags = VmFlags::from(prot_flags) + | VmFlags::from(map_flags) + | VmFlags::VM_MAYREAD + | VmFlags::VM_MAYWRITE + | VmFlags::VM_MAYEXEC; + + // debug!("map_anonymous: len = {}", len); + + let binding = ProcessManager::current_pcb().fd_table(); + let fd_table_guard = binding.read(); + + let file = fd_table_guard.get_file_by_fd(fd); + if file.is_none() { + return Err(SystemError::EBADF); + } + // drop guard 以避免无法调度的问题 + drop(fd_table_guard); + + let file = Arc::downgrade(&file.unwrap()); + + // offset需要4K对齐 + if !offset & (MMArch::PAGE_SIZE - 1) == 0 { + return Err(SystemError::EINVAL); + } + let pgoff = offset >> MMArch::PAGE_SHIFT; + + let start_page: VirtPageFrame = if allocate_at_once { + self.mmap( + round_hint_to_min(start_vaddr), + PageFrameCount::from_bytes(len).unwrap(), + prot_flags, + map_flags, + move |page, count, flags, mapper, flusher| { + VMA::zeroed( + page, + count, + vm_flags, + flags, + mapper, + flusher, + Some(file), + Some(pgoff), + ) + }, + )? + } else { + self.mmap( + round_hint_to_min(start_vaddr), + PageFrameCount::from_bytes(len).unwrap(), + prot_flags, + map_flags, + move |page, count, flags, _mapper, _flusher| { + Ok(LockedVMA::new(VMA::new( + VirtRegion::new(page.virt_address(), count.data() * MMArch::PAGE_SIZE), + vm_flags, + flags, + Some(file), + Some(pgoff), + false, + ))) + }, + )? + }; + return Ok(start_page); + } + /// 向进程的地址空间映射页面 /// /// # 参数 @@ -1188,8 +1306,8 @@ impl LockedVMA { /// 判断VMA是否为匿名映射 pub fn is_anonymous(&self) -> bool { - //TODO: 实现匿名映射判断逻辑,目前仅支持匿名映射 - true + let guard = self.lock(); + guard.vm_file.is_none() } /// 判断VMA是否为大页映射 @@ -1241,7 +1359,7 @@ pub struct VMA { user_address_space: Option>, self_ref: Weak, - vm_file: Option>, + vm_file: Option>, /// VMA映射的文件部分相对于整个文件的偏移页数 file_pgoff: Option, @@ -1268,6 +1386,8 @@ impl VMA { region: VirtRegion, vm_flags: VmFlags, flags: EntryFlags, + file: Option>, + pgoff: Option, mapped: bool, ) -> Self { VMA { @@ -1278,8 +1398,8 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, - file_pgoff: None, - vm_file: None, + vm_file: file, + file_pgoff: pgoff, } } @@ -1291,7 +1411,7 @@ impl VMA { return &self.vm_flags; } - pub fn vm_file(&self) -> Option> { + pub fn vm_file(&self) -> Option> { return self.vm_file.clone(); } @@ -1438,6 +1558,8 @@ impl VMA { VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, flags, + None, + None, true, )); @@ -1470,6 +1592,8 @@ impl VMA { flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, + file: Option>, + pgoff: Option, ) -> Result, SystemError> { let mut cur_dest: VirtPageFrame = destination; // debug!( @@ -1496,6 +1620,8 @@ impl VMA { ), vm_flags, flags, + file, + pgoff, true, )); drop(flusher); From f1ad5b35ea7e67a450942881673175cc09041a62 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Thu, 6 Jun 2024 18:03:40 +0800 Subject: [PATCH 13/60] update 20240606 1800 --- kernel/src/filesystem/fat/fs.rs | 6 ++++++ kernel/src/filesystem/vfs/file.rs | 11 ++++++++--- kernel/src/filesystem/vfs/mount.rs | 4 ++++ kernel/src/mm/fault.rs | 5 ++++- kernel/src/mm/page.rs | 2 +- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index 11febb291..7402c5c05 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -13,6 +13,7 @@ use alloc::{ }; use crate::driver::base::device::device_number::DeviceNumber; +use crate::filesystem::vfs::file::PageCache; use crate::filesystem::vfs::utils::DName; use crate::filesystem::vfs::{Magic, SpecialNodeData, SuperBlock}; use crate::ipc::pipe::LockedPipeInode; @@ -117,6 +118,9 @@ pub struct FATInode { /// 目录名 dname: DName, + + /// 页缓存 + page_cache: PageCache, } impl FATInode { @@ -214,6 +218,7 @@ impl LockedFATInode { }, special_node: None, dname, + page_cache: PageCache::default(), }))); inode.0.lock().self_ref = Arc::downgrade(&inode); @@ -347,6 +352,7 @@ impl FATFileSystem { }, special_node: None, dname: DName::default(), + page_cache: PageCache::default(), }))); let result: Arc = Arc::new(FATFileSystem { diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index fd3883ff1..93ca96e1b 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -122,15 +122,14 @@ impl FileMode { } #[allow(dead_code)] +#[derive(Debug)] pub struct PageCache { - inode_ref: Weak, map: HashMap>, } impl PageCache { - pub fn new(inode_ref: Weak) -> PageCache { + pub fn new() -> PageCache { Self { - inode_ref, map: HashMap::new(), } } @@ -154,6 +153,12 @@ impl PageCache { // } } +impl Default for PageCache { + fn default() -> Self { + Self::new() + } +} + pub trait PageCacheOperations: IndexNode { fn write_page(&self, page: Page); fn read_ahead(&self); diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index 54c07ad5d..8b7397966 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -520,6 +520,10 @@ impl IndexNode for MountFSInode { fn parent(&self) -> Result, SystemError> { return self.do_parent().map(|inode| inode as Arc); } + + fn page_cache(&self) -> Option { + self.inner_inode.page_cache() + } } impl FileSystem for MountFS { diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index b9b95890e..49fddf5a6 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -449,6 +449,7 @@ impl PageFaultHandler { } pub unsafe fn do_fault_around(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + log::info!("do_fault_around"); if mapper.get_table(*pfm.address(), 0).is_none() { mapper .allocate_table(*pfm.address(), 0) @@ -456,7 +457,9 @@ impl PageFaultHandler { } let vma = pfm.vma(); let vma_guard = vma.lock(); - let vma_region = vma_guard.region(); + let vma_region = *vma_guard.region(); + drop(vma_guard); + // 缺页在VMA中的偏移量 let vm_pgoff = (*pfm.address() - vma_region.start()) >> MMArch::PAGE_SHIFT; diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index b359c129a..3468f3edf 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -106,7 +106,7 @@ bitflags! { const PG_SWAPBACKED = 1 << 19; } } - +#[derive(Debug)] /// 物理页面信息 pub struct Page { /// 映射计数 From d7211d086b910a3a0a1cb3aa4165efb0d89cf32b Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Fri, 7 Jun 2024 01:59:29 +0800 Subject: [PATCH 14/60] update 20240607 0200 --- kernel/src/filesystem/fat/fs.rs | 10 +++++++--- kernel/src/filesystem/vfs/file.rs | 13 ++++++------- kernel/src/filesystem/vfs/mod.rs | 4 ++-- kernel/src/filesystem/vfs/mount.rs | 8 +++++--- kernel/src/mm/fault.rs | 14 +++----------- kernel/src/mm/ucontext.rs | 14 ++++++-------- 6 files changed, 29 insertions(+), 34 deletions(-) diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index 7402c5c05..879a10412 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -120,7 +120,7 @@ pub struct FATInode { dname: DName, /// 页缓存 - page_cache: PageCache, + page_cache: Arc, } impl FATInode { @@ -218,7 +218,7 @@ impl LockedFATInode { }, special_node: None, dname, - page_cache: PageCache::default(), + page_cache: Arc::new(PageCache::default()), }))); inode.0.lock().self_ref = Arc::downgrade(&inode); @@ -352,7 +352,7 @@ impl FATFileSystem { }, special_node: None, dname: DName::default(), - page_cache: PageCache::default(), + page_cache: Arc::new(PageCache::default()), }))); let result: Arc = Arc::new(FATFileSystem { @@ -1837,6 +1837,10 @@ impl IndexNode for LockedFATInode { .map(|item| item as Arc) .ok_or(SystemError::EINVAL) } + + fn page_cache(&self) -> Option> { + Some(self.0.lock().page_cache.clone()) + } } impl Default for FATFsInfo { diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 93ca96e1b..3452c8bf2 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -124,22 +124,22 @@ impl FileMode { #[allow(dead_code)] #[derive(Debug)] pub struct PageCache { - map: HashMap>, + map: RwLock>>, } impl PageCache { pub fn new() -> PageCache { Self { - map: HashMap::new(), + map: RwLock::new(HashMap::new()), } } - pub fn add_page(&mut self, offset: usize, page: Arc) { - self.map.insert(offset, page); + pub fn add_page(&self, offset: usize, page: Arc) { + self.map.write().insert(offset, page); } pub fn get_page(&self, offset: usize) -> Option> { - self.map.get(&offset).cloned() + self.map.read().get(&offset).cloned() } // pub fn get_pages(&self, start_pgoff: usize, end_pgoff: usize) -> Vec> { @@ -693,9 +693,8 @@ impl FileDescriptorVec { self.get_file_by_fd(fd).ok_or(SystemError::EBADF)?; // 把文件描述符数组对应位置设置为空 - let file = self.fds[fd as usize].take().unwrap(); + self.fds[fd as usize].take().unwrap(); - assert!(Arc::strong_count(&file) == 1); return Ok(()); } diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 09c04f072..cccea6d55 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -554,8 +554,8 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { return self.find(".."); } - fn page_cache(&self) -> Option { - None + fn page_cache(&self) -> Option> { + panic!("function page_cache() has not yet been implemented"); } } diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index 8b7397966..cf3d9cd26 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -22,8 +22,10 @@ use crate::{ }; use super::{ - file::FileMode, syscall::ModeType, utils::DName, FilePrivateData, FileSystem, FileType, - IndexNode, InodeId, Magic, SuperBlock, + file::{FileMode, PageCache}, + syscall::ModeType, + utils::DName, + FilePrivateData, FileSystem, FileType, IndexNode, InodeId, Magic, SuperBlock, }; const MOUNTFS_BLOCK_SIZE: u64 = 512; @@ -521,7 +523,7 @@ impl IndexNode for MountFSInode { return self.do_parent().map(|inode| inode as Arc); } - fn page_cache(&self) -> Option { + fn page_cache(&self) -> Option> { self.inner_inode.page_cache() } } diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 49fddf5a6..3a7a8e40f 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -518,11 +518,7 @@ impl PageFaultHandler { ) -> VmFaultReason { let vma = pfm.vma(); let vma_guard = vma.lock(); - let file = vma_guard - .vm_file() - .expect("no vm_file in vma") - .upgrade() - .expect("struct file not exist"); + let file = vma_guard.vm_file().expect("no vm_file in vma"); let page_cache = file.inode().page_cache().unwrap(); // 起始页地址 @@ -559,12 +555,8 @@ impl PageFaultHandler { log::info!("filemap_fault"); let vma = pfm.vma(); let vma_guard = vma.lock(); - let file = vma_guard - .vm_file() - .expect("no vm_file in vma") - .upgrade() - .expect("struct file not exist"); - let mut page_cache = file.inode().page_cache().unwrap(); + let file = vma_guard.vm_file().expect("no vm_file in vma"); + let page_cache = file.inode().page_cache().unwrap(); let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); if let Some(page) = page_cache.get_page(file_pgoff) { diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index d437e8074..c59c95579 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -383,8 +383,6 @@ impl InnerAddressSpace { // drop guard 以避免无法调度的问题 drop(fd_table_guard); - let file = Arc::downgrade(&file.unwrap()); - // offset需要4K对齐 if !offset & (MMArch::PAGE_SIZE - 1) == 0 { return Err(SystemError::EINVAL); @@ -405,7 +403,7 @@ impl InnerAddressSpace { flags, mapper, flusher, - Some(file), + file, Some(pgoff), ) }, @@ -421,7 +419,7 @@ impl InnerAddressSpace { VirtRegion::new(page.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, flags, - Some(file), + file, Some(pgoff), false, ))) @@ -1359,7 +1357,7 @@ pub struct VMA { user_address_space: Option>, self_ref: Weak, - vm_file: Option>, + vm_file: Option>, /// VMA映射的文件部分相对于整个文件的偏移页数 file_pgoff: Option, @@ -1386,7 +1384,7 @@ impl VMA { region: VirtRegion, vm_flags: VmFlags, flags: EntryFlags, - file: Option>, + file: Option>, pgoff: Option, mapped: bool, ) -> Self { @@ -1411,7 +1409,7 @@ impl VMA { return &self.vm_flags; } - pub fn vm_file(&self) -> Option> { + pub fn vm_file(&self) -> Option> { return self.vm_file.clone(); } @@ -1592,7 +1590,7 @@ impl VMA { flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, - file: Option>, + file: Option>, pgoff: Option, ) -> Result, SystemError> { let mut cur_dest: VirtPageFrame = destination; From 62ec0e0dbe052bcd785c4253fe0f135831900ed3 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Mon, 17 Jun 2024 17:47:40 +0800 Subject: [PATCH 15/60] update 20240617 1747 --- kernel/src/mm/fault.rs | 58 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 3a7a8e40f..db3f2e894 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -56,6 +56,8 @@ pub struct PageFaultMessage { flags: FaultFlags, /// 缺页的文件页在文件中的偏移量 file_pgoff: Option, + /// 缺页对应PageCache中的文件页 + page: Option>, } impl PageFaultMessage { @@ -69,6 +71,7 @@ impl PageFaultMessage { address, flags, file_pgoff, + page: None, } } @@ -104,6 +107,7 @@ impl Clone for PageFaultMessage { address: self.address, flags: self.flags, file_pgoff: self.file_pgoff, + page: None, } } } @@ -333,11 +337,15 @@ impl PageFaultHandler { // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_read_fault log::info!("do_read_fault"); - let ret = Self::do_fault_around(pfm.clone(), mapper); + let mut ret = Self::do_fault_around(pfm.clone(), mapper); if !ret.is_empty() { return ret; } - return Self::filemap_fault(pfm.clone(), mapper); + ret = Self::filemap_fault(pfm.clone(), mapper); + + ret = ret.union(Self::finish_fault(pfm, mapper)); + + ret } /// 处理对共享文件映射区写入引起的缺页 @@ -551,13 +559,17 @@ impl PageFaultHandler { VmFaultReason::empty() } - pub unsafe fn filemap_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn filemap_fault( + mut pfm: PageFaultMessage, + mapper: &mut PageMapper, + ) -> VmFaultReason { log::info!("filemap_fault"); let vma = pfm.vma(); let vma_guard = vma.lock(); let file = vma_guard.vm_file().expect("no vm_file in vma"); let page_cache = file.inode().page_cache().unwrap(); let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); + let mut ret = VmFaultReason::empty(); if let Some(page) = page_cache.get_page(file_pgoff) { // TODO 异步从磁盘中预读页面进PageCache @@ -573,6 +585,8 @@ impl PageFaultHandler { new_frame.copy_from_nonoverlapping(frame, MMArch::PAGE_SIZE); } else { // TODO 同步预读 + //涉及磁盘IO,返回标志为VM_FAULT_MAJOR + ret = VmFaultReason::VM_FAULT_MAJOR; let mut buf: Vec = vec![0; MMArch::PAGE_SIZE]; file.pread( file_pgoff * MMArch::PAGE_SIZE, @@ -586,15 +600,43 @@ impl PageFaultHandler { let new_cache_page = allocator.allocate_one().unwrap(); (phys_2_virt(new_cache_page.data()) as *mut u8) .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); - page_cache.add_page( - file_pgoff, - Arc::new(Page::new(false, PhysPageFrame::new(new_cache_page))), - ); + let page = Arc::new(Page::new(false, PhysPageFrame::new(new_cache_page))); + + page_cache.add_page(file_pgoff, page.clone()); + + pfm.page = Some(page); + + // // 分配空白页并映射到缺页地址 + // mapper.map(pfm.address, vma_guard.flags()).unwrap().flush(); + // let new_frame = phys_2_virt(mapper.translate(pfm.address).unwrap().0.data()); + // (new_frame as *mut u8).copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); + } + ret + } + + pub unsafe fn finish_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + let vma = pfm.vma(); + let vma_guard = vma.lock(); + let page = pfm.page.clone().expect("no page in pfm"); + let cache_frame = page.phys_frame().phys_address(); + + if pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) + && !pfm.vma().lock().vm_flags().contains(VmFlags::VM_SHARED) + { + // 私有文件映射的写时复制场景 // 分配空白页并映射到缺页地址 mapper.map(pfm.address, vma_guard.flags()).unwrap().flush(); + + //复制PageCache内容到新的页内 let new_frame = phys_2_virt(mapper.translate(pfm.address).unwrap().0.data()); - (new_frame as *mut u8).copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); + (new_frame as *mut u8).copy_from_nonoverlapping( + phys_2_virt(cache_frame.data()) as *mut u8, + MMArch::PAGE_SIZE, + ); + } else { + // 直接映射到PageCache + mapper.map_phys(*pfm.address(), cache_frame, vma_guard.flags()); } VmFaultReason::VM_FAULT_COMPLETED } From 3ac055a7b0dff7ef50fbe23d22ec2d8ffe45f263 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 18 Jun 2024 17:37:11 +0800 Subject: [PATCH 16/60] =?UTF-8?q?=E9=87=8D=E5=86=99=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E4=BF=9D=E6=8A=A4=E6=A0=87=E5=BF=97=E7=9A=84=E6=9E=84=E9=80=A0?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/x86_64/mm/mod.rs | 35 ++++++++++++++++++ kernel/src/arch/x86_64/mm/pkru.rs | 2 +- kernel/src/mm/fault.rs | 54 +++++++++++++++++++++++----- kernel/src/mm/mod.rs | 60 ++++++++++++++++++++++++++++++- kernel/src/mm/page.rs | 11 +++--- kernel/src/mm/ucontext.rs | 4 +++ 6 files changed, 150 insertions(+), 16 deletions(-) diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index 841ae4335..ad3936bd7 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -326,6 +326,41 @@ impl MemoryManagementArch for X86_64MMArch { } pkru::pkru_allows_pkey(pkru::vma_pkey(vma), write) } + + const PAGE_NONE: usize = + Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_ACCESSED | Self::ENTRY_FLAG_GLOBAL; + + const PAGE_SHARED: usize = Self::ENTRY_FLAG_PRESENT + | Self::ENTRY_FLAG_READWRITE + | Self::ENTRY_FLAG_USER + | Self::ENTRY_FLAG_ACCESSED + | Self::ENTRY_FLAG_NO_EXEC; + + const PAGE_SHARED_EXEC: usize = Self::ENTRY_FLAG_PRESENT + | Self::ENTRY_FLAG_READWRITE + | Self::ENTRY_FLAG_USER + | Self::ENTRY_FLAG_ACCESSED; + + const PAGE_COPY_NOEXEC: usize = Self::ENTRY_FLAG_PRESENT + | Self::ENTRY_FLAG_USER + | Self::ENTRY_FLAG_ACCESSED + | Self::ENTRY_FLAG_NO_EXEC; + + const PAGE_COPY_EXEC: usize = + Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_USER | Self::ENTRY_FLAG_ACCESSED; + + const PAGE_COPY: usize = Self::ENTRY_FLAG_PRESENT + | Self::ENTRY_FLAG_USER + | Self::ENTRY_FLAG_ACCESSED + | Self::ENTRY_FLAG_NO_EXEC; + + const PAGE_READONLY: usize = Self::ENTRY_FLAG_PRESENT + | Self::ENTRY_FLAG_USER + | Self::ENTRY_FLAG_ACCESSED + | Self::ENTRY_FLAG_NO_EXEC; + + const PAGE_READONLY_EXEC: usize = + Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_USER | Self::ENTRY_FLAG_ACCESSED; } impl X86_64MMArch { diff --git a/kernel/src/arch/x86_64/mm/pkru.rs b/kernel/src/arch/x86_64/mm/pkru.rs index f467f8d19..b904fb84c 100644 --- a/kernel/src/arch/x86_64/mm/pkru.rs +++ b/kernel/src/arch/x86_64/mm/pkru.rs @@ -17,7 +17,7 @@ const PKEY_MASK: usize = 1 << 32 | 1 << 33 | 1 << 34 | 1 << 35; /// - `u16`: vma的protection_key pub fn vma_pkey(vma: Arc) -> u16 { let guard = vma.lock(); - ((guard.vm_flags().bits() & PKEY_MASK as u64) >> VM_PKEY_SHIFT) as u16 + ((guard.vm_flags().bits() & PKEY_MASK) >> VM_PKEY_SHIFT) as u16 } // TODO pkru实现参考:https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/include/asm/pkru.h diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index db3f2e894..3f310c5df 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -307,14 +307,20 @@ impl PageFaultHandler { /// - VmFaultReason: 页面错误处理信息标志 #[allow(dead_code, unused_variables)] pub unsafe fn do_cow_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - panic!( - "do_cow_fault has not yet been implemented, - fault message: {:?}, - pid: {}\n", - pfm, - crate::process::ProcessManager::current_pid().data() - ); + // panic!( + // "do_cow_fault has not yet been implemented, + // fault message: {:?}, + // pid: {}\n", + // pfm, + // crate::process::ProcessManager::current_pid().data() + // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_cow_fault + + let mut ret = Self::filemap_fault(pfm.clone(), mapper); + + ret = ret.union(Self::finish_fault(pfm, mapper)); + + ret } /// 处理文件映射页的缺页异常 @@ -344,7 +350,7 @@ impl PageFaultHandler { ret = Self::filemap_fault(pfm.clone(), mapper); ret = ret.union(Self::finish_fault(pfm, mapper)); - + ret } @@ -456,6 +462,14 @@ impl PageFaultHandler { } } + /// 缺页附近页预读 + /// ## 参数 + /// + /// - `pfm`: 缺页异常信息 + /// - `mapper`: 页表映射器 + /// + /// ## 返回值 + /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn do_fault_around(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { log::info!("do_fault_around"); if mapper.get_table(*pfm.address(), 0).is_none() { @@ -518,6 +532,14 @@ impl PageFaultHandler { VmFaultReason::empty() } + /// 通用的VMA文件映射页面映射函数 + /// ## 参数 + /// + /// - `pfm`: 缺页异常信息 + /// - `mapper`: 页表映射器 + /// + /// ## 返回值 + /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn filemap_map_pages( pfm: PageFaultMessage, mapper: &mut PageMapper, @@ -559,6 +581,14 @@ impl PageFaultHandler { VmFaultReason::empty() } + /// 通用的VMA文件映射错误处理函数 + /// ## 参数 + /// + /// - `pfm`: 缺页异常信息 + /// - `mapper`: 页表映射器 + /// + /// ## 返回值 + /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn filemap_fault( mut pfm: PageFaultMessage, mapper: &mut PageMapper, @@ -626,7 +656,13 @@ impl PageFaultHandler { { // 私有文件映射的写时复制场景 // 分配空白页并映射到缺页地址 - mapper.map(pfm.address, vma_guard.flags()).unwrap().flush(); + mapper + .map( + pfm.address, + vma_guard.flags().set_write(true).set_dirty(true), + ) + .unwrap() + .flush(); //复制PageCache内容到新的页内 let new_frame = phys_2_virt(mapper.translate(pfm.address).unwrap().0.data()); diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index bfd051933..f95db12c4 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -1,4 +1,5 @@ use alloc::sync::Arc; +use page::EntryFlags; use system_error::SystemError; use crate::{arch::MMArch, include::bindings::bindings::PAGE_OFFSET}; @@ -40,7 +41,7 @@ static mut __IDLE_PROCESS_ADDRESS_SPACE: Option> = None; bitflags! { /// Virtual memory flags #[allow(clippy::bad_bit_mask)] - pub struct VmFlags:u64{ + pub struct VmFlags:usize{ const VM_NONE = 0x00000000; const VM_READ = 0x00000001; @@ -96,6 +97,20 @@ bitflags! { } } +impl core::ops::Index for [usize] { + type Output = usize; + + fn index(&self, index: VmFlags) -> &Self::Output { + &self[index.bits] + } +} + +impl core::ops::IndexMut for [usize] { + fn index_mut(&mut self, index: VmFlags) -> &mut Self::Output { + &mut self[index.bits] + } +} + /// 获取内核IDLE进程的用户地址空间结构体 #[allow(non_snake_case)] #[inline(always)] @@ -642,6 +657,49 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { ) -> bool { true } + + const PAGE_NONE: usize; + const PAGE_SHARED: usize; + const PAGE_SHARED_EXEC: usize; + const PAGE_COPY_NOEXEC: usize; + const PAGE_COPY_EXEC: usize; + const PAGE_COPY: usize; + const PAGE_READONLY: usize; + const PAGE_READONLY_EXEC: usize; + + fn protection_map() -> [usize; 16] { + let mut map = [0; 16]; + map[VmFlags::VM_NONE] = Self::PAGE_NONE; + map[VmFlags::VM_READ] = Self::PAGE_READONLY; + map[VmFlags::VM_WRITE] = Self::PAGE_COPY; + map[VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY; + map[VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_COPY_EXEC; + map[VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY_EXEC; + map[VmFlags::VM_SHARED] = Self::PAGE_NONE; + map[VmFlags::VM_SHARED | VmFlags::VM_READ] = Self::PAGE_READONLY; + map[VmFlags::VM_SHARED | VmFlags::VM_WRITE] = Self::PAGE_SHARED; + map[VmFlags::VM_SHARED | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_SHARED; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_SHARED_EXEC; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = + Self::PAGE_SHARED_EXEC; + map + } + + /// 页面保护标志转换函数 + /// ## 参数 + /// + /// - `vm_flags`: VmFlags标志 + /// + /// ## 返回值 + /// - EntryFlags: 页面的保护位 + fn vm_get_page_prot(vm_flags: VmFlags) -> EntryFlags { + let map = Self::protection_map(); + unsafe { EntryFlags::from_data(map[vm_flags]) } + } } /// @brief 虚拟地址范围 diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 3468f3edf..8161d1c36 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -530,11 +530,12 @@ impl EntryFlags { /// - prot_flags: 页的保护标志 /// - user: 用户空间是否可访问 pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> EntryFlags { - let flags: EntryFlags = EntryFlags::new() - .set_user(user) - .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) - .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); - + let vm_flags = super::VmFlags::from(prot_flags); + // let flags: EntryFlags = EntryFlags::new() + // .set_user(user) + // .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) + // .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); + let flags = Arch::vm_get_page_prot(vm_flags).set_user(user); return flags; } diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index c59c95579..47ef7b461 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -1425,6 +1425,10 @@ impl VMA { self.mapped = mapped; } + pub fn set_flags(&mut self) { + self.flags = MMArch::vm_get_page_prot(self.vm_flags); + } + /// # 拷贝当前VMA的内容 /// /// ### 安全性 From 7fd7c6e63c102416f15d3c3ef811af5677f64a74 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Thu, 20 Jun 2024 17:26:52 +0800 Subject: [PATCH 17/60] update20240620 1726 --- kernel/src/arch/x86_64/mm/mod.rs | 27 ++++++++++++++++++++++++++- kernel/src/mm/fault.rs | 20 +++++++++++++------- kernel/src/mm/mod.rs | 27 +++++++++------------------ kernel/src/mm/page.rs | 4 ++++ 4 files changed, 52 insertions(+), 26 deletions(-) diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index ad3936bd7..b545c5a3e 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -29,7 +29,7 @@ use crate::{ use crate::mm::kernel_mapper::KernelMapper; use crate::mm::page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}; -use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr}; +use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, VmFlags}; use system_error::SystemError; @@ -327,6 +327,28 @@ impl MemoryManagementArch for X86_64MMArch { pkru::pkru_allows_pkey(pkru::vma_pkey(vma), write) } + fn protection_map() -> [usize; 16] { + let mut map = [0; 16]; + map[VmFlags::VM_NONE] = Self::PAGE_NONE; + map[VmFlags::VM_READ] = Self::PAGE_READONLY; + map[VmFlags::VM_WRITE] = Self::PAGE_COPY; + map[VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY; + map[VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_COPY_EXEC; + map[VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY_EXEC; + map[VmFlags::VM_SHARED] = Self::PAGE_NONE; + map[VmFlags::VM_SHARED | VmFlags::VM_READ] = Self::PAGE_READONLY; + map[VmFlags::VM_SHARED | VmFlags::VM_WRITE] = Self::PAGE_SHARED; + map[VmFlags::VM_SHARED | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_SHARED; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_SHARED_EXEC; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = + Self::PAGE_SHARED_EXEC; + map + } + const PAGE_NONE: usize = Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_ACCESSED | Self::ENTRY_FLAG_GLOBAL; @@ -361,6 +383,9 @@ impl MemoryManagementArch for X86_64MMArch { const PAGE_READONLY_EXEC: usize = Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_USER | Self::ENTRY_FLAG_ACCESSED; + + const PAGE_READ: usize = 0; + const PAGE_READ_EXEC: usize = 0; } impl X86_64MMArch { diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 3f310c5df..de6c63b5a 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -364,14 +364,20 @@ impl PageFaultHandler { /// - VmFaultReason: 页面错误处理信息标志 #[allow(dead_code, unused_variables)] pub unsafe fn do_shared_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - panic!( - "do_shared_fault has not yet been implemented, - fault message: {:?}, - pid: {}\n", - pfm, - crate::process::ProcessManager::current_pid().data() - ); + // panic!( + // "do_shared_fault has not yet been implemented, + // fault message: {:?}, + // pid: {}\n", + // pfm, + // crate::process::ProcessManager::current_pid().data() + // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_shared_fault + + let mut ret = Self::filemap_fault(pfm.clone(), mapper); + + ret = ret.union(Self::finish_fault(pfm, mapper)); + + ret } /// 处理被置换页面的缺页异常 diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index f95db12c4..2f6b7b9ea 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -667,25 +667,16 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { const PAGE_READONLY: usize; const PAGE_READONLY_EXEC: usize; + const PAGE_READ: usize; + const PAGE_READ_EXEC: usize; + + /// 获取保护标志的映射表 + /// + /// + /// ## 返回值 + /// - `[usize; 16]`: 长度为16的映射表 fn protection_map() -> [usize; 16] { - let mut map = [0; 16]; - map[VmFlags::VM_NONE] = Self::PAGE_NONE; - map[VmFlags::VM_READ] = Self::PAGE_READONLY; - map[VmFlags::VM_WRITE] = Self::PAGE_COPY; - map[VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY; - map[VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_COPY_EXEC; - map[VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY_EXEC; - map[VmFlags::VM_SHARED] = Self::PAGE_NONE; - map[VmFlags::VM_SHARED | VmFlags::VM_READ] = Self::PAGE_READONLY; - map[VmFlags::VM_SHARED | VmFlags::VM_WRITE] = Self::PAGE_SHARED; - map[VmFlags::VM_SHARED | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_SHARED; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_SHARED_EXEC; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = - Self::PAGE_SHARED_EXEC; + let map = [0; 16]; map } diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 8161d1c36..dd155e91e 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -191,6 +191,10 @@ impl Page { pub fn phys_frame(&self) -> &PhysPageFrame { &self.phys_frame } + + pub fn lock(&self) { + //TODO 使用读写锁包装Page结构体保证多进程的修改互斥操作 + } } #[derive(Debug)] From 00183e013e194b2c9ede7ce8fb64bcc2bf498d67 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Mon, 24 Jun 2024 03:59:29 +0800 Subject: [PATCH 18/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Riscv64=E7=9A=84protect?= =?UTF-8?q?ion=5Fmap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/riscv64/mm/mod.rs | 54 ++++++++++++++++++++++++++++++- kernel/src/arch/x86_64/mm/mod.rs | 7 ++++ kernel/src/mm/mod.rs | 3 ++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/kernel/src/arch/riscv64/mm/mod.rs b/kernel/src/arch/riscv64/mm/mod.rs index a335dee51..8f4f62419 100644 --- a/kernel/src/arch/riscv64/mm/mod.rs +++ b/kernel/src/arch/riscv64/mm/mod.rs @@ -14,7 +14,7 @@ use crate::{ kernel_mapper::KernelMapper, page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}, ucontext::UserMapper, - MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, + MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, VmFlags, }, smp::cpu::ProcessorId, }; @@ -256,8 +256,60 @@ impl MemoryManagementArch for RiscV64MMArch { ) -> bool { true } + + fn protection_map() -> [usize; 16] { + let mut map = [0; 16]; + map[VmFlags::VM_NONE] = Self::PAGE_NONE; + map[VmFlags::VM_READ] = Self::PAGE_READONLY; + map[VmFlags::VM_WRITE] = Self::PAGE_COPY; + map[VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY; + map[VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_COPY_EXEC; + map[VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY_EXEC; + map[VmFlags::VM_SHARED] = Self::PAGE_NONE; + map[VmFlags::VM_SHARED | VmFlags::VM_READ] = Self::PAGE_READONLY; + map[VmFlags::VM_SHARED | VmFlags::VM_WRITE] = Self::PAGE_SHARED; + map[VmFlags::VM_SHARED | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_SHARED; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_SHARED_EXEC; + map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = + Self::PAGE_SHARED_EXEC; + map + } + + const PAGE_NONE: usize = Self::ENTRY_FLAG_GLOBAL | Self::ENTRY_FLAG_READONLY; + + const PAGE_READ: usize = PAGE_ENTRY_BASE | Self::ENTRY_FLAG_READONLY; + + const PAGE_WRITE: usize = + PAGE_ENTRY_BASE | Self::ENTRY_FLAG_READONLY | Self::ENTRY_FLAG_WRITEABLE; + + const PAGE_EXEC: usize = PAGE_ENTRY_BASE | Self::ENTRY_FLAG_EXEC; + + const PAGE_READ_EXEC: usize = + PAGE_ENTRY_BASE | Self::ENTRY_FLAG_READONLY | Self::ENTRY_FLAG_EXEC; + + const PAGE_WRITE_EXEC: usize = PAGE_ENTRY_BASE + | Self::ENTRY_FLAG_READONLY + | Self::ENTRY_FLAG_EXEC + | Self::ENTRY_FLAG_WRITEABLE; + + const PAGE_COPY: usize = Self::PAGE_READ; + const PAGE_COPY_EXEC: usize = Self::PAGE_READ_EXEC; + const PAGE_SHARED: usize = Self::PAGE_WRITE; + const PAGE_SHARED_EXEC: usize = Self::PAGE_WRITE_EXEC; + + const PAGE_COPY_NOEXEC: usize = 0; + const PAGE_READONLY: usize = 0; + const PAGE_READONLY_EXEC: usize = 0; } +const PAGE_ENTRY_BASE: usize = RiscV64MMArch::ENTRY_FLAG_PRESENT + | RiscV64MMArch::ENTRY_FLAG_ACCESSED + | RiscV64MMArch::ENTRY_FLAG_USER; + impl VirtAddr { /// 判断虚拟地址是否合法 #[inline(always)] diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index b545c5a3e..7c8f16ffc 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -346,6 +346,10 @@ impl MemoryManagementArch for X86_64MMArch { map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_SHARED_EXEC; map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_SHARED_EXEC; + + if Self::is_xd_reserved() { + map.iter_mut().for_each(|x| *x &= !Self::ENTRY_FLAG_NO_EXEC) + } map } @@ -386,6 +390,9 @@ impl MemoryManagementArch for X86_64MMArch { const PAGE_READ: usize = 0; const PAGE_READ_EXEC: usize = 0; + const PAGE_WRITE: usize = 0; + const PAGE_WRITE_EXEC: usize = 0; + const PAGE_EXEC: usize = 0; } impl X86_64MMArch { diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index 2f6b7b9ea..281e3aa8b 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -669,6 +669,9 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { const PAGE_READ: usize; const PAGE_READ_EXEC: usize; + const PAGE_WRITE: usize; + const PAGE_WRITE_EXEC: usize; + const PAGE_EXEC: usize; /// 获取保护标志的映射表 /// From 5ef40372deec7b3aece0033ab61cb48ff9a95f5f Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 25 Jun 2024 04:11:21 +0800 Subject: [PATCH 19/60] =?UTF-8?q?=E7=AE=80=E5=8D=95=E5=AE=9E=E7=8E=B0fat?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=B3=BB=E7=BB=9F=E7=9A=84=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E6=98=A0=E5=B0=84=EF=BC=8C=E6=B7=BB=E5=8A=A0msync=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/x86_64/mm/fault.rs | 2 +- kernel/src/filesystem/vfs/file.rs | 14 ++-- kernel/src/mm/fault.rs | 93 ++++++++++++++------------- kernel/src/mm/mod.rs | 6 ++ kernel/src/mm/page.rs | 4 -- kernel/src/mm/syscall.rs | 100 ++++++++++++++++++++++++++++- kernel/src/mm/ucontext.rs | 2 +- kernel/src/syscall/mod.rs | 6 ++ 8 files changed, 169 insertions(+), 58 deletions(-) diff --git a/kernel/src/arch/x86_64/mm/fault.rs b/kernel/src/arch/x86_64/mm/fault.rs index 00eb4cefd..fb51f0545 100644 --- a/kernel/src/arch/x86_64/mm/fault.rs +++ b/kernel/src/arch/x86_64/mm/fault.rs @@ -271,7 +271,7 @@ impl X86_64MMArch { let mapper = &mut space_guard.user_mapper.utable; fault = PageFaultHandler::handle_mm_fault( - PageFaultMessage::new(vma.clone(), address, flags), + &mut PageFaultMessage::new(vma.clone(), address, flags), mapper, ); diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index b697e112a..5feb98915 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -17,7 +17,7 @@ use crate::{ filesystem::procfs::ProcfsFilePrivateData, ipc::pipe::{LockedPipeInode, PipeFsPrivateData}, libs::{rwlock::RwLock, spinlock::SpinLock}, - mm::page::Page, + mm::{page::Page, PhysAddr}, net::{ event_poll::{EPollItem, EPollPrivateData, EventPoll}, socket::SocketInode, @@ -124,7 +124,7 @@ impl FileMode { #[allow(dead_code)] #[derive(Debug)] pub struct PageCache { - map: RwLock>>, + map: RwLock>, } impl PageCache { @@ -134,12 +134,14 @@ impl PageCache { } } - pub fn add_page(&self, offset: usize, page: Arc) { - self.map.write().insert(offset, page); + pub fn add_page(&self, offset: usize, page_phys_address: PhysAddr) { + self.map.write().insert(offset, page_phys_address); } - pub fn get_page(&self, offset: usize) -> Option> { - self.map.read().get(&offset).cloned() + pub fn get_page(&self, offset: usize) -> Option { + let guard = self.map.read(); + let phys = guard.get(&offset).cloned(); + phys } // pub fn get_pages(&self, start_pgoff: usize, end_pgoff: usize) -> Vec> { diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index de6c63b5a..9dab87a5c 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -23,7 +23,7 @@ use crate::mm::MemoryManagementArch; use super::{ allocator::page_frame::{FrameAllocator, PhysPageFrame}, page::{Page, PageFlags}, - phys_2_virt, + phys_2_virt, PhysAddr, }; bitflags! { @@ -56,8 +56,8 @@ pub struct PageFaultMessage { flags: FaultFlags, /// 缺页的文件页在文件中的偏移量 file_pgoff: Option, - /// 缺页对应PageCache中的文件页 - page: Option>, + /// 缺页对应PageCache中的文件页的物理地址 + page_phys_address: Option, } impl PageFaultMessage { @@ -71,7 +71,7 @@ impl PageFaultMessage { address, flags, file_pgoff, - page: None, + page_phys_address: None, } } @@ -107,7 +107,7 @@ impl Clone for PageFaultMessage { address: self.address, flags: self.flags, file_pgoff: self.file_pgoff, - page: None, + page_phys_address: None, } } } @@ -124,7 +124,10 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn handle_mm_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn handle_mm_fault( + pfm: &mut PageFaultMessage, + mapper: &mut PageMapper, + ) -> VmFaultReason { let flags = pfm.flags(); let vma = pfm.vma(); let current_pcb = ProcessManager::current_pcb(); @@ -161,7 +164,7 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn handle_normal_fault( - pfm: PageFaultMessage, + pfm: &mut PageFaultMessage, mapper: &mut PageMapper, ) -> VmFaultReason { let address = pfm.address_aligned_down(); @@ -198,12 +201,9 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn handle_pte_fault( - pfm: PageFaultMessage, + pfm: &mut PageFaultMessage, mapper: &mut PageMapper, ) -> VmFaultReason { - if pfm.address.data() == 0x10000 { - log::info!("handle_pte_fault: {:?}", pfm); - } let address = pfm.address_aligned_down(); let flags = pfm.flags; let vma = pfm.vma.clone(); @@ -223,9 +223,9 @@ impl PageFaultHandler { } } } else if vma.is_anonymous() { - ret = Self::do_anonymous_page(pfm.clone(), mapper); + ret = Self::do_anonymous_page(pfm, mapper); } else { - ret = Self::do_fault(pfm.clone(), mapper); + ret = Self::do_fault(pfm, mapper); } vma.lock().set_mapped(true); @@ -242,7 +242,7 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn do_anonymous_page( - pfm: PageFaultMessage, + pfm: &mut PageFaultMessage, mapper: &mut PageMapper, ) -> VmFaultReason { let address = pfm.address_aligned_down(); @@ -277,8 +277,7 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] - pub unsafe fn do_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - log::info!("do_fault"); + pub unsafe fn do_fault(pfm: &mut PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { // panic!( // "do_fault has not yet been implemented, // fault message: {:?}, @@ -306,7 +305,10 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 #[allow(dead_code, unused_variables)] - pub unsafe fn do_cow_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn do_cow_fault( + pfm: &mut PageFaultMessage, + mapper: &mut PageMapper, + ) -> VmFaultReason { // panic!( // "do_cow_fault has not yet been implemented, // fault message: {:?}, @@ -316,7 +318,7 @@ impl PageFaultHandler { // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_cow_fault - let mut ret = Self::filemap_fault(pfm.clone(), mapper); + let mut ret = Self::filemap_fault(pfm, mapper); ret = ret.union(Self::finish_fault(pfm, mapper)); @@ -332,7 +334,10 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 #[allow(dead_code, unused_variables)] - pub unsafe fn do_read_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn do_read_fault( + pfm: &mut PageFaultMessage, + mapper: &mut PageMapper, + ) -> VmFaultReason { // panic!( // "do_read_fault has not yet been implemented, // fault message: {:?}, @@ -342,12 +347,12 @@ impl PageFaultHandler { // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_read_fault - log::info!("do_read_fault"); + let mut ret = Self::do_fault_around(pfm.clone(), mapper); if !ret.is_empty() { return ret; } - ret = Self::filemap_fault(pfm.clone(), mapper); + ret = Self::filemap_fault(pfm, mapper); ret = ret.union(Self::finish_fault(pfm, mapper)); @@ -363,7 +368,10 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 #[allow(dead_code, unused_variables)] - pub unsafe fn do_shared_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn do_shared_fault( + pfm: &mut PageFaultMessage, + mapper: &mut PageMapper, + ) -> VmFaultReason { // panic!( // "do_shared_fault has not yet been implemented, // fault message: {:?}, @@ -373,7 +381,7 @@ impl PageFaultHandler { // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_shared_fault - let mut ret = Self::filemap_fault(pfm.clone(), mapper); + let mut ret = Self::filemap_fault(pfm, mapper); ret = ret.union(Self::finish_fault(pfm, mapper)); @@ -477,7 +485,6 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn do_fault_around(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - log::info!("do_fault_around"); if mapper.get_table(*pfm.address(), 0).is_none() { mapper .allocate_table(*pfm.address(), 0) @@ -538,7 +545,7 @@ impl PageFaultHandler { VmFaultReason::empty() } - /// 通用的VMA文件映射页面映射函数 + /// 通用的VMA文件映射页面映射函数,将页面从PageCache中映射到内存 /// ## 参数 /// /// - `pfm`: 缺页异常信息 @@ -556,6 +563,7 @@ impl PageFaultHandler { let vma_guard = vma.lock(); let file = vma_guard.vm_file().expect("no vm_file in vma"); let page_cache = file.inode().page_cache().unwrap(); + let page_manager_guard = page_manager_lock_irqsave(); // 起始页地址 let addr = vma_guard.region().start @@ -569,7 +577,8 @@ impl PageFaultHandler { // .iter() // .filter(|page| page.flags().contains(PageFlags::PG_UPTODATE)); for pgoff in start_pgoff..=end_pgoff { - if let Some(page) = page_cache.get_page(pgoff) { + if let Some(page_phys) = page_cache.get_page(pgoff) { + let page = page_manager_guard.get(&page_phys).unwrap(); if page.flags().contains(PageFlags::PG_UPTODATE) { let phys = page.phys_frame().phys_address(); let virt = phys_2_virt(phys.data()); @@ -596,29 +605,22 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn filemap_fault( - mut pfm: PageFaultMessage, + pfm: &mut PageFaultMessage, mapper: &mut PageMapper, ) -> VmFaultReason { - log::info!("filemap_fault"); let vma = pfm.vma(); let vma_guard = vma.lock(); let file = vma_guard.vm_file().expect("no vm_file in vma"); let page_cache = file.inode().page_cache().unwrap(); let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); let mut ret = VmFaultReason::empty(); + let mut page_manager_guard = page_manager_lock_irqsave(); if let Some(page) = page_cache.get_page(file_pgoff) { // TODO 异步从磁盘中预读页面进PageCache - let address = vma_guard.region().start - + ((file_pgoff - - vma_guard - .file_page_offset() - .expect("file_page_offset is none")) - << MMArch::PAGE_SHIFT); - mapper.map(address, vma_guard.flags()).unwrap().flush(); - let frame = phys_2_virt(page.phys_frame().phys_address().data()) as *mut u8; - let new_frame = phys_2_virt(mapper.translate(address).unwrap().0.data()) as *mut u8; - new_frame.copy_from_nonoverlapping(frame, MMArch::PAGE_SIZE); + + // 直接将PageCache中的页面作为要映射的页面 + pfm.page_phys_address = Some(page); } else { // TODO 同步预读 //涉及磁盘IO,返回标志为VM_FAULT_MAJOR @@ -637,11 +639,12 @@ impl PageFaultHandler { (phys_2_virt(new_cache_page.data()) as *mut u8) .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); - let page = Arc::new(Page::new(false, PhysPageFrame::new(new_cache_page))); + let page = Page::new(false, PhysPageFrame::new(new_cache_page)); + pfm.page_phys_address = Some(page.phys_frame().phys_address()); - page_cache.add_page(file_pgoff, page.clone()); + page_cache.add_page(file_pgoff, page.phys_frame().phys_address()); - pfm.page = Some(page); + page_manager_guard.insert(new_cache_page, page); // // 分配空白页并映射到缺页地址 // mapper.map(pfm.address, vma_guard.flags()).unwrap().flush(); @@ -651,11 +654,13 @@ impl PageFaultHandler { ret } - pub unsafe fn finish_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn finish_fault( + pfm: &mut PageFaultMessage, + mapper: &mut PageMapper, + ) -> VmFaultReason { let vma = pfm.vma(); let vma_guard = vma.lock(); - let page = pfm.page.clone().expect("no page in pfm"); - let cache_frame = page.phys_frame().phys_address(); + let cache_frame = pfm.page_phys_address.expect("no page in pfm"); if pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) && !pfm.vma().lock().vm_flags().contains(VmFlags::VM_SHARED) diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index 281e3aa8b..d416279e0 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -95,6 +95,12 @@ bitflags! { const VM_FAULT_COMPLETED = 0x004000; const VM_FAULT_HINDEX_MASK = 0x0f0000; } + + pub struct MsFlags:usize { + const MS_ASYNC = 1; + const MS_INVALIDATE = 2; + const MS_SYNC = 4; + } } impl core::ops::Index for [usize] { diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index dd155e91e..8161d1c36 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -191,10 +191,6 @@ impl Page { pub fn phys_frame(&self) -> &PhysPageFrame { &self.phys_frame } - - pub fn lock(&self) { - //TODO 使用读写锁包装Page结构体保证多进程的修改互斥操作 - } } #[derive(Debug)] diff --git a/kernel/src/mm/syscall.rs b/kernel/src/mm/syscall.rs index 10409a717..0d7008a1c 100644 --- a/kernel/src/mm/syscall.rs +++ b/kernel/src/mm/syscall.rs @@ -1,4 +1,4 @@ -use core::intrinsics::unlikely; +use core::{intrinsics::unlikely, slice::from_raw_parts}; use alloc::sync::Arc; use log::error; @@ -6,6 +6,7 @@ use system_error::SystemError; use crate::{ arch::MMArch, + driver::base::block::SeekFrom, ipc::shm::ShmFlags, libs::align::{check_aligned, page_align_up}, mm::MemoryManagementArch, @@ -15,7 +16,7 @@ use crate::{ use super::{ allocator::page_frame::{PageFrameCount, VirtPageFrame}, ucontext::{AddressSpace, DEFAULT_MMAP_MIN_ADDR}, - verify_area, VirtAddr, VmFlags, + verify_area, MsFlags, VirtAddr, VmFlags, }; bitflags! { @@ -154,6 +155,10 @@ impl From for VmFlags { vm_flags |= VmFlags::VM_SYNC; } + if map_flags.contains(MapFlags::MAP_SHARED) { + vm_flags |= VmFlags::VM_SHARED; + } + vm_flags } } @@ -545,4 +550,95 @@ impl Syscall { .map_err(|_| SystemError::EINVAL)?; return Ok(0); } + + /// ## msync系统调用 + /// + /// ## 参数 + /// + /// - `start`:起始地址(已经对齐到页) + /// - `len`:长度(已经对齐到页) + /// - `flags`:标志 + pub fn msync(start: VirtAddr, len: usize, flags: usize) -> Result { + let mut start = start.data(); + let end = start + len; + let flags = MsFlags::from_bits_truncate(flags); + let mut err = Err(SystemError::EINVAL); + let mut unmapped_error = Ok(0); + if !flags.intersects(MsFlags::MS_ASYNC | MsFlags::MS_INVALIDATE | MsFlags::MS_SYNC) { + return err; + } + if flags.contains(MsFlags::MS_ASYNC | MsFlags::MS_SYNC) { + return err; + } + + err = Err(SystemError::ENOMEM); + if end < start { + return err; + } + err = Ok(0); + + if start == end { + return err; + } + + let current_address_space = AddressSpace::current()?; + loop { + err = Err(SystemError::ENOMEM); + + if let Some(vma) = current_address_space + .read() + .mappings + .find_nearest(VirtAddr::new(start)) + { + let guard = vma.lock(); + let vm_start = guard.region().start().data(); + let vm_end = guard.region().end().data(); + if start < vm_start { + if flags == MsFlags::MS_ASYNC { + break; + } + start = vm_start; + if start >= vm_end { + break; + } + unmapped_error = Err(SystemError::ENOMEM); + } + let vm_flags = *guard.vm_flags(); + if flags.contains(MsFlags::MS_INVALIDATE) && vm_flags.contains(VmFlags::VM_LOCKED) { + err = Err(SystemError::EBUSY); + break; + } + let file = guard.vm_file(); + let fstart = (start - vm_start) + + (guard.file_page_offset().unwrap_or(0) << MMArch::PAGE_SHIFT); + let fend = fstart + (core::cmp::min(end, vm_end) - start) - 1; + let old_start = start; + start = vm_end; + // log::info!("flags: {:?}", flags); + // log::info!("vm_flags: {:?}", vm_flags); + // log::info!("file: {:?}", file); + if flags.contains(MsFlags::MS_SYNC) && vm_flags.contains(VmFlags::VM_SHARED) { + if let Some(file) = file { + let old_pos = file.lseek(SeekFrom::SeekCurrent(0)).unwrap(); + file.lseek(SeekFrom::SeekSet(fstart as i64)).unwrap(); + err = file.write(len, unsafe { + from_raw_parts(old_start as *mut u8, fend - fstart + 1) + }); + file.lseek(SeekFrom::SeekSet(old_pos as i64)).unwrap(); + + if err.is_err() || start >= end { + err = Ok(0); + break; + } + } + } else if start >= end { + err = Ok(0); + break; + } + } else { + break; + } + } + return if err.is_err() { err } else { unmapped_error }; + } } diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 47ef7b461..a798cb6cc 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -907,7 +907,7 @@ impl UserMappings { if guard.region.contains(vaddr) { return Some(v.clone()); } - if guard.region.start > vaddr + if guard.region.start >= vaddr && if let Some(ref nearest) = nearest { guard.region.start < nearest.lock().region.start } else { diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 497e50843..478d01ba5 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -1103,6 +1103,12 @@ impl Syscall { Self::shmctl(id, cmd, user_buf, from_user) } + SYS_MSYNC => { + let start = page_align_up(args[0]); + let len = page_align_up(args[1]); + let flags = args[2]; + Self::msync(VirtAddr::new(start), len, flags) + } _ => panic!("Unsupported syscall ID: {}", syscall_num), }; From 98c9be54a101eefabbf04803b4b2ca48a293cb21 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 25 Jun 2024 11:50:59 +0800 Subject: [PATCH 20/60] =?UTF-8?q?trait=20FileSystem=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/filesystem/fat/fs.rs | 17 +++++++++++++++++ kernel/src/filesystem/vfs/mod.rs | 16 ++++++++++++++++ kernel/src/mm/fault.rs | 28 +++++++++++++--------------- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index 879a10412..a22f8766c 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -12,11 +12,14 @@ use alloc::{ vec::Vec, }; +use crate::arch::mm::PageMapper; use crate::driver::base::device::device_number::DeviceNumber; use crate::filesystem::vfs::file::PageCache; use crate::filesystem::vfs::utils::DName; use crate::filesystem::vfs::{Magic, SpecialNodeData, SuperBlock}; use crate::ipc::pipe::LockedPipeInode; +use crate::mm::fault::{PageFaultHandler, PageFaultMessage}; +use crate::mm::VmFaultReason; use crate::{ driver::base::block::{block_device::LBA_SIZE, disk_info::Partition, SeekFrom}, filesystem::vfs::{ @@ -275,6 +278,20 @@ impl FileSystem for FATFileSystem { FAT_MAX_NAMELEN, ) } + + unsafe fn fault(&self, pfm: &mut PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + PageFaultHandler::filemap_fault(pfm, mapper) + } + + unsafe fn map_pages( + &self, + pfm: &mut PageFaultMessage, + mapper: &mut PageMapper, + start_pgoff: usize, + end_pgoff: usize, + ) -> VmFaultReason { + PageFaultHandler::filemap_map_pages(pfm, mapper, start_pgoff, end_pgoff) + } } impl FATFileSystem { diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 1d6d8afb9..6614e3f00 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -12,6 +12,7 @@ use intertrait::CastFromSync; use system_error::SystemError; use crate::{ + arch::mm::PageMapper, driver::base::{ block::block_device::BlockDevice, char::CharDevice, device::device_number::DeviceNumber, }, @@ -20,6 +21,7 @@ use crate::{ casting::DowncastArc, spinlock::{SpinLock, SpinLockGuard}, }, + mm::{fault::PageFaultMessage, VmFaultReason}, time::PosixTimeSpec, }; @@ -814,6 +816,20 @@ pub trait FileSystem: Any + Sync + Send + Debug { fn name(&self) -> &str; fn super_block(&self) -> SuperBlock; + + unsafe fn fault(&self, _pfm: &mut PageFaultMessage, _mapper: &mut PageMapper) -> VmFaultReason { + panic!("fault() has not yet been implemented for this filesystem") + } + + unsafe fn map_pages( + &self, + _pfm: &mut PageFaultMessage, + _mapper: &mut PageMapper, + _start_pgoff: usize, + _end_pgoff: usize, + ) -> VmFaultReason { + panic!("map_pages() has not yet been implemented for this filesystem") + } } impl DowncastArc for dyn FileSystem { diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 9dab87a5c..3b11fa471 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -317,6 +317,7 @@ impl PageFaultHandler { // crate::process::ProcessManager::current_pid().data() // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_cow_fault + let file = pfm.vma().lock().vm_file().unwrap(); let mut ret = Self::filemap_fault(pfm, mapper); @@ -338,21 +339,14 @@ impl PageFaultHandler { pfm: &mut PageFaultMessage, mapper: &mut PageMapper, ) -> VmFaultReason { - // panic!( - // "do_read_fault has not yet been implemented, - // fault message: {:?}, - // pid: {}\n", - // pfm, - // crate::process::ProcessManager::current_pid().data() - // ); - - // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_read_fault + let fs = pfm.vma().lock().vm_file().unwrap().inode().fs(); - let mut ret = Self::do_fault_around(pfm.clone(), mapper); + let mut ret = Self::do_fault_around(pfm, mapper); if !ret.is_empty() { return ret; } - ret = Self::filemap_fault(pfm, mapper); + + ret = fs.fault(pfm, mapper); ret = ret.union(Self::finish_fault(pfm, mapper)); @@ -484,7 +478,10 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_fault_around(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn do_fault_around( + pfm: &mut PageFaultMessage, + mapper: &mut PageMapper, + ) -> VmFaultReason { if mapper.get_table(*pfm.address(), 0).is_none() { mapper .allocate_table(*pfm.address(), 0) @@ -534,9 +531,10 @@ impl PageFaultHandler { return VmFaultReason::VM_FAULT_OOM; } + let fs = pfm.vma().lock().vm_file().unwrap().inode().fs(); // from_pte - pte_pgoff得出预读起始pte相对缺失页的偏移,加上pfm.file_pgoff(缺失页在文件中的偏移)得出起始页在文件中的偏移,结束pte同理 - Self::filemap_map_pages( - pfm.clone(), + fs.map_pages( + pfm, mapper, file_pgoff + (from_pte - pte_pgoff), file_pgoff + (to_pte - pte_pgoff), @@ -554,7 +552,7 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn filemap_map_pages( - pfm: PageFaultMessage, + pfm: &mut PageFaultMessage, mapper: &mut PageMapper, start_pgoff: usize, end_pgoff: usize, From d5b57183751acbb0a6c0d4f57b26f3ed96463b40 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 25 Jun 2024 17:03:18 +0800 Subject: [PATCH 21/60] =?UTF-8?q?MountFS=E5=AE=9E=E7=8E=B0=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=98=A0=E5=B0=84=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/filesystem/vfs/mod.rs | 10 ++++++++-- kernel/src/filesystem/vfs/mount.rs | 17 +++++++++++++++++ kernel/src/libs/mod.rs | 1 + kernel/src/libs/name.rs | 13 +++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 kernel/src/libs/name.rs diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 6614e3f00..a61103ff8 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -818,7 +818,10 @@ pub trait FileSystem: Any + Sync + Send + Debug { fn super_block(&self) -> SuperBlock; unsafe fn fault(&self, _pfm: &mut PageFaultMessage, _mapper: &mut PageMapper) -> VmFaultReason { - panic!("fault() has not yet been implemented for this filesystem") + panic!( + "fault() has not yet been implemented for filesystem: {}", + crate::libs::name::get_type_name(&self) + ) } unsafe fn map_pages( @@ -828,7 +831,10 @@ pub trait FileSystem: Any + Sync + Send + Debug { _start_pgoff: usize, _end_pgoff: usize, ) -> VmFaultReason { - panic!("map_pages() has not yet been implemented for this filesystem") + panic!( + "map_pages() has not yet been implemented for filesystem: {}", + crate::libs::name::get_type_name(&self) + ) } } diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index cf3d9cd26..9a5af00d2 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -12,6 +12,7 @@ use alloc::{ use system_error::SystemError; use crate::{ + arch::mm::PageMapper, driver::base::device::device_number::DeviceNumber, filesystem::vfs::ROOT_INODE, libs::{ @@ -19,6 +20,7 @@ use crate::{ rwlock::RwLock, spinlock::{SpinLock, SpinLockGuard}, }, + mm::{fault::PageFaultMessage, VmFaultReason}, }; use super::{ @@ -553,6 +555,21 @@ impl FileSystem for MountFS { fn super_block(&self) -> SuperBlock { SuperBlock::new(Magic::MOUNT_MAGIC, MOUNTFS_BLOCK_SIZE, MOUNTFS_MAX_NAMELEN) } + + unsafe fn fault(&self, pfm: &mut PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + self.inner_filesystem.fault(pfm, mapper) + } + + unsafe fn map_pages( + &self, + pfm: &mut PageFaultMessage, + mapper: &mut PageMapper, + start_pgoff: usize, + end_pgoff: usize, + ) -> VmFaultReason { + self.inner_filesystem + .map_pages(pfm, mapper, start_pgoff, end_pgoff) + } } /// MountList diff --git a/kernel/src/libs/mod.rs b/kernel/src/libs/mod.rs index f15630bd3..52b7f0f58 100644 --- a/kernel/src/libs/mod.rs +++ b/kernel/src/libs/mod.rs @@ -27,3 +27,4 @@ pub mod rand; pub mod wait_queue; pub mod font; +pub mod name; diff --git a/kernel/src/libs/name.rs b/kernel/src/libs/name.rs new file mode 100644 index 000000000..bca4467c1 --- /dev/null +++ b/kernel/src/libs/name.rs @@ -0,0 +1,13 @@ +use core::any::type_name; + +use alloc::string::{String, ToString}; + +#[allow(dead_code)] +pub fn get_full_type_name(_: &T) -> String { + type_name::().to_string() +} + +pub fn get_type_name(_: &T) -> String { + let full_name = type_name::(); + full_name[(full_name.rfind("::").unwrap_or(0) + 2)..].to_string() +} From eee564dcc103573beed36d9cdab1b7c3c2ff18e2 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 25 Jun 2024 22:54:13 +0800 Subject: [PATCH 22/60] =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/x86_64/interrupt/mod.rs | 6 ++++++ kernel/src/mm/ucontext.rs | 22 +++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/kernel/src/arch/x86_64/interrupt/mod.rs b/kernel/src/arch/x86_64/interrupt/mod.rs index 1d0878973..f010a3cca 100644 --- a/kernel/src/arch/x86_64/interrupt/mod.rs +++ b/kernel/src/arch/x86_64/interrupt/mod.rs @@ -172,3 +172,9 @@ impl TrapFrame { return (self.cs & 0x3) != 0; } } + +impl Default for TrapFrame { + fn default() -> Self { + Self::new() + } +} diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index a798cb6cc..3e0e9c88b 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -323,12 +323,15 @@ impl InnerAddressSpace { /// - `len`:映射的长度 /// - `prot_flags`:保护标志 /// - `map_flags`:映射标志 + /// - `fd`:文件描述符 + /// - `offset`:映射偏移量 /// - `round_to_min`:是否将`start_vaddr`对齐到`mmap_min`,如果为`true`,则当`start_vaddr`不为0时,会对齐到`mmap_min`,否则仅向下对齐到页边界 /// - `allocate_at_once`:是否立即分配物理空间 /// /// ## 返回 /// /// 返回映射的起始虚拟页帧 + #[allow(clippy::too_many_arguments)] pub fn file_mapping( &mut self, start_vaddr: VirtAddr, @@ -340,7 +343,6 @@ impl InnerAddressSpace { round_to_min: bool, allocate_at_once: bool, ) -> Result { - log::info!("file_mapping"); let allocate_at_once = if MMArch::PAGE_FAULT_ENABLED { allocate_at_once } else { @@ -1579,14 +1581,20 @@ impl VMA { } /// 从页分配器中分配一些物理页,并把它们映射到指定的虚拟地址,然后创建VMA + /// ## 参数 /// - /// @param destination 要映射到的虚拟地址 - /// @param count 要映射的页帧数量 - /// @param flags 页面标志位 - /// @param mapper 页表映射器 - /// @param flusher 页表项刷新器 + /// - `destination`: 要映射到的虚拟地址 + /// - `page_count`: 要映射的页帧数量 + /// - `vm_flags`: VMA标志位 + /// - `flags`: 页面标志位 + /// - `mapper`: 页表映射器 + /// - `flusher`: 页表项刷新器 + /// - `file`: 映射文件 + /// - `pgoff`: 返回映射后的虚拟内存区域 /// - /// @return 返回映射后的虚拟内存区域 + /// ## 返回值 + /// - 页面错误处理信息标志 + #[allow(clippy::too_many_arguments)] pub fn zeroed( destination: VirtPageFrame, page_count: PageFrameCount, From 34c585e7c529195cea5b495b0c764bcd0b6188a4 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Wed, 3 Jul 2024 01:54:41 +0800 Subject: [PATCH 23/60] =?UTF-8?q?pagecache=E5=AD=98=E5=82=A8=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=E7=94=B1HashMap=E6=94=B9=E4=B8=BAXArray?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/Cargo.toml | 2 +- kernel/src/filesystem/vfs/file.rs | 35 ++++++++++++++++++++++++------- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 308834371..bd4203220 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -54,7 +54,7 @@ uefi-raw = "=0.5.0" paste = "=1.0.14" slabmalloc = { path = "crates/rust-slabmalloc" } log = "0.4.21" - +xarray = "0.1.0" # target为x86_64时,使用下面的依赖 [target.'cfg(target_arch = "x86_64")'.dependencies] diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 5feb98915..385b5c9e1 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -5,11 +5,12 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use hashbrown::HashMap; use log::error; use system_error::SystemError; +use xarray::XArray; use crate::{ + arch::MMArch, driver::{ base::{block::SeekFrom, device::DevicePrivateData}, tty::tty_device::TtyFilePrivateData, @@ -17,7 +18,7 @@ use crate::{ filesystem::procfs::ProcfsFilePrivateData, ipc::pipe::{LockedPipeInode, PipeFsPrivateData}, libs::{rwlock::RwLock, spinlock::SpinLock}, - mm::{page::Page, PhysAddr}, + mm::{page::Page, MemoryManagementArch, PhysAddr}, net::{ event_poll::{EPollItem, EPollPrivateData, EventPoll}, socket::SocketInode, @@ -122,25 +123,43 @@ impl FileMode { } #[allow(dead_code)] -#[derive(Debug)] pub struct PageCache { - map: RwLock>, + xarray: SpinLock>>, +} + +impl core::fmt::Debug for PageCache { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("PageCache") + .field( + "xarray", + &self + .xarray + .lock() + .range(0..((MMArch::PAGE_ADDRESS_SIZE >> MMArch::PAGE_SHIFT) as u64)) + .map(|(_, r)| **r) + .collect::>(), + ) + .finish() + } } impl PageCache { pub fn new() -> PageCache { Self { - map: RwLock::new(HashMap::new()), + xarray: SpinLock::new(XArray::new()), } } pub fn add_page(&self, offset: usize, page_phys_address: PhysAddr) { - self.map.write().insert(offset, page_phys_address); + let mut guard = self.xarray.lock(); + let mut cursor = guard.cursor_mut(offset as u64); + cursor.store(Arc::new(page_phys_address)); } pub fn get_page(&self, offset: usize) -> Option { - let guard = self.map.read(); - let phys = guard.get(&offset).cloned(); + let mut guard = self.xarray.lock(); + let mut cursor = guard.cursor_mut(offset as u64); + let phys = cursor.load().map(|r| **r); phys } From 2558777bba3bdd72679469f17fc8c71b7aa373ea Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Wed, 24 Jul 2024 23:35:15 +0800 Subject: [PATCH 24/60] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E8=AF=BB=E5=86=99?= =?UTF-8?q?=E9=94=81=E5=8C=85=E8=A3=85Page=E7=BB=93=E6=9E=84=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/ipc/shm.rs | 11 ++--- kernel/src/ipc/syscall.rs | 7 ++-- kernel/src/mm/allocator/page_frame.rs | 5 ++- kernel/src/mm/fault.rs | 34 ++++++++------- kernel/src/mm/page.rs | 60 ++++++++++++++++++++++----- kernel/src/mm/ucontext.rs | 41 +++++++++--------- 6 files changed, 103 insertions(+), 55 deletions(-) diff --git a/kernel/src/ipc/shm.rs b/kernel/src/ipc/shm.rs index f3f87eb74..9c8cf4239 100644 --- a/kernel/src/ipc/shm.rs +++ b/kernel/src/ipc/shm.rs @@ -165,8 +165,8 @@ impl ShmManager { let mut page_manager_guard = page_manager_lock_irqsave(); let mut cur_phys = PhysPageFrame::new(phys_page.0); for _ in 0..page_count.data() { - let mut page = Page::new(true, cur_phys); - page.set_shm_id(shm_id); + let page = Page::new(true, cur_phys); + page.write().set_shm_id(shm_id); let paddr = cur_phys.phys_address(); page_manager_guard.insert(paddr, page); cur_phys = cur_phys.next(); @@ -324,8 +324,8 @@ impl ShmManager { if map_count > 0 { // 设置共享内存物理页当映射计数等于0时可被回收 for _ in 0..count.data() { - let page = page_manager_guard.get_mut(&cur_phys.phys_address()); - page.set_dealloc_when_zero(true); + let page = page_manager_guard.get_unwrap(&cur_phys.phys_address()); + page.write().set_dealloc_when_zero(true); cur_phys = cur_phys.next(); } @@ -444,7 +444,8 @@ impl KernelShm { for _ in 0..page_count.data() { let page = page_manager_guard.get(&cur_phys.phys_address()).unwrap(); id_set.extend( - page.anon_vma() + page.read() + .anon_vma() .iter() .map(|vma| vma.id()) .collect::>(), diff --git a/kernel/src/ipc/syscall.rs b/kernel/src/ipc/syscall.rs index 6cc525bba..7a2f7fae4 100644 --- a/kernel/src/ipc/syscall.rs +++ b/kernel/src/ipc/syscall.rs @@ -371,7 +371,7 @@ impl Syscall { vma.unmap(&mut address_write_guard.user_mapper.utable, flusher); // 将该虚拟内存区域映射到共享内存区域 - let mut page_manager_guard = page_manager_lock_irqsave(); + let page_manager_guard = page_manager_lock_irqsave(); let mut virt = VirtPageFrame::new(vaddr); for _ in 0..count.data() { let r = unsafe { @@ -386,7 +386,8 @@ impl Syscall { // 将vma加入到对应Page的anon_vma page_manager_guard - .get_mut(&phys.phys_address()) + .get_unwrap(&phys.phys_address()) + .write() .insert_vma(vma.clone()); phys = phys.next(); @@ -442,7 +443,7 @@ impl Syscall { // 如果物理页的shm_id为None,代表不是共享页 let page_manager_guard = page_manager_lock_irqsave(); let page = page_manager_guard.get(&paddr).ok_or(SystemError::EINVAL)?; - let shm_id = page.shm_id().ok_or(SystemError::EINVAL)?; + let shm_id = page.read().shm_id().ok_or(SystemError::EINVAL)?; drop(page_manager_guard); // 获取对应共享页管理信息 diff --git a/kernel/src/mm/allocator/page_frame.rs b/kernel/src/mm/allocator/page_frame.rs index 75a79529f..e59db4cf8 100644 --- a/kernel/src/mm/allocator/page_frame.rs +++ b/kernel/src/mm/allocator/page_frame.rs @@ -371,8 +371,9 @@ pub unsafe fn deallocate_page_frames( if let Some(page) = page { // 如果page是共享页,将其共享页信息从SHM_MANAGER中删去 - if page.shared() { - shm_manager_lock().free_id(&page.shm_id().unwrap()); + let page_guard = page.read(); + if page_guard.shared() { + shm_manager_lock().free_id(&page_guard.shm_id().unwrap()); } } diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 3b11fa471..efaa231c9 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -259,9 +259,9 @@ impl PageFaultHandler { klog_types::LogSource::Buddy, ); let paddr = mapper.translate(address).unwrap().0; - let mut anon_vma_guard = page_manager_lock_irqsave(); - let page = anon_vma_guard.get_mut(&paddr); - page.insert_vma(vma.clone()); + let anon_vma_guard = page_manager_lock_irqsave(); + let page = anon_vma_guard.get_unwrap(&paddr); + page.write().insert_vma(vma.clone()); VmFaultReason::VM_FAULT_COMPLETED } else { VmFaultReason::VM_FAULT_OOM @@ -434,8 +434,8 @@ impl PageFaultHandler { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); let old_paddr = mapper.translate(address).unwrap().0; - let mut page_manager = page_manager_lock_irqsave(); - let map_count = page_manager.get_mut(&old_paddr).map_count(); + let page_manager = page_manager_lock_irqsave(); + let map_count = page_manager.get_unwrap(&old_paddr).read().map_count(); drop(page_manager); let mut entry = mapper.get_entry(address, 0).unwrap(); @@ -448,16 +448,16 @@ impl PageFaultHandler { table.set_entry(i, entry); VmFaultReason::VM_FAULT_COMPLETED } else if let Some(flush) = mapper.map(address, new_flags) { - let mut page_manager = page_manager_lock_irqsave(); - let old_page = page_manager.get_mut(&old_paddr); - old_page.remove_vma(&vma); + let page_manager = page_manager_lock_irqsave(); + let old_page = page_manager.get_unwrap(&old_paddr); + old_page.write().remove_vma(&vma); drop(page_manager); flush.flush(); let paddr = mapper.translate(address).unwrap().0; - let mut anon_vma_guard = page_manager_lock_irqsave(); - let page = anon_vma_guard.get_mut(&paddr); - page.insert_vma(vma.clone()); + let anon_vma_guard = page_manager_lock_irqsave(); + let page = anon_vma_guard.get_unwrap(&paddr); + page.write().insert_vma(vma.clone()); (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( MMArch::phys_2_virt(old_paddr).unwrap().data() as *mut u8, @@ -577,8 +577,9 @@ impl PageFaultHandler { for pgoff in start_pgoff..=end_pgoff { if let Some(page_phys) = page_cache.get_page(pgoff) { let page = page_manager_guard.get(&page_phys).unwrap(); - if page.flags().contains(PageFlags::PG_UPTODATE) { - let phys = page.phys_frame().phys_address(); + let page_guard = page.read(); + if page_guard.flags().contains(PageFlags::PG_UPTODATE) { + let phys = page_guard.phys_frame().phys_address(); let virt = phys_2_virt(phys.data()); let address = @@ -638,10 +639,11 @@ impl PageFaultHandler { .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); let page = Page::new(false, PhysPageFrame::new(new_cache_page)); - pfm.page_phys_address = Some(page.phys_frame().phys_address()); - - page_cache.add_page(file_pgoff, page.phys_frame().phys_address()); + let page_guard = page.read(); + pfm.page_phys_address = Some(page_guard.phys_frame().phys_address()); + page_cache.add_page(file_pgoff, page_guard.phys_frame().phys_address()); + drop(page_guard); page_manager_guard.insert(new_cache_page, page); // // 分配空白页并映射到缺页地址 diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 8161d1c36..8d06c7da6 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -14,7 +14,10 @@ use crate::{ arch::{interrupt::ipi::send_ipi, MMArch}, exception::ipi::{IpiKind, IpiTarget}, ipc::shm::ShmId, - libs::spinlock::{SpinLock, SpinLockGuard}, + libs::{ + rwlock::RwLock, + spinlock::{SpinLock, SpinLockGuard}, + }, }; use super::{ @@ -53,7 +56,7 @@ pub fn page_manager_lock_irqsave() -> SpinLockGuard<'static, PageManager> { // 物理页管理器 pub struct PageManager { - phys2page: HashMap, + phys2page: HashMap>, } impl PageManager { @@ -67,18 +70,26 @@ impl PageManager { self.phys2page.contains_key(paddr) } - pub fn get(&self, paddr: &PhysAddr) -> Option<&Page> { - self.phys2page.get(paddr) + pub fn get(&self, paddr: &PhysAddr) -> Option> { + self.phys2page.get(paddr).map(|x| x.clone()) } - pub fn get_mut(&mut self, paddr: &PhysAddr) -> &mut Page { + pub fn get_unwrap(&self, paddr: &PhysAddr) -> Arc { self.phys2page - .get_mut(paddr) - .unwrap_or_else(|| panic!("{:?}", paddr)) + .get(paddr) + .unwrap_or_else(|| panic!("Phys Page not found, {:?}", paddr)) + .clone() } + // pub fn get_mut(&mut self, paddr: &PhysAddr) -> RwLockWriteGuard { + // self.phys2page + // .get_mut(paddr) + // .unwrap_or_else(|| panic!("{:?}", paddr)) + // .write() + // } + pub fn insert(&mut self, paddr: PhysAddr, page: Page) { - self.phys2page.insert(paddr, page); + self.phys2page.insert(paddr, Arc::new(page)); } pub fn remove_page(&mut self, paddr: &PhysAddr) { @@ -106,9 +117,38 @@ bitflags! { const PG_SWAPBACKED = 1 << 19; } } + #[derive(Debug)] -/// 物理页面信息 pub struct Page { + inner: RwLock, +} + +impl core::ops::Deref for Page { + type Target = RwLock; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl core::ops::DerefMut for Page { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl Page { + pub fn new(shared: bool, phys_frame: PhysPageFrame) -> Self { + let inner = InnerPage::new(shared, phys_frame); + Self { + inner: RwLock::new(inner), + } + } +} + +#[derive(Debug)] +/// 物理页面信息 +pub struct InnerPage { /// 映射计数 map_count: usize, /// 是否为共享页 @@ -125,7 +165,7 @@ pub struct Page { phys_frame: PhysPageFrame, } -impl Page { +impl InnerPage { pub fn new(shared: bool, phys_frame: PhysPageFrame) -> Self { let dealloc_when_zero = !shared; Self { diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index cc57dcddb..724b91ac3 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -187,11 +187,11 @@ impl InnerAddressSpace { // debug!("new vma: {:x?}", new_vma); let new_vma_guard = new_vma.lock(); let new_mapper = &new_guard.user_mapper.utable; - let mut anon_vma_guard = page_manager_lock_irqsave(); + let anon_vma_guard = page_manager_lock_irqsave(); for page in new_vma_guard.pages().map(|p| p.virt_address()) { if let Some((paddr, _)) = new_mapper.translate(page) { - let page = anon_vma_guard.get_mut(&paddr); - page.insert_vma(new_vma.clone()); + let page = anon_vma_guard.get_unwrap(&paddr); + page.write().insert_vma(new_vma.clone()); } } @@ -1177,12 +1177,13 @@ impl LockedVMA { .expect("Failed to unmap, beacuse of some page is not mapped"); // 从anon_vma中删除当前VMA - let page = page_manager_guard.get_mut(&paddr); - page.remove_vma(self); + let page = page_manager_guard.get_unwrap(&paddr); + page.write().remove_vma(self); // 如果物理页的anon_vma链表长度为0并且不是共享页,则释放物理页. - if page.can_deallocate() { + if page.read().can_deallocate() { unsafe { + drop(page); deallocate_page_frames( PhysPageFrame::new(paddr), PageFrameCount::new(1), @@ -1250,14 +1251,15 @@ impl LockedVMA { }); // 重新设置before、after这两个VMA里面的物理页的anon_vma - let mut page_manager_guard = page_manager_lock_irqsave(); + let page_manager_guard = page_manager_lock_irqsave(); if let Some(before) = before.clone() { let virt_iter = before.lock().region.iter_pages(); for frame in virt_iter { if let Some((paddr, _)) = utable.translate(frame.virt_address()) { - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(before.clone()); - page.remove_vma(self); + let page = page_manager_guard.get_unwrap(&paddr); + let mut page_guard = page.write(); + page_guard.insert_vma(before.clone()); + page_guard.remove_vma(self); before.lock().mapped = true; } } @@ -1267,9 +1269,10 @@ impl LockedVMA { let virt_iter = after.lock().region.iter_pages(); for frame in virt_iter { if let Some((paddr, _)) = utable.translate(frame.virt_address()) { - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(after.clone()); - page.remove_vma(self); + let page = page_manager_guard.get_unwrap(&paddr); + let mut page_guard = page.write(); + page_guard.insert_vma(after.clone()); + page_guard.remove_vma(self); after.lock().mapped = true; } } @@ -1569,12 +1572,12 @@ impl VMA { )); // 将VMA加入到anon_vma中 - let mut page_manager_guard = page_manager_lock_irqsave(); + let page_manager_guard = page_manager_lock_irqsave(); cur_phy = phys; for _ in 0..count.data() { let paddr = cur_phy.phys_address(); - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(r.clone()); + let page = page_manager_guard.get_unwrap(&paddr); + page.write().insert_vma(r.clone()); cur_phy = cur_phy.next(); } @@ -1639,15 +1642,15 @@ impl VMA { // debug!("VMA::zeroed: flusher dropped"); // 清空这些内存并将VMA加入到anon_vma中 - let mut page_manager_guard = page_manager_lock_irqsave(); + let page_manager_guard = page_manager_lock_irqsave(); let virt_iter: VirtPageFrameIter = VirtPageFrameIter::new(destination, destination.add(page_count)); for frame in virt_iter { let paddr = mapper.translate(frame.virt_address()).unwrap().0; // 将VMA加入到anon_vma - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(r.clone()); + let page = page_manager_guard.get_unwrap(&paddr); + page.write().insert_vma(r.clone()); } // debug!("VMA::zeroed: done"); return Ok(r); From c9e790be1b79de595e68b30c72fa7992a9e1cbd2 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Thu, 25 Jul 2024 02:28:20 +0800 Subject: [PATCH 25/60] =?UTF-8?q?PageCache=E7=94=B1=E5=AD=98=E6=94=BE?= =?UTF-8?q?=E7=89=A9=E7=90=86=E5=9C=B0=E5=9D=80=E6=94=B9=E4=B8=BA=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=AD=98=E6=94=BE=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/filesystem/vfs/file.rs | 18 +++++----- kernel/src/ipc/shm.rs | 6 ++-- kernel/src/mm/fault.rs | 55 +++++++++++++------------------ kernel/src/mm/page.rs | 18 +++++++--- 4 files changed, 48 insertions(+), 49 deletions(-) diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 385b5c9e1..04cc4ad22 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -18,7 +18,7 @@ use crate::{ filesystem::procfs::ProcfsFilePrivateData, ipc::pipe::{LockedPipeInode, PipeFsPrivateData}, libs::{rwlock::RwLock, spinlock::SpinLock}, - mm::{page::Page, MemoryManagementArch, PhysAddr}, + mm::{page::Page, MemoryManagementArch}, net::{ event_poll::{EPollItem, EPollPrivateData, EventPoll}, socket::SocketInode, @@ -124,7 +124,7 @@ impl FileMode { #[allow(dead_code)] pub struct PageCache { - xarray: SpinLock>>, + xarray: SpinLock>>, } impl core::fmt::Debug for PageCache { @@ -136,8 +136,8 @@ impl core::fmt::Debug for PageCache { .xarray .lock() .range(0..((MMArch::PAGE_ADDRESS_SIZE >> MMArch::PAGE_SHIFT) as u64)) - .map(|(_, r)| **r) - .collect::>(), + .map(|(_, r)| (*r).clone()) + .collect::>>(), ) .finish() } @@ -150,17 +150,17 @@ impl PageCache { } } - pub fn add_page(&self, offset: usize, page_phys_address: PhysAddr) { + pub fn add_page(&self, offset: usize, page: &Arc) { let mut guard = self.xarray.lock(); let mut cursor = guard.cursor_mut(offset as u64); - cursor.store(Arc::new(page_phys_address)); + cursor.store(page.clone()); } - pub fn get_page(&self, offset: usize) -> Option { + pub fn get_page(&self, offset: usize) -> Option> { let mut guard = self.xarray.lock(); let mut cursor = guard.cursor_mut(offset as u64); - let phys = cursor.load().map(|r| **r); - phys + let page = cursor.load().map(|r| (*r).clone()); + page } // pub fn get_pages(&self, start_pgoff: usize, end_pgoff: usize) -> Vec> { diff --git a/kernel/src/ipc/shm.rs b/kernel/src/ipc/shm.rs index 9c8cf4239..713c047b6 100644 --- a/kernel/src/ipc/shm.rs +++ b/kernel/src/ipc/shm.rs @@ -14,7 +14,7 @@ use crate::{ syscall::user_access::{UserBufferReader, UserBufferWriter}, time::PosixTimeSpec, }; -use alloc::vec::Vec; +use alloc::{sync::Arc, vec::Vec}; use core::sync::atomic::{compiler_fence, Ordering}; use hashbrown::{HashMap, HashSet}; use ida::IdAllocator; @@ -165,10 +165,10 @@ impl ShmManager { let mut page_manager_guard = page_manager_lock_irqsave(); let mut cur_phys = PhysPageFrame::new(phys_page.0); for _ in 0..page_count.data() { - let page = Page::new(true, cur_phys); + let page = Arc::new(Page::new(true, cur_phys)); page.write().set_shm_id(shm_id); let paddr = cur_phys.phys_address(); - page_manager_guard.insert(paddr, page); + page_manager_guard.insert(paddr, &page); cur_phys = cur_phys.next(); } diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index efaa231c9..fc7d940f3 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -23,7 +23,6 @@ use crate::mm::MemoryManagementArch; use super::{ allocator::page_frame::{FrameAllocator, PhysPageFrame}, page::{Page, PageFlags}, - phys_2_virt, PhysAddr, }; bitflags! { @@ -57,7 +56,7 @@ pub struct PageFaultMessage { /// 缺页的文件页在文件中的偏移量 file_pgoff: Option, /// 缺页对应PageCache中的文件页的物理地址 - page_phys_address: Option, + page: Option>, } impl PageFaultMessage { @@ -71,7 +70,7 @@ impl PageFaultMessage { address, flags, file_pgoff, - page_phys_address: None, + page: None, } } @@ -107,7 +106,7 @@ impl Clone for PageFaultMessage { address: self.address, flags: self.flags, file_pgoff: self.file_pgoff, - page_phys_address: None, + page: None, } } } @@ -543,7 +542,7 @@ impl PageFaultHandler { VmFaultReason::empty() } - /// 通用的VMA文件映射页面映射函数,将页面从PageCache中映射到内存 + /// 通用的VMA文件映射页面映射函数,将PageCache中的页面映射到进程空间 /// ## 参数 /// /// - `pfm`: 缺页异常信息 @@ -561,7 +560,6 @@ impl PageFaultHandler { let vma_guard = vma.lock(); let file = vma_guard.vm_file().expect("no vm_file in vma"); let page_cache = file.inode().page_cache().unwrap(); - let page_manager_guard = page_manager_lock_irqsave(); // 起始页地址 let addr = vma_guard.region().start @@ -570,25 +568,19 @@ impl PageFaultHandler { .file_page_offset() .expect("file_page_offset is none")) << MMArch::PAGE_SHIFT); - // let pages = page_cache.get_pages(start_pgoff, end_pgoff); - // let uptodate_pages = pages - // .iter() - // .filter(|page| page.flags().contains(PageFlags::PG_UPTODATE)); + for pgoff in start_pgoff..=end_pgoff { - if let Some(page_phys) = page_cache.get_page(pgoff) { - let page = page_manager_guard.get(&page_phys).unwrap(); + if let Some(page) = page_cache.get_page(pgoff) { let page_guard = page.read(); if page_guard.flags().contains(PageFlags::PG_UPTODATE) { - let phys = page_guard.phys_frame().phys_address(); - let virt = phys_2_virt(phys.data()); + let phys = page_guard.phys_address(); let address = VirtAddr::new(addr.data() + ((pgoff - start_pgoff) << MMArch::PAGE_SHIFT)); - mapper.map(address, vma_guard.flags()).unwrap().flush(); - let frame = virt as *mut u8; - let new_frame = - phys_2_virt(mapper.translate(address).unwrap().0.data()) as *mut u8; - new_frame.copy_from_nonoverlapping(frame, MMArch::PAGE_SIZE); + mapper + .map_phys(address, phys, vma_guard.flags()) + .unwrap() + .flush(); } } } @@ -619,7 +611,7 @@ impl PageFaultHandler { // TODO 异步从磁盘中预读页面进PageCache // 直接将PageCache中的页面作为要映射的页面 - pfm.page_phys_address = Some(page); + pfm.page = Some(page.clone()); } else { // TODO 同步预读 //涉及磁盘IO,返回标志为VM_FAULT_MAJOR @@ -635,16 +627,14 @@ impl PageFaultHandler { // 分配一个物理页面作为加入PageCache的新页 let new_cache_page = allocator.allocate_one().unwrap(); - (phys_2_virt(new_cache_page.data()) as *mut u8) + (MMArch::phys_2_virt(new_cache_page).unwrap().data() as *mut u8) .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); - let page = Page::new(false, PhysPageFrame::new(new_cache_page)); - let page_guard = page.read(); - pfm.page_phys_address = Some(page_guard.phys_frame().phys_address()); + let page = Arc::new(Page::new(false, PhysPageFrame::new(new_cache_page))); + pfm.page = Some(page.clone()); - page_cache.add_page(file_pgoff, page_guard.phys_frame().phys_address()); - drop(page_guard); - page_manager_guard.insert(new_cache_page, page); + page_manager_guard.insert(new_cache_page, &page); + page_cache.add_page(file_pgoff, &page); // // 分配空白页并映射到缺页地址 // mapper.map(pfm.address, vma_guard.flags()).unwrap().flush(); @@ -660,7 +650,8 @@ impl PageFaultHandler { ) -> VmFaultReason { let vma = pfm.vma(); let vma_guard = vma.lock(); - let cache_frame = pfm.page_phys_address.expect("no page in pfm"); + let cache_page = pfm.page.clone().expect("no page in pfm"); + let page_phys = cache_page.read().phys_address(); if pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) && !pfm.vma().lock().vm_flags().contains(VmFlags::VM_SHARED) @@ -676,14 +667,14 @@ impl PageFaultHandler { .flush(); //复制PageCache内容到新的页内 - let new_frame = phys_2_virt(mapper.translate(pfm.address).unwrap().0.data()); - (new_frame as *mut u8).copy_from_nonoverlapping( - phys_2_virt(cache_frame.data()) as *mut u8, + let new_frame = MMArch::phys_2_virt(mapper.translate(pfm.address).unwrap().0).unwrap(); + (new_frame.data() as *mut u8).copy_from_nonoverlapping( + MMArch::phys_2_virt(page_phys).unwrap().data() as *mut u8, MMArch::PAGE_SIZE, ); } else { // 直接映射到PageCache - mapper.map_phys(*pfm.address(), cache_frame, vma_guard.flags()); + mapper.map_phys(*pfm.address(), page_phys, vma_guard.flags()); } VmFaultReason::VM_FAULT_COMPLETED } diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 8d06c7da6..c7b940076 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -71,7 +71,7 @@ impl PageManager { } pub fn get(&self, paddr: &PhysAddr) -> Option> { - self.phys2page.get(paddr).map(|x| x.clone()) + self.phys2page.get(paddr).cloned() } pub fn get_unwrap(&self, paddr: &PhysAddr) -> Arc { @@ -88,8 +88,8 @@ impl PageManager { // .write() // } - pub fn insert(&mut self, paddr: PhysAddr, page: Page) { - self.phys2page.insert(paddr, Arc::new(page)); + pub fn insert(&mut self, paddr: PhysAddr, page: &Arc) { + self.phys2page.insert(paddr, page.clone()); } pub fn remove_page(&mut self, paddr: &PhysAddr) { @@ -231,6 +231,11 @@ impl InnerPage { pub fn phys_frame(&self) -> &PhysPageFrame { &self.phys_frame } + + #[inline(always)] + pub fn phys_address(&self) -> PhysAddr { + self.phys_frame.phys_address() + } } #[derive(Debug)] @@ -407,7 +412,10 @@ impl PageTable { } else { let phys = allocator.allocate_one()?; let mut anon_vma_guard = page_manager_lock_irqsave(); - anon_vma_guard.insert(phys, Page::new(false, PhysPageFrame::new(phys))); + anon_vma_guard.insert( + phys, + &Arc::new(Page::new(false, PhysPageFrame::new(phys))), + ); let old_phys = entry.address().unwrap(); let frame = MMArch::phys_2_virt(phys).unwrap().data() as *mut u8; frame.copy_from_nonoverlapping( @@ -953,7 +961,7 @@ impl PageMapper { let mut page_manager_guard: SpinLockGuard<'static, PageManager> = page_manager_lock_irqsave(); if !page_manager_guard.contains(&phys) { - page_manager_guard.insert(phys, Page::new(false, PhysPageFrame::new(phys))) + page_manager_guard.insert(phys, &Arc::new(Page::new(false, PhysPageFrame::new(phys)))) } return self.map_phys(virt, phys, flags); From 8f338fb44afb8ce384347fde7407b86df6f089bf Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Thu, 25 Jul 2024 02:54:21 +0800 Subject: [PATCH 26/60] =?UTF-8?q?=E4=BC=98=E5=8C=96protection=5Fmap?= =?UTF-8?q?=E7=9A=84=E5=88=9D=E5=A7=8B=E5=8C=96=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/riscv64/mm/mod.rs | 53 ++++++++++-------- kernel/src/arch/x86_64/mm/mod.rs | 90 ++++++++++++++++++++++--------- kernel/src/mm/mod.rs | 39 ++++++++++---- 3 files changed, 124 insertions(+), 58 deletions(-) diff --git a/kernel/src/arch/riscv64/mm/mod.rs b/kernel/src/arch/riscv64/mm/mod.rs index 8f4f62419..bdbfb1ae6 100644 --- a/kernel/src/arch/riscv64/mm/mod.rs +++ b/kernel/src/arch/riscv64/mm/mod.rs @@ -257,28 +257,6 @@ impl MemoryManagementArch for RiscV64MMArch { true } - fn protection_map() -> [usize; 16] { - let mut map = [0; 16]; - map[VmFlags::VM_NONE] = Self::PAGE_NONE; - map[VmFlags::VM_READ] = Self::PAGE_READONLY; - map[VmFlags::VM_WRITE] = Self::PAGE_COPY; - map[VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY; - map[VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_COPY_EXEC; - map[VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY_EXEC; - map[VmFlags::VM_SHARED] = Self::PAGE_NONE; - map[VmFlags::VM_SHARED | VmFlags::VM_READ] = Self::PAGE_READONLY; - map[VmFlags::VM_SHARED | VmFlags::VM_WRITE] = Self::PAGE_SHARED; - map[VmFlags::VM_SHARED | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_SHARED; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_SHARED_EXEC; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = - Self::PAGE_SHARED_EXEC; - map - } - const PAGE_NONE: usize = Self::ENTRY_FLAG_GLOBAL | Self::ENTRY_FLAG_READONLY; const PAGE_READ: usize = PAGE_ENTRY_BASE | Self::ENTRY_FLAG_READONLY; @@ -304,6 +282,37 @@ impl MemoryManagementArch for RiscV64MMArch { const PAGE_COPY_NOEXEC: usize = 0; const PAGE_READONLY: usize = 0; const PAGE_READONLY_EXEC: usize = 0; + + const PROTECTION_MAP: [usize; 16] = protection_map(); +} + +const fn protection_map() -> [usize; 16] { + type Arch = RiscV64MMArch; + let mut map = [0; 16]; + map[VmFlags::VM_NONE.bits()] = Arch::PAGE_NONE; + map[VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY; + map[VmFlags::VM_WRITE.bits()] = Arch::PAGE_COPY; + map[VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_COPY; + map[VmFlags::VM_EXEC.bits()] = Arch::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = Arch::PAGE_COPY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + Arch::PAGE_COPY_EXEC; + map[VmFlags::VM_SHARED.bits()] = Arch::PAGE_NONE; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits()] = Arch::PAGE_SHARED; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + Arch::PAGE_SHARED; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits()] = Arch::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = + Arch::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = + Arch::PAGE_SHARED_EXEC; + map[VmFlags::VM_SHARED.bits() + | VmFlags::VM_EXEC.bits() + | VmFlags::VM_WRITE.bits() + | VmFlags::VM_READ.bits()] = Arch::PAGE_SHARED_EXEC; + map } const PAGE_ENTRY_BASE: usize = RiscV64MMArch::ENTRY_FLAG_PRESENT diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index 7c8f16ffc..b71e7c0c4 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -327,31 +327,33 @@ impl MemoryManagementArch for X86_64MMArch { pkru::pkru_allows_pkey(pkru::vma_pkey(vma), write) } - fn protection_map() -> [usize; 16] { - let mut map = [0; 16]; - map[VmFlags::VM_NONE] = Self::PAGE_NONE; - map[VmFlags::VM_READ] = Self::PAGE_READONLY; - map[VmFlags::VM_WRITE] = Self::PAGE_COPY; - map[VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY; - map[VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_COPY_EXEC; - map[VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY_EXEC; - map[VmFlags::VM_SHARED] = Self::PAGE_NONE; - map[VmFlags::VM_SHARED | VmFlags::VM_READ] = Self::PAGE_READONLY; - map[VmFlags::VM_SHARED | VmFlags::VM_WRITE] = Self::PAGE_SHARED; - map[VmFlags::VM_SHARED | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_SHARED; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_SHARED_EXEC; - map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = - Self::PAGE_SHARED_EXEC; - - if Self::is_xd_reserved() { - map.iter_mut().for_each(|x| *x &= !Self::ENTRY_FLAG_NO_EXEC) - } - map - } + // fn protection_map() -> [usize; 16] { + // let mut map = [0; 16]; + // map[VmFlags::VM_NONE] = Self::PAGE_NONE; + // map[VmFlags::VM_READ] = Self::PAGE_READONLY; + // map[VmFlags::VM_WRITE] = Self::PAGE_COPY; + // map[VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY; + // map[VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; + // map[VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; + // map[VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_COPY_EXEC; + // map[VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY_EXEC; + // map[VmFlags::VM_SHARED] = Self::PAGE_NONE; + // map[VmFlags::VM_SHARED | VmFlags::VM_READ] = Self::PAGE_READONLY; + // map[VmFlags::VM_SHARED | VmFlags::VM_WRITE] = Self::PAGE_SHARED; + // map[VmFlags::VM_SHARED | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_SHARED; + // map[VmFlags::VM_SHARED | VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; + // map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; + // map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_SHARED_EXEC; + // map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = + // Self::PAGE_SHARED_EXEC; + + // if Self::is_xd_reserved() { + // map.iter_mut().for_each(|x| *x &= !Self::ENTRY_FLAG_NO_EXEC) + // } + // map + // } + + const PROTECTION_MAP: [usize; 16] = protection_map(); const PAGE_NONE: usize = Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_ACCESSED | Self::ENTRY_FLAG_GLOBAL; @@ -395,6 +397,44 @@ impl MemoryManagementArch for X86_64MMArch { const PAGE_EXEC: usize = 0; } +/// 获取保护标志的映射表 +/// +/// +/// ## 返回值 +/// - `[usize; 16]`: 长度为16的映射表 +const fn protection_map() -> [usize; 16] { + type Arch = X86_64MMArch; + let mut map = [0; 16]; + map[VmFlags::VM_NONE.bits()] = Arch::PAGE_NONE; + map[VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY; + map[VmFlags::VM_WRITE.bits()] = Arch::PAGE_COPY; + map[VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_COPY; + map[VmFlags::VM_EXEC.bits()] = Arch::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = Arch::PAGE_COPY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + Arch::PAGE_COPY_EXEC; + map[VmFlags::VM_SHARED.bits()] = X86_64MMArch::PAGE_NONE; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits()] = Arch::PAGE_SHARED; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + Arch::PAGE_SHARED; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits()] = Arch::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = + Arch::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = + Arch::PAGE_SHARED_EXEC; + map[VmFlags::VM_SHARED.bits() + | VmFlags::VM_EXEC.bits() + | VmFlags::VM_WRITE.bits() + | VmFlags::VM_READ.bits()] = Arch::PAGE_SHARED_EXEC; + + // if X86_64MMArch::is_xd_reserved() { + // map.iter_mut().for_each(|x| *x &= !Self::ENTRY_FLAG_NO_EXEC) + // } + map +} + impl X86_64MMArch { unsafe fn get_load_base_paddr() -> PhysAddr { let mut mb2_lb_info: [multiboot_tag_load_base_addr_t; 512] = mem::zeroed(); diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index d416279e0..3d90eecb7 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -679,15 +679,17 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { const PAGE_WRITE_EXEC: usize; const PAGE_EXEC: usize; - /// 获取保护标志的映射表 - /// - /// - /// ## 返回值 - /// - `[usize; 16]`: 长度为16的映射表 - fn protection_map() -> [usize; 16] { - let map = [0; 16]; - map - } + // /// 获取保护标志的映射表 + // /// + // /// + // /// ## 返回值 + // /// - `[usize; 16]`: 长度为16的映射表 + // fn protection_map() -> [usize; 16] { + // let map = [0; 16]; + // map + // } + + const PROTECTION_MAP: [usize; 16]; /// 页面保护标志转换函数 /// ## 参数 @@ -697,8 +699,23 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { /// ## 返回值 /// - EntryFlags: 页面的保护位 fn vm_get_page_prot(vm_flags: VmFlags) -> EntryFlags { - let map = Self::protection_map(); - unsafe { EntryFlags::from_data(map[vm_flags]) } + let map = Self::PROTECTION_MAP; + let mut ret = unsafe { + EntryFlags::from_data( + map[vm_flags.intersection( + VmFlags::VM_READ | VmFlags::VM_WRITE | VmFlags::VM_EXEC | VmFlags::VM_SHARED, + )], + ) + }; + + #[cfg(target_arch = "x86_64")] + { + // 如果xd位被保留,那么将可执行性设置为true + if crate::arch::mm::X86_64MMArch::is_xd_reserved() { + ret = ret.set_execute(true); + } + } + ret } } From a6ff58c1453468b67306da436e7dc86e5f9e6026 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Fri, 26 Jul 2024 21:42:59 +0800 Subject: [PATCH 27/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0shrink=5Flist=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E9=87=8A=E6=94=BE=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/Cargo.toml | 1 + kernel/src/arch/x86_64/mm/mod.rs | 10 ++- kernel/src/filesystem/vfs/file.rs | 6 ++ kernel/src/ipc/shm.rs | 4 +- kernel/src/ipc/syscall.rs | 4 +- kernel/src/mm/fault.rs | 37 +++++++--- kernel/src/mm/page.rs | 112 +++++++++++++++++++++--------- kernel/src/mm/ucontext.rs | 31 +++++++-- 8 files changed, 148 insertions(+), 57 deletions(-) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index bd4203220..d56994767 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -55,6 +55,7 @@ paste = "=1.0.14" slabmalloc = { path = "crates/rust-slabmalloc" } log = "0.4.21" xarray = "0.1.0" +lru = "0.12.3" # target为x86_64时,使用下面的依赖 [target.'cfg(target_arch = "x86_64")'.dependencies] diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index b71e7c0c4..c709a2923 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -28,7 +28,7 @@ use crate::{ }; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}; +use crate::mm::page::{page_manager_lock_irqsave, EntryFlags, PageEntry, PAGE_1G_SHIFT}; use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, VmFlags}; use system_error::SystemError; @@ -734,7 +734,13 @@ impl FrameAllocator for LockedFrameAllocator { unsafe fn allocate(&mut self, mut count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { count = count.next_power_of_two(); if let Some(ref mut allocator) = *INNER_ALLOCATOR.lock_irqsave() { - return allocator.allocate(count); + // 首次分配时内存不足 + allocator.allocate(count).or_else(|| { + let mut page_manager_guard = page_manager_lock_irqsave(); + // 释放部分页面并再次尝试分配 + page_manager_guard.shrink_list(); + return allocator.allocate(count); + }) } else { return None; } diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 04cc4ad22..516a515b0 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -163,6 +163,12 @@ impl PageCache { page } + pub fn remove_page(&self, offset: usize) { + let mut guard = self.xarray.lock(); + let mut cursor = guard.cursor_mut(offset as u64); + cursor.remove(); + } + // pub fn get_pages(&self, start_pgoff: usize, end_pgoff: usize) -> Vec> { // let mut vec = Vec::new(); // for pgoff in start_pgoff..=end_pgoff { diff --git a/kernel/src/ipc/shm.rs b/kernel/src/ipc/shm.rs index 713c047b6..981d8eeb6 100644 --- a/kernel/src/ipc/shm.rs +++ b/kernel/src/ipc/shm.rs @@ -165,7 +165,7 @@ impl ShmManager { let mut page_manager_guard = page_manager_lock_irqsave(); let mut cur_phys = PhysPageFrame::new(phys_page.0); for _ in 0..page_count.data() { - let page = Arc::new(Page::new(true, cur_phys)); + let page = Arc::new(Page::new(true, cur_phys.phys_address())); page.write().set_shm_id(shm_id); let paddr = cur_phys.phys_address(); page_manager_guard.insert(paddr, &page); @@ -436,7 +436,7 @@ impl KernelShm { /// 共享内存段的映射计数(有多少个不同的VMA映射) pub fn map_count(&self) -> usize { - let page_manager_guard = page_manager_lock_irqsave(); + let mut page_manager_guard = page_manager_lock_irqsave(); let mut id_set: HashSet = HashSet::new(); let mut cur_phys = PhysPageFrame::new(self.shm_start_paddr); let page_count = PageFrameCount::from_bytes(page_align_up(self.shm_size)).unwrap(); diff --git a/kernel/src/ipc/syscall.rs b/kernel/src/ipc/syscall.rs index 7a2f7fae4..01efc7cfe 100644 --- a/kernel/src/ipc/syscall.rs +++ b/kernel/src/ipc/syscall.rs @@ -371,7 +371,7 @@ impl Syscall { vma.unmap(&mut address_write_guard.user_mapper.utable, flusher); // 将该虚拟内存区域映射到共享内存区域 - let page_manager_guard = page_manager_lock_irqsave(); + let mut page_manager_guard = page_manager_lock_irqsave(); let mut virt = VirtPageFrame::new(vaddr); for _ in 0..count.data() { let r = unsafe { @@ -441,7 +441,7 @@ impl Syscall { .0; // 如果物理页的shm_id为None,代表不是共享页 - let page_manager_guard = page_manager_lock_irqsave(); + let mut page_manager_guard = page_manager_lock_irqsave(); let page = page_manager_guard.get(&paddr).ok_or(SystemError::EINVAL)?; let shm_id = page.read().shm_id().ok_or(SystemError::EINVAL)?; drop(page_manager_guard); diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index fc7d940f3..80e525222 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -21,7 +21,7 @@ use crate::{ use crate::mm::MemoryManagementArch; use super::{ - allocator::page_frame::{FrameAllocator, PhysPageFrame}, + allocator::page_frame::FrameAllocator, page::{Page, PageFlags}, }; @@ -258,8 +258,8 @@ impl PageFaultHandler { klog_types::LogSource::Buddy, ); let paddr = mapper.translate(address).unwrap().0; - let anon_vma_guard = page_manager_lock_irqsave(); - let page = anon_vma_guard.get_unwrap(&paddr); + let mut page_manager_guard = page_manager_lock_irqsave(); + let page = page_manager_guard.get_unwrap(&paddr); page.write().insert_vma(vma.clone()); VmFaultReason::VM_FAULT_COMPLETED } else { @@ -433,7 +433,7 @@ impl PageFaultHandler { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); let old_paddr = mapper.translate(address).unwrap().0; - let page_manager = page_manager_lock_irqsave(); + let mut page_manager = page_manager_lock_irqsave(); let map_count = page_manager.get_unwrap(&old_paddr).read().map_count(); drop(page_manager); @@ -447,15 +447,15 @@ impl PageFaultHandler { table.set_entry(i, entry); VmFaultReason::VM_FAULT_COMPLETED } else if let Some(flush) = mapper.map(address, new_flags) { - let page_manager = page_manager_lock_irqsave(); - let old_page = page_manager.get_unwrap(&old_paddr); + let mut page_manager_guard = page_manager_lock_irqsave(); + let old_page = page_manager_guard.get_unwrap(&old_paddr); old_page.write().remove_vma(&vma); - drop(page_manager); + // drop(page_manager_guard); flush.flush(); let paddr = mapper.translate(address).unwrap().0; - let anon_vma_guard = page_manager_lock_irqsave(); - let page = anon_vma_guard.get_unwrap(&paddr); + // let mut page_manager_guard = page_manager_lock_irqsave(); + let page = page_manager_guard.get_unwrap(&paddr); page.write().insert_vma(vma.clone()); (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( @@ -630,12 +630,15 @@ impl PageFaultHandler { (MMArch::phys_2_virt(new_cache_page).unwrap().data() as *mut u8) .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); - let page = Arc::new(Page::new(false, PhysPageFrame::new(new_cache_page))); + let page = Arc::new(Page::new(false, new_cache_page)); pfm.page = Some(page.clone()); page_manager_guard.insert(new_cache_page, &page); page_cache.add_page(file_pgoff, &page); + page.write() + .set_page_cache_index(Some(page_cache), Some(file_pgoff)); + // // 分配空白页并映射到缺页地址 // mapper.map(pfm.address, vma_guard.flags()).unwrap().flush(); // let new_frame = phys_2_virt(mapper.translate(pfm.address).unwrap().0.data()); @@ -667,11 +670,23 @@ impl PageFaultHandler { .flush(); //复制PageCache内容到新的页内 - let new_frame = MMArch::phys_2_virt(mapper.translate(pfm.address).unwrap().0).unwrap(); + let new_phys = mapper.translate(pfm.address).unwrap().0; + let new_frame = MMArch::phys_2_virt(new_phys).unwrap(); (new_frame.data() as *mut u8).copy_from_nonoverlapping( MMArch::phys_2_virt(page_phys).unwrap().data() as *mut u8, MMArch::PAGE_SIZE, ); + + let vma = pfm.vma(); + let vma_guard = vma.lock(); + let file = vma_guard.vm_file().expect("no vm_file in vma"); + let page_cache = file.inode().page_cache().unwrap(); + let page_guard = cache_page.read(); + let new_page = Arc::new(Page::new(page_guard.shared(), new_phys)); + page_cache.add_page(pfm.file_pgoff.unwrap(), &new_page); + new_page + .write() + .set_page_cache_index(cache_page.read().page_cache(), cache_page.read().index()); } else { // 直接映射到PageCache mapper.map_phys(*pfm.address(), page_phys, vma_guard.flags()); diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index c7b940076..6e93f46e0 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -7,12 +7,14 @@ use core::{ }; use alloc::sync::Arc; -use hashbrown::{HashMap, HashSet}; +use hashbrown::HashSet; use log::{error, info}; +use lru::LruCache; use crate::{ arch::{interrupt::ipi::send_ipi, MMArch}, exception::ipi::{IpiKind, IpiTarget}, + filesystem::vfs::file::PageCache, ipc::shm::ShmId, libs::{ rwlock::RwLock, @@ -21,7 +23,7 @@ use crate::{ }; use super::{ - allocator::page_frame::{FrameAllocator, PageFrameCount, PhysPageFrame}, + allocator::page_frame::{FrameAllocator, PageFrameCount}, syscall::ProtFlags, ucontext::LockedVMA, MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, @@ -56,44 +58,56 @@ pub fn page_manager_lock_irqsave() -> SpinLockGuard<'static, PageManager> { // 物理页管理器 pub struct PageManager { - phys2page: HashMap>, + phys2page: LruCache>, } impl PageManager { pub fn new() -> Self { Self { - phys2page: HashMap::new(), + phys2page: LruCache::unbounded(), } } pub fn contains(&self, paddr: &PhysAddr) -> bool { - self.phys2page.contains_key(paddr) + self.phys2page.peek(paddr).is_some() } - pub fn get(&self, paddr: &PhysAddr) -> Option> { + pub fn get(&mut self, paddr: &PhysAddr) -> Option> { self.phys2page.get(paddr).cloned() } - pub fn get_unwrap(&self, paddr: &PhysAddr) -> Arc { + pub fn get_unwrap(&mut self, paddr: &PhysAddr) -> Arc { self.phys2page .get(paddr) .unwrap_or_else(|| panic!("Phys Page not found, {:?}", paddr)) .clone() } - // pub fn get_mut(&mut self, paddr: &PhysAddr) -> RwLockWriteGuard { - // self.phys2page - // .get_mut(paddr) - // .unwrap_or_else(|| panic!("{:?}", paddr)) - // .write() - // } - pub fn insert(&mut self, paddr: PhysAddr, page: &Arc) { - self.phys2page.insert(paddr, page.clone()); + self.phys2page.put(paddr, page.clone()); } pub fn remove_page(&mut self, paddr: &PhysAddr) { - self.phys2page.remove(paddr); + self.phys2page.pop(paddr); + } + + pub fn shrink_list(&mut self) { + let entry = self.phys2page.peek_lru().unwrap(); + let page = entry.1.clone(); + let phys = *entry.0; + let page_cache = page.read().page_cache().unwrap(); + for vma in page.read().anon_vma() { + let address_space = vma.lock().address_space().unwrap(); + let address_space = address_space.upgrade().unwrap(); + let mut guard = address_space.write(); + let mapper = &mut guard.user_mapper.utable; + let virt = vma.lock().page_address(&page).unwrap(); + unsafe { + mapper.unmap(virt, false).unwrap().flush(); + } + } + self.phys2page.pop(&phys); + page_cache.remove_page(page.read().index().unwrap()); } } @@ -138,8 +152,8 @@ impl core::ops::DerefMut for Page { } impl Page { - pub fn new(shared: bool, phys_frame: PhysPageFrame) -> Self { - let inner = InnerPage::new(shared, phys_frame); + pub fn new(shared: bool, phys_addr: PhysAddr) -> Self { + let inner = InnerPage::new(shared, phys_addr); Self { inner: RwLock::new(inner), } @@ -162,11 +176,14 @@ pub struct InnerPage { /// 标志 flags: PageFlags, /// 页所在的物理页帧号 - phys_frame: PhysPageFrame, + phys_addr: PhysAddr, + /// 在pagecache中的偏移 + index: Option, + page_cache: Option>, } impl InnerPage { - pub fn new(shared: bool, phys_frame: PhysPageFrame) -> Self { + pub fn new(shared: bool, phys_addr: PhysAddr) -> Self { let dealloc_when_zero = !shared; Self { map_count: 0, @@ -175,7 +192,9 @@ impl InnerPage { shm_id: None, anon_vma: HashSet::new(), flags: PageFlags::empty(), - phys_frame, + phys_addr, + index: None, + page_cache: None, } } @@ -204,6 +223,31 @@ impl InnerPage { self.shm_id } + pub fn index(&self) -> Option { + self.index + } + + pub fn page_cache(&self) -> Option> { + self.page_cache.clone() + } + + pub fn set_page_cache(&mut self, page_cache: Option>) { + self.page_cache = page_cache; + } + + pub fn set_index(&mut self, index: Option) { + self.index = index; + } + + pub fn set_page_cache_index( + &mut self, + page_cache: Option>, + index: Option, + ) { + self.page_cache = page_cache; + self.index = index; + } + pub fn set_shm_id(&mut self, shm_id: ShmId) { self.shm_id = Some(shm_id); } @@ -227,14 +271,9 @@ impl InnerPage { &self.flags } - #[inline(always)] - pub fn phys_frame(&self) -> &PhysPageFrame { - &self.phys_frame - } - #[inline(always)] pub fn phys_address(&self) -> PhysAddr { - self.phys_frame.phys_address() + self.phys_addr } } @@ -411,11 +450,18 @@ impl PageTable { new_table.set_entry(i, entry); } else { let phys = allocator.allocate_one()?; - let mut anon_vma_guard = page_manager_lock_irqsave(); - anon_vma_guard.insert( - phys, - &Arc::new(Page::new(false, PhysPageFrame::new(phys))), - ); + let mut page_manager_guard = page_manager_lock_irqsave(); + let old_phys = entry.address().unwrap(); + let old_page = page_manager_guard.get_unwrap(&old_phys); + let new_page = Arc::new(Page::new(old_page.read().shared(), phys)); + if let Some(ref page_cache) = old_page.read().page_cache() { + new_page.write().set_page_cache_index( + Some(page_cache.clone()), + old_page.read().index(), + ); + } + + page_manager_guard.insert(phys, &new_page); let old_phys = entry.address().unwrap(); let frame = MMArch::phys_2_virt(phys).unwrap().data() as *mut u8; frame.copy_from_nonoverlapping( @@ -961,7 +1007,7 @@ impl PageMapper { let mut page_manager_guard: SpinLockGuard<'static, PageManager> = page_manager_lock_irqsave(); if !page_manager_guard.contains(&phys) { - page_manager_guard.insert(phys, &Arc::new(Page::new(false, PhysPageFrame::new(phys)))) + page_manager_guard.insert(phys, &Arc::new(Page::new(false, phys))) } return self.map_phys(virt, phys, flags); diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 724b91ac3..7fd51dd58 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -35,7 +35,7 @@ use super::{ allocator::page_frame::{ deallocate_page_frames, PageFrameCount, PhysPageFrame, VirtPageFrame, VirtPageFrameIter, }, - page::{EntryFlags, Flusher, InactiveFlusher, PageFlushAll}, + page::{EntryFlags, Flusher, InactiveFlusher, Page, PageFlushAll}, syscall::{MadvFlags, MapFlags, MremapFlags, ProtFlags}, MemoryManagementArch, PageTableKind, VirtAddr, VirtRegion, VmFlags, }; @@ -187,15 +187,15 @@ impl InnerAddressSpace { // debug!("new vma: {:x?}", new_vma); let new_vma_guard = new_vma.lock(); let new_mapper = &new_guard.user_mapper.utable; - let anon_vma_guard = page_manager_lock_irqsave(); + let mut page_manager_guard = page_manager_lock_irqsave(); for page in new_vma_guard.pages().map(|p| p.virt_address()) { if let Some((paddr, _)) = new_mapper.translate(page) { - let page = anon_vma_guard.get_unwrap(&paddr); + let page = page_manager_guard.get_unwrap(&paddr); page.write().insert_vma(new_vma.clone()); } } - drop(anon_vma_guard); + drop(page_manager_guard); drop(vma_guard); drop(new_vma_guard); } @@ -1251,7 +1251,7 @@ impl LockedVMA { }); // 重新设置before、after这两个VMA里面的物理页的anon_vma - let page_manager_guard = page_manager_lock_irqsave(); + let mut page_manager_guard = page_manager_lock_irqsave(); if let Some(before) = before.clone() { let virt_iter = before.lock().region.iter_pages(); for frame in virt_iter { @@ -1419,6 +1419,10 @@ impl VMA { return self.vm_file.clone(); } + pub fn address_space(&self) -> Option> { + return self.user_address_space.clone(); + } + pub fn set_vm_flags(&mut self, vm_flags: VmFlags) { self.vm_flags = vm_flags; } @@ -1572,7 +1576,7 @@ impl VMA { )); // 将VMA加入到anon_vma中 - let page_manager_guard = page_manager_lock_irqsave(); + let mut page_manager_guard = page_manager_lock_irqsave(); cur_phy = phys; for _ in 0..count.data() { let paddr = cur_phy.phys_address(); @@ -1642,7 +1646,7 @@ impl VMA { // debug!("VMA::zeroed: flusher dropped"); // 清空这些内存并将VMA加入到anon_vma中 - let page_manager_guard = page_manager_lock_irqsave(); + let mut page_manager_guard = page_manager_lock_irqsave(); let virt_iter: VirtPageFrameIter = VirtPageFrameIter::new(destination, destination.add(page_count)); for frame in virt_iter { @@ -1655,6 +1659,19 @@ impl VMA { // debug!("VMA::zeroed: done"); return Ok(r); } + + pub fn page_address(&self, page: &Arc) -> Result { + let page_guard = page.read(); + let index = page_guard.index().unwrap(); + if index >= self.file_pgoff.unwrap() { + let address = + self.region.start + ((index - self.file_pgoff.unwrap()) << MMArch::PAGE_SHIFT); + if address <= self.region.end() { + return Ok(address); + } + } + return Err(SystemError::EFAULT); + } } impl Drop for VMA { From 9ba2fa3869ea75c903e91f064564cc6e3f560eac Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Sun, 28 Jul 2024 23:04:07 +0800 Subject: [PATCH 28/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=9B=9E=E6=94=B6=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/riscv64/mm/mod.rs | 45 +++++++------- kernel/src/arch/x86_64/mm/mod.rs | 82 ++++++++++++++----------- kernel/src/filesystem/fat/fs.rs | 12 ++++ kernel/src/filesystem/vfs/file.rs | 15 ++--- kernel/src/mm/fault.rs | 20 +++++-- kernel/src/mm/mod.rs | 12 ++-- kernel/src/mm/page.rs | 99 +++++++++++++++++++++++-------- kernel/src/sched/mod.rs | 1 + 8 files changed, 188 insertions(+), 98 deletions(-) diff --git a/kernel/src/arch/riscv64/mm/mod.rs b/kernel/src/arch/riscv64/mm/mod.rs index bdbfb1ae6..1970d3d6f 100644 --- a/kernel/src/arch/riscv64/mm/mod.rs +++ b/kernel/src/arch/riscv64/mm/mod.rs @@ -283,36 +283,41 @@ impl MemoryManagementArch for RiscV64MMArch { const PAGE_READONLY: usize = 0; const PAGE_READONLY_EXEC: usize = 0; - const PROTECTION_MAP: [usize; 16] = protection_map(); + const PROTECTION_MAP: [EntryFlags; 16] = protection_map(); } -const fn protection_map() -> [usize; 16] { - type Arch = RiscV64MMArch; +const fn protection_map() -> [EntryFlags; 16] { let mut map = [0; 16]; - map[VmFlags::VM_NONE.bits()] = Arch::PAGE_NONE; - map[VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY; - map[VmFlags::VM_WRITE.bits()] = Arch::PAGE_COPY; - map[VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_COPY; - map[VmFlags::VM_EXEC.bits()] = Arch::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = Arch::PAGE_COPY_EXEC; + map[VmFlags::VM_NONE.bits()] = MMArch::PAGE_NONE; + map[VmFlags::VM_READ.bits()] = MMArch::PAGE_READONLY; + map[VmFlags::VM_WRITE.bits()] = MMArch::PAGE_COPY; + map[VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = MMArch::PAGE_COPY; + map[VmFlags::VM_EXEC.bits()] = MMArch::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = MMArch::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = MMArch::PAGE_COPY_EXEC; map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = - Arch::PAGE_COPY_EXEC; - map[VmFlags::VM_SHARED.bits()] = Arch::PAGE_NONE; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits()] = Arch::PAGE_SHARED; + MMArch::PAGE_COPY_EXEC; + map[VmFlags::VM_SHARED.bits()] = MMArch::PAGE_NONE; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_READ.bits()] = MMArch::PAGE_READONLY; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits()] = MMArch::PAGE_SHARED; map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = - Arch::PAGE_SHARED; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits()] = Arch::PAGE_READONLY_EXEC; + MMArch::PAGE_SHARED; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits()] = MMArch::PAGE_READONLY_EXEC; map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = - Arch::PAGE_READONLY_EXEC; + MMArch::PAGE_READONLY_EXEC; map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = - Arch::PAGE_SHARED_EXEC; + MMArch::PAGE_SHARED_EXEC; map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits() - | VmFlags::VM_READ.bits()] = Arch::PAGE_SHARED_EXEC; - map + | VmFlags::VM_READ.bits()] = MMArch::PAGE_SHARED_EXEC; + let mut ret = [unsafe { EntryFlags::from_data(0) }; 16]; + let mut index = 0; + while index < 16 { + ret[index] = unsafe { EntryFlags::from_data(map[index]) }; + index += 1; + } + ret } const PAGE_ENTRY_BASE: usize = RiscV64MMArch::ENTRY_FLAG_PRESENT diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index c709a2923..c1d952cd7 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -353,7 +353,7 @@ impl MemoryManagementArch for X86_64MMArch { // map // } - const PROTECTION_MAP: [usize; 16] = protection_map(); + const PROTECTION_MAP: [EntryFlags; 16] = protection_map(); const PAGE_NONE: usize = Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_ACCESSED | Self::ENTRY_FLAG_GLOBAL; @@ -402,33 +402,39 @@ impl MemoryManagementArch for X86_64MMArch { /// /// ## 返回值 /// - `[usize; 16]`: 长度为16的映射表 -const fn protection_map() -> [usize; 16] { - type Arch = X86_64MMArch; - let mut map = [0; 16]; - map[VmFlags::VM_NONE.bits()] = Arch::PAGE_NONE; - map[VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY; - map[VmFlags::VM_WRITE.bits()] = Arch::PAGE_COPY; - map[VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_COPY; - map[VmFlags::VM_EXEC.bits()] = Arch::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = Arch::PAGE_COPY_EXEC; - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = - Arch::PAGE_COPY_EXEC; - map[VmFlags::VM_SHARED.bits()] = X86_64MMArch::PAGE_NONE; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_READ.bits()] = Arch::PAGE_READONLY; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits()] = Arch::PAGE_SHARED; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = - Arch::PAGE_SHARED; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits()] = Arch::PAGE_READONLY_EXEC; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = - Arch::PAGE_READONLY_EXEC; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = - Arch::PAGE_SHARED_EXEC; - map[VmFlags::VM_SHARED.bits() - | VmFlags::VM_EXEC.bits() - | VmFlags::VM_WRITE.bits() - | VmFlags::VM_READ.bits()] = Arch::PAGE_SHARED_EXEC; - +const fn protection_map() -> [EntryFlags; 16] { + let mut map = [unsafe { EntryFlags::from_data(0) }; 16]; + unsafe { + map[VmFlags::VM_NONE.bits()] = EntryFlags::from_data(MMArch::PAGE_NONE); + map[VmFlags::VM_READ.bits()] = EntryFlags::from_data(MMArch::PAGE_READONLY); + map[VmFlags::VM_WRITE.bits()] = EntryFlags::from_data(MMArch::PAGE_COPY); + map[VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_COPY); + map[VmFlags::VM_EXEC.bits()] = EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = + EntryFlags::from_data(MMArch::PAGE_COPY_EXEC); + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_COPY_EXEC); + map[VmFlags::VM_SHARED.bits()] = EntryFlags::from_data(MMArch::PAGE_NONE); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_READONLY); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits()] = + EntryFlags::from_data(MMArch::PAGE_SHARED); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_SHARED); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits()] = + EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = + EntryFlags::from_data(MMArch::PAGE_SHARED_EXEC); + map[VmFlags::VM_SHARED.bits() + | VmFlags::VM_EXEC.bits() + | VmFlags::VM_WRITE.bits() + | VmFlags::VM_READ.bits()] = EntryFlags::from_data(MMArch::PAGE_SHARED_EXEC); + } // if X86_64MMArch::is_xd_reserved() { // map.iter_mut().for_each(|x| *x &= !Self::ENTRY_FLAG_NO_EXEC) // } @@ -734,13 +740,21 @@ impl FrameAllocator for LockedFrameAllocator { unsafe fn allocate(&mut self, mut count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { count = count.next_power_of_two(); if let Some(ref mut allocator) = *INNER_ALLOCATOR.lock_irqsave() { - // 首次分配时内存不足 - allocator.allocate(count).or_else(|| { + let usage = self.usage(); + if usage.used().data() + count.data() > usage.total().data() / 2 { + log::info!("shrink: {:?}", usage); let mut page_manager_guard = page_manager_lock_irqsave(); - // 释放部分页面并再次尝试分配 - page_manager_guard.shrink_list(); - return allocator.allocate(count); - }) + // 释放部分页面 + page_manager_guard.shrink_list(count); + } + allocator.allocate(count) + // // 首次分配时内存不足 + // allocator.allocate(count).or_else(|| { + // let mut page_manager_guard = page_manager_lock_irqsave(); + // // 释放部分页面并再次尝试分配 + // page_manager_guard.shrink_list(count); + // return allocator.allocate(count); + // }) } else { return None; } diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index 36f6e9a02..c429ec08e 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -194,6 +194,8 @@ impl LockedFATInode { FileType::File }; + let mut page_cache = PageCache::default(); + let inode: Arc = Arc::new(LockedFATInode(SpinLock::new(FATInode { parent, self_ref: Weak::default(), @@ -225,10 +227,20 @@ impl LockedFATInode { page_cache: Arc::new(PageCache::default()), }))); + page_cache.inode = Some(Arc::downgrade(&inode) as Weak); + + inode.0.lock().page_cache = Arc::new(page_cache); + inode.0.lock().self_ref = Arc::downgrade(&inode); inode.0.lock().update_metadata(); + // inode + // .0 + // .lock() + // .page_cache + // .set_inode(Arc::downgrade(&inode) as Weak); + return inode; } } diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 516a515b0..ea36d6848 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -125,6 +125,7 @@ impl FileMode { #[allow(dead_code)] pub struct PageCache { xarray: SpinLock>>, + pub inode: Option>, } impl core::fmt::Debug for PageCache { @@ -144,9 +145,10 @@ impl core::fmt::Debug for PageCache { } impl PageCache { - pub fn new() -> PageCache { + pub fn new(inode: Option>) -> PageCache { Self { xarray: SpinLock::new(XArray::new()), + inode, } } @@ -169,6 +171,10 @@ impl PageCache { cursor.remove(); } + pub fn set_inode(&mut self, inode: Weak) { + self.inode = Some(inode) + } + // pub fn get_pages(&self, start_pgoff: usize, end_pgoff: usize) -> Vec> { // let mut vec = Vec::new(); // for pgoff in start_pgoff..=end_pgoff { @@ -182,15 +188,10 @@ impl PageCache { impl Default for PageCache { fn default() -> Self { - Self::new() + Self::new(None) } } -pub trait PageCacheOperations: IndexNode { - fn write_page(&self, page: Page); - fn read_ahead(&self); -} - /// @brief 抽象文件结构体 #[derive(Debug)] pub struct File { diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 80e525222..aefe72035 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -633,6 +633,7 @@ impl PageFaultHandler { let page = Arc::new(Page::new(false, new_cache_page)); pfm.page = Some(page.clone()); + page.write().add_flags(PageFlags::PG_LRU); page_manager_guard.insert(new_cache_page, &page); page_cache.add_page(file_pgoff, &page); @@ -677,19 +678,28 @@ impl PageFaultHandler { MMArch::PAGE_SIZE, ); - let vma = pfm.vma(); - let vma_guard = vma.lock(); - let file = vma_guard.vm_file().expect("no vm_file in vma"); - let page_cache = file.inode().page_cache().unwrap(); + let mut page_manager_guard = page_manager_lock_irqsave(); let page_guard = cache_page.read(); let new_page = Arc::new(Page::new(page_guard.shared(), new_phys)); - page_cache.add_page(pfm.file_pgoff.unwrap(), &new_page); + + // 新页加入页管理器中 + page_manager_guard.insert(new_phys, &new_page); new_page .write() .set_page_cache_index(cache_page.read().page_cache(), cache_page.read().index()); + + // 将vma插入页的vma链表中 + new_page.write().insert_vma(pfm.vma()); } else { // 直接映射到PageCache mapper.map_phys(*pfm.address(), page_phys, vma_guard.flags()); + + if pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) + && pfm.vma().lock().vm_flags().contains(VmFlags::VM_SHARED) + { + // 如果是共享写映射,将pagecache页设为脏页,以便回收时能够回写 + cache_page.write().add_flags(PageFlags::PG_DIRTY) + } } VmFaultReason::VM_FAULT_COMPLETED } diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index 0a057ea1b..18fc78d69 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -678,7 +678,7 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { // map // } - const PROTECTION_MAP: [usize; 16]; + const PROTECTION_MAP: [EntryFlags; 16]; /// 页面保护标志转换函数 /// ## 参数 @@ -689,13 +689,11 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { /// - EntryFlags: 页面的保护位 fn vm_get_page_prot(vm_flags: VmFlags) -> EntryFlags { let map = Self::PROTECTION_MAP; - let mut ret = unsafe { - EntryFlags::from_data( - map[vm_flags.intersection( - VmFlags::VM_READ | VmFlags::VM_WRITE | VmFlags::VM_EXEC | VmFlags::VM_SHARED, - )], + let mut ret = map[vm_flags + .intersection( + VmFlags::VM_READ | VmFlags::VM_WRITE | VmFlags::VM_EXEC | VmFlags::VM_SHARED, ) - }; + .bits()]; #[cfg(target_arch = "x86_64")] { diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 6e93f46e0..e756de677 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -7,14 +7,14 @@ use core::{ }; use alloc::sync::Arc; -use hashbrown::HashSet; +use hashbrown::{HashMap, HashSet}; use log::{error, info}; use lru::LruCache; use crate::{ arch::{interrupt::ipi::send_ipi, MMArch}, exception::ipi::{IpiKind, IpiTarget}, - filesystem::vfs::file::PageCache, + filesystem::vfs::{file::PageCache, FilePrivateData}, ipc::shm::ShmId, libs::{ rwlock::RwLock, @@ -58,25 +58,29 @@ pub fn page_manager_lock_irqsave() -> SpinLockGuard<'static, PageManager> { // 物理页管理器 pub struct PageManager { - phys2page: LruCache>, + phys2page: HashMap>, + lru: LruCache>, } impl PageManager { pub fn new() -> Self { Self { - phys2page: LruCache::unbounded(), + phys2page: HashMap::new(), + lru: LruCache::unbounded(), } } pub fn contains(&self, paddr: &PhysAddr) -> bool { - self.phys2page.peek(paddr).is_some() + self.phys2page.contains_key(paddr) } pub fn get(&mut self, paddr: &PhysAddr) -> Option> { + self.lru.promote(paddr); self.phys2page.get(paddr).cloned() } pub fn get_unwrap(&mut self, paddr: &PhysAddr) -> Arc { + self.lru.promote(paddr); self.phys2page .get(paddr) .unwrap_or_else(|| panic!("Phys Page not found, {:?}", paddr)) @@ -84,30 +88,60 @@ impl PageManager { } pub fn insert(&mut self, paddr: PhysAddr, page: &Arc) { - self.phys2page.put(paddr, page.clone()); + self.phys2page.insert(paddr, page.clone()); + if page.read().flags.contains(PageFlags::PG_LRU) { + self.lru.put(paddr, page.clone()); + } } pub fn remove_page(&mut self, paddr: &PhysAddr) { - self.phys2page.pop(paddr); - } - - pub fn shrink_list(&mut self) { - let entry = self.phys2page.peek_lru().unwrap(); - let page = entry.1.clone(); - let phys = *entry.0; - let page_cache = page.read().page_cache().unwrap(); - for vma in page.read().anon_vma() { - let address_space = vma.lock().address_space().unwrap(); - let address_space = address_space.upgrade().unwrap(); - let mut guard = address_space.write(); - let mapper = &mut guard.user_mapper.utable; - let virt = vma.lock().page_address(&page).unwrap(); - unsafe { - mapper.unmap(virt, false).unwrap().flush(); + self.phys2page.remove(paddr); + } + + pub fn shrink_list(&mut self, count: PageFrameCount) { + for _ in 0..count.data() { + let entry = self.lru.pop_lru().unwrap(); + let page = entry.1.clone(); + let page_cache = page.read().page_cache().unwrap(); + for vma in page.read().anon_vma() { + let address_space = vma.lock().address_space().unwrap(); + let address_space = address_space.upgrade().unwrap(); + let mut guard = address_space.write(); + let mapper = &mut guard.user_mapper.utable; + let virt = vma.lock().page_address(&page).unwrap(); + unsafe { + mapper.unmap(virt, false).unwrap().flush(); + } + } + page_cache.remove_page(page.read().index().unwrap()); + if page.read().flags.contains(PageFlags::PG_DIRTY) { + //TODO 回写页面 + let inode = page + .read() + .page_cache + .clone() + .unwrap() + .inode + .clone() + .unwrap() + .upgrade() + .unwrap(); + inode + .write_at( + page.read().index().unwrap(), + MMArch::PAGE_SIZE, + unsafe { + core::slice::from_raw_parts( + MMArch::phys_2_virt(page.read().phys_addr).unwrap().data() + as *mut u8, + MMArch::PAGE_SIZE, + ) + }, + SpinLock::new(FilePrivateData::Unused).lock(), + ) + .unwrap(); } } - self.phys2page.pop(&phys); - page_cache.remove_page(page.read().index().unwrap()); } } @@ -271,6 +305,21 @@ impl InnerPage { &self.flags } + #[inline(always)] + pub fn set_flags(&mut self, flags: PageFlags) { + self.flags = flags + } + + #[inline(always)] + pub fn add_flags(&mut self, flags: PageFlags) { + self.flags = self.flags.union(flags); + } + + #[inline(always)] + pub fn remove_flags(&mut self, flags: PageFlags) { + self.flags = self.flags.difference(flags); + } + #[inline(always)] pub fn phys_address(&self) -> PhysAddr { self.phys_addr @@ -623,7 +672,7 @@ impl EntryFlags { /// /// - prot_flags: 页的保护标志 /// - user: 用户空间是否可访问 - pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> EntryFlags { + pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> Self { let vm_flags = super::VmFlags::from(prot_flags); // let flags: EntryFlags = EntryFlags::new() // .set_user(user) diff --git a/kernel/src/sched/mod.rs b/kernel/src/sched/mod.rs index 3930f11f4..f453673e6 100644 --- a/kernel/src/sched/mod.rs +++ b/kernel/src/sched/mod.rs @@ -112,6 +112,7 @@ pub trait Scheduler { ); /// ## 选择接下来最适合运行的任务 + #[allow(dead_code)] fn pick_task(rq: &mut CpuRunQueue) -> Option>; /// ## 选择接下来最适合运行的任务 From d7e76ec72089955b408641460545b6bd1b404f37 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 30 Jul 2024 22:42:25 +0800 Subject: [PATCH 29/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E5=9B=9E=E6=94=B6=E5=86=85=E6=A0=B8=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/Cargo.toml | 2 +- kernel/src/arch/x86_64/mm/mod.rs | 18 +----- kernel/src/init/initcall.rs | 2 + kernel/src/mm/fault.rs | 6 +- kernel/src/mm/init.rs | 8 ++- kernel/src/mm/page.rs | 102 +++++++++++++++++++++++++++---- 6 files changed, 106 insertions(+), 32 deletions(-) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 59e46d0e1..056a69cb4 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -88,4 +88,4 @@ debug = true # Controls whether the compiler passes `-g` # The release profile, used for `cargo build --release` [profile.release] -debug = false +debug = true diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index c1d952cd7..378c4dc8c 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -28,7 +28,7 @@ use crate::{ }; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, EntryFlags, PageEntry, PAGE_1G_SHIFT}; +use crate::mm::page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}; use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, VmFlags}; use system_error::SystemError; @@ -740,21 +740,7 @@ impl FrameAllocator for LockedFrameAllocator { unsafe fn allocate(&mut self, mut count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> { count = count.next_power_of_two(); if let Some(ref mut allocator) = *INNER_ALLOCATOR.lock_irqsave() { - let usage = self.usage(); - if usage.used().data() + count.data() > usage.total().data() / 2 { - log::info!("shrink: {:?}", usage); - let mut page_manager_guard = page_manager_lock_irqsave(); - // 释放部分页面 - page_manager_guard.shrink_list(count); - } - allocator.allocate(count) - // // 首次分配时内存不足 - // allocator.allocate(count).or_else(|| { - // let mut page_manager_guard = page_manager_lock_irqsave(); - // // 释放部分页面并再次尝试分配 - // page_manager_guard.shrink_list(count); - // return allocator.allocate(count); - // }) + return allocator.allocate(count); } else { return None; } diff --git a/kernel/src/init/initcall.rs b/kernel/src/init/initcall.rs index a9e25690d..2d52d8dbd 100644 --- a/kernel/src/init/initcall.rs +++ b/kernel/src/init/initcall.rs @@ -10,6 +10,7 @@ define_public_unified_initializer_slice!(INITCALL_FS); define_public_unified_initializer_slice!(INITCALL_ROOTFS); define_public_unified_initializer_slice!(INITCALL_DEVICE); define_public_unified_initializer_slice!(INITCALL_LATE); +define_public_unified_initializer_slice!(INITCALL_MM); pub fn do_initcalls() -> Result<(), SystemError> { unified_init!(INITCALL_PURE); @@ -21,5 +22,6 @@ pub fn do_initcalls() -> Result<(), SystemError> { unified_init!(INITCALL_ROOTFS); unified_init!(INITCALL_DEVICE); unified_init!(INITCALL_LATE); + unified_init!(INITCALL_MM); return Ok(()); } diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index aefe72035..e6d49ec2c 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -22,7 +22,7 @@ use crate::mm::MemoryManagementArch; use super::{ allocator::page_frame::FrameAllocator, - page::{Page, PageFlags}, + page::{page_reclaimer_lock_irqsave, Page, PageFlags}, }; bitflags! { @@ -605,7 +605,6 @@ impl PageFaultHandler { let page_cache = file.inode().page_cache().unwrap(); let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); let mut ret = VmFaultReason::empty(); - let mut page_manager_guard = page_manager_lock_irqsave(); if let Some(page) = page_cache.get_page(file_pgoff) { // TODO 异步从磁盘中预读页面进PageCache @@ -634,7 +633,8 @@ impl PageFaultHandler { pfm.page = Some(page.clone()); page.write().add_flags(PageFlags::PG_LRU); - page_manager_guard.insert(new_cache_page, &page); + page_manager_lock_irqsave().insert(new_cache_page, &page); + page_reclaimer_lock_irqsave().insert_page(new_cache_page, &page); page_cache.add_page(file_pgoff, &page); page.write() diff --git a/kernel/src/mm/init.rs b/kernel/src/mm/init.rs index 0f44ef7ca..3fe0e72fb 100644 --- a/kernel/src/mm/init.rs +++ b/kernel/src/mm/init.rs @@ -8,7 +8,11 @@ use crate::{ filesystem::procfs::kmsg::kmsg_init, ipc::shm::shm_manager_init, libs::printk::PrintkWriter, - mm::{allocator::slab::slab_init, mmio_buddy::mmio_init, page::page_manager_init}, + mm::{ + allocator::slab::slab_init, + mmio_buddy::mmio_init, + page::{page_manager_init, page_reclaimer_init}, + }, }; use super::MemoryManagementArch; @@ -57,6 +61,8 @@ pub unsafe fn mm_init() { page_manager_init(); // enable SHM_MANAGER shm_manager_init(); + // enable PAGE_RECLAIMER + page_reclaimer_init(); MM_INIT .compare_exchange( diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index e756de677..2f19e5fef 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -1,3 +1,4 @@ +use alloc::string::ToString; use core::{ fmt::{self, Debug, Error, Formatter}, marker::PhantomData, @@ -5,6 +6,8 @@ use core::{ ops::Add, sync::atomic::{compiler_fence, Ordering}, }; +use system_error::SystemError; +use unified_init::macros::unified_init; use alloc::sync::Arc; use hashbrown::{HashMap, HashSet}; @@ -12,14 +15,17 @@ use log::{error, info}; use lru::LruCache; use crate::{ - arch::{interrupt::ipi::send_ipi, MMArch}, + arch::{interrupt::ipi::send_ipi, mm::LockedFrameAllocator, MMArch}, exception::ipi::{IpiKind, IpiTarget}, filesystem::vfs::{file::PageCache, FilePrivateData}, + init::initcall::INITCALL_MM, ipc::shm::ShmId, libs::{ rwlock::RwLock, spinlock::{SpinLock, SpinLockGuard}, }, + process::{ProcessControlBlock, ProcessManager}, + time::{sleep::usleep, PosixTimeSpec}, }; use super::{ @@ -59,14 +65,12 @@ pub fn page_manager_lock_irqsave() -> SpinLockGuard<'static, PageManager> { // 物理页管理器 pub struct PageManager { phys2page: HashMap>, - lru: LruCache>, } impl PageManager { pub fn new() -> Self { Self { phys2page: HashMap::new(), - lru: LruCache::unbounded(), } } @@ -75,12 +79,12 @@ impl PageManager { } pub fn get(&mut self, paddr: &PhysAddr) -> Option> { - self.lru.promote(paddr); + page_reclaimer_lock_irqsave().get(paddr); self.phys2page.get(paddr).cloned() } pub fn get_unwrap(&mut self, paddr: &PhysAddr) -> Arc { - self.lru.promote(paddr); + page_reclaimer_lock_irqsave().get(paddr); self.phys2page .get(paddr) .unwrap_or_else(|| panic!("Phys Page not found, {:?}", paddr)) @@ -89,19 +93,89 @@ impl PageManager { pub fn insert(&mut self, paddr: PhysAddr, page: &Arc) { self.phys2page.insert(paddr, page.clone()); - if page.read().flags.contains(PageFlags::PG_LRU) { - self.lru.put(paddr, page.clone()); - } } pub fn remove_page(&mut self, paddr: &PhysAddr) { self.phys2page.remove(paddr); } +} + +pub static mut PAGE_RECLAIMER: Option> = None; + +pub fn page_reclaimer_init() { + info!("page_reclaimer_init"); + let page_reclaimer = SpinLock::new(PageReclaimer::new()); + + compiler_fence(Ordering::SeqCst); + unsafe { PAGE_RECLAIMER = Some(page_reclaimer) }; + compiler_fence(Ordering::SeqCst); + + info!("page_reclaimer_init done"); +} + +static mut PAGE_RECLAIMER_THREAD: Option> = None; + +#[unified_init(INITCALL_MM)] +fn page_reclaimer_thread_init() -> Result<(), SystemError> { + let closure = crate::process::kthread::KernelThreadClosure::StaticEmptyClosure(( + &(page_reclaim_thread as fn() -> i32), + (), + )); + let pcb = crate::process::kthread::KernelThreadMechanism::create_and_run( + closure, + "page_reclaim".to_string(), + ) + .ok_or("") + .expect("create tty_refresh thread failed"); + unsafe { + PAGE_RECLAIMER_THREAD = Some(pcb); + } + Ok(()) +} + +fn page_reclaim_thread() -> i32 { + loop { + let usage = unsafe { LockedFrameAllocator.usage() }; + // log::info!("usage{:?}", usage); + + // 保留4096个页面,总计16MB的空闲空间 + if usage.free().data() < 4096 { + let page_to_free = 4096; + page_reclaimer_lock_irqsave().shrink_list(PageFrameCount::new(page_to_free)); + } else { + // 休眠5秒 + // log::info!("sleep"); + let _ = usleep(PosixTimeSpec::new(5, 0)); + } + } +} + +pub fn page_reclaimer_lock_irqsave() -> SpinLockGuard<'static, PageReclaimer> { + unsafe { PAGE_RECLAIMER.as_ref().unwrap().lock_irqsave() } +} + +pub struct PageReclaimer { + lru: LruCache>, +} + +impl PageReclaimer { + pub fn new() -> Self { + Self { + lru: LruCache::unbounded(), + } + } + + pub fn get(&mut self, paddr: &PhysAddr) -> Option> { + self.lru.get(paddr).cloned() + } + + pub fn insert_page(&mut self, paddr: PhysAddr, page: &Arc) { + self.lru.put(paddr, page.clone()); + } pub fn shrink_list(&mut self, count: PageFrameCount) { for _ in 0..count.data() { - let entry = self.lru.pop_lru().unwrap(); - let page = entry.1.clone(); + let (paddr, page) = self.lru.pop_lru().expect("pagecache is empty"); let page_cache = page.read().page_cache().unwrap(); for vma in page.read().anon_vma() { let address_space = vma.lock().address_space().unwrap(); @@ -114,6 +188,7 @@ impl PageManager { } } page_cache.remove_page(page.read().index().unwrap()); + page_manager_lock_irqsave().remove_page(&paddr); if page.read().flags.contains(PageFlags::PG_DIRTY) { //TODO 回写页面 let inode = page @@ -143,6 +218,11 @@ impl PageManager { } } } + + pub fn wakeup_claim_thread() { + log::info!("wakeup_claim_thread"); + let _ = ProcessManager::wakeup(unsafe { PAGE_RECLAIMER_THREAD.as_ref().unwrap() }); + } } bitflags! { @@ -1058,7 +1138,7 @@ impl PageMapper { if !page_manager_guard.contains(&phys) { page_manager_guard.insert(phys, &Arc::new(Page::new(false, phys))) } - + drop(page_manager_guard); return self.map_phys(virt, phys, flags); } From 2f6b28dbcbe23dca3c308bd3715ce7725ec797cc Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 6 Aug 2024 22:19:00 +0800 Subject: [PATCH 30/60] =?UTF-8?q?=E7=BC=BA=E9=A1=B5=E4=B8=AD=E6=96=AD?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84=E9=94=81=E4=BF=AE=E6=94=B9=E4=B8=BA?= =?UTF-8?q?irq=5Fsave;=20=E6=B7=BB=E5=8A=A0=E8=84=8F=E9=A1=B5=E5=9B=9E?= =?UTF-8?q?=E5=86=99=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/Cargo.toml | 2 +- kernel/src/arch/x86_64/mm/fault.rs | 4 +- kernel/src/arch/x86_64/mm/pkru.rs | 2 +- kernel/src/ipc/shm.rs | 6 +- kernel/src/ipc/syscall.rs | 10 +- kernel/src/mm/allocator/page_frame.rs | 2 +- kernel/src/mm/fault.rs | 150 ++++++++++++++++++-------- kernel/src/mm/madvise.rs | 2 +- kernel/src/mm/page.rs | 137 ++++++++++++++--------- kernel/src/mm/syscall.rs | 4 +- kernel/src/mm/ucontext.rs | 83 ++++++++------ 11 files changed, 253 insertions(+), 149 deletions(-) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 056a69cb4..59e46d0e1 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -88,4 +88,4 @@ debug = true # Controls whether the compiler passes `-g` # The release profile, used for `cargo build --release` [profile.release] -debug = true +debug = false diff --git a/kernel/src/arch/x86_64/mm/fault.rs b/kernel/src/arch/x86_64/mm/fault.rs index fb51f0545..da276dbf0 100644 --- a/kernel/src/arch/x86_64/mm/fault.rs +++ b/kernel/src/arch/x86_64/mm/fault.rs @@ -28,7 +28,7 @@ pub type PageMapper = impl X86_64MMArch { pub fn vma_access_error(vma: Arc, error_code: X86PfErrorCode) -> bool { - let vm_flags = *vma.lock().vm_flags(); + let vm_flags = *vma.lock_irqsave().vm_flags(); let foreign = false; if error_code.contains(X86PfErrorCode::X86_PF_PK) { return true; @@ -236,7 +236,7 @@ impl X86_64MMArch { address.data(), ) }); - let guard = vma.lock(); + let guard = vma.lock_irqsave(); let region = *guard.region(); let vm_flags = *guard.vm_flags(); drop(guard); diff --git a/kernel/src/arch/x86_64/mm/pkru.rs b/kernel/src/arch/x86_64/mm/pkru.rs index b904fb84c..c40f5f0fa 100644 --- a/kernel/src/arch/x86_64/mm/pkru.rs +++ b/kernel/src/arch/x86_64/mm/pkru.rs @@ -16,7 +16,7 @@ const PKEY_MASK: usize = 1 << 32 | 1 << 33 | 1 << 34 | 1 << 35; /// ## 返回值 /// - `u16`: vma的protection_key pub fn vma_pkey(vma: Arc) -> u16 { - let guard = vma.lock(); + let guard = vma.lock_irqsave(); ((guard.vm_flags().bits() & PKEY_MASK) >> VM_PKEY_SHIFT) as u16 } diff --git a/kernel/src/ipc/shm.rs b/kernel/src/ipc/shm.rs index 981d8eeb6..99cce2e45 100644 --- a/kernel/src/ipc/shm.rs +++ b/kernel/src/ipc/shm.rs @@ -166,7 +166,7 @@ impl ShmManager { let mut cur_phys = PhysPageFrame::new(phys_page.0); for _ in 0..page_count.data() { let page = Arc::new(Page::new(true, cur_phys.phys_address())); - page.write().set_shm_id(shm_id); + page.write_irqsave().set_shm_id(shm_id); let paddr = cur_phys.phys_address(); page_manager_guard.insert(paddr, &page); cur_phys = cur_phys.next(); @@ -325,7 +325,7 @@ impl ShmManager { // 设置共享内存物理页当映射计数等于0时可被回收 for _ in 0..count.data() { let page = page_manager_guard.get_unwrap(&cur_phys.phys_address()); - page.write().set_dealloc_when_zero(true); + page.write_irqsave().set_dealloc_when_zero(true); cur_phys = cur_phys.next(); } @@ -444,7 +444,7 @@ impl KernelShm { for _ in 0..page_count.data() { let page = page_manager_guard.get(&cur_phys.phys_address()).unwrap(); id_set.extend( - page.read() + page.read_irqsave() .anon_vma() .iter() .map(|vma| vma.id()) diff --git a/kernel/src/ipc/syscall.rs b/kernel/src/ipc/syscall.rs index 01efc7cfe..2d0b9b6b2 100644 --- a/kernel/src/ipc/syscall.rs +++ b/kernel/src/ipc/syscall.rs @@ -351,7 +351,7 @@ impl Syscall { .mappings .contains(vaddr) .ok_or(SystemError::EINVAL)?; - if vma.lock().region().start() != vaddr { + if vma.lock_irqsave().region().start() != vaddr { return Err(SystemError::EINVAL); } @@ -387,7 +387,7 @@ impl Syscall { // 将vma加入到对应Page的anon_vma page_manager_guard .get_unwrap(&phys.phys_address()) - .write() + .write_irqsave() .insert_vma(vma.clone()); phys = phys.next(); @@ -395,7 +395,7 @@ impl Syscall { } // 更新vma的映射状态 - vma.lock().set_mapped(true); + vma.lock_irqsave().set_mapped(true); vaddr.data() } @@ -428,7 +428,7 @@ impl Syscall { .ok_or(SystemError::EINVAL)?; // 判断vaddr是否为起始地址 - if vma.lock().region().start() != vaddr { + if vma.lock_irqsave().region().start() != vaddr { return Err(SystemError::EINVAL); } @@ -443,7 +443,7 @@ impl Syscall { // 如果物理页的shm_id为None,代表不是共享页 let mut page_manager_guard = page_manager_lock_irqsave(); let page = page_manager_guard.get(&paddr).ok_or(SystemError::EINVAL)?; - let shm_id = page.read().shm_id().ok_or(SystemError::EINVAL)?; + let shm_id = page.read_irqsave().shm_id().ok_or(SystemError::EINVAL)?; drop(page_manager_guard); // 获取对应共享页管理信息 diff --git a/kernel/src/mm/allocator/page_frame.rs b/kernel/src/mm/allocator/page_frame.rs index e59db4cf8..180a2ac25 100644 --- a/kernel/src/mm/allocator/page_frame.rs +++ b/kernel/src/mm/allocator/page_frame.rs @@ -371,7 +371,7 @@ pub unsafe fn deallocate_page_frames( if let Some(page) = page { // 如果page是共享页,将其共享页信息从SHM_MANAGER中删去 - let page_guard = page.read(); + let page_guard = page.read_irqsave(); if page_guard.shared() { shm_manager_lock().free_id(&page_guard.shm_id().unwrap()); } diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index e6d49ec2c..ea7df2911 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -61,7 +61,7 @@ pub struct PageFaultMessage { impl PageFaultMessage { pub fn new(vma: Arc, address: VirtAddr, flags: FaultFlags) -> Self { - let guard = vma.lock(); + let guard = vma.lock_irqsave(); let file_pgoff = guard.file_page_offset().map(|file_page_offset| { ((address - guard.region().start()) >> MMArch::PAGE_SHIFT) + file_page_offset }); @@ -142,7 +142,7 @@ impl PageFaultHandler { return VmFaultReason::VM_FAULT_SIGSEGV; } - let guard = vma.lock(); + let guard = vma.lock_irqsave(); let vm_flags = *guard.vm_flags(); drop(guard); if unlikely(vm_flags.contains(VmFlags::VM_HUGETLB)) { @@ -173,7 +173,7 @@ impl PageFaultHandler { .allocate_table(address, 2) .expect("failed to allocate PUD table"); } - let page_flags = vma.lock().flags(); + let page_flags = vma.lock_irqsave().flags(); for level in 2..=3 { let level = MMArch::PAGE_LEVELS - level; @@ -207,13 +207,17 @@ impl PageFaultHandler { let flags = pfm.flags; let vma = pfm.vma.clone(); let mut ret = VmFaultReason::VM_FAULT_COMPLETED; + + // pte存在 if let Some(mut entry) = mapper.get_entry(address, 0) { if !entry.present() { ret = Self::do_swap_page(pfm.clone(), mapper); } + if entry.protnone() && vma.is_accessible() { ret = Self::do_numa_page(pfm.clone(), mapper); } + if flags.intersects(FaultFlags::FAULT_FLAG_WRITE | FaultFlags::FAULT_FLAG_UNSHARE) { if !entry.write() { ret = Self::do_wp_page(pfm.clone(), mapper); @@ -227,7 +231,7 @@ impl PageFaultHandler { ret = Self::do_fault(pfm, mapper); } - vma.lock().set_mapped(true); + vma.lock_irqsave().set_mapped(true); return ret; } @@ -246,7 +250,7 @@ impl PageFaultHandler { ) -> VmFaultReason { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); - let guard = vma.lock(); + let guard = vma.lock_irqsave(); if let Some(flush) = mapper.map(address, guard.flags()) { flush.flush(); crate::debug::klog::mm::mm_debug_log( @@ -260,7 +264,7 @@ impl PageFaultHandler { let paddr = mapper.translate(address).unwrap().0; let mut page_manager_guard = page_manager_lock_irqsave(); let page = page_manager_guard.get_unwrap(&paddr); - page.write().insert_vma(vma.clone()); + page.write_irqsave().insert_vma(vma.clone()); VmFaultReason::VM_FAULT_COMPLETED } else { VmFaultReason::VM_FAULT_OOM @@ -288,7 +292,12 @@ impl PageFaultHandler { if !pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) { return Self::do_read_fault(pfm, mapper); - } else if !pfm.vma().lock().vm_flags().contains(VmFlags::VM_SHARED) { + } else if !pfm + .vma() + .lock_irqsave() + .vm_flags() + .contains(VmFlags::VM_SHARED) + { return Self::do_cow_fault(pfm, mapper); } else { return Self::do_shared_fault(pfm, mapper); @@ -316,7 +325,7 @@ impl PageFaultHandler { // crate::process::ProcessManager::current_pid().data() // ); // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_cow_fault - let file = pfm.vma().lock().vm_file().unwrap(); + let file = pfm.vma().lock_irqsave().vm_file().unwrap(); let mut ret = Self::filemap_fault(pfm, mapper); @@ -338,7 +347,7 @@ impl PageFaultHandler { pfm: &mut PageFaultMessage, mapper: &mut PageMapper, ) -> VmFaultReason { - let fs = pfm.vma().lock().vm_file().unwrap().inode().fs(); + let fs = pfm.vma().lock_irqsave().vm_file().unwrap().inode().fs(); let mut ret = Self::do_fault_around(pfm, mapper); if !ret.is_empty() { @@ -434,38 +443,75 @@ impl PageFaultHandler { let vma = pfm.vma.clone(); let old_paddr = mapper.translate(address).unwrap().0; let mut page_manager = page_manager_lock_irqsave(); - let map_count = page_manager.get_unwrap(&old_paddr).read().map_count(); + let old_page = page_manager.get_unwrap(&old_paddr); + let map_count = old_page.read_irqsave().map_count(); drop(page_manager); let mut entry = mapper.get_entry(address, 0).unwrap(); - let new_flags = entry.flags().set_write(true); + let new_flags = entry.flags().set_write(true).set_dirty(true); - if map_count == 1 { + if vma.lock().vm_flags().contains(VmFlags::VM_SHARED) { + // 共享映射,直接修改页表项保护位,标记为脏页 let table = mapper.get_table(address, 0).unwrap(); let i = table.index_of(address).unwrap(); entry.set_flags(new_flags); table.set_entry(i, entry); - VmFaultReason::VM_FAULT_COMPLETED - } else if let Some(flush) = mapper.map(address, new_flags) { - let mut page_manager_guard = page_manager_lock_irqsave(); - let old_page = page_manager_guard.get_unwrap(&old_paddr); - old_page.write().remove_vma(&vma); - // drop(page_manager_guard); - flush.flush(); - let paddr = mapper.translate(address).unwrap().0; - // let mut page_manager_guard = page_manager_lock_irqsave(); - let page = page_manager_guard.get_unwrap(&paddr); - page.write().insert_vma(vma.clone()); - - (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( - MMArch::phys_2_virt(old_paddr).unwrap().data() as *mut u8, - MMArch::PAGE_SIZE, - ); + old_page.write_irqsave().add_flags(PageFlags::PG_DIRTY); VmFaultReason::VM_FAULT_COMPLETED + } else if vma.is_anonymous() { + // 私有匿名映射,根据引用计数判断是否拷贝页面 + if map_count == 1 { + let table = mapper.get_table(address, 0).unwrap(); + let i = table.index_of(address).unwrap(); + entry.set_flags(new_flags); + table.set_entry(i, entry); + VmFaultReason::VM_FAULT_COMPLETED + } else if let Some(flush) = mapper.map(address, new_flags) { + let mut page_manager_guard = page_manager_lock_irqsave(); + let old_page = page_manager_guard.get_unwrap(&old_paddr); + old_page.write_irqsave().remove_vma(&vma); + // drop(page_manager_guard); + + flush.flush(); + let paddr = mapper.translate(address).unwrap().0; + // let mut page_manager_guard = page_manager_lock_irqsave(); + let page = page_manager_guard.get_unwrap(&paddr); + page.write_irqsave().insert_vma(vma.clone()); + + (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( + MMArch::phys_2_virt(old_paddr).unwrap().data() as *mut u8, + MMArch::PAGE_SIZE, + ); + + VmFaultReason::VM_FAULT_COMPLETED + } else { + VmFaultReason::VM_FAULT_OOM + } } else { - VmFaultReason::VM_FAULT_OOM + // 私有文件映射,必须拷贝页面 + if let Some(flush) = mapper.map(address, new_flags) { + let mut page_manager_guard = page_manager_lock_irqsave(); + let old_page = page_manager_guard.get_unwrap(&old_paddr); + old_page.write_irqsave().remove_vma(&vma); + // drop(page_manager_guard); + + flush.flush(); + let paddr = mapper.translate(address).unwrap().0; + // let mut page_manager_guard = page_manager_lock_irqsave(); + let page = page_manager_guard.get_unwrap(&paddr); + page.write_irqsave().insert_vma(vma.clone()); + + (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( + MMArch::phys_2_virt(old_paddr).unwrap().data() as *mut u8, + MMArch::PAGE_SIZE, + ); + + VmFaultReason::VM_FAULT_COMPLETED + } else { + VmFaultReason::VM_FAULT_OOM + } } } @@ -487,7 +533,7 @@ impl PageFaultHandler { .expect("failed to allocate pte table"); } let vma = pfm.vma(); - let vma_guard = vma.lock(); + let vma_guard = vma.lock_irqsave(); let vma_region = *vma_guard.region(); drop(vma_guard); @@ -530,7 +576,7 @@ impl PageFaultHandler { return VmFaultReason::VM_FAULT_OOM; } - let fs = pfm.vma().lock().vm_file().unwrap().inode().fs(); + let fs = pfm.vma().lock_irqsave().vm_file().unwrap().inode().fs(); // from_pte - pte_pgoff得出预读起始pte相对缺失页的偏移,加上pfm.file_pgoff(缺失页在文件中的偏移)得出起始页在文件中的偏移,结束pte同理 fs.map_pages( pfm, @@ -557,7 +603,7 @@ impl PageFaultHandler { end_pgoff: usize, ) -> VmFaultReason { let vma = pfm.vma(); - let vma_guard = vma.lock(); + let vma_guard = vma.lock_irqsave(); let file = vma_guard.vm_file().expect("no vm_file in vma"); let page_cache = file.inode().page_cache().unwrap(); @@ -571,7 +617,7 @@ impl PageFaultHandler { for pgoff in start_pgoff..=end_pgoff { if let Some(page) = page_cache.get_page(pgoff) { - let page_guard = page.read(); + let page_guard = page.read_irqsave(); if page_guard.flags().contains(PageFlags::PG_UPTODATE) { let phys = page_guard.phys_address(); @@ -600,7 +646,7 @@ impl PageFaultHandler { mapper: &mut PageMapper, ) -> VmFaultReason { let vma = pfm.vma(); - let vma_guard = vma.lock(); + let vma_guard = vma.lock_irqsave(); let file = vma_guard.vm_file().expect("no vm_file in vma"); let page_cache = file.inode().page_cache().unwrap(); let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); @@ -629,15 +675,15 @@ impl PageFaultHandler { (MMArch::phys_2_virt(new_cache_page).unwrap().data() as *mut u8) .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); - let page = Arc::new(Page::new(false, new_cache_page)); + let page = Arc::new(Page::new(true, new_cache_page)); pfm.page = Some(page.clone()); - page.write().add_flags(PageFlags::PG_LRU); + page.write_irqsave().add_flags(PageFlags::PG_LRU); page_manager_lock_irqsave().insert(new_cache_page, &page); page_reclaimer_lock_irqsave().insert_page(new_cache_page, &page); page_cache.add_page(file_pgoff, &page); - page.write() + page.write_irqsave() .set_page_cache_index(Some(page_cache), Some(file_pgoff)); // // 分配空白页并映射到缺页地址 @@ -653,12 +699,16 @@ impl PageFaultHandler { mapper: &mut PageMapper, ) -> VmFaultReason { let vma = pfm.vma(); - let vma_guard = vma.lock(); + let vma_guard = vma.lock_irqsave(); let cache_page = pfm.page.clone().expect("no page in pfm"); - let page_phys = cache_page.read().phys_address(); + let page_phys = cache_page.read_irqsave().phys_address(); if pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) - && !pfm.vma().lock().vm_flags().contains(VmFlags::VM_SHARED) + && !pfm + .vma() + .lock_irqsave() + .vm_flags() + .contains(VmFlags::VM_SHARED) { // 私有文件映射的写时复制场景 // 分配空白页并映射到缺页地址 @@ -679,26 +729,32 @@ impl PageFaultHandler { ); let mut page_manager_guard = page_manager_lock_irqsave(); - let page_guard = cache_page.read(); + let page_guard = cache_page.read_irqsave(); let new_page = Arc::new(Page::new(page_guard.shared(), new_phys)); // 新页加入页管理器中 page_manager_guard.insert(new_phys, &new_page); - new_page - .write() - .set_page_cache_index(cache_page.read().page_cache(), cache_page.read().index()); + new_page.write_irqsave().set_page_cache_index( + cache_page.read_irqsave().page_cache(), + cache_page.read_irqsave().index(), + ); // 将vma插入页的vma链表中 - new_page.write().insert_vma(pfm.vma()); + new_page.write_irqsave().insert_vma(pfm.vma()); } else { // 直接映射到PageCache mapper.map_phys(*pfm.address(), page_phys, vma_guard.flags()); + cache_page.write_irqsave().insert_vma(pfm.vma()); if pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) - && pfm.vma().lock().vm_flags().contains(VmFlags::VM_SHARED) + && pfm + .vma() + .lock_irqsave() + .vm_flags() + .contains(VmFlags::VM_SHARED) { // 如果是共享写映射,将pagecache页设为脏页,以便回收时能够回写 - cache_page.write().add_flags(PageFlags::PG_DIRTY) + cache_page.write_irqsave().add_flags(PageFlags::PG_DIRTY) } } VmFaultReason::VM_FAULT_COMPLETED diff --git a/kernel/src/mm/madvise.rs b/kernel/src/mm/madvise.rs index 5e9587a40..9089f88f6 100644 --- a/kernel/src/mm/madvise.rs +++ b/kernel/src/mm/madvise.rs @@ -12,7 +12,7 @@ impl LockedVMA { _flusher: impl Flusher, ) -> Result<(), SystemError> { //TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/madvise.c?fi=madvise#do_madvise - let mut vma = self.lock(); + let mut vma = self.lock_irqsave(); let mut new_flags = *vma.vm_flags(); match behavior { MadvFlags::MADV_REMOVE => { diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 2f19e5fef..501e3998f 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -21,7 +21,7 @@ use crate::{ init::initcall::INITCALL_MM, ipc::shm::ShmId, libs::{ - rwlock::RwLock, + rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, spinlock::{SpinLock, SpinLockGuard}, }, process::{ProcessControlBlock, ProcessManager}, @@ -143,6 +143,8 @@ fn page_reclaim_thread() -> i32 { let page_to_free = 4096; page_reclaimer_lock_irqsave().shrink_list(PageFrameCount::new(page_to_free)); } else { + //TODO 暂时让页面回收线程负责脏页回写任务,后续需要分离 + page_reclaimer_lock_irqsave().flush_dirty_pages(); // 休眠5秒 // log::info!("sleep"); let _ = usleep(PosixTimeSpec::new(5, 0)); @@ -176,53 +178,91 @@ impl PageReclaimer { pub fn shrink_list(&mut self, count: PageFrameCount) { for _ in 0..count.data() { let (paddr, page) = self.lru.pop_lru().expect("pagecache is empty"); - let page_cache = page.read().page_cache().unwrap(); - for vma in page.read().anon_vma() { - let address_space = vma.lock().address_space().unwrap(); + let page_cache = page.read_irqsave().page_cache().unwrap(); + for vma in page.read_irqsave().anon_vma() { + let address_space = vma.lock_irqsave().address_space().unwrap(); let address_space = address_space.upgrade().unwrap(); let mut guard = address_space.write(); let mapper = &mut guard.user_mapper.utable; - let virt = vma.lock().page_address(&page).unwrap(); + let virt = vma.lock_irqsave().page_address(&page).unwrap(); unsafe { mapper.unmap(virt, false).unwrap().flush(); } } - page_cache.remove_page(page.read().index().unwrap()); + page_cache.remove_page(page.read_irqsave().index().unwrap()); page_manager_lock_irqsave().remove_page(&paddr); - if page.read().flags.contains(PageFlags::PG_DIRTY) { - //TODO 回写页面 - let inode = page - .read() - .page_cache - .clone() - .unwrap() - .inode - .clone() - .unwrap() - .upgrade() - .unwrap(); - inode - .write_at( - page.read().index().unwrap(), - MMArch::PAGE_SIZE, - unsafe { - core::slice::from_raw_parts( - MMArch::phys_2_virt(page.read().phys_addr).unwrap().data() - as *mut u8, - MMArch::PAGE_SIZE, - ) - }, - SpinLock::new(FilePrivateData::Unused).lock(), - ) - .unwrap(); + if page.read_irqsave().flags.contains(PageFlags::PG_DIRTY) { + Self::page_writeback(&page, true); } } } pub fn wakeup_claim_thread() { - log::info!("wakeup_claim_thread"); + // log::info!("wakeup_claim_thread"); let _ = ProcessManager::wakeup(unsafe { PAGE_RECLAIMER_THREAD.as_ref().unwrap() }); } + + pub fn page_writeback(page: &Arc, unmap: bool) { + if !unmap { + page.write_irqsave().remove_flags(PageFlags::PG_DIRTY); + } + + for vma in page.read_irqsave().anon_vma() { + let address_space = vma.lock_irqsave().address_space().unwrap(); + let address_space = address_space.upgrade().unwrap(); + let mut guard = address_space.write(); + let mapper = &mut guard.user_mapper.utable; + let virt = vma.lock_irqsave().page_address(page).unwrap(); + if unmap { + unsafe { + mapper.unmap(virt, false).unwrap().flush(); + } + } else { + unsafe { + // 保护位设为只读 + mapper.remap( + virt, + mapper.get_entry(virt, 0).unwrap().flags().set_write(false), + ) + }; + } + } + let inode = page + .read_irqsave() + .page_cache + .clone() + .unwrap() + .inode + .clone() + .unwrap() + .upgrade() + .unwrap(); + inode + .write_at( + page.read_irqsave().index().unwrap(), + MMArch::PAGE_SIZE, + unsafe { + core::slice::from_raw_parts( + MMArch::phys_2_virt(page.read_irqsave().phys_addr) + .unwrap() + .data() as *mut u8, + MMArch::PAGE_SIZE, + ) + }, + SpinLock::new(FilePrivateData::Unused).lock(), + ) + .unwrap(); + } + + pub fn flush_dirty_pages(&self) { + // log::info!("flush_dirty_pages"); + let iter = self.lru.iter(); + for (_, page) in iter { + if page.read_irqsave().flags().contains(PageFlags::PG_DIRTY) { + Self::page_writeback(page, false); + } + } + } } bitflags! { @@ -251,20 +291,6 @@ pub struct Page { inner: RwLock, } -impl core::ops::Deref for Page { - type Target = RwLock; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl core::ops::DerefMut for Page { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - impl Page { pub fn new(shared: bool, phys_addr: PhysAddr) -> Self { let inner = InnerPage::new(shared, phys_addr); @@ -272,6 +298,14 @@ impl Page { inner: RwLock::new(inner), } } + + pub fn read_irqsave(&self) -> RwLockReadGuard { + self.inner.read_irqsave() + } + + pub fn write_irqsave(&self) -> RwLockWriteGuard { + self.inner.write_irqsave() + } } #[derive(Debug)] @@ -582,11 +616,12 @@ impl PageTable { let mut page_manager_guard = page_manager_lock_irqsave(); let old_phys = entry.address().unwrap(); let old_page = page_manager_guard.get_unwrap(&old_phys); - let new_page = Arc::new(Page::new(old_page.read().shared(), phys)); - if let Some(ref page_cache) = old_page.read().page_cache() { - new_page.write().set_page_cache_index( + let new_page = + Arc::new(Page::new(old_page.read_irqsave().shared(), phys)); + if let Some(ref page_cache) = old_page.read_irqsave().page_cache() { + new_page.write_irqsave().set_page_cache_index( Some(page_cache.clone()), - old_page.read().index(), + old_page.read_irqsave().index(), ); } diff --git a/kernel/src/mm/syscall.rs b/kernel/src/mm/syscall.rs index 0d7008a1c..6a35a9d6b 100644 --- a/kernel/src/mm/syscall.rs +++ b/kernel/src/mm/syscall.rs @@ -416,7 +416,7 @@ impl Syscall { return Err(SystemError::EINVAL); } let vma = vma.unwrap(); - let vm_flags = *vma.lock().vm_flags(); + let vm_flags = *vma.lock_irqsave().vm_flags(); // 暂时不支持巨页映射 if vm_flags.contains(VmFlags::VM_HUGETLB) { @@ -590,7 +590,7 @@ impl Syscall { .mappings .find_nearest(VirtAddr::new(start)) { - let guard = vma.lock(); + let guard = vma.lock_irqsave(); let vm_start = guard.region().start().data(); let vm_end = guard.region().end().data(); if start < vm_start { diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index e39e929af..d181e5ac9 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -179,19 +179,19 @@ impl InnerAddressSpace { for vma in self.mappings.vmas.iter() { // TODO: 增加对VMA是否为文件映射的判断,如果是的话,就跳过 - let vma_guard: SpinLockGuard<'_, VMA> = vma.lock(); + let vma_guard: SpinLockGuard<'_, VMA> = vma.lock_irqsave(); // 仅拷贝VMA信息并添加反向映射,因为UserMapper克隆时已经分配了新的物理页 let new_vma = LockedVMA::new(vma_guard.clone_info_only()); new_guard.mappings.vmas.insert(new_vma.clone()); // debug!("new vma: {:x?}", new_vma); - let new_vma_guard = new_vma.lock(); + let new_vma_guard = new_vma.lock_irqsave(); let new_mapper = &new_guard.user_mapper.utable; let mut page_manager_guard = page_manager_lock_irqsave(); for page in new_vma_guard.pages().map(|p| p.virt_address()) { if let Some((paddr, _)) = new_mapper.translate(page) { let page = page_manager_guard.get_unwrap(&paddr); - page.write().insert_vma(new_vma.clone()); + page.write_irqsave().insert_vma(new_vma.clone()); } } @@ -598,9 +598,9 @@ impl InnerAddressSpace { let regions: Vec> = self.mappings.conflicts(to_unmap).collect::>(); for r in regions { - let r = r.lock().region; + let r = r.lock_irqsave().region; let r = self.mappings.remove_vma(&r).unwrap(); - let intersection = r.lock().region().intersect(&to_unmap).unwrap(); + let intersection = r.lock_irqsave().region().intersect(&to_unmap).unwrap(); let split_result = r.extract(intersection, &self.user_mapper.utable).unwrap(); // TODO: 当引入后备页映射后,这里需要增加通知文件的逻辑 @@ -652,10 +652,10 @@ impl InnerAddressSpace { for r in regions { // debug!("mprotect: r: {:?}", r); - let r = *r.lock().region(); + let r = *r.lock_irqsave().region(); let r = self.mappings.remove_vma(&r).unwrap(); - let intersection = r.lock().region().intersect(®ion).unwrap(); + let intersection = r.lock_irqsave().region().intersect(®ion).unwrap(); let split_result = r .extract(intersection, mapper) .expect("Failed to extract VMA"); @@ -667,7 +667,7 @@ impl InnerAddressSpace { self.mappings.insert_vma(after); } - let mut r_guard = r.lock(); + let mut r_guard = r.lock_irqsave(); // 如果VMA的保护标志不允许指定的修改,则返回错误 if !r_guard.can_have_flags(prot_flags) { drop(r_guard); @@ -710,10 +710,10 @@ impl InnerAddressSpace { let regions = self.mappings.conflicts(region).collect::>(); for r in regions { - let r = *r.lock().region(); + let r = *r.lock_irqsave().region(); let r = self.mappings.remove_vma(&r).unwrap(); - let intersection = r.lock().region().intersect(®ion).unwrap(); + let intersection = r.lock_irqsave().region().intersect(®ion).unwrap(); let split_result = r .extract(intersection, mapper) .expect("Failed to extract VMA"); @@ -886,7 +886,7 @@ impl UserMappings { #[allow(dead_code)] pub fn contains(&self, vaddr: VirtAddr) -> Option> { for v in self.vmas.iter() { - let guard = v.lock(); + let guard = v.lock_irqsave(); if guard.region.contains(vaddr) { return Some(v.clone()); } @@ -906,13 +906,13 @@ impl UserMappings { pub fn find_nearest(&self, vaddr: VirtAddr) -> Option> { let mut nearest: Option> = None; for v in self.vmas.iter() { - let guard = v.lock(); + let guard = v.lock_irqsave(); if guard.region.contains(vaddr) { return Some(v.clone()); } if guard.region.start >= vaddr && if let Some(ref nearest) = nearest { - guard.region.start < nearest.lock().region.start + guard.region.start < nearest.lock_irqsave().region.start } else { true } @@ -928,7 +928,7 @@ impl UserMappings { let r = self .vmas .iter() - .filter(move |v| v.lock().region.intersect(&request).is_some()) + .filter(move |v| v.lock_irqsave().region.intersect(&request).is_some()) .cloned(); return r; } @@ -1050,7 +1050,7 @@ impl UserMappings { /// 在当前进程的映射关系中,插入一个新的VMA。 pub fn insert_vma(&mut self, vma: Arc) { - let region = vma.lock().region; + let region = vma.lock_irqsave().region; // 要求插入的地址范围必须是空闲的,也就是说,当前进程的地址空间中,不能有任何与之重叠的VMA。 assert!(self.conflicts(region).next().is_none()); self.reserve_hole(®ion); @@ -1070,7 +1070,7 @@ impl UserMappings { // 请注意,由于这里会对每个VMA加锁,因此性能很低 let vma: Arc = self .vmas - .drain_filter(|vma| vma.lock().region == *region) + .drain_filter(|vma| vma.lock_irqsave().region == *region) .next()?; self.unreserve_hole(region); @@ -1120,7 +1120,7 @@ impl LockedVMA { id: LOCKEDVMA_ID_ALLOCATOR.alloc().unwrap(), vma: SpinLock::new(vma), }); - r.vma.lock().self_ref = Arc::downgrade(&r); + r.vma.lock_irqsave().self_ref = Arc::downgrade(&r); return r; } @@ -1132,6 +1132,10 @@ impl LockedVMA { return self.vma.lock(); } + pub fn lock_irqsave(&self) -> SpinLockGuard { + return self.vma.lock_irqsave(); + } + /// 调整当前VMA的页面的标志位 /// /// TODO:增加调整虚拟页映射的物理地址的功能 @@ -1146,7 +1150,7 @@ impl LockedVMA { mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { - let mut guard = self.lock(); + let mut guard = self.lock_irqsave(); for page in guard.region.pages() { // 暂时要求所有的页帧都已经映射到页表 // TODO: 引入Lazy Mapping, 通过缺页中断来映射页帧,这里就不必要求所有的页帧都已经映射到页表了 @@ -1164,7 +1168,7 @@ impl LockedVMA { pub fn unmap(&self, mapper: &mut PageMapper, mut flusher: impl Flusher) { // todo: 如果当前vma与文件相关,完善文件相关的逻辑 - let mut guard = self.lock(); + let mut guard = self.lock_irqsave(); // 获取物理页的anon_vma的守卫 let mut page_manager_guard: SpinLockGuard<'_, crate::mm::page::PageManager> = @@ -1178,10 +1182,10 @@ impl LockedVMA { // 从anon_vma中删除当前VMA let page = page_manager_guard.get_unwrap(&paddr); - page.write().remove_vma(self); + page.write_irqsave().remove_vma(self); // 如果物理页的anon_vma链表长度为0并且不是共享页,则释放物理页. - if page.read().can_deallocate() { + if page.read_irqsave().can_deallocate() { unsafe { drop(page); deallocate_page_frames( @@ -1195,10 +1199,19 @@ impl LockedVMA { flusher.consume(flush); } guard.mapped = false; + + // 当vma对应共享文件的写映射时,唤醒脏页回写线程 + if guard.vm_file().is_some() + && guard + .vm_flags() + .contains(VmFlags::VM_SHARED | VmFlags::VM_WRITE) + { + crate::mm::page::PageReclaimer::wakeup_claim_thread(); + } } pub fn mapped(&self) -> bool { - return self.vma.lock().mapped; + return self.vma.lock_irqsave().mapped; } /// 将当前VMA进行切分,切分成3个VMA,分别是: @@ -1210,7 +1223,7 @@ impl LockedVMA { assert!(region.start().check_aligned(MMArch::PAGE_SIZE)); assert!(region.end().check_aligned(MMArch::PAGE_SIZE)); - let mut guard = self.lock(); + let mut guard = self.lock_irqsave(); { // 如果传入的region不在当前VMA的范围内,则直接返回None if unlikely(region.start() < guard.region.start() || region.end() > guard.region.end()) @@ -1253,27 +1266,27 @@ impl LockedVMA { // 重新设置before、after这两个VMA里面的物理页的anon_vma let mut page_manager_guard = page_manager_lock_irqsave(); if let Some(before) = before.clone() { - let virt_iter = before.lock().region.iter_pages(); + let virt_iter = before.lock_irqsave().region.iter_pages(); for frame in virt_iter { if let Some((paddr, _)) = utable.translate(frame.virt_address()) { let page = page_manager_guard.get_unwrap(&paddr); - let mut page_guard = page.write(); + let mut page_guard = page.write_irqsave(); page_guard.insert_vma(before.clone()); page_guard.remove_vma(self); - before.lock().mapped = true; + before.lock_irqsave().mapped = true; } } } if let Some(after) = after.clone() { - let virt_iter = after.lock().region.iter_pages(); + let virt_iter = after.lock_irqsave().region.iter_pages(); for frame in virt_iter { if let Some((paddr, _)) = utable.translate(frame.virt_address()) { let page = page_manager_guard.get_unwrap(&paddr); - let mut page_guard = page.write(); + let mut page_guard = page.write_irqsave(); page_guard.insert_vma(after.clone()); page_guard.remove_vma(self); - after.lock().mapped = true; + after.lock_irqsave().mapped = true; } } } @@ -1289,7 +1302,7 @@ impl LockedVMA { /// 判断VMA是否为外部(非当前进程空间)的VMA pub fn is_foreign(&self) -> bool { - let guard = self.lock(); + let guard = self.lock_irqsave(); if let Some(space) = guard.user_address_space.clone() { if let Some(space) = space.upgrade() { return AddressSpace::is_current(&space); @@ -1303,14 +1316,14 @@ impl LockedVMA { /// 判断VMA是否可访问 pub fn is_accessible(&self) -> bool { - let guard = self.lock(); + let guard = self.lock_irqsave(); let vm_access_flags: VmFlags = VmFlags::VM_READ | VmFlags::VM_WRITE | VmFlags::VM_EXEC; guard.vm_flags().intersects(vm_access_flags) } /// 判断VMA是否为匿名映射 pub fn is_anonymous(&self) -> bool { - let guard = self.lock(); + let guard = self.lock_irqsave(); guard.vm_file.is_none() } @@ -1582,7 +1595,7 @@ impl VMA { for _ in 0..count.data() { let paddr = cur_phy.phys_address(); let page = page_manager_guard.get_unwrap(&paddr); - page.write().insert_vma(r.clone()); + page.write_irqsave().insert_vma(r.clone()); cur_phy = cur_phy.next(); } @@ -1655,14 +1668,14 @@ impl VMA { // 将VMA加入到anon_vma let page = page_manager_guard.get_unwrap(&paddr); - page.write().insert_vma(r.clone()); + page.write_irqsave().insert_vma(r.clone()); } // debug!("VMA::zeroed: done"); return Ok(r); } pub fn page_address(&self, page: &Arc) -> Result { - let page_guard = page.read(); + let page_guard = page.read_irqsave(); let index = page_guard.index().unwrap(); if index >= self.file_pgoff.unwrap() { let address = From 732a6d7d4dce49b0002071030382a03222a37bba Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Sat, 10 Aug 2024 19:24:32 +0800 Subject: [PATCH 31/60] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=BB=93=E6=9E=84=EF=BC=8C=E6=B7=BB=E5=8A=A0=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/Cargo.toml | 2 +- kernel/src/arch/x86_64/mm/fault.rs | 8 +- kernel/src/arch/x86_64/mm/mod.rs | 26 --- kernel/src/filesystem/fat/fs.rs | 8 +- kernel/src/filesystem/vfs/file.rs | 12 +- kernel/src/filesystem/vfs/mod.rs | 10 +- kernel/src/filesystem/vfs/mount.rs | 9 +- kernel/src/init/initcall.rs | 2 - kernel/src/mm/fault.rs | 342 +++++++++++++---------------- kernel/src/mm/mod.rs | 11 +- kernel/src/mm/page.rs | 23 +- kernel/src/mm/syscall.rs | 80 ++++--- kernel/src/mm/ucontext.rs | 60 ++--- 13 files changed, 257 insertions(+), 336 deletions(-) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 59e46d0e1..056a69cb4 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -88,4 +88,4 @@ debug = true # Controls whether the compiler passes `-g` # The release profile, used for `cargo build --release` [profile.release] -debug = false +debug = true diff --git a/kernel/src/arch/x86_64/mm/fault.rs b/kernel/src/arch/x86_64/mm/fault.rs index da276dbf0..8d7c3346d 100644 --- a/kernel/src/arch/x86_64/mm/fault.rs +++ b/kernel/src/arch/x86_64/mm/fault.rs @@ -223,7 +223,7 @@ impl X86_64MMArch { } let current_address_space: Arc = AddressSpace::current().unwrap(); - let mut space_guard = current_address_space.write(); + let mut space_guard = current_address_space.write_irqsave(); let mut fault; loop { let vma = space_guard.mappings.find_nearest(address); @@ -269,11 +269,9 @@ impl X86_64MMArch { ); } let mapper = &mut space_guard.user_mapper.utable; + let message = PageFaultMessage::new(vma.clone(), address, flags, mapper); - fault = PageFaultHandler::handle_mm_fault( - &mut PageFaultMessage::new(vma.clone(), address, flags), - mapper, - ); + fault = PageFaultHandler::handle_mm_fault(message); if fault.contains(VmFaultReason::VM_FAULT_COMPLETED) { return; diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index 378c4dc8c..f5c5badc6 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -327,32 +327,6 @@ impl MemoryManagementArch for X86_64MMArch { pkru::pkru_allows_pkey(pkru::vma_pkey(vma), write) } - // fn protection_map() -> [usize; 16] { - // let mut map = [0; 16]; - // map[VmFlags::VM_NONE] = Self::PAGE_NONE; - // map[VmFlags::VM_READ] = Self::PAGE_READONLY; - // map[VmFlags::VM_WRITE] = Self::PAGE_COPY; - // map[VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY; - // map[VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; - // map[VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; - // map[VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_COPY_EXEC; - // map[VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_COPY_EXEC; - // map[VmFlags::VM_SHARED] = Self::PAGE_NONE; - // map[VmFlags::VM_SHARED | VmFlags::VM_READ] = Self::PAGE_READONLY; - // map[VmFlags::VM_SHARED | VmFlags::VM_WRITE] = Self::PAGE_SHARED; - // map[VmFlags::VM_SHARED | VmFlags::VM_WRITE | VmFlags::VM_READ] = Self::PAGE_SHARED; - // map[VmFlags::VM_SHARED | VmFlags::VM_EXEC] = Self::PAGE_READONLY_EXEC; - // map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_READ] = Self::PAGE_READONLY_EXEC; - // map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE] = Self::PAGE_SHARED_EXEC; - // map[VmFlags::VM_SHARED | VmFlags::VM_EXEC | VmFlags::VM_WRITE | VmFlags::VM_READ] = - // Self::PAGE_SHARED_EXEC; - - // if Self::is_xd_reserved() { - // map.iter_mut().for_each(|x| *x &= !Self::ENTRY_FLAG_NO_EXEC) - // } - // map - // } - const PROTECTION_MAP: [EntryFlags; 16] = protection_map(); const PAGE_NONE: usize = diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index c429ec08e..a5119ff53 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -12,7 +12,6 @@ use alloc::{ vec::Vec, }; -use crate::arch::mm::PageMapper; use crate::driver::base::device::device_number::DeviceNumber; use crate::filesystem::vfs::file::PageCache; use crate::filesystem::vfs::utils::DName; @@ -292,18 +291,17 @@ impl FileSystem for FATFileSystem { ) } - unsafe fn fault(&self, pfm: &mut PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - PageFaultHandler::filemap_fault(pfm, mapper) + unsafe fn fault(&self, pfm: &mut PageFaultMessage) -> VmFaultReason { + PageFaultHandler::filemap_fault(pfm) } unsafe fn map_pages( &self, pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, start_pgoff: usize, end_pgoff: usize, ) -> VmFaultReason { - PageFaultHandler::filemap_map_pages(pfm, mapper, start_pgoff, end_pgoff) + PageFaultHandler::filemap_map_pages(pfm, start_pgoff, end_pgoff) } } diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index ea36d6848..16ff716dc 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -122,7 +122,7 @@ impl FileMode { } } -#[allow(dead_code)] +/// 页面缓存 pub struct PageCache { xarray: SpinLock>>, pub inode: Option>, @@ -174,16 +174,6 @@ impl PageCache { pub fn set_inode(&mut self, inode: Weak) { self.inode = Some(inode) } - - // pub fn get_pages(&self, start_pgoff: usize, end_pgoff: usize) -> Vec> { - // let mut vec = Vec::new(); - // for pgoff in start_pgoff..=end_pgoff { - // if let Some(page) = self.map.get(&pgoff) { - // vec.push(page.clone()); - // } - // } - // vec - // } } impl Default for PageCache { diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index a61103ff8..00ce6ba50 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -12,7 +12,6 @@ use intertrait::CastFromSync; use system_error::SystemError; use crate::{ - arch::mm::PageMapper, driver::base::{ block::block_device::BlockDevice, char::CharDevice, device::device_number::DeviceNumber, }, @@ -565,7 +564,11 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { } fn page_cache(&self) -> Option> { - panic!("function page_cache() has not yet been implemented"); + log::error!( + "function page_cache() has not yet been implemented for inode:{}", + crate::libs::name::get_type_name(&self) + ); + None } } @@ -817,7 +820,7 @@ pub trait FileSystem: Any + Sync + Send + Debug { fn super_block(&self) -> SuperBlock; - unsafe fn fault(&self, _pfm: &mut PageFaultMessage, _mapper: &mut PageMapper) -> VmFaultReason { + unsafe fn fault(&self, _pfm: &mut PageFaultMessage) -> VmFaultReason { panic!( "fault() has not yet been implemented for filesystem: {}", crate::libs::name::get_type_name(&self) @@ -827,7 +830,6 @@ pub trait FileSystem: Any + Sync + Send + Debug { unsafe fn map_pages( &self, _pfm: &mut PageFaultMessage, - _mapper: &mut PageMapper, _start_pgoff: usize, _end_pgoff: usize, ) -> VmFaultReason { diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index 3aab7ecd4..cc479883b 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -12,7 +12,6 @@ use alloc::{ use system_error::SystemError; use crate::{ - arch::mm::PageMapper, driver::base::device::device_number::DeviceNumber, filesystem::vfs::ROOT_INODE, libs::{ @@ -537,19 +536,17 @@ impl FileSystem for MountFS { SuperBlock::new(Magic::MOUNT_MAGIC, MOUNTFS_BLOCK_SIZE, MOUNTFS_MAX_NAMELEN) } - unsafe fn fault(&self, pfm: &mut PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - self.inner_filesystem.fault(pfm, mapper) + unsafe fn fault(&self, pfm: &mut PageFaultMessage) -> VmFaultReason { + self.inner_filesystem.fault(pfm) } unsafe fn map_pages( &self, pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, start_pgoff: usize, end_pgoff: usize, ) -> VmFaultReason { - self.inner_filesystem - .map_pages(pfm, mapper, start_pgoff, end_pgoff) + self.inner_filesystem.map_pages(pfm, start_pgoff, end_pgoff) } } diff --git a/kernel/src/init/initcall.rs b/kernel/src/init/initcall.rs index 2d52d8dbd..a9e25690d 100644 --- a/kernel/src/init/initcall.rs +++ b/kernel/src/init/initcall.rs @@ -10,7 +10,6 @@ define_public_unified_initializer_slice!(INITCALL_FS); define_public_unified_initializer_slice!(INITCALL_ROOTFS); define_public_unified_initializer_slice!(INITCALL_DEVICE); define_public_unified_initializer_slice!(INITCALL_LATE); -define_public_unified_initializer_slice!(INITCALL_MM); pub fn do_initcalls() -> Result<(), SystemError> { unified_init!(INITCALL_PURE); @@ -22,6 +21,5 @@ pub fn do_initcalls() -> Result<(), SystemError> { unified_init!(INITCALL_ROOTFS); unified_init!(INITCALL_DEVICE); unified_init!(INITCALL_LATE); - unified_init!(INITCALL_MM); return Ok(()); } diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index ea7df2911..323a5886c 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -5,7 +5,7 @@ use core::{ panic, }; -use alloc::{sync::Arc, vec::Vec}; +use alloc::sync::Arc; use crate::{ arch::{mm::PageMapper, MMArch}, @@ -46,21 +46,30 @@ bitflags! { /// # 缺页异常信息结构体 /// 包含了页面错误处理的相关信息,例如出错的地址、VMA等 #[derive(Debug)] -pub struct PageFaultMessage { +pub struct PageFaultMessage<'a> { /// 产生缺页的VMA结构体 vma: Arc, /// 缺页地址 address: VirtAddr, /// 异常处理标志 flags: FaultFlags, + /// 页表映射器 + mapper: &'a mut PageMapper, /// 缺页的文件页在文件中的偏移量 file_pgoff: Option, - /// 缺页对应PageCache中的文件页的物理地址 + /// 缺页对应PageCache中的文件页 page: Option>, + /// 写时拷贝需要的页面 + cow_page: Option>, } -impl PageFaultMessage { - pub fn new(vma: Arc, address: VirtAddr, flags: FaultFlags) -> Self { +impl<'a> PageFaultMessage<'a> { + pub fn new( + vma: Arc, + address: VirtAddr, + flags: FaultFlags, + mapper: &'a mut PageMapper, + ) -> Self { let guard = vma.lock_irqsave(); let file_pgoff = guard.file_page_offset().map(|file_page_offset| { ((address - guard.region().start()) >> MMArch::PAGE_SHIFT) + file_page_offset @@ -71,6 +80,8 @@ impl PageFaultMessage { flags, file_pgoff, page: None, + mapper, + cow_page: None, } } @@ -82,8 +93,8 @@ impl PageFaultMessage { #[inline(always)] #[allow(dead_code)] - pub fn address(&self) -> &VirtAddr { - &self.address + pub fn address(&self) -> VirtAddr { + self.address } #[inline(always)] @@ -94,20 +105,8 @@ impl PageFaultMessage { #[inline(always)] #[allow(dead_code)] - pub fn flags(&self) -> &FaultFlags { - &self.flags - } -} - -impl Clone for PageFaultMessage { - fn clone(&self) -> Self { - Self { - vma: self.vma.clone(), - address: self.address, - flags: self.flags, - file_pgoff: self.file_pgoff, - page: None, - } + pub fn flags(&self) -> FaultFlags { + self.flags } } @@ -123,10 +122,7 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn handle_mm_fault( - pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { + pub unsafe fn handle_mm_fault(mut pfm: PageFaultMessage) -> VmFaultReason { let flags = pfm.flags(); let vma = pfm.vma(); let current_pcb = ProcessManager::current_pcb(); @@ -148,7 +144,7 @@ impl PageFaultHandler { if unlikely(vm_flags.contains(VmFlags::VM_HUGETLB)) { //TODO: 添加handle_hugetlb_fault处理大页缺页异常 } else { - Self::handle_normal_fault(pfm, mapper); + Self::handle_normal_fault(&mut pfm); } VmFaultReason::VM_FAULT_COMPLETED @@ -162,12 +158,10 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn handle_normal_fault( - pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { + pub unsafe fn handle_normal_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); + let mapper = &mut pfm.mapper; if mapper.get_entry(address, 3).is_none() { mapper .allocate_table(address, 2) @@ -188,7 +182,7 @@ impl PageFaultHandler { } } - Self::handle_pte_fault(pfm, mapper) + Self::handle_pte_fault(pfm) } /// 处理页表项异常 @@ -199,36 +193,34 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn handle_pte_fault( - pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { + pub unsafe fn handle_pte_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { let address = pfm.address_aligned_down(); let flags = pfm.flags; let vma = pfm.vma.clone(); let mut ret = VmFaultReason::VM_FAULT_COMPLETED; + let mapper = &pfm.mapper; // pte存在 if let Some(mut entry) = mapper.get_entry(address, 0) { if !entry.present() { - ret = Self::do_swap_page(pfm.clone(), mapper); + ret = Self::do_swap_page(pfm); } if entry.protnone() && vma.is_accessible() { - ret = Self::do_numa_page(pfm.clone(), mapper); + ret = Self::do_numa_page(pfm); } if flags.intersects(FaultFlags::FAULT_FLAG_WRITE | FaultFlags::FAULT_FLAG_UNSHARE) { if !entry.write() { - ret = Self::do_wp_page(pfm.clone(), mapper); + ret = Self::do_wp_page(pfm); } else { entry.set_flags(EntryFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); } } } else if vma.is_anonymous() { - ret = Self::do_anonymous_page(pfm, mapper); + ret = Self::do_anonymous_page(pfm); } else { - ret = Self::do_fault(pfm, mapper); + ret = Self::do_fault(pfm); } vma.lock_irqsave().set_mapped(true); @@ -244,13 +236,12 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_anonymous_page( - pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { + pub unsafe fn do_anonymous_page(pfm: &mut PageFaultMessage) -> VmFaultReason { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); let guard = vma.lock_irqsave(); + let mapper = &mut pfm.mapper; + if let Some(flush) = mapper.map(address, guard.flags()) { flush.flush(); crate::debug::klog::mm::mm_debug_log( @@ -279,28 +270,18 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - #[allow(unused_variables)] - pub unsafe fn do_fault(pfm: &mut PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - // panic!( - // "do_fault has not yet been implemented, - // fault message: {:?}, - // pid: {}\n", - // pfm, - // crate::process::ProcessManager::current_pid().data() - // ); - // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_fault - + pub unsafe fn do_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { if !pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) { - return Self::do_read_fault(pfm, mapper); + return Self::do_read_fault(pfm); } else if !pfm .vma() .lock_irqsave() .vm_flags() .contains(VmFlags::VM_SHARED) { - return Self::do_cow_fault(pfm, mapper); + return Self::do_cow_fault(pfm); } else { - return Self::do_shared_fault(pfm, mapper); + return Self::do_shared_fault(pfm); } } @@ -312,24 +293,52 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - #[allow(dead_code, unused_variables)] - pub unsafe fn do_cow_fault( - pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { - // panic!( - // "do_cow_fault has not yet been implemented, - // fault message: {:?}, - // pid: {}\n", - // pfm, - // crate::process::ProcessManager::current_pid().data() - // ); - // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_cow_fault - let file = pfm.vma().lock_irqsave().vm_file().unwrap(); + pub unsafe fn do_cow_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + let cache_page = pfm.page.clone().unwrap(); + let mapper = &mut pfm.mapper; + + let cow_page_phys = mapper.allocator_mut().allocate_one(); + if cow_page_phys.is_none() { + return VmFaultReason::VM_FAULT_OOM; + } + let cow_page_phys = cow_page_phys.unwrap(); + + let cow_page = Arc::new(Page::new(false, cow_page_phys)); + pfm.cow_page = Some(cow_page.clone()); - let mut ret = Self::filemap_fault(pfm, mapper); + let mut ret = Self::filemap_fault(pfm); + + if unlikely(ret.intersects( + VmFaultReason::VM_FAULT_ERROR + | VmFaultReason::VM_FAULT_NOPAGE + | VmFaultReason::VM_FAULT_RETRY + | VmFaultReason::VM_FAULT_DONE_COW, + )) { + return ret; + } - ret = ret.union(Self::finish_fault(pfm, mapper)); + //复制PageCache内容到新的页内 + let new_frame = MMArch::phys_2_virt(cow_page_phys).unwrap(); + (new_frame.data() as *mut u8).copy_from_nonoverlapping( + MMArch::phys_2_virt(cache_page.read_irqsave().phys_address()) + .unwrap() + .data() as *mut u8, + MMArch::PAGE_SIZE, + ); + + let mut page_manager_guard = page_manager_lock_irqsave(); + + // 新页加入页管理器中 + page_manager_guard.insert(cow_page_phys, &cow_page); + cow_page.write_irqsave().set_page_cache_index( + cow_page.read_irqsave().page_cache(), + cow_page.read_irqsave().index(), + ); + + // 将vma插入页的vma表中 + cow_page.write_irqsave().insert_vma(pfm.vma()); + + ret = ret.union(Self::finish_fault(pfm)); ret } @@ -342,21 +351,17 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - #[allow(dead_code, unused_variables)] - pub unsafe fn do_read_fault( - pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { + pub unsafe fn do_read_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { let fs = pfm.vma().lock_irqsave().vm_file().unwrap().inode().fs(); - let mut ret = Self::do_fault_around(pfm, mapper); + let mut ret = Self::do_fault_around(pfm); if !ret.is_empty() { return ret; } - ret = fs.fault(pfm, mapper); + ret = fs.fault(pfm); - ret = ret.union(Self::finish_fault(pfm, mapper)); + ret = ret.union(Self::finish_fault(pfm)); ret } @@ -369,23 +374,14 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - #[allow(dead_code, unused_variables)] - pub unsafe fn do_shared_fault( - pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { - // panic!( - // "do_shared_fault has not yet been implemented, - // fault message: {:?}, - // pid: {}\n", - // pfm, - // crate::process::ProcessManager::current_pid().data() - // ); - // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_shared_fault + pub unsafe fn do_shared_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + let mut ret = Self::filemap_fault(pfm); - let mut ret = Self::filemap_fault(pfm, mapper); + let cache_page = pfm.page.clone().expect("no cache_page in PageFaultMessage"); - ret = ret.union(Self::finish_fault(pfm, mapper)); + // 将pagecache页设为脏页,以便回收时能够回写 + cache_page.write_irqsave().add_flags(PageFlags::PG_DIRTY); + ret = ret.union(Self::finish_fault(pfm)); ret } @@ -399,7 +395,7 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] - pub unsafe fn do_swap_page(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn do_swap_page(pfm: &mut PageFaultMessage) -> VmFaultReason { panic!( "do_swap_page has not yet been implemented, fault message: {:?}, @@ -419,7 +415,7 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] - pub unsafe fn do_numa_page(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn do_numa_page(pfm: &mut PageFaultMessage) -> VmFaultReason { panic!( "do_numa_page has not yet been implemented, fault message: {:?}, @@ -438,9 +434,11 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_wp_page(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn do_wp_page(pfm: &mut PageFaultMessage) -> VmFaultReason { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); + let mapper = &mut pfm.mapper; + let old_paddr = mapper.translate(address).unwrap().0; let mut page_manager = page_manager_lock_irqsave(); let old_page = page_manager.get_unwrap(&old_paddr); @@ -523,26 +521,25 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_fault_around( - pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { - if mapper.get_table(*pfm.address(), 0).is_none() { + pub unsafe fn do_fault_around(pfm: &mut PageFaultMessage) -> VmFaultReason { + let vma = pfm.vma(); + let address = pfm.address(); + let mapper = &mut pfm.mapper; + + if mapper.get_table(address, 0).is_none() { mapper - .allocate_table(*pfm.address(), 0) + .allocate_table(address, 0) .expect("failed to allocate pte table"); } - let vma = pfm.vma(); let vma_guard = vma.lock_irqsave(); let vma_region = *vma_guard.region(); drop(vma_guard); // 缺页在VMA中的偏移量 - let vm_pgoff = (*pfm.address() - vma_region.start()) >> MMArch::PAGE_SHIFT; + let vm_pgoff = (address - vma_region.start()) >> MMArch::PAGE_SHIFT; // 缺页在PTE中的偏移量 - let pte_pgoff = - (pfm.address().data() >> MMArch::PAGE_SHIFT) & (1 << MMArch::PAGE_ENTRY_SHIFT); + let pte_pgoff = (address.data() >> MMArch::PAGE_SHIFT) & (1 << MMArch::PAGE_ENTRY_SHIFT); // 缺页在文件中的偏移量 let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); @@ -570,9 +567,7 @@ impl PageFaultHandler { ); // 预先分配pte页表(如果不存在) - if mapper.get_table(*pfm.address(), 0).is_none() - && mapper.allocate_table(*pfm.address(), 0).is_none() - { + if mapper.get_table(address, 0).is_none() && mapper.allocate_table(address, 0).is_none() { return VmFaultReason::VM_FAULT_OOM; } @@ -580,7 +575,6 @@ impl PageFaultHandler { // from_pte - pte_pgoff得出预读起始pte相对缺失页的偏移,加上pfm.file_pgoff(缺失页在文件中的偏移)得出起始页在文件中的偏移,结束pte同理 fs.map_pages( pfm, - mapper, file_pgoff + (from_pte - pte_pgoff), file_pgoff + (to_pte - pte_pgoff), ); @@ -598,7 +592,7 @@ impl PageFaultHandler { /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn filemap_map_pages( pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, + start_pgoff: usize, end_pgoff: usize, ) -> VmFaultReason { @@ -606,6 +600,7 @@ impl PageFaultHandler { let vma_guard = vma.lock_irqsave(); let file = vma_guard.vm_file().expect("no vm_file in vma"); let page_cache = file.inode().page_cache().unwrap(); + let mapper = &mut pfm.mapper; // 起始页地址 let addr = vma_guard.region().start @@ -641,15 +636,13 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn filemap_fault( - pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { + pub unsafe fn filemap_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { let vma = pfm.vma(); let vma_guard = vma.lock_irqsave(); let file = vma_guard.vm_file().expect("no vm_file in vma"); let page_cache = file.inode().page_cache().unwrap(); let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); + let mapper = &mut pfm.mapper; let mut ret = VmFaultReason::empty(); if let Some(page) = page_cache.get_page(file_pgoff) { @@ -661,19 +654,23 @@ impl PageFaultHandler { // TODO 同步预读 //涉及磁盘IO,返回标志为VM_FAULT_MAJOR ret = VmFaultReason::VM_FAULT_MAJOR; - let mut buf: Vec = vec![0; MMArch::PAGE_SIZE]; - file.pread( - file_pgoff * MMArch::PAGE_SIZE, - MMArch::PAGE_SIZE, - &mut buf[..], - ) - .unwrap(); + // let mut buf: Vec = vec![0; MMArch::PAGE_SIZE]; + let allocator = mapper.allocator_mut(); // 分配一个物理页面作为加入PageCache的新页 let new_cache_page = allocator.allocate_one().unwrap(); - (MMArch::phys_2_virt(new_cache_page).unwrap().data() as *mut u8) - .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); + // (MMArch::phys_2_virt(new_cache_page).unwrap().data() as *mut u8) + // .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); + file.pread( + file_pgoff * MMArch::PAGE_SIZE, + MMArch::PAGE_SIZE, + core::slice::from_raw_parts_mut( + MMArch::phys_2_virt(new_cache_page).unwrap().data() as *mut u8, + MMArch::PAGE_SIZE, + ), + ) + .expect("failed to read file to create pagecache page"); let page = Arc::new(Page::new(true, new_cache_page)); pfm.page = Some(page.clone()); @@ -685,78 +682,41 @@ impl PageFaultHandler { page.write_irqsave() .set_page_cache_index(Some(page_cache), Some(file_pgoff)); - - // // 分配空白页并映射到缺页地址 - // mapper.map(pfm.address, vma_guard.flags()).unwrap().flush(); - // let new_frame = phys_2_virt(mapper.translate(pfm.address).unwrap().0.data()); - // (new_frame as *mut u8).copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); } ret } - pub unsafe fn finish_fault( - pfm: &mut PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { + /// 将文件页映射到缺页地址 + /// ## 参数 + /// + /// - `pfm`: 缺页异常信息 + /// - `mapper`: 页表映射器 + /// + /// ## 返回值 + /// - VmFaultReason: 页面错误处理信息标志 + pub unsafe fn finish_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { let vma = pfm.vma(); let vma_guard = vma.lock_irqsave(); - let cache_page = pfm.page.clone().expect("no page in pfm"); - let page_phys = cache_page.read_irqsave().phys_address(); - - if pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) - && !pfm - .vma() - .lock_irqsave() - .vm_flags() - .contains(VmFlags::VM_SHARED) - { - // 私有文件映射的写时复制场景 - // 分配空白页并映射到缺页地址 - mapper - .map( - pfm.address, - vma_guard.flags().set_write(true).set_dirty(true), - ) - .unwrap() - .flush(); - - //复制PageCache内容到新的页内 - let new_phys = mapper.translate(pfm.address).unwrap().0; - let new_frame = MMArch::phys_2_virt(new_phys).unwrap(); - (new_frame.data() as *mut u8).copy_from_nonoverlapping( - MMArch::phys_2_virt(page_phys).unwrap().data() as *mut u8, - MMArch::PAGE_SIZE, - ); - - let mut page_manager_guard = page_manager_lock_irqsave(); - let page_guard = cache_page.read_irqsave(); - let new_page = Arc::new(Page::new(page_guard.shared(), new_phys)); - - // 新页加入页管理器中 - page_manager_guard.insert(new_phys, &new_page); - new_page.write_irqsave().set_page_cache_index( - cache_page.read_irqsave().page_cache(), - cache_page.read_irqsave().index(), - ); + let flags = pfm.flags(); + let cache_page = pfm.page.clone(); + let cow_page = pfm.cow_page.clone(); + let address = pfm.address(); + let mapper = &mut pfm.mapper; - // 将vma插入页的vma链表中 - new_page.write_irqsave().insert_vma(pfm.vma()); + let page_to_map = if flags.contains(FaultFlags::FAULT_FLAG_WRITE) + && !vma_guard.vm_flags().contains(VmFlags::VM_SHARED) + { + // 私有文件映射的写时复制 + cow_page.expect("no cow_page in PageFaultMessage") } else { // 直接映射到PageCache - mapper.map_phys(*pfm.address(), page_phys, vma_guard.flags()); - cache_page.write_irqsave().insert_vma(pfm.vma()); - - if pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) - && pfm - .vma() - .lock_irqsave() - .vm_flags() - .contains(VmFlags::VM_SHARED) - { - // 如果是共享写映射,将pagecache页设为脏页,以便回收时能够回写 - cache_page.write_irqsave().add_flags(PageFlags::PG_DIRTY) - } - } + cache_page.expect("no cache_page in PageFaultMessage") + }; + + let page_phys = page_to_map.read_irqsave().phys_address(); + + mapper.map_phys(address, page_phys, vma_guard.flags()); + page_to_map.write_irqsave().insert_vma(pfm.vma()); VmFaultReason::VM_FAULT_COMPLETED } } diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index 18fc78d69..e95e90198 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -94,6 +94,7 @@ bitflags! { const VM_FAULT_NEEDDSYNC = 0x002000; const VM_FAULT_COMPLETED = 0x004000; const VM_FAULT_HINDEX_MASK = 0x0f0000; + const VM_FAULT_ERROR = 0x000001 | 0x000002 | 0x000040 | 0x000010 | 0x000020 | 0x000800; } pub struct MsFlags:usize { @@ -668,16 +669,6 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { const PAGE_WRITE_EXEC: usize; const PAGE_EXEC: usize; - // /// 获取保护标志的映射表 - // /// - // /// - // /// ## 返回值 - // /// - `[usize; 16]`: 长度为16的映射表 - // fn protection_map() -> [usize; 16] { - // let map = [0; 16]; - // map - // } - const PROTECTION_MAP: [EntryFlags; 16]; /// 页面保护标志转换函数 diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 501e3998f..be17d6bf3 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -18,7 +18,7 @@ use crate::{ arch::{interrupt::ipi::send_ipi, mm::LockedFrameAllocator, MMArch}, exception::ipi::{IpiKind, IpiTarget}, filesystem::vfs::{file::PageCache, FilePrivateData}, - init::initcall::INITCALL_MM, + init::initcall::INITCALL_CORE, ipc::shm::ShmId, libs::{ rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, @@ -113,9 +113,11 @@ pub fn page_reclaimer_init() { info!("page_reclaimer_init done"); } +/// 页面回收线程 static mut PAGE_RECLAIMER_THREAD: Option> = None; -#[unified_init(INITCALL_MM)] +/// 页面回收线程初始化函数 +#[unified_init(INITCALL_CORE)] fn page_reclaimer_thread_init() -> Result<(), SystemError> { let closure = crate::process::kthread::KernelThreadClosure::StaticEmptyClosure(( &(page_reclaim_thread as fn() -> i32), @@ -133,6 +135,7 @@ fn page_reclaimer_thread_init() -> Result<(), SystemError> { Ok(()) } +/// 页面回收线程执行的函数 fn page_reclaim_thread() -> i32 { loop { let usage = unsafe { LockedFrameAllocator.usage() }; @@ -152,10 +155,12 @@ fn page_reclaim_thread() -> i32 { } } +/// 获取页面回收器 pub fn page_reclaimer_lock_irqsave() -> SpinLockGuard<'static, PageReclaimer> { unsafe { PAGE_RECLAIMER.as_ref().unwrap().lock_irqsave() } } +/// 页面回收器 pub struct PageReclaimer { lru: LruCache>, } @@ -175,6 +180,10 @@ impl PageReclaimer { self.lru.put(paddr, page.clone()); } + /// lru链表缩减 + /// ## 参数 + /// + /// - `count`: 需要缩减的页面数量 pub fn shrink_list(&mut self, count: PageFrameCount) { for _ in 0..count.data() { let (paddr, page) = self.lru.pop_lru().expect("pagecache is empty"); @@ -197,11 +206,20 @@ impl PageReclaimer { } } + /// 唤醒页面回收线程 pub fn wakeup_claim_thread() { // log::info!("wakeup_claim_thread"); let _ = ProcessManager::wakeup(unsafe { PAGE_RECLAIMER_THREAD.as_ref().unwrap() }); } + /// 脏页回写函数 + /// ## 参数 + /// + /// - `page`: 需要回写的脏页 + /// - `unmap`: 是否取消映射 + /// + /// ## 返回值 + /// - VmFaultReason: 页面错误处理信息标志 pub fn page_writeback(page: &Arc, unmap: bool) { if !unmap { page.write_irqsave().remove_flags(PageFlags::PG_DIRTY); @@ -254,6 +272,7 @@ impl PageReclaimer { .unwrap(); } + /// lru脏页刷新 pub fn flush_dirty_pages(&self) { // log::info!("flush_dirty_pages"); let iter = self.lru.iter(); diff --git a/kernel/src/mm/syscall.rs b/kernel/src/mm/syscall.rs index 6a35a9d6b..6c9646a3f 100644 --- a/kernel/src/mm/syscall.rs +++ b/kernel/src/mm/syscall.rs @@ -316,11 +316,6 @@ impl Syscall { ); return Err(SystemError::EINVAL); } - // 暂时不支持除匿名页以外的映射 - // if !map_flags.contains(MapFlags::MAP_ANONYMOUS) { - // error!("mmap: not support file mapping"); - // return Err(SystemError::ENOSYS); - // } // 暂时不支持巨页映射 if map_flags.contains(MapFlags::MAP_HUGETLB) { @@ -329,6 +324,7 @@ impl Syscall { } let current_address_space = AddressSpace::current()?; let start_page = if map_flags.contains(MapFlags::MAP_ANONYMOUS) { + // 匿名映射 current_address_space.write().map_anonymous( start_vaddr, len, @@ -338,6 +334,7 @@ impl Syscall { false, )? } else { + // 文件映射 current_address_space.write().file_mapping( start_vaddr, len, @@ -349,14 +346,7 @@ impl Syscall { false, )? }; - // let start_page = current_address_space.write().map_anonymous( - // start_vaddr, - // len, - // prot_flags, - // map_flags, - // true, - // false, - // )?; + return Ok(start_page.virt_address().data()); } @@ -559,37 +549,46 @@ impl Syscall { /// - `len`:长度(已经对齐到页) /// - `flags`:标志 pub fn msync(start: VirtAddr, len: usize, flags: usize) -> Result { + if !start.check_aligned(MMArch::PAGE_SIZE) || !check_aligned(len, MMArch::PAGE_SIZE) { + return Err(SystemError::EINVAL); + } + + if unlikely(verify_area(start, len).is_err()) { + return Err(SystemError::EINVAL); + } + if unlikely(len == 0) { + return Err(SystemError::EINVAL); + } + let mut start = start.data(); let end = start + len; let flags = MsFlags::from_bits_truncate(flags); - let mut err = Err(SystemError::EINVAL); let mut unmapped_error = Ok(0); + if !flags.intersects(MsFlags::MS_ASYNC | MsFlags::MS_INVALIDATE | MsFlags::MS_SYNC) { - return err; + return Err(SystemError::EINVAL); } + if flags.contains(MsFlags::MS_ASYNC | MsFlags::MS_SYNC) { - return err; + return Err(SystemError::EINVAL); } - err = Err(SystemError::ENOMEM); if end < start { - return err; + return Err(SystemError::ENOMEM); } - err = Ok(0); if start == end { - return err; + return Ok(0); } let current_address_space = AddressSpace::current()?; + let mut err = Err(SystemError::ENOMEM); + let mut next_vma = current_address_space + .read() + .mappings + .find_nearest(VirtAddr::new(start)); loop { - err = Err(SystemError::ENOMEM); - - if let Some(vma) = current_address_space - .read() - .mappings - .find_nearest(VirtAddr::new(start)) - { + if let Some(vma) = next_vma.clone() { let guard = vma.lock_irqsave(); let vm_start = guard.region().start().data(); let vm_end = guard.region().end().data(); @@ -625,20 +624,31 @@ impl Syscall { from_raw_parts(old_start as *mut u8, fend - fstart + 1) }); file.lseek(SeekFrom::SeekSet(old_pos as i64)).unwrap(); - - if err.is_err() || start >= end { - err = Ok(0); + if err.is_err() { + break; + } else if start >= end { + err = unmapped_error; break; } + next_vma = current_address_space + .read() + .mappings + .find_nearest(VirtAddr::new(start)); } - } else if start >= end { - err = Ok(0); - break; + } else { + if start >= end { + err = unmapped_error; + break; + } + next_vma = current_address_space + .read() + .mappings + .find_nearest(VirtAddr::new(vm_end)); } } else { - break; + return Err(SystemError::ENOMEM); } } - return if err.is_err() { err } else { unmapped_error }; + return err; } } diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index d181e5ac9..40934b65c 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -283,23 +283,15 @@ impl InnerAddressSpace { // debug!("map_anonymous: len = {}", len); - let start_page: VirtPageFrame = if allocate_at_once { - self.mmap( - round_hint_to_min(start_vaddr), - PageFrameCount::from_bytes(len).unwrap(), - prot_flags, - map_flags, - move |page, count, flags, mapper, flusher| { + let start_page: VirtPageFrame = self.mmap( + round_hint_to_min(start_vaddr), + PageFrameCount::from_bytes(len).unwrap(), + prot_flags, + map_flags, + move |page, count, flags, mapper, flusher| { + if allocate_at_once { VMA::zeroed(page, count, vm_flags, flags, mapper, flusher, None, None) - }, - )? - } else { - self.mmap( - round_hint_to_min(start_vaddr), - PageFrameCount::from_bytes(len).unwrap(), - prot_flags, - map_flags, - move |page, count, flags, _mapper, _flusher| { + } else { Ok(LockedVMA::new(VMA::new( VirtRegion::new(page.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, @@ -308,9 +300,9 @@ impl InnerAddressSpace { None, false, ))) - }, - )? - }; + } + }, + )?; return Ok(start_page); } @@ -391,13 +383,13 @@ impl InnerAddressSpace { } let pgoff = offset >> MMArch::PAGE_SHIFT; - let start_page: VirtPageFrame = if allocate_at_once { - self.mmap( - round_hint_to_min(start_vaddr), - PageFrameCount::from_bytes(len).unwrap(), - prot_flags, - map_flags, - move |page, count, flags, mapper, flusher| { + let start_page: VirtPageFrame = self.mmap( + round_hint_to_min(start_vaddr), + PageFrameCount::from_bytes(len).unwrap(), + prot_flags, + map_flags, + move |page, count, flags, mapper, flusher| { + if allocate_at_once { VMA::zeroed( page, count, @@ -408,15 +400,7 @@ impl InnerAddressSpace { file, Some(pgoff), ) - }, - )? - } else { - self.mmap( - round_hint_to_min(start_vaddr), - PageFrameCount::from_bytes(len).unwrap(), - prot_flags, - map_flags, - move |page, count, flags, _mapper, _flusher| { + } else { Ok(LockedVMA::new(VMA::new( VirtRegion::new(page.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, @@ -425,9 +409,9 @@ impl InnerAddressSpace { Some(pgoff), false, ))) - }, - )? - }; + } + }, + )?; return Ok(start_page); } From b5aea85b8f9f46314a8272d195efc197adafca9b Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Thu, 22 Aug 2024 15:57:13 +0800 Subject: [PATCH 32/60] =?UTF-8?q?=E4=BC=98=E5=8C=96PageCache=E7=9A=84?= =?UTF-8?q?=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/filesystem/fat/fs.rs | 23 ++++++++--------------- kernel/src/filesystem/vfs/file.rs | 19 +++++++++---------- kernel/src/mm/page.rs | 2 +- 3 files changed, 18 insertions(+), 26 deletions(-) diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index a5119ff53..9e6fc0126 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -123,7 +123,7 @@ pub struct FATInode { dname: DName, /// 页缓存 - page_cache: Arc, + page_cache: Option>, } impl FATInode { @@ -193,8 +193,6 @@ impl LockedFATInode { FileType::File }; - let mut page_cache = PageCache::default(); - let inode: Arc = Arc::new(LockedFATInode(SpinLock::new(FATInode { parent, self_ref: Weak::default(), @@ -223,23 +221,18 @@ impl LockedFATInode { }, special_node: None, dname, - page_cache: Arc::new(PageCache::default()), + page_cache: None, }))); - page_cache.inode = Some(Arc::downgrade(&inode) as Weak); - - inode.0.lock().page_cache = Arc::new(page_cache); + if !inode.0.lock().inode_type.is_dir() { + let page_cache = PageCache::new(Some(Arc::downgrade(&inode) as Weak)); + inode.0.lock().page_cache = Some(page_cache); + } inode.0.lock().self_ref = Arc::downgrade(&inode); inode.0.lock().update_metadata(); - // inode - // .0 - // .lock() - // .page_cache - // .set_inode(Arc::downgrade(&inode) as Weak); - return inode; } } @@ -380,7 +373,7 @@ impl FATFileSystem { }, special_node: None, dname: DName::default(), - page_cache: Arc::new(PageCache::default()), + page_cache: None, }))); let result: Arc = Arc::new(FATFileSystem { @@ -1877,7 +1870,7 @@ impl IndexNode for LockedFATInode { } fn page_cache(&self) -> Option> { - Some(self.0.lock().page_cache.clone()) + self.0.lock().page_cache.clone() } } diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 8613a5843..aa9494223 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -125,7 +125,7 @@ impl FileMode { /// 页面缓存 pub struct PageCache { xarray: SpinLock>>, - pub inode: Option>, + inode: Option>, } impl core::fmt::Debug for PageCache { @@ -145,11 +145,16 @@ impl core::fmt::Debug for PageCache { } impl PageCache { - pub fn new(inode: Option>) -> PageCache { - Self { + pub fn new(inode: Option>) -> Arc { + let page_cache = Self { xarray: SpinLock::new(XArray::new()), inode, - } + }; + Arc::new(page_cache) + } + + pub fn inode(&self) -> Option> { + self.inode.clone() } pub fn add_page(&self, offset: usize, page: &Arc) { @@ -176,12 +181,6 @@ impl PageCache { } } -impl Default for PageCache { - fn default() -> Self { - Self::new(None) - } -} - /// @brief 抽象文件结构体 #[derive(Debug)] pub struct File { diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index be17d6bf3..27b399dd4 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -250,7 +250,7 @@ impl PageReclaimer { .page_cache .clone() .unwrap() - .inode + .inode() .clone() .unwrap() .upgrade() From 1a627e450fc005c04f9696d197cc4b6dda6af8e5 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Wed, 28 Aug 2024 16:13:50 +0800 Subject: [PATCH 33/60] =?UTF-8?q?=E5=B0=86=E5=85=A5=E5=8F=A3=E7=82=B9?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E9=93=BE=E6=8E=A5=E5=99=A8=EF=BC=9B=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3=E9=93=BE=E6=8E=A5=E5=99=A8=E5=8A=A0=E8=BD=BD=E5=9C=B0?= =?UTF-8?q?=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/libs/elf.rs | 30 ++++++++++++------------------ kernel/src/process/exec.rs | 8 +++++++- kernel/src/syscall/mod.rs | 6 ++++-- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/kernel/src/libs/elf.rs b/kernel/src/libs/elf.rs index 4abbba61c..6c74dec1a 100644 --- a/kernel/src/libs/elf.rs +++ b/kernel/src/libs/elf.rs @@ -361,23 +361,22 @@ impl ElfLoader { let mut last_bss: VirtAddr = VirtAddr::new(0); let mut bss_prot: Option = None; for section in phdr_table { - log::debug!("loading {:?}", section); if section.p_type == PT_LOAD { + log::debug!("loading {:?}", section); let mut elf_type = MapFlags::MAP_PRIVATE; let elf_prot = Self::make_prot(section.p_flags, true, true); let vaddr = TryInto::::try_into(section.p_vaddr).unwrap(); + let mut addr_to_map = load_addr + vaddr; if interp_hdr.e_type == ET_EXEC || load_addr_set { elf_type.insert(MapFlags::MAP_FIXED) //TODO 应当为MapFlags::MAP_FIXED,暂时未支持 - } - load_addr += vaddr; - if load_bias != 0 && interp_hdr.e_type == ET_DYN { - load_addr -= vaddr; + } else if load_bias != 0 && interp_hdr.e_type == ET_DYN { + addr_to_map = VirtAddr::new(0); } let map_addr = Self::load_elf_segment( &mut interp_elf_ex.vm().clone().write(), interp_elf_ex, §ion, - load_addr, + addr_to_map, &elf_prot, &elf_type, total_size, @@ -452,9 +451,8 @@ impl ElfLoader { _ => return ExecError::InvalidParemeter, })?; } - if load_addr + TryInto::::try_into(interp_hdr.e_entry).unwrap() - > MMArch::USER_END_VADDR - { + load_addr += TryInto::::try_into(interp_hdr.e_entry).unwrap(); + if load_addr > MMArch::USER_END_VADDR { return Err(ExecError::BadAddress(Some( load_addr + TryInto::::try_into(interp_hdr.e_entry).unwrap(), ))); @@ -868,6 +866,7 @@ impl BinaryLoader for ElfLoader { // 加载这个段到用户空间 + log::debug!("bias: {load_bias}"); let e = Self::load_elf_segment( &mut user_vm, param, @@ -882,7 +881,7 @@ impl BinaryLoader for ElfLoader { SystemError::ENOMEM => ExecError::OutOfMemory, _ => ExecError::Other(format!("load_elf_segment failed: {:?}", e)), })?; - + log::debug!("e.0={:?}", e.0); // 如果地址不对,那么就报错 if !e.1 { return Err(ExecError::BadAddress(Some(e.0))); @@ -1006,12 +1005,7 @@ impl BinaryLoader for ElfLoader { } // debug!("to create auxv"); let mut user_vm = binding.write(); - self.create_auxv( - param, - interp_load_addr.unwrap_or(program_entrypoint), - phdr_vaddr, - &ehdr, - )?; + self.create_auxv(param, program_entrypoint, phdr_vaddr, &ehdr)?; // debug!("auxv create ok"); user_vm.start_code = start_code.unwrap_or(VirtAddr::new(0)); @@ -1019,8 +1013,8 @@ impl BinaryLoader for ElfLoader { user_vm.start_data = start_data.unwrap_or(VirtAddr::new(0)); user_vm.end_data = end_data.unwrap_or(VirtAddr::new(0)); - let result = BinaryLoaderResult::new(program_entrypoint); - // debug!("elf load OK!!!"); + let result = BinaryLoaderResult::new(interp_load_addr.unwrap_or(program_entrypoint)); + // kdebug!("elf load OK!!!"); return Ok(result); } } diff --git a/kernel/src/process/exec.rs b/kernel/src/process/exec.rs index 909234641..a8f1e73f0 100644 --- a/kernel/src/process/exec.rs +++ b/kernel/src/process/exec.rs @@ -228,9 +228,15 @@ impl ProcInitInfo { &self, ustack: &mut UserStack, ) -> Result<(VirtAddr, VirtAddr), SystemError> { + // TODO: 实现栈的16字节对齐 + // self.push_slice( + // ustack, + // vec![0u8; 0x10 - (ustack.sp().data() & 0b1111)].as_slice(), + // )?; + self.push_slice(ustack, vec![0u8; 8].as_slice())?; + // 先把程序的名称压入栈中 self.push_str(ustack, &self.proc_name)?; - // 然后把环境变量压入栈中 let envps = self .envs diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index c9600fdf0..b41169b9b 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -860,8 +860,10 @@ impl Syscall { } SYS_EXIT_GROUP => { - warn!("SYS_EXIT_GROUP has not yet been implemented"); - Ok(0) + let exit_code = args[0]; + Self::exit(exit_code) + // warn!("SYS_EXIT_GROUP has not yet been implemented"); + // Ok(0) } SYS_MADVISE => { From d8cde6e8ae2fff1628e9a07d37349938a710f5c5 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Wed, 28 Aug 2024 16:56:37 +0800 Subject: [PATCH 34/60] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/filesystem/vfs/file.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index c9b36bc07..69994b73f 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -745,9 +745,7 @@ impl FileDescriptorVec { // 把文件描述符数组对应位置设置为空 let file = self.fds[fd as usize].take().unwrap(); - self.fds[fd as usize].take().unwrap(); - assert!(Arc::strong_count(&file) == 1); return Ok(file); } From e13cf395cb0fbb5afeee75f3051c5fe3ebd9b59b Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Wed, 28 Aug 2024 18:31:51 +0800 Subject: [PATCH 35/60] =?UTF-8?q?=E4=BF=AE=E5=A4=8Ddo=5Fcow=5Fpage?= =?UTF-8?q?=E6=AD=BB=E9=94=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/mm/fault.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 323a5886c..e6607b025 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -294,6 +294,17 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 pub unsafe fn do_cow_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + let mut ret = Self::filemap_fault(pfm); + + if unlikely(ret.intersects( + VmFaultReason::VM_FAULT_ERROR + | VmFaultReason::VM_FAULT_NOPAGE + | VmFaultReason::VM_FAULT_RETRY + | VmFaultReason::VM_FAULT_DONE_COW, + )) { + return ret; + } + let cache_page = pfm.page.clone().unwrap(); let mapper = &mut pfm.mapper; @@ -306,17 +317,6 @@ impl PageFaultHandler { let cow_page = Arc::new(Page::new(false, cow_page_phys)); pfm.cow_page = Some(cow_page.clone()); - let mut ret = Self::filemap_fault(pfm); - - if unlikely(ret.intersects( - VmFaultReason::VM_FAULT_ERROR - | VmFaultReason::VM_FAULT_NOPAGE - | VmFaultReason::VM_FAULT_RETRY - | VmFaultReason::VM_FAULT_DONE_COW, - )) { - return ret; - } - //复制PageCache内容到新的页内 let new_frame = MMArch::phys_2_virt(cow_page_phys).unwrap(); (new_frame.data() as *mut u8).copy_from_nonoverlapping( @@ -331,8 +331,8 @@ impl PageFaultHandler { // 新页加入页管理器中 page_manager_guard.insert(cow_page_phys, &cow_page); cow_page.write_irqsave().set_page_cache_index( - cow_page.read_irqsave().page_cache(), - cow_page.read_irqsave().index(), + cache_page.read_irqsave().page_cache(), + cache_page.read_irqsave().index(), ); // 将vma插入页的vma表中 From 94f1324f5b547e768a7289063e4ef11505937f18 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Wed, 28 Aug 2024 21:52:23 +0800 Subject: [PATCH 36/60] =?UTF-8?q?=E5=B0=86PageFaultMessage=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E5=9C=B0=E5=9D=80=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/mm/fault.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index e6607b025..9055cdf5f 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -76,7 +76,7 @@ impl<'a> PageFaultMessage<'a> { }); Self { vma: vma.clone(), - address, + address: VirtAddr::new(crate::libs::align::page_align_down(address.data())), flags, file_pgoff, page: None, From 16c423225160dcb1ed710e9a3bfde62faa589de6 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Wed, 28 Aug 2024 21:58:42 +0800 Subject: [PATCH 37/60] =?UTF-8?q?auxv=E6=B7=BB=E5=8A=A0=E9=9A=8F=E6=9C=BA?= =?UTF-8?q?=E6=95=B0=E6=8C=87=E9=92=88=EF=BC=9B=E4=BF=AE=E5=A4=8DAtType?= =?UTF-8?q?=E5=BA=8F=E5=8F=B7=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/x86_64/process/syscall.rs | 6 +++++- kernel/src/process/abi.rs | 4 ++-- kernel/src/process/exec.rs | 9 ++++++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/kernel/src/arch/x86_64/process/syscall.rs b/kernel/src/arch/x86_64/process/syscall.rs index d2652beb9..e91a99852 100644 --- a/kernel/src/arch/x86_64/process/syscall.rs +++ b/kernel/src/arch/x86_64/process/syscall.rs @@ -72,6 +72,10 @@ impl Syscall { param.init_info_mut().args = argv; param.init_info_mut().envs = envp; + // 生成16字节随机数 + // TODO 暂时设为0 + param.init_info_mut().rand_num = [0; 16]; + // 把proc_init_info写到用户栈上 let mut ustack_message = unsafe { address_space @@ -82,7 +86,7 @@ impl Syscall { }; let (user_sp, argv_ptr) = unsafe { param - .init_info() + .init_info_mut() .push_at( // address_space // .write() diff --git a/kernel/src/process/abi.rs b/kernel/src/process/abi.rs index cbaab9d0a..a39f4ff3a 100644 --- a/kernel/src/process/abi.rs +++ b/kernel/src/process/abi.rs @@ -38,7 +38,7 @@ pub enum AtType { /// Frequency at which times() increments. ClkTck, /// Secure mode boolean. - Secure, + Secure = 23, /// String identifying real platform, may differ from AT_PLATFORM. BasePlatform, /// Address of 16 random bytes. @@ -46,7 +46,7 @@ pub enum AtType { /// Extension of AT_HWCAP. HwCap2, /// Filename of program. - ExecFn, + ExecFn = 31, /// Minimal stack size for signal delivery. MinSigStackSize, } diff --git a/kernel/src/process/exec.rs b/kernel/src/process/exec.rs index a8f1e73f0..31c202689 100644 --- a/kernel/src/process/exec.rs +++ b/kernel/src/process/exec.rs @@ -206,6 +206,7 @@ pub struct ProcInitInfo { pub args: Vec, pub envs: Vec, pub auxv: BTreeMap, + pub rand_num: [u8; 16], } impl ProcInitInfo { @@ -215,6 +216,7 @@ impl ProcInitInfo { args: Vec::new(), envs: Vec::new(), auxv: BTreeMap::new(), + rand_num: [0; 16], } } @@ -225,7 +227,7 @@ impl ProcInitInfo { /// /// 返回值是一个元组,第一个元素是最终的用户栈顶地址,第二个元素是环境变量pointer数组的起始地址 pub unsafe fn push_at( - &self, + &mut self, ustack: &mut UserStack, ) -> Result<(VirtAddr, VirtAddr), SystemError> { // TODO: 实现栈的16字节对齐 @@ -256,6 +258,11 @@ impl ProcInitInfo { }) .collect::>(); + // 压入随机数,把指针放入auxv + self.push_slice(ustack, &self.rand_num)?; + self.auxv + .insert(super::abi::AtType::Random as u8, ustack.sp().data()); + // 压入auxv self.push_slice(ustack, &[null::(), null::()])?; for (&k, &v) in self.auxv.iter() { From 421d3ae6f448f428d68e1281391996f9d956525a Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Wed, 28 Aug 2024 23:17:28 +0800 Subject: [PATCH 38/60] =?UTF-8?q?=E7=AE=80=E5=8D=95=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=A0=88=E7=9A=8416=E5=AD=97=E8=8A=82?= =?UTF-8?q?=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/x86_64/process/syscall.rs | 2 +- kernel/src/process/exec.rs | 23 ++++++++++++----------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/kernel/src/arch/x86_64/process/syscall.rs b/kernel/src/arch/x86_64/process/syscall.rs index e91a99852..add3c6692 100644 --- a/kernel/src/arch/x86_64/process/syscall.rs +++ b/kernel/src/arch/x86_64/process/syscall.rs @@ -74,7 +74,7 @@ impl Syscall { // 生成16字节随机数 // TODO 暂时设为0 - param.init_info_mut().rand_num = [0; 16]; + param.init_info_mut().rand_num = 0; // 把proc_init_info写到用户栈上 let mut ustack_message = unsafe { diff --git a/kernel/src/process/exec.rs b/kernel/src/process/exec.rs index 31c202689..9004e61ba 100644 --- a/kernel/src/process/exec.rs +++ b/kernel/src/process/exec.rs @@ -206,7 +206,7 @@ pub struct ProcInitInfo { pub args: Vec, pub envs: Vec, pub auxv: BTreeMap, - pub rand_num: [u8; 16], + pub rand_num: u128, } impl ProcInitInfo { @@ -216,7 +216,7 @@ impl ProcInitInfo { args: Vec::new(), envs: Vec::new(), auxv: BTreeMap::new(), - rand_num: [0; 16], + rand_num: 0, } } @@ -230,15 +230,9 @@ impl ProcInitInfo { &mut self, ustack: &mut UserStack, ) -> Result<(VirtAddr, VirtAddr), SystemError> { - // TODO: 实现栈的16字节对齐 - // self.push_slice( - // ustack, - // vec![0u8; 0x10 - (ustack.sp().data() & 0b1111)].as_slice(), - // )?; - self.push_slice(ustack, vec![0u8; 8].as_slice())?; - // 先把程序的名称压入栈中 self.push_str(ustack, &self.proc_name)?; + // 然后把环境变量压入栈中 let envps = self .envs @@ -248,6 +242,7 @@ impl ProcInitInfo { ustack.sp() }) .collect::>(); + // 然后把参数压入栈中 let argps = self .args @@ -259,13 +254,20 @@ impl ProcInitInfo { .collect::>(); // 压入随机数,把指针放入auxv - self.push_slice(ustack, &self.rand_num)?; + self.push_slice(ustack, &[self.rand_num])?; self.auxv .insert(super::abi::AtType::Random as u8, ustack.sp().data()); + // 实现栈的16字节对齐,如果argv、envp、argc在栈中的长度加起来不能对齐16字节,则手动填充一个空字节 + // TODO 感觉这样对齐有点简陋,后续可能要完善一下 + if (envps.len() + argps.len() + 1) * core::mem::align_of::() % 0x10 != 0 { + self.push_slice(ustack, &vec![0u8; 1])?; + } + // 压入auxv self.push_slice(ustack, &[null::(), null::()])?; for (&k, &v) in self.auxv.iter() { + log::debug!("auxv: {:?}", ustack.sp()); self.push_slice(ustack, &[k as usize, v])?; } @@ -276,7 +278,6 @@ impl ProcInitInfo { // 把参数指针压入栈中 self.push_slice(ustack, &[null::()])?; self.push_slice(ustack, argps.as_slice())?; - let argv_ptr = ustack.sp(); // 把argc压入栈中 From 2207c1c4e19480a84ed043eba97c325464bb91aa Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Thu, 29 Aug 2024 22:21:21 +0800 Subject: [PATCH 39/60] =?UTF-8?q?=E9=80=9A=E8=BF=87check=20fmt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/process/exec.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kernel/src/process/exec.rs b/kernel/src/process/exec.rs index 9004e61ba..7535a9f49 100644 --- a/kernel/src/process/exec.rs +++ b/kernel/src/process/exec.rs @@ -261,13 +261,12 @@ impl ProcInitInfo { // 实现栈的16字节对齐,如果argv、envp、argc在栈中的长度加起来不能对齐16字节,则手动填充一个空字节 // TODO 感觉这样对齐有点简陋,后续可能要完善一下 if (envps.len() + argps.len() + 1) * core::mem::align_of::() % 0x10 != 0 { - self.push_slice(ustack, &vec![0u8; 1])?; + self.push_slice(ustack, &[0u8; 1])?; } // 压入auxv self.push_slice(ustack, &[null::(), null::()])?; for (&k, &v) in self.auxv.iter() { - log::debug!("auxv: {:?}", ustack.sp()); self.push_slice(ustack, &[k as usize, v])?; } From 6c4958b3e677a0a0df58b39474530c94ef7ae211 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Fri, 30 Aug 2024 15:15:47 +0800 Subject: [PATCH 40/60] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=A0=88=E7=9A=84=E5=AD=97=E8=8A=82=E5=AF=B9=E9=BD=90=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/x86_64/process/syscall.rs | 10 ++-------- kernel/src/process/exec.rs | 17 ++++++++++------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/kernel/src/arch/x86_64/process/syscall.rs b/kernel/src/arch/x86_64/process/syscall.rs index add3c6692..7fe4944cf 100644 --- a/kernel/src/arch/x86_64/process/syscall.rs +++ b/kernel/src/arch/x86_64/process/syscall.rs @@ -74,7 +74,7 @@ impl Syscall { // 生成16字节随机数 // TODO 暂时设为0 - param.init_info_mut().rand_num = 0; + param.init_info_mut().rand_num = [0u8; 16]; // 把proc_init_info写到用户栈上 let mut ustack_message = unsafe { @@ -87,13 +87,7 @@ impl Syscall { let (user_sp, argv_ptr) = unsafe { param .init_info_mut() - .push_at( - // address_space - // .write() - // .user_stack_mut() - // .expect("No user stack found"), - &mut ustack_message, - ) + .push_at(&mut ustack_message) .expect("Failed to push proc_init_info to user stack") }; address_space.write().user_stack = Some(ustack_message); diff --git a/kernel/src/process/exec.rs b/kernel/src/process/exec.rs index 7535a9f49..9d0cba351 100644 --- a/kernel/src/process/exec.rs +++ b/kernel/src/process/exec.rs @@ -206,7 +206,7 @@ pub struct ProcInitInfo { pub args: Vec, pub envs: Vec, pub auxv: BTreeMap, - pub rand_num: u128, + pub rand_num: [u8; 16], } impl ProcInitInfo { @@ -216,7 +216,7 @@ impl ProcInitInfo { args: Vec::new(), envs: Vec::new(), auxv: BTreeMap::new(), - rand_num: 0, + rand_num: [0u8; 16], } } @@ -258,11 +258,14 @@ impl ProcInitInfo { self.auxv .insert(super::abi::AtType::Random as u8, ustack.sp().data()); - // 实现栈的16字节对齐,如果argv、envp、argc在栈中的长度加起来不能对齐16字节,则手动填充一个空字节 - // TODO 感觉这样对齐有点简陋,后续可能要完善一下 - if (envps.len() + argps.len() + 1) * core::mem::align_of::() % 0x10 != 0 { - self.push_slice(ustack, &[0u8; 1])?; - } + // 实现栈的16字节对齐 + // 用当前栈顶地址减去后续要压栈的长度,得到的压栈后的栈顶地址与0xF按位与操作得到对齐要填充的字节数 + let length_to_push = (self.auxv.len() + envps.len() + 1 + argps.len() + 1 + 1) + * core::mem::align_of::(); + self.push_slice( + ustack, + &vec![0u8; (ustack.sp().data() - length_to_push) & 0xF], + )?; // 压入auxv self.push_slice(ustack, &[null::(), null::()])?; From fae0f7a2c0047630beb8f70136dea342a56500ba Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Fri, 30 Aug 2024 15:23:41 +0800 Subject: [PATCH 41/60] =?UTF-8?q?=E9=80=9A=E8=BF=87riscv64=E7=BC=96?= =?UTF-8?q?=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/riscv64/process/syscall.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/kernel/src/arch/riscv64/process/syscall.rs b/kernel/src/arch/riscv64/process/syscall.rs index b60f347b4..f914a3402 100644 --- a/kernel/src/arch/riscv64/process/syscall.rs +++ b/kernel/src/arch/riscv64/process/syscall.rs @@ -79,14 +79,8 @@ impl Syscall { }; let (user_sp, argv_ptr) = unsafe { param - .init_info() - .push_at( - // address_space - // .write() - // .user_stack_mut() - // .expect("No user stack found"), - &mut ustack_message, - ) + .init_info_mut() + .push_at(&mut ustack_message) .expect("Failed to push proc_init_info to user stack") }; address_space.write().user_stack = Some(ustack_message); From bacf49872e82f196c5c0848ef4f7ea551327b3da Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Fri, 30 Aug 2024 15:59:52 +0800 Subject: [PATCH 42/60] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- user/dadk/config/test_glibc-0.1.0.dadk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user/dadk/config/test_glibc-0.1.0.dadk b/user/dadk/config/test_glibc-0.1.0.dadk index 58be8fff3..ee9f94dcf 100644 --- a/user/dadk/config/test_glibc-0.1.0.dadk +++ b/user/dadk/config/test_glibc-0.1.0.dadk @@ -14,7 +14,7 @@ "build_command": "make install" }, "install": { - "in_dragonos_path": "/bin" + "in_dragonos_path": "/" }, "clean": { "clean_command": "make clean" From 9aaa1b0fd7a8279584db140832e47b55adb92770 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Fri, 30 Aug 2024 20:41:20 +0800 Subject: [PATCH 43/60] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=A8=E6=80=81?= =?UTF-8?q?=E5=BA=93libgcc=5Fs.so.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- user/apps/libgcc/Makefile | 2 ++ user/apps/libgcc/lib/libgcc_s.so.1 | Bin 0 -> 732952 bytes user/dadk/config/libgcc.dadk | 24 ++++++++++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 user/apps/libgcc/Makefile create mode 100644 user/apps/libgcc/lib/libgcc_s.so.1 create mode 100644 user/dadk/config/libgcc.dadk diff --git a/user/apps/libgcc/Makefile b/user/apps/libgcc/Makefile new file mode 100644 index 000000000..1a15d2447 --- /dev/null +++ b/user/apps/libgcc/Makefile @@ -0,0 +1,2 @@ +install: + cp -r lib $(DADK_CURRENT_BUILD_DIR) \ No newline at end of file diff --git a/user/apps/libgcc/lib/libgcc_s.so.1 b/user/apps/libgcc/lib/libgcc_s.so.1 new file mode 100644 index 0000000000000000000000000000000000000000..f758425aee23c11880dd691b64a869bdc4d00d7c GIT binary patch literal 732952 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�t;k99mD+N6IG}R+J;ky%EhUzQoxfPs);Xbd)W4Ne2T>cJ~lHB{d$C8+H|b zHpqJ@1$`CgO(JW}k0F6IzE>HMLA{9Y5AYoGz4g<0yu+>=dn4xOc}jDv{g)vz$p zayw~QmK$mSE8$M|fX2C+n#Kipg7^Qe?EUHZ*ixW=I2;6)jpo&m%W(W;^ehQAtZLPx z5D~ddFXrtSy6T7M3qAtxP(gP=x|CHYT7&iLMJEyah4+Hzq616!ZE{BPFv0m8*fB5` z4s4UYAc#1=u?8bN+kxSWRA>uSf$~%MT(qE=5TvXWa)|e!S|M~Z9w|aMx1;j11z}#6 z?B1SCra8HwLupQD3@1%MV}2p+;s?0N4!eL+nEcHXb?mKU?dim4vzV3CG%mQ>GWuV* zZ&&|HdRu2CmTo2H7huaCA-ecsm^8f+`t$-JMVVAQG-W0h+MIY4MfkR<{u>X*hHr}z*`Ry*FJ^_4hPdOw(rz34gcr2Bdw6<( zw3Lwb0%paUWQgveB?)CdjB-NmOZ5nh7Ny|U;WPe4-bzOA)xx1+sOLv{6gz%@!MqR& z)_5XIVb2q#d;#q?yd}Ia&mrKfHo`{L&|+Dl6mu0URG4}jdoH;Mb?BaOz~&ef6Otp7 z6jBl5Y=jc!RuYcFjebBLfM9E`Mbns1z>|p4VlPw*X$odpl1R@LYM-~7pmNa>%zt8F z5p0B5{CKu1=rS-fuf$ELuMy`*&HDk-=6##Y?jh+4E|aEgK8%_x(-ximUG_#+QI@28 z7E#80XfHZ!?)-K}|H?Mh->K@1Z5o&p7NL6~ba1FK|491?njcT% z;4BONeM)Q}v3Y2G#U8_TC68kFKPo@qY7Tb#YJ82!`WJ`Aj-g(3r6>ebHN9vJ>^@x< zf=?Be8uLx^zI#cS*c(K74MUCiO|B&u4Nb?_5fV4vl@;~2%dz>k_FZz^JxyLujS`VMlEl!PA@nw+X7eW!cs zG1~$KzNlH5j-xk@W6qu+;&DI9?!=*=zMEZ`mKEKDQe6BGGL<+UB(_d)pl1zSk2om1 zSzpjY%m@7#G1lQ=UqLVN`s6r9&CvLZgK54-jwor7jd;i4PmioOmb}Eev)jD53X34ldVcPvFTzJ|G}n^h z3-5AhcV%7op38CSwsxG;t#w6i){DLccN_xMi*AATuqa-SGp>dYKj>L9uPJTG9i7EG zYtNU%L6#bSJRQ^GEk}UE@hn&5NuHp^7I%tgeJ5kdfAk~JuKB5O5SY8sZrtO=4(}(d zm%geIzjQH{Jkyj4zW4~JU|la*^EWMr$To5ay>rkh@;}>hai#t4u|A$Y91~~?Qi0EZ(?=B-$M1* zi#|XJqUu~?$K!DF32fVYDI5wA8yYX6<1M+<&wq`CuNHewgchtkm+g$K;aP9|2nbfV z)rdTeUvA(__-mjf;gl0A%)g=pOFw91hjlD|H44EGFYymVXpiP`=%LuxC;%Zc-;%g| zC74%7Zgbxa$B3P@^)ZRn^LJ^Ju(x+yi{aGwEtG_KGUGXj<5=ibn8jliitoIBY4s#A z2|@+?{NaUC-)H0rRq~rW}0Gk8+tw5k2Ug#gS!-U_%+4q3u z^uEL48H|@Ka*e*=MMUu<3G-__rCyxAjp@F!^d&j?bZwcI-V;!>3&*rQ9&{fk4%YW_ z;rynzP(Wq6XAUCijy6}%CRdUl+31w%PR&xVq+iol;b%TABRuPpjjR^-TI?0vs=vs+ zgC+NrqL-ov!#qa=W`Ds+QE@)r699b$y$8LBQ{?#^bHWs4a$!!fWsg|_u$WeHraf$f zM3y~JrM~r|qxtPuL~)ynbakF=pc!?4vOU{z`=?m6o4>~UbY!^e-Yld)yD zIDLGWzoMwe8xt&w@H;=>RRBK5H64KH^N%V!Fp>`H z-fJ;ib3z3R_}?%>{2BZmz1>H^iGFVQ8pHy{<_WMV z=-XtO0JaYdJ@tfv4e-A5BWJQ7@a?1bI&eW#VxI&}$%!Q1Kghu!Q%JQ&oZsFFV~oCJ zJtT7CMGRNDL z680=AlZO9Ccgn%q#6cw1qbM8eP4SCVzCWqNT(Jq$j@{kGiSJ5IKyWVmf7A9Za8g(G z|NpM6!Kmv8O!F>_qOx9c*Gr0t%;ZutwJ3SXE?V}b=wc=1Y-$=T&Wt+YB3;bPE^1j; zmVWCS0;cSM!h*`R6y^I>K!y3RDi?FPsPKQj-sgNiGXtpg`+ooSi2cm@oXFNX8N(4$L9oWErEoHo2o6$F6z zOz+?AqZIst4UBVw#^0RoY(c z#hn~pikVsQAHAEt?R2Wius>e;*u3ztGDX?YH zjmnL3-PgLJz3Z^YfQUjYD(bOZ<2y z0zRYD)SpThAG2pTG>IPQ72TBMPe%0cjG4pf=cDU6JNO6uSjwsHJk92iek(9ld2xIF z>`eXR!4FE*Zm;hNrp?z&(z_|*M|*SHs-$AjGM3<1NNYYpr15|i;T8u*qF1@XejOkz&DYf7x0?_hw?bN_5gZXeiD z#giH2m!j2p_s>;4l4o=%!_A=sqFPY(TZ2QkMjVNVT7C zSR<>i+iCWtHI1nD%UNTu4($D8Bv;=6xS-GmNFYOZOVv597nwf_h8f3x`?Pf^$GPn& zdK{33%D25=kY5RaJaLEF;ZP~Eqz~eXR24-T%#4V)+G2pH88;TgQ;9{2FlZmrtu!3M zB@$cG@eYwT^E;rEx6xNHKD&M+_MA?v5At6ZNDo_Q6rsX!C0K3|=E5t%InTEhccdrH zt?PvJIa=9h5VAa-5kE}(b)lH{XdUX_pH_@7oU49=hu`9K_4cldF%NY#D>I2L%Ji5wSFN@}&V6Prv^OvG8)8mH723nR_+q@@mFDgo2&SHs zP=N(QhO`r+TEa7_C3&s6hCIljarOn~#TxXMDt_KmpDF&61%xl#`7@f`pz?ghSaLmk z_?HZb%abDcSbPJBje@2}hJ~{!Fe!p*E5c5CF+D=({z9659iZ0iqSYM8f{KLFdVod9 zA4GC#B;@x|qNxe_#dXMo@~Mx&+#;1jEP7Kt`TcakZfEhiD6{g_BItdgU0cM87twAS zTg2qbVDpvl#b6%Jc*5`UkICuw$tMhjekur*H2`7*7!*X(KK!xZ`UgZd#aH13uc9`W zgTi^d89n^*;Q9~qAP%+MSrV%ZNu5Ahhq+-M9UpN+S?>zXi{pvdoy|S;LU4+bBWQr+ zqeOR5Xap`<2;_1#3|06PJ(t#wtY~fV>eG4n$oc_QAw8v4EoPQ;V!vOOown$DfPBNq zn#JdhhWBxR%I=H?@@0dEdz8woKwLOy_qIc>6+eTzbskOh{>j!=23>Hc2K?k%1EUtJf^Xpr=7%*1ro#WS71SoG zFZXNhbIRX)i2y=-4|x~k4??<907v677pVAT@88|JE_S27^dJjYHqzEWc39yQT#z&u zJ+tFaz4y>zrdU?8UQAvUnxXFUP}fHrnl9(TDTHr>SjC_k0#ZEm{@p%0)QD1ZZ|G(2 z!vPdowH-`E?`o?y6m|(D=_Df!7QNWMtg9n!?j4)Gt ztF3ukX84;y{$wqerNNX>6Q(H7VIg699brwq?%vegI{aTxSPs{&krlaG&lVf41~4e@t1fOi1bSV#(+L8yhxN+}KRW$U3|(iP>VT!=DXq$>FDK z;|j52Pg~cRdNZ=iB%3#5Y%8({i?sv}kt>TwqtyB=@pZ(cV!O~PEh$K^kO!W&;u=Ih z(MW6RLyq7;P$msyi-l8REC4(U3;<*OC^U@C1}&LM9%EnR;z~OCijZ&~xXt8d6S?O;*#ESe$!#-h{0)J%2PyC3>aE89;V_>VIac*aWoY+YmLmitBT3g=r(?9t5$ z@>>F0#cn&vHB)7(4e6O$^{a@7a7|F9U`Gl#F}!UF%%#~{PXpL{b(%Ie?DRfyJ*_*+3dG%w68$Qxot!*wL$6}+8}eXb%#;;4U{-4A6;bMc#)%W9O&}n;7eCL z=;*{Ysax~e4Ur9%n6G!`_WCE=nPc{z70VZf`@;Vsev;`rzkc&<7w@VYyEWU11%jn; zmth{gEwr%FP0)h88~If%z)XEuA)Ak)fQm#cKFcJuBe|9Rd*4Gjv|--+AyA*>wi#i$ zMNJm3z;(%B#LMl$JSxU^Gk^II`bT_2Bd7-rS=e{U#CO_niVUceL8E0V4~YiC^ty;s zoj=K}%Y!YT;7xESO_0nW=v9w*p+)Cn2CjuTFXD>B1W>FKX+>jE6 zK;-(5C^bJPVs{@B$vS!n_mm7WIEAA%^(1;X1%<0=*2O5AoyIHSz$QdU6eb+EyM816 zL`#Ft0ZyxfOo6EmqMgE3H-jIWt&aDkaxd0*B-8U|OBYPdw0Kp|Hi>tb5o?GJ<3oDF zhY-bv$8K0gQ0QS4+Qij@Rm6OFhkIX5huts|CX0Q*bYk7;#6qLS_kzpOy6jztB-^a% z)&XNq^V7rw9Xy)>u11x+P;=k>WV^H?WS%HqG3|+UHl^V&OgR<$gZwO7@KfMD14)UQ zPaAU*UX3oVI!ZM5SC*e`kDo7MA$Us4nz=U}|B8t+w`ibHI;1D1M|P!% z4_$7W?+o-M;zNb>WvfkQT+D!x84?&=ijH#pI0V#{hOx4IiF9Gd#fOM?BS^Hf1@yOe z!+L2Xe4<+ais@!DV>2;%h_G43N6us!`dD(@I_sO4iF3SQOvEJR=)@b5q|789H%S>j zMHd#C!3@rTMG0w96sn=sqpu0{?IsA-HbzdKC7RuJW5m%hsLi%SXsxVpL$GkUzmZ8& zQ_!(;*Ls+3R9ernh}WB}txczlZhjX2BfsF)S~FAe4tHCl;afO6bso_?f-Hmlg`1Hd`}R$qg-(T-jP17HNx&M@xlrZPEe#HnY;%Gg{4Z9U`GnavS|`hBjmtag#)i7Rl<{WTeOmy1Gkci z+ZkKq-;L?Lp?coF#*bE%ci4q`w8j23!;7q_9fQ@S6oS&K5d;^tCpdkJg0SD{;Uz_S zvQNK~NYB}$hePrAvEtF^(!&>UW)jbpR&YbMnhM9=suwv5Q!|s0Am$h5SgM(zNq7xPG}LB#>?+90)C=?G zphTlvgm2XF_x#_gXt+VzSfN`XiMe@ugyR{;R9i}!<`B(0oJ7!4wEn&jt-qrr=A!kd zsFTX#dz}VYd_KDQ7)B?1mw47+LV3N+)ihX7*dm=?u@vOz!&f^gjenI=WMZ;)R z9+5>vUs-SAC61*+fA;-oeumnH8#mSV!P&Ol&H9ro#)47tU1Sm~!n+vWRPapetvejg zS;!(Xi09B)#Kla0u&*Myp!QMk@Of6RZ}W(hTKmxKh%Z%`V|(JM5G2ulw6b)C6zeI% z+z~>fWZbhr0dolQPwQc(6VUHS|H1W#LMb(Cpkp6T|JT>?O3%e5;p@!&R3~X;{ueRJ z@E$^^wEjJrV@=Ae`-Nn;3m7EY(=BPQbra39`PqYaioF^y6{^nl+4d<%bv!0*0M*dPlaWS!^D7c}1%*B84qa;-t_{_xOofzM-wJv;Kk||2MavYtl(I;Q`@SjQ)9Z>_6nULFN~Ecvk^Oz zALV}G^?bq<+DaHf4{rUj1V-}$$!Vj(3<)<9N9WwPPAqo%P?fNqimiQymGFMhk*N@s zqGrD}F&_P27`-(S_G!(T%ngswS}>aIm|(Df44j1HXsC)h)ex~7)dN>fiEHe4kOYF2 z8o343Fi~@_At1bsekv=mFp-~zemfPgKt9vg)!8>GMlZHDtjQqPPMdrvbYBotTuhOz zL_8Wd()C0c;xjY3`CbDz=Hig?I*9RecM)wkj1dk%RYrUFChxTtm* z(S)`#h6>;h@L9XM`2e7VXYvniyVqhV-{-YKaUcMMhw@JqHh#n^M&0mEVD8xi8rZIx zkzs}(gnxNcB-{_6VywW>97-NqUoB1Cd1<@L($)?D%le?IZDcv&2)P^x&9Rx>lEJ%2 zP!YM>oI?OJGdEkuN$i0J`5Q!JBUBvZkAucqyKM=ii)3zX`wIh{?Ba>$aKChS#|MeB zVnX@G)p8=B11+YMeFClKBnzPK~D4@LH!$^tsiTxB7fD?&(IQ2>R1JVT>~y+3M^dXDfm zsiKhgakVsjTX4PNqUrM~UKoDNpPPGmm`oNW;2&mDVCl)^Y)_{u|IsIw8%g1KcbHZ- zc{$8;v71gD4NB9*4KcE=ZsJk4?C%CM%x4pSG@Zx)8UAmxIZWrPQTOm{zyX z#z{0uR-rQ5OPiljv!ih6Y|D1q*Wu-i$Ipjr7yA1T%4Qu&AK_@8x^Cc~i4J=sc(l&l z&-e3ls_=CI3!4XsTk} zy3XB*V>gns%-fKj8D|4!^F}w|cvk@Ku%SdK)hxj+%BpfM4%w1ep#6g~9pUIAFHJiG zrtQN>cHSHyrkbdL?%rQ%J~+Gh>I${8DIYw{T^gH4c6}7M*209Q8#Ie`AWCJ6f}Ow( zP*_V9+>fYlcS^yc7#7>wB<@%OU2(_J4u!+3blu+HR}i+F^#pmeYXwx+lgPu;hArj> z@|f$%gV39_BadUv(Ki=p{8}>J_#9`dc;2YhBz`brL=t*9BRxW63=03v7kR{=;*+I* z1o!o{(o|$-*L4PkXLyBsJLtHy_d)F6MKi9@mk;)(SvN#zwqOj8obV<7O=g3xDQ$BBg*8r7^G)!TQ8cS!vg8!bvk|-3)$Zl&-`s)jm2G#r(wZ z+IzQO=k|+*@Pp_#(lpBF3cq7m?xX}CyiMmV6<$fIsm&$*a9pii2WL{f_s$9}Yu*fh zEU+(es^}wv+`)}|SR9vxb@s`LB4){L(KdYZ_7vthfv>E6CmR@6aEs)9*Lkht?#tH9 zVE>`c^*Vzd`48}h^z=5WKgES~^S?fwd0Q5G z4*Q8+=W#Un&fJO%l_UDG@iHui+|HnO%&d%A!`oJTDm`{7(`8|DHfJ3+9zPB5}*bD7XFIuL6q=$vrwOI>&azcaxG= z=@Aq78x;0|K26$d{|;S@j1F(gAmitmRO|hnvlG^}a2QJ|J>nbs@RlFaoufV2lo6HS zK>BN~K#1^<)J*XXp+#Z8qzSY2*o<1R(i}USXksE+XP|?qbUg&SRIpT^>n-k87y`wG zXYh~UNNh9sIP`@A$GqNSuN=PtfbY*T0Mfxd+vtnkt5r$DL^$JR)I1xh?v2zkp*Ku^ zyE(oxN6625xQVUW7!m1aB$#2w^YW;5a>Pn3~n&;(_IAE1LDohb1iG@L4d45i9p|jotE8{_bIl<{Kf+8l5 zqwn3BJR&{L=wjk0;e=s}I3c{)W_-dIyKZrq-2i4c3A23_%+Bqd>G-MVe*ggoL>}7U zMSi>Chkh3){9@JoRg=_&^J}@JLo)6sC-mTv#GRO2+UjupYPx=dTL;Xokp0%^x6BXSU=Z&1)DB%TBoV#MG`VL2KgjZ!;u@@ zqhJ^3gd^L~(y@JSdYTvQ#pmsqs%up#c)0f=t-o~c^^wiJsp8A+W9LkI!{q*yBXMp_ zdgEKX4Q`PsnLTtyJF`2wxU8$=pExc#E*e&|)-so#XPP#j!s#G{C+Ykvb_Nft-4?t= zt@-Q}-B(U_3YQ$QU+%(84`RFBWvT=-*Pe1^!rCL`yk4TDw6;+!asa)@Ise$RWZ|)Y zxqhI@6W)lZCQ}U^rbRw<#hQ6PA2B6~EpA4JEGDpx<~NMQe{W9W@}2icD{wn9O*-d| z`xzu{h~BBViPXVa+W4-DVTpm##y2WyW#fBiChZO(k?QT+2lWeP3iTXRj-KdJ!DZ;{HTJz@DvCgYe?;(gY`HZ7P}q$wB_?D zmd~^8V;8hH6X~Y#%A#97Y_C{!R)x%*1sHzScybbjA)Q4H)!s#L4=3NPVHvP2wr7G- zoQS`X>&9#aY1=`fR4zV$LlYHAx9y|yY$~&@3DjI&M%dU>;pZKRL%%o~vmdl|lIpr( zqjnFAG(<#Jxwxba*@h{zY>fkvG0y@j{)1?S%nRqy$ka-aQ)Y%hV{^m%;`W){*t^$4 zshe$Bs?B!TNRhJ(qq)_DyGeMppUrbdhIQ=}gZi3TW@wt?s@Vv?ZVfwoK$6H#Yvg24 z4z_(zhIPNlCrtW?>@n%CPmSk&U0rF-;L-~R5|NUa!|emP zN9`0)ySf3q(F$7+9ZA-Ab}W;=ZMr;c+z^D;U3y_~*LlmEw>ahn6R(XMv7`Q~$xgj|Qv3{Mmv2fd2N!i9{v0`2 zI_c1Qr2K`!`m%GPdsksUvHz!f{5>}Q1*I@3t=p@(Ft^%`|D*M}RYzpspYHnU21aiv zup@p-FPsXyIZii+csy`Xg+X9=Sk4eS$p0mQY$5=Gi)H%E4c;sJ0g_z)jn~NwOb?ke z{g?(UD!i^MPj#)9|N0A$6xVcR0JO_RTBR3`Gf=$7xn*KHL2Q^|DY)?~P^81ubmdPO zqG);~z9LnV;~x6$W*L2iDL;*<;SD2U()fNymN?B2bGb>!%Ma7R)|{L-?e9A6co-~b&BVmQ z8Ly10dfRFV3kmaW<8nziPh;>O}^>w)fE*zY^` zquIxj?;PoRenmSE@as#YI`fr-5FCAW`X^y z`O?Ak-7(KEmWp%n1DC=Bi7QwmHmva8u2lWK+q13duG>4AN>^?Ua51|zhl|sRg~0Q~ zsv|aHd>FR>DVve042Bum;AcdRWfydQJ|4 z{|l%<7B8&`U3GP0cZluiJ5n6UpbKuajR01EUGHBjC{Q&Zwk~S<-!d!rO};DtGu9`X z$A4ZD`qmvtnvEfEX}arM^^?{}cg&7=h3QGNJKu);Vd7zRIh`t6`lU4o8-PP(7sd{= z&h!xU8PZ>8ZUQ~{cQE{`7i1?L);Wu@@N*tbag~3DIoNLPRg{95r67(Yj$D3%=oSb)`(w-wxX_BLNyp{V<^?{BY8kS zRk{;&vJ3xn@-R~Nf;v{j1ZId3YR*OC1gr9?a;xKxJ9>x0rwEiJXql(%=dgquK;Bth zQ|r#+O_5DkDRg4PZzlmz?;`D6_OaKrcj5mArwPLcjT+eXImQb^Nj*;P+&8%8R7$+V zbDLQx>yUk|l6@6fDOj+fcLxtqFA|H6Dr(_cJaZogBtbc($ID3D0&*5vjifRfS$b_;PaF>JMHDdhz~a_;sCX zcg_vSt%j$*%Kul5j{mz>s@}t4S(gfP_{F zcIFsN6l+adJ2-xT9d3f-1-jusoSq=T^^f^{cGbjRuN`%ZFn4>&QwievAw{vi+uCO{ z?;RR`|D;;~MSRW%ym6(le{S_e?&b}qZRD7c`bld|01T!Od5U3DT6JJ)^}xgeu5bvx zf%r8hf~;KtE9_{Y0`^k&MXK9kQq{#EYoGGWA6UTEKKepmM)Nj5dlU$e$unub8AUyv z+eTjXE2rr{7U^edmk%4g@xwKXaq6$lzo*w~f3oOkGFY8uRJub;4`=dRAy8*thcKsC zM|#E|+&6CoE=#EkOiRDtx%36`oA$2n4`d&o?9C%4e7cQW54kRJxkY|v2m64r2O6S) zvBJYVnsa^7dyrupQqzag1bdpDPh8iA3joqD|M~kYvIkiMHF!{D=#-wx8>O^zuf&!F+c4lXo^NenXwTp}`q$dX zV>Z?q_^Ihf>d_Cv2fAH^>f%RpGLItOAJCUgLMqpzNczq5>YA{J^Ysid!}1nJf_$4m z1|oo%goZ3@IuJBrBUsly1$ym!VHSYu(BQx%9&-!PnWgf{soaKoGWaEaxxKBM;cH~2 za&zYs6FBM%ND8x!kYifw1bk;Ft4ZIU4Goa`zI#AwI&jw6HbaF-SZM1{A@TNL;dj}} z8OKt^aNJEWpiXOM!&v(;+tOVPBgqiC_dP*jn8qf%eJ5m{Q9;%L9$6!)nws81qhKmr zQsJYi&2z+TvKPz0wZ5O>VRew$RdEk^3D}nx;4OCW=wOU=ZeBxJmJig{A9+u;4)MXC-$Wrgs=%u`jXj7;0S=dqdWj{^=L4!itH8;RF zcQWs!w-uJtpnQ_8^N+4eg7Y7h1Wh4FEAeFN`+7cb^XFP%`&Tm<>VyD453>@qWu5g< z80sIxi-Bt8n*gGQkg}(EWb4KRq4|e=RE>q4ehbatvDP~2!Hu8kR+IVva1{pV`BJOH zVLv?s!;h<6i~cJB?&il2y~S#oyzJ|E@ANv@gX2-*r9|wpUQ;tgWd2HT|7s6DvLQ@o znp{JoWfE1*D#ou=S}iIoF1D4g#9ZRR=XXp7_z33@TJuRmQC{I^ddE0CTrL~~xHx(# zwfULN%h_L8wWD9IdvJKB#-R3s!nbHd--Tb{!Hvs?c|8F7hJI>GFR5v5sGZh)vk}=g zS1~%}r|?%J2Ke0pvJ0{+J5(9ZI0J+F z6d7rRV<~a#tnd>aZT@t#hqi}zMY+x%(nw8>Nb;l7batEl3a_EGvfj-e#ujdp9@~P- zW1ftCu&oYax0-;+U|M!4%&(0zaHKoNn^savMkKpm?VsVQi1x1i?HoyxMHl+pH}ZEw z51C12dA%~ZEk{fkjhVMkv8Pr9>Yf=ty07wRBJi6Hu<6$<9JcgA(7EWk@`K;gs2**C zXGeZTrT3cLW3PvxpwelG-+k>2J5H%V@GaI66<4t!0TZz|N5X*?axdVs-AfKz*B1W3 zQhQ#&cKfwB{3koUzm^5ShmaP^-z^hmY$!oa(FbU^zheus*ZGX}#gTf>}m$_27mnQli8xZ8hzZsiA z1^0{(f2vKPSv9}o?bKOK{0slcDeJD=kn}6s$zUOhYO;8}%l+j34xG>3O~;74$nk;V0=MVnaub65KKkCD5T`?x#SO;* z&@3mTyX}%<;Em*@O#!j+P*q#v@x*I(wG{7-e0FNPo1sQ+4^^8$sSXPN$zRm`h_|>C zVWFMyWvx{kr=eYqdenn=Aw7SVSRxu?r0;S?3rkcgL2A+#p&`isj$!CWQa>K0D^rfH zrIg6#h+wmqS*BQN9n8tjgRv=kh}s5a*klMdKHM?hiZn4|-sB*3qfN@2*bi(lLCZix z+5|?@e|0|c2@pa?D?5i?`435!o zkiJ&Gh;T#IqzT7&@uj0hYsrW)ygQZ$o(a9gfKx^?*1e9_>%&gIG5rVswtxa;F+Zc$t%+GVL z2Z%^ZJiB!iPP0eXKlM7^`;g?d1N{M{fcTCZWO#HWpKJ(M`1TyGzrrt*pZSY@Ii1_9 z-rHk(+g0waqjJss!TJY}XD& z4(a{05TFwYhWu|#COcrm-||Dqz1TO}>2RV2wq{VVPj=bN}>Vqx2uhdsU03O34 zN8vyKb~^zW*?p+=aN_Pc`o7~@(-xElV-TA7i@@}2;lOyl| zbJs5ses1lSwU^-C0;WCTO}ztzc}%@CcEZ<)3ce2U_&R~Au@qSt(R*t1Y_s#u17J|4 z0U)dsJq9-GnLPZu%dqcIyu4J}gZA{JDH3r*t3}-4$gc2mF8qS^6w51T*D3$b%(`D2 zt7`KzztbQT?^I>i8NR@TqpGT`99f*YHzpJ#_x*Ek(WIK|ymZAvbe-32pjp%QC4E?& z7hOP8xI(|h%>EA01fs--<7b6tdT>)Vxr)EK+-IXhRT!mXRW9d!pUCk|+H>kwM>J03 zbCtRzE6YE99mcJ;afLz2AWM>cphvmlE*V* z`))C%q5d(tH;4_GJ!rEA@v(i^0+_lXZFC%1Qfk_e>UeKQ>FYsX!Rw*N*uu^_uRf`bwxl~4|LuZ&FP(eQ914*=<8 zw#|p1fbc-xXru<1&i{&A{+gM4-8-3kar|t&f0yFiKW*TpN@c%;hY{C9&|EjV{gWb% zOxJk7ofqV_ua?P`$D2UAzzejco<}uBEb0oxfa6d>;aZ;ah>MkoG?s@!k5|(PwCApa zs%iw9$+H8sK1=Hw@$Y^BX{PdFrX0!`4bH?MB182q`|BgLd#B|CB?ov-s?frFrCAi z^5WBy&g&2Matz>tQS6AV8`!yUg4o7lF~l{@V67bUO~vi(BEOFE@@tahcf*W*HEKyP zZaIAw33hUH+K4H}iKm#<4-glYTjOlDMvVtjMSlGY_!&a&i2a4n3D&8R{Q8Ih^#UxG zUaYT3ew`l6FXx|h@~a`=-Ep|NF}!>7pvXQ8C(=CYXlCS(#8bpsLXunTAcPMa=c4^u z5{xESdQUXD+61w6Dh7V2=toESKs!3@TP7asry(3@-6i3~?;y9#AIUU{PMFYGZkZfY zn513UQq}w?gx*+g{RW_SC$FYZZt`kRXw9-;!=FUsGk#htufE8;T7K)~)ojTtmSPgD zo4~?Tc{`9sGiCy77$5toGNf?J(l@L5cST;kMscWrc9K_n(d16ll5LHUt@R;1>(roX zJpVzzh3C7iLF|tjd3CdU?c|kC7mvp$d6m^$p|{hp(#eE+d3B+y3LQv}RLd)O%_jYr za#SO)j;?Os@LMadI9VNewFWOD@@fIdLVrzcg6MRfiu$RNSIa0e{HUG$JIbpC@=reN z{gbl?`}--a|1Me)7Z3A1MDmL9P6bk%rBJ5{}oZp};VWqKi({&B!)u3Uo?zefK+oSsfMAY6@ z?9?6|2SM{|LL|@J^>?v>b{7)NOXNTCXa?F|woT^^w86H!s6D!Uza1wB8^?C~h~J;% z8o^b^BFJQ)mC6U3RC8w9>Eb6aPc9EBl?e1SBj7+O=rP|{zzZa{^S&GgxECYJuzTro!2K^`S-_&8R14?U>y|iMlxq*8a z^E4>7=~+fu!z#{n+Zsmp+J0W%VXv_- zmpO(G6|JEZk|i?^0RkLkZO!xL1|suBPRbJYnq&I5J9mf%h{<+ZjnYlFU@`BX zoxS!A6HLgz8DecU@~^(SeRW@`k$*(>yM-T2n+@qfceeZww+4~3-PQ8X-PlV1HjIk= zn{Vi?ve!_b>ksZuZC;-1*)De(v~NCT>tEY)CjTzu!Oeq>w-ZQRLsjz6+iO+R{4Vy| z-ZVf=mgH?6pI8W3TxERoQFv-^N~JIwO0{aQ|-hns51X6MOUD z&R%mlIsYqvr1o3zM=G8bPnyVL!l!E3M=bH=hx8KfJ|0ct4cLvuQ+UpyY|65ASP9Q@ z^QU>8a+1lC^5N!}Jf_MoY1gJYfhYF1tH3jQDonJA0ig?dFi>|V>^@#$-0$~{`v$m3 zMc8#m%%zdRn0wLH9df%`-YN3r$71)j`oK<-``?y#FIV{JF81EPCGVbiM|n4qZgwZ{zC?LMl9G4M zMVZw&o&Ty<-kq%%|F*pAHhH%P-`4minYyqIwi>lpvG<;!G}J%U@(zvj-R0eFd{iy( zbjzm6y9w4>Cqx^|yKb`!zUXQ=dG}d<#N(5^8?CpJcf0r}Pjpq0H#^I_@g|;R?}@S1 z$h+-MsN@9(e1##^$UAf2v3Ut~I`vx;Pwq)cJk3Y7yt~i3p?@1jMc#Sagt`-s!me8gow$ST{f~@f`kQ|K#rEogYw@ zy!%)DlfwPG$vfZjm*3t$d4b|ls?H;_fK1}TXMtV*V|sNGes`KX2Z+h!jwklxxPQhS zgOA~k*dQci&Ip`@>8=mg`=Ctu$I_EtwJ7sR+qFeE;nQrWAGUKy%BF@``0e0vl$@8k zYY5|Uq#Ii$M?#n5&M6=CWlbl67f~9B!*4L4P3#m=a8PVEZmnF zF#k69bHo7egpn8#N#J~57+CM@K`!5t-;+Sv<@%OLmLB5Ps}osDz5U#1TGY0pT23`Q64}D#dW5#j<#UT)=;EQcaMGH(%dm>J*EKPk{BV%Ub5hS1*NH{) zl0GQTjeIfrF3oZr;&6k>yv{oQ^YJS!QT)n2YHX_-?${I3>o_nA&2iWA5w*EIRuz@r~Y0sRx;m_?87$cHm{ z#i^Q5Cy`7#M)(yEd-!+SwR+)gJUKYrVXj6?wv{6G`di+5nwraS%+nYC#ek}_o13~O z;K1);D##+{Zo^fTLuq>yZS+Dqu{`WB9D#MbiFXQ{aSX_4*Lfa*EzT%=s2jhFGO`P+ z>V*bT!1GADw$IgVy9@75V5WNTD~>5tG&)_feUGYjWcQ z?ls~RC*=G2QPGzqPY=^;AkUv?YqwK)bMyFCMgAz%hyOM>`_as~qj>v>h^{79LTBJs z+P9%fhqDIL#x2VG;B+Z&Ad9$v_*sv4>M5=>isO-^)5c-&ySPr`Y5N>lAF04uTx^Lh zQA$TpM~LU1JGhWQR~Ib|-;D5KaLCcAKEk5~qi>&aONzOcb3AdCiv38j3qa&Ip3)Bi zq#OzPR}f0@Lq_3;Q#}-g*=Bs&O&xa{%x!2NvJh8;yAPzWqvJQ87>s^?OH&bDy2eac zgbT^eGX4oEd`Zw$w@Bhfb&J_QX*32|J5?j}w9|A%e}=yU!B>0Nx%h0uGq`Muy+#F> zGH{&_7=5MCDb6$KbKx>C^2?ie+U<1HES*amA+naM!(;BGGBmD(ITRmbN4K0&C7|sD z9x@Z6tSlA|em726;G9Jr(`^|OvihB5UP8*mCN`oT6TxM=uC9-Kvhpt}$U{ORf27u1 zY2|*Lv5xd)r>qs*fb$#zxk8IQnZy#rW-DO^aMQ7qW~ zoZ-@p#Jf(PBT4Dki@DlRv&?@?A!oHq)8S~OvvsJMoU?SH4PeW7#=jY-JZ~sIRa)6! zdCzlW5CrUFngqKV-NpG-sXH8z%}ek2{2Cy7`koKqa$yw2gMHawDf1+b#Q@Z%+4%#S zHSY>?E%X3(_&EPm^E>eq8u9|Q_;@5mZ>GsvCsWza#Z`xC9`ct12#sY40${g;J0~1p zx|b=@JwRsrK_nwsyZzib8WBUdjKo3IM9}4PxMM_BrH1bgPvMi zM}82^Sm9du1geA^llP~3ovyCaC+;y*HJRPqBD`{9X?6eN+{9Dy6!i}C{l?kcmBnIt zU2#pNmOp<6n&qh+=lUCa1=`&rH=NVD^p`HtDqtoKfj=7r7v``lUkjt>;JNN8$l9Y-I%Q>*FbpL7!?nR|5G61P(c zbP_@M0cvAM9x#Bpt)ylqwOoI}^%o|KR>9f0956fve+F)2OG=-rX6VWeGW5k<{RTvMWi4x+$N3Lc;>-@zM4ZiHIQ$2vAgi3PKNM{; z(y=!YMk8Z8^Kqov;&j)!Bml1L7bQRZ5o*KOw4`^;DSl13s7<_zxFweXafD}7KZFf? zrgSHdI_vahMSm_cI(sBH-hB*C9|EuwzO4&1i-yYU!Cx8$kvZyxx za|M;6eZ(mK^L~9;7618h{e}w9G&GsX=&rwVrVhHZf5S)B{?#ArxAqgetTpS$ z;YnAFupT>H1?)jNQTb8f7qSOGqt`NbZmrCh!_ir)>-jlxY!7upRK|zGDE{*it>zl{ zYGY0NHl$kr>IPOj-A_)KOX+{&GY$q2&XbT0iD zyvg2n2s_6x2)xiu_J#JYG1y@1_Rf$(gLR1wM&&1HH5z~nK8Ig!sI^>?uTkRJ|6M%V zu$AWr#D;1oLS49RIE)t>mjU@venjmS&2A05D5^}g&0{Rz{?GIKeh(pNJ8`7`xb4LA zk#M`D_$jZ?kMJrUZ8LEK(xH;#=Oo-xZU?Av?#R9sLaOup&cLSLd*?jA8^t%UEQp_3 z?(!T=zQtL7k}v;yp5JhQ^8#*P9^*W}4E)>F^|#IQQyxa&qe#C>o}Xg4&5mRrp_1ok zBd(QiyU+6@T~w}N<@uSMbD4hHOsqx~^oOrgqH}k7ep`c^#|c3$&u?pxzZaNh3}^bt z^Xvcb=lK=++!V$KXu|UR#LwS8&rjKH5PhCMaFZtfVLsqPNF_s7obTtTD0`U3Y$<=N zYw-)_G5jA}-pR;c`VRSi;ATf^hDpQ!%X~jtm0!fk(fQ0rBuAC>HxlUFy0#UtfGEt% z5O>?^7;TW3DjNkz9tQFZ2a+?|k*`kox?R8J=6g{K+1Bsd<^7!sIEc=vyUF*vi^<+K z-|tFFQB-%y_uG5-`F@eVRQq3*d_TXy2)=$nr+pA1Vw01EA9}v**rF1DsuZC^A#rjApiIIel`1=5{tby_sCs*=ErI{^1mLz zgU2IEdH#NWI>P9Mu#K*%ALseikYG&`gMuGiJ1`69xZGQWPah^J|HORq#HmD7lu{~$cp{v&cBR~4gG&h_`8!?-6_CkjG|WdiQ*OF0GlZ$VwzwMg*$jO znYdn7U!3;tWa1;-YXx5)7>Nq`r7HP;Cn5{qhb*ilyuW`JLCDdc#y}%`g71C4-%_A8 z=)93v6GHel`F^HsSF!Fm-!BTN83EDnj(wlZta-gs@icbUW@Ot&gX;t!nFG7e_iNcj z3Mv7i8aeiTrRm7t1OF(CvAtK5@ArOsY_&XhMUk#2Ut&K+)abbVEk4m&;iKfZuX6bk zhiiP0?ymU~AH)`kn7qZ0qI$PdJK{;<*UcBr*V;Jw8??+3Kh#MU7 z-JSFOsPgvm>Q}M6^7Oe=^2agIQ8bv{Hb9p0wKoQ*ru; znRU+Ii}EL&!AHU$9PK7rjAqdKvH(&$(!iLGOCwhrf}aOtQr4+b{VMr>2C4 zzcMkFCG@sn+A|i)Wd@%VxE5St`F_Zq z>U=-OyW9BDD1V}AntcFi$)+?uWhl;{Z~>&<;%f}fRl?#t7~o_NW`F4OC9wOVJU=>r z3;^SSe2yh+{4bVcV=3`Ui0nO)M*bEaEL~zde92pWhWKzWZ-h15?n_!Vk0N)h3zxA{ zh+b{>UhVm&^K?Ml;L>`3$h6Y?a0G5ncO7PDw#aokX_b1+eg|*e4jzKMjweZ%dIwtM z<|!7rX|BZl&7xk#d$tw*Ev%82dkBIo;u(D~L&FpDIR7brm@U1@90AYD5kOLnIUwhR zJv3@-uV3>^3ulG+F2$?(i0_q}FM%40Q$JC$&v^2s{Mf3FGW7U;~4}UUzU-Fc6I~lv24b_0Tch0Fk}k8?Ojb4 z4sfnq=sPa6$s8Xs3l3WL$S#P=N#t>ynKJMitZNmXRdTK5V>=-ZtRT2bW@gm(ilXpXKJw*N^lNdL@(UivYY(R_ ziv#$r229gq^CD8XU2Lt7LK|FqI(#h-u?M~IYlh!?RT>@YB6`9vjU)*}TTF z&Z6(ge;1$6&+*-}i%Z;@o1`)5Y;g|V%&i`*Q2mJ;&LDpWt&tq&#KLpXIPn~wE6ec6 zBo?Rox{1L!1uU8KV{>j%Q?1<_Bc9^5Ky6%?IrPNz3`u*(wr%$T)xF8$5y!6N{oxLFY97VY6ghX)eALcc_Wp4WAl z!HMkP?h#`9j^i7f5CwP@7Zf);4LrB#$Vjr?gd#DUAO{T zbo`{+e--5C(qS}jOBtcVNXGz4vfuNr$Ye)R1Nm>Nvfre7OqJapl~uloca9&nV$%|`?4{eH}U3}qCB*HRC@VE)XHh>-vo#|$zt;pb^E;@`#1QS6=w z#3DemW25lB`{|JFt4rF8&qbRGmQR_<&1q0f5`xo+VJ!_-6VLr%qlS3wrmGUQ!dcdL zQerG9sC`cCytfJ-8y`%`EgzP7EI*d9f^_$?o7du;+(P#Kg5qZLEEwk(K54h(J_G7} z>H@?7fE^H1u|I}1$e+qv+@Ik|);Ih2RkW||kQ(qqv2xe1TB{!V%dT#v2OH{Blo}Ad za9ljn;c5yz{W5Mv-i3eWAKk}eLQ4Evz{Y@vs$+Z|)^ZQ6)V{si-nMBS(O!Y~{=Ow^ zO=GPrxJoPPlvZeIgs&?Gc+e^IFV(O?`0*?44wEjx5-B;`_l<_A9!t z&7`TFimj9I%RgX)Dm|I71w2j!8reZeNtVgB!P!rDKG6_NA7R~f|auKPnD<-$xhB)IbL z5?fYDZp&P&BgMI-NEaC9cDhnfHx)-$3IaLT3K9*}32?i^&tiw4r*%Ki`n}j@D{jrL zb@{2W}|%~c86^A(Cx+|Jn)u&3GJstnyoJ&8srSe`bO|zL{C}3U^VZx;}O;_qV+St~X1`s=mo;lL|OxJF!)Fx{Xe( z=Frd1m!5a`_WfC-(fCG>CCuGULyiMKBI#u5B*dT(LrHKynig_L@zt_i`qM*rI8@|jRa zrt6!WPO}xwZ;={cO!4V70yMEOg6lBgVg$r8aS!iWO$nFfJssXpaV!rBYeL)6cOr`~ zM4$E<+h_jcpy|BCx%6F!Nt22|=0GR#1sdtUKt#vFQ=p6C<)ev|W-_%o$In?sl-HS+$N+11$fTk8+noFyGy zgg5ak?qZ<2P`gD%Z>ZBZf2B^J1GH4CyTzBqVz5!~PrEkSn^V=CF!JU6%fh>5?> zZD;iuAzI_tc1&GUAyd|q^{5pYAYMYRQaoonhg&kL&zByK&_iHA)>VYfS9*W$u} zFv#0xPH7#7P3+|slDiyWI<@P4vUTgw@v{-k)FTMs>ThZ}9uI;qT6 zlbm$4l7Rx{mhh7?zcpFW`NS8jeyLL51>t?(@ALnd7GmT#-OVq5;eqzmflqzAg|4H^ ztSz6PVxX#u)|^#IJY}?gw)Iu_h}N++EqkY~_wSKiur_d?=5{n>XUPK+p()hickz0x z`kq9LI*zTaG7q8mDtTaG`3H&0$iLVcJb3sQ?gHt$#<8AB;oyAMz3wU~tw%ES1vjbG zTnc;Zb@1?fXVi0ZRP(~(lT7K14tgA2d~|g2$>wl$@W&?{r|ufPw-_}SZW{xg0MV$G0$ZTGo$t~gj(Pzw( zd>!0_ik{R%+imQ#wWhkhl1y>S?4&ntplo6`dqFNdBZZY(d_!%rjOYePCyq)iYfG%C z>BBibUn{MZckUpANDjI;swz3Tt`jZTJbxjy}arlX9cTTXA|dQVViE89UWg)sR#0 zOD~b^`o@Tl^F8+y_7rW5*i7IBn+f~f%|qDf`e9mX{B&QjV&L;#|E;F$7QKD&@H>7{ zk%2+cVvQd>{ALQn<6MOaQH4-REW7$k#C0D${1R%V6B}^JGZ5B+7J(wOy7|f7P<<|C zIM+9_c>|Jh?1rxM_O^>I7l@jj>IaG2YJj^e=C(|oevKy5yp>halUWwRAyNET0Fb_} zJ?eb%4ZBmAj-6A)2SWz=6QES@fh?5OKOtj%b8&l~B@(Wt>2-Q*ys`hRCY~X%#Ny(d z^6yVHTK0Rwzz_f5=7#{SUXyIzGQtS;)7cL}s4N6XZy?9O`38*iApbmVss~G(8_1lo z-&EW{b@dWeVg>So?>Xcds*GNMq!Er76^dlUNPz50PmDrRjD+mZ!)?@2KW-Sc9r=Uv z>N;O)>NuXc`u)!$BGX3gxT)U30*mY#(-1DDCAVIv`rWvyXqgU)thX-ImuPTq@wMg=fOdt#II5L7hOD%ZQs$*8$0U1 zeIa(1<&3?k3DIN~m)mvAW`8Y37(a$dGMF>nxam4IB5}*#KHkwx)NlnO0`^+}^$jd={196P>*(}XIwsiX?p~l6gYCoT zb$lj0d~wGoT2~~`;O2BZ{yVsQHZd=_bx!z!SnjIIdmSXQK-ko9^k!|k^6)>XEuVr; z@2zHHfMgcq)NZci*iWzjkv;ZoEV z=^3r2@VFj|o4ID%oW()DnW@hV?+FTBe3~9UJGe#PU3YT(@TY?7>v+rz-xU0)d-U+f zuQdPIE5iKnP2Uj5x2KG>dk z4!^2;_B?EKH@d=Epjj;zZZ+U~I&|u1 zefr>TA_?nz2Uq&NyjAt2Gu9(8aN7wi=venzjZQ4!@>gpydu8hm!^t%?jNJ7th0Xk= zG0%@8$p4~FeWgdwP?zwmq(^;6nhvw)I7@q8H`K!V=lr!Pc=XK&;%>-nW7-nWbu4dn zSt)ehabNh;8jg_WoCX8L_o4cqjWCw|uCSFC*8zYXy?5An{1o3qX>L({c%8tH>67tt zxe-OJJ9<~y`i)X4;fkej1Ru?kA{grC65Ng_P!J8Xr`{Hjx(fGKc?I3!;Z%?08}`Ki zpY+~wWkl=Gi=!@J(>@Z>VIn{&!7dEv=V)(e;z46kg6h z&d;Vubxy4(hKW?4;54*a~J!RJ4^X z7qMH}rLi$1B>mbDVi_`=0mkI8xo%u^W^{naM+*C)bS5o5OgT6ien;HuJU#xev z^xr_yZt1`G&Il#Y`cbOY-P>go`96MB)BAh)9nrha18K4RtQrQt^DS+#`+=Ea5B9zo zxB>+v1v2i45N>GCps$n7%R4cBY)Lqz%i`+DNSRJ7$96J7tckU$nIw*)Z z!<*>9;a7X~R{sxC{k?dn`u;qQ2)LstwkCQ%V0@8&52aVvs-4qp^W+_{u4+88WB53R z+318%-TETY_@tSlyN>JFUn)KwYGx|)Ywgq5-SOuU+#YF@`P3?f5+vQM(!oc%p`q&K%qZg;W=9d($s>?LgB<^25Ei@0D2C`!mCn+1t_| zA*M+$PX$OWlsxgfv))QiM}?txn$^NG=)CLzl>p&LsO5C zEBGi{{fuFOD&_v}?TGa^3&0g_2<<~JQ|%?{J!lM58i=yBhSiFYc}LVtOS#*D z(LQv%)pym@M$+oGl&^^DHkJSBx>ViY@tMIDYVN!~WnU!A-=&(SP*60EH}I_2qg_jl zmr|r}n|u$AzI>D~Z=syA_APfa{(dWt@%FqcK5a9Lj&Yv~buo`YGgq&wzkyMI$@1&e zv^kVYsdo+0yQcE(zVVH;QVq)_U;d0Q_pp4`7tglh7?ulM@!=2o+VLFj6A|1bH@0{V zMTCdDul5;QKF7U1?RdUtELof8syUon9z+eBLj|2OhIXqW!hiZ{PYO-tH(i687u9#w zG%8_4^Zf@`mjmpKP*14L`6@nDO;JX>)i0HQ9=Acyl$T_Tm2dQr+}fRNdMrDZa-m`U z{PH(kd2BSU%G?me>7a*0m5_Djyk@AMDEA zq(a;2;_{&Rl%V z;z(o4e}Bpo$Qx*RVdKkxp#tSRYAD5{j6;ILWcl{!)4-KA-s}1mwnw1S^im8BnaR2ZnWYVe*E){Q_z!CmnXg@m@lt8I(=sy`s|b z<-zW8AL9c_1H0<|z;FlK6BYjCn5&>)>%d_Zc17*Bl>h7sHTUzPM;3Lol3ENO2448W zw(53I`|^#N50-EfuSa~^eqGVH)Rmqkb}&~wYP`{4`AmS;j1-o5*7Q5~0j#_H9N_5P zl)w6SUD3PC%6mlLt<wf(q-lQ z7%Dw>9Hv);D=q#gfnIF`b=sg&*yMpqm#);{SyU`4^; zYd7T2mda6{jsffI(P>LGhqKGS)mR6HE(@ue!#W8o@QD z{0rAIk5+A8s)cK`I%0c(ulD?;`>JuV{bhXuLVl>&Re6L*PeCqy^>Cl7>t8?T0hsdj zX#U-6VRnz`(~tXi->ke_xg6Ar^XeN z=n%NXhb{4&{xnV zW6I~aj+4PJ9*ZWt+sz*eK6RgrCmb%b0{!169B!)_7vq834kq7)Cw{9heI;!s;YyP^Iw<8$NxH zDSw^5L8nF)D&&jTC*D5Eu=6!Ri7*Yf8#D0!j#_)Dn}y|3xWF4Z7dnR1R6e;1QfAe` zc6d$UcnT$t`nyuevHZ|Z+O+bP@_M}aJR*|x!W&hP~)V%(!`!blakk?uiVs;Mj@}Yc6gQuP1zE~JRACqen<0Ov7$Te?3 zief3fqA}o`^7p`}R$P87ye?|I{HVk7&unzmjrg4{G-G$3E*!fnmCy0b6c)H4xJ6Ma zA0L%3b>$-t!5sof*9~o{p#BCcyJB9@{5OaC*WHLa4i}hTc5fY~%Rl9iX5dW)zhJ#z zapL1Ry=V?vtAIH}WMPjQFMd05Rl-70*cv5J!@zGAEQ?(ReN2l>2aU|I zc_MC(p*!Xm@gg_BrShMAI|ZF%O6$f9OXb_5;^*AR+#G79UW!A7dd7Yn2ZQ0HU17w) z=JT~6O??+gPuS%HHS@lxv_*?oGea}=)y)hnA4vkGn%Tx(rsbE2u`*Uhi4(9GF=iMY z8Z=)n!>GH?8%7mAHwFNx@9E-tn)h^JH}+NP4U6j8w0_#vGnRN)>=zOwbv5-)u4+%| z2(CRg(t?5l!uVj%^8j5R&a=M=xXUz#%gU!SG(d4m=kHx5l|RpiT57kT+v&qS_w^0j z5H$cJRK-5mcLxCuS2TX5(!eiV0~0g)c*(V{_l*14u}Am)H1|Cxj=7E+kFK=wX%EB{ z?K4iLp@>)}MISluWkf7rjf%Zq7A^Smqw<5HQENu?jQ#7&^U9f^|y;DO}2QSM;V!WO=S|oKQlr#ZD5hymxP?VopH* z33Y{h<-Pm<@E%a-zxCKAF=Pg zm%P4+LG%>gxW=P1*wZmdb20-x>U~1wdK~MH|vB<%@(Wja>|$V z@@#9qVQ9ya!YkLjkKe{C_I&diR_AT=v^q^4A^Mg8QajzR)5Cwja_R`Py+3gK@pCHR z+x>)d_w1O*ap!aQyu$tYiu*Ix{khcrx!C>rxcl=l_h*#*Gt&JT?*5$O{+#Ik9P9oJ zaeoeVe-6+ex4)Qs`JfDk5*Ii0-9TX-heUTi^1LALAieYIGuRerfPP!M+q(`}JePp1 zW8FGuzdyTgs`IQp1_XuEL|Da>iT>r3*e!#C{4e=D)%mx2u&p;tJx0aImMcGHv!#8DTAiMY3Be!+Hl|%bV4IS5zeEyqY+S$pQ&g?7g zk-Z~$FuU*eamDOj+k0=ZeSpblTffCK)03~;myoaP_Tu)d?+_5ac1yZ>Tm6!B^HT&$ zy_UYmKr4LFeC0_7lvAHoISX z@;;l-qQ!Gnxj2aZ?sReg&2z?8K=keZq`H0j>*ja5v}GizTgCVAyY_wY!P?)GZ@hD- z@2A)NR(xr0eSNyPrhLhxKtfFVo{D}LsI^xOjlWNA@{JGFd_Vcdd!yf}i+psS*Pb^khIFrKGsO*ua1|^e)MAcq`fQt@6)H(MQ?Gi_5w1_pE(_8{b2pOjt&2f`j+Q zHe8>sUwrnCm5#nIB1~$O5}~vE(a6Ab(Dr(|ep7zMHT}|)IV`xQr*Tt*8>fSj*1q1K zxpm4XW3n5ukbYdI9-dDev+d4A1QSmaw3?(@?o~QJ@dHlPBdQ|eUP@Q0t zU0-W=>N~X;ZZ^SS^~*U{7U&QEQ{tdPf9ZLXG`IRMJ$!SJ``duJI?@MTN?n{OKArl+ zD9#bTsJ-jNL07|}E+inIjjhL#3I|2w$#`0w{FfdzQ7Vtj^fPKr51)JGM}len(_I(W zlkvZ*yK`$YJEU`KXZDiLt=-v?XPp$>_#t4*jBP8_wbz&Z{N*_Lw0=Xnn6u`HsTscZ zTX&?pzPS7|iporVHuX7$W|G%iqGoQ@$j+^Uu3A1f-%ks)u6H5l9d#1|#Jkq{U~we* z7H1MX3+Zp?tYDG`KzAmwF`3_S^-IKbW=gC2r8>6^y6RTn1oN`boZcJL#UFOl9-EN! z=jQI_X^&{U!8G``+S{POHzJBPee+047|4&=d?B)5ODPT7p5JY~f*SG8lAOl84L31cSajhKDOJ z3kHq9jE8H-2ZOF2&qJ=027^JJ`oqu3!f`O@CjH^(5AU>}Q}u_RcRXM}f1*F>$&)TeOa_C_N%D{e%8v=k zF_#5{F1U<`PXgs<1m&HbG#(84mHzPaUI*nLJd_XIX>b3cKfImppnS+d>A^E#+lvnf z$_z{7CKS5XKF;!j&SLu6QZ?Umjef`q)OlZ?zAEoCiEew(DWZdd8_%Gzx%pEB&K~W> zhS$Pled~^2?B@G4M_P+M->rQ@B{Cc->$+J2l~Le(f{f|uVai0pZX9nFr;omkNHDW2Wy8zc>GX?7aSATeiD z)TpSVMh%KKsi;JQW?%wmU?NdIM8z5fDOIc~On@rN=p?{&7^M{}ZLy`TR&7yft4J+q z5KK@}qg9KQN~9<=1|$~cOU(R#YwvR=laNsR-h2P|eV?c2$z+|g_S)ZTuf6u#UqoV+ zFB_HNpgXKsGt2i7e*e1hx8DCX`6`nibN)Skd<|2vVu-I%{(F<#AViXrsGZeQ51%|4JWR63{#+jN z@;@B9ng0zTzoD&-7p3L%zo8!mji+Lph*v2{ z3o{iYRnpz*NYY>ST)*@evZ}RTpGGO1sJcQ|V2|{lB6&9r^!?-Zb^!(VMLCP&fKQXZ^|3RezF23YN;@7yab_xIcow zBf{TWfqJYP1!{3xfjTT-XZBbBrv7w6M`r3s%6}<89sT>++3Ehhb0*hyRiOUxZ|E!k zZvWGReq$?7##4V*rJwGll0{Zx{)N6O;jR_EY;}iZM=xuA;(tOv>Rf)2t_sq0TS4m3 zH~x?L@&6C~=p|d>=tysm@;XC5y7#|DKa%4G|2_TaSUb62r~m(V`SVcy=ut~u!ESiC ze(Vta=(extPw6-19r}$UdA{ZG-_Va79sYYKcmMb6N3V9LAALZiT0in#WyXKJ2toCy zeEwk-9zF6DA{~6he9cQbu3pFb{ zPB5y}Ep-ymB5pb~R`x`?HB-dBh*pB3ZYw8V5rT*|wuhFyOx&dA3+4p0J+ZsM-m>Do zKcRf38Y7?ak|O!Xh7a6s)XDw=*~urDZ3iNa&R{&pF@=fTPlkpM9675@j@g>jZ&A?s z;-#sr*0_#bOa2zzFCj@l;*VoYUae$t{?UPGlSRiT=b}=?!I(<3FIGW~yk|x~!G0Bp zhjOynb+NBD-;DgZg^g{jsLzi9&yfM zzZZ2;>HH}CXF0Kpru9cjlu@^cY++y&1BE1U9C>>Q%bMJsq%CWKK80kR0jSzKR0}5->F9ZRzwlTXPAex z?l?9xdjCEGNWKbM^SLr<_$9I3RQ_L8eKkfTJF<3Syfffz3dAp`fZ4x+x)zAOo*V<5 zBKBENAntRi%kh+4@kmYRx{=}&?%~KGW;fQhi1wmo2p#S^oj0DY~C(uWHyJYGA+s~m83d6 z!46!fIPM9W>Y4%xsS)R@sxRbb20Snu<4WCG|3piRP_68+<16%aIJ(jqu7kbRN~@hwV9*DG7+v(lFz*oZK(K5UJgEVmdsPtxmU6+UiEnd-_No>o;&tUN1^Jvabk+$Dc|> zsU#xuDXssitiU7=lbW;MgS>PTlWDv#*C{g%0a^m}5Z?b|WB&Do8cEiK_n&FZci~r# zXq=S2Fz9Td2rPLfcBz%eaw};k35(v0g#B3eV_UnLI(?Bu4N1eQ^XTL4sme(WW<2N1 zM;(r#Om{j)$AT|8e0%Df$Vho8cUX9J&L&=&2bUSonc^YVC*a&KrO(-B5hVn4{;tMR zCI|nTGM8ENDM|v*nNKeps-p0Gsl!Dc6;m;1d(Lql&6yw$+KJ0)5sB zPmvec%9#HHyhpZ{8+CW{(lKI#DtVidE~Zmioi>cS7<_2x(Q7teBFdr zJ=|>*GSW@RsBa>p(u}?#o??A8UnDOhXse#lwv)s8v;>qH-H?`16eTsZN2j}+`oW&9 z>PJ*z?A_heUqeqa0n?0H{IKK^%8JDl@thsMmLkb6u8al0$dCpsTe%=Z8ccb}~9`!xY$vZMo4``~Xpt#qZ)0h78Ve;CJk7V-lj*tCGpZF{hy5xoOY9(l?4&-z} zas4E&1-x>9y7?1xuaq-Gm}HK)a*`vi2N=uY%87Y<_bS&nliLMlJjFpVdz0_;9_XVe zo8{H1USHx>^`ELVfAVWxnv|q!MB)9X8}p+eBgx~#`}-R6@8sQ#57~Z=FbDl{_I;*v zAAM0^FZ$Nrq|7HU`!283c$KswjP`B{8i5VTYrya+B8GNuth`9em(2P07nS}UaK4#5 z7h*)7s-;tTFLT#^BN$VkM~Rbu?aga4`K$I+a-00yBLDs-|K5>*&HPKtH(B?P@{JEf zS@)n;y4<~^08XG!>CNvdFaNHWndxtr^Y1p9k)EX%X^O*W#OQ5vS#mkHP+aGWV3QSk zRkE`Hs%f$={pmuDR(mp5TMN?ll2iQLCrYce{)f*8m+=Ywh3KrnM;kPJsu{@Ls4bTo z0~V^)#W+6O0@4_;xUfONrG-Ky3&~p)Twd6w;M0Y2zQ~Ne;njsH5RTeB^ozm?=ohl5 z+Gnp~Vr|xZwcv^&#JpcY7ibzWSm>pZ42M!0NfR&-d%gj*HIC~>o>=YKjI$Kdbhekg zoY?z?RPjr)>dPC5KD9`^zt1X@e@q}(vO)~y@BUwqc*cd{)hCO4j-iT^#MG(pPRmB% zlR&KO18x61M4s94%gBlN&1f<)lLW9BufKcdsmXUagS zTS7C`O7k15{XQ@!y%=7dy-&Ov^=xm20sQN|>6(~tr@*03uh(+@-^=ejhQM}s9K}u2 zTYqbZqw{N3yHO|G`CH@Z^hFM^cJ7bAP+gnNuREy+A-rwMDxuOQ0%BymYQ4oLm)P96~Ty-d0$Xmm$0(y!G|b` zmHk|1Y}$HnIwCk$7gFsJY6;*;DzIBw{P*Mvf=J5kU*RD);FmRrYQ>&Wzn_t;a8yA$ z6nA5t=6m730&$y=?2XUlUm(RkFs`IQovo=>JFyb#9cwGUk^<)Mf|#ICj!AdNcq%`&RF;QV)J1MwkCmqIxI zp>_ysXM~6Rnor6ph>u`&a-n-qM61`m8zZVQs)dpgcXSK+_ya+qdJP+FHq{kw;G?&M z&3gk-p2WGLB1C_u(wE7E4D;#~MR>>nqIA^OHRgCF?#7%GH0Y;6z6Nd$TpAqwnWEgI zL7N7jXs}I#4>Z`M!CM+^)Syv=1_4Po{FXUByvjE|7{A~F+(ZETRwH3#LE2YT!)^wR zQVr_7UR4pT218!SoIHWZdPZ|nG>P%8lI<yhMWj{zIZ(h3G?Sp-xe zhj;u9uTqsQKz&v?%Og1<3UgF+6T(8Bw32F$?Bfvoc4#8|sGvu9QNABtDmA4dT}vM; z0qw&LR$T_b%eCZBard22noAI1c`WGcx6ZCvxH@YSq{!j$enK8EWU-^n$eohW`z3Qm z-Khkk?4||s68Uq3crX_9<*TG&w`rWbL77SqFvJ;oNxv4Ou8lXLGiRt>XKH?o70KCAUn+6(p7V?_mHvpm7r zTw0kjTG4V^kG1vJZtLymN#s=YMKSxzebAA8IY!+z>IJirGOkc9m9qR=YUcS9NS~@6 zU+P_HXwpP7@)A0yqZXgtV%qHMg7o9yxgwDHNs7Zs3Sh7 zVyH^z;i__xV!bX^nQvERPN%ArrJ9*lxjkK#a{5_E`TNw_D`@@F5V$ukqfb%wO_Awr zca%~(=em^Y{zibU&)VZfswP{Cse?YVROR}wY(&!nWz*C3?rjZsq({~-BQiNPQAy1) z0a?_pTIxO$Xhyf1ajkfuN1M@4WpaSU3(>klVeCwisfAWzDW)WDRK=0LP(&1JidcSK zq1(+kx)ZOKUf8eKg2h=+3&jCvsCUWB;RCK(Ry7A-BG%wKuUJ@F!hTfkJ5rsG;g2yc zdKNEJ^kOZ+Z<6qit<$Q~k;6PViBJ`%1Ww3S`tnT_x8#Oh3X3GanJcDM{?&R}ky~qI zuelko_4vjGqF-S_N0hySQO`Fv7=J{LAsb`HA8Mwzxv~mbCg)U8^zOubBnLYr>aCsX z@isNgz3l-9l;9uoX!wX%!zYS?#(;VW2#%mP%fi?vmatJ#a zw>=Qnt`99z=tn#n{m3%)`Gi-0KCwc5K2xkepJ`B^Ys&c~er4Ak5U*BW57a2|P*{OS z7Af$=G6kMl0Z86Y!7h?!Dz(dUSk6Lf2o(jipbgTTDPrB6McVelj>fl=w)1%jMn4wG zJ`qg@zmJu?g}teYCe%B0krJ;(4`zth#V<9J(mnOyQsp5;Yz_vNlr3Vv`mq+V(Fdl2 zwE{lN`cSoYexwFS zNqY$=5Ur5yTTg^R1*2ODqeT;~acKC=A`RhpneTnWRW ziPm@(_Ms|`^;pz%yY@vq*vZWeUVs01}rm&JxL5znU*I`T-3f@#;n? z&S#{g&Da_ViIWve8WMjb<95##I#cEXar#q=qHN6t|76lUS3WpejsLTD3MwI%{b=YI z4)$U#bv$?kFXNiKt1B~hD>bt5>ePps?UO;BZkxgQ6D{OSRJ^xEy`xH0gH$3*8sr|v z2b6WaP*JnI#;xLez(F#lhNOss(Mbg~>}HL6#H-;G#mZ?0vj-1sA^@nLDc4SGs({jR z6~)IAMYY1jTQotuMtwferavDDtIvmI#gjz3NPRvc3&r_-WSRPWLN?p*`NRtK`3z?n zt2i6f=bCaprLigyuU6oJ8U?Nk8v|}yWDK}RX%~U4U1ohO$mGa)Fvop)lz)mN{v|Uop3LNjG&3|)Qc6Ti zeQSo;-kBjjVv(BEmJpyR@Y*dyST(L$rV~F{m5#?QykOV7Vgw1dMpc{Q0Aqk%^NJC0 z?T!P?$Q{fM4EXP21EYQu)s>impjB=8?H?))HyGWXNNwn`>bJo=)QM6=L*MCA^<#Ui z`n$J=iLCZ`yN)w!|74k`R1UW5f1LvsB}UP-)zN~8aO?(|{2+R))P6_RN9KAQwI?Pn zic5#lU6&x;x3G~dQC63c9a>2+{){kH;)&0MUp^G&GQLJIyC1Q5B#1DIZehz*;!k!- z-?m6fdXF@+2b#JmX=!3}&`);eTjLMXj&|}+BVsBym5lJyd_@OFwr663wpc`qh zN+}2;oqC6cCdYJ&J{_rt4wiO(3sL&ishF15=#TEp*t?|5=W+zrmF`ud@R+5qOlNZY&Ep%C ze&M?+IBCCc{D^qyGN-aY`lBzwliYzQcSJDuq5~}SRFpQ(@g>7}uZ(W2h<%lPP3&@7jB_)?q(UR#UZgm`wJ5 z%vsxCj#B2V$TrjX{c*B7BV(fTNaKe^ZzcZAFIpj``ol88nkaiG1_j|l!2s|5L z#%teeUewW05bnUa$mdLW*QK{W5#;_fO z8Ttp#ephFNzRS9usu05(YtP}|NfVZ->kt{8_8gkAV>y*wZ8z-4JwqQd3F`?O{?~)f zS7Z|p=1q^PS2I4e^NvSB6PVhQ=;^Qr8qZ32Y&W#VXDayY)?cK{dy=@b-OwIK8!spi z$=l&O(yy)x%Gi&Cah&TG8=RHVzsVH@ax2Z+-oZX^Rn)yd`yXmKww8mIKbha|nO@E3 z9VrYR`A4}?*PFLMlUnygxCE+Ba_?N8!teDj--VIzW!5Of%U*+L#jeZFSj>9?*co$( ztia*i@K*N6`mX9&Qx?D3GbRw-Nwc5IB)_y6-2&q3kUN?IN4fq<8iAmT8CuH9vOF17 zF{mvM#=pyPue&Nrwwdv|!U@@eo>pj9MiW5>l2z*4Us&Xez9Vv|WM>dl;9yy@^?r~h z=8Y!&QE+n#3w+U+D%eqXip0cWQJ=1o7inpnARa0#susaAmx#%?ej68nJ80;3n>Og2N*Ix+-U|be%8q z4$E}7B&GDPweS0A?9RPrZ0YBcd|&A<jvLZpEE0#-R z>m>OOG-z%i%&IyVH4g?#8*6t-c1iip%IHfz#w>Dzzwo6mnn>r9RDDn}pv_m>KrS2n z(YAofrOF^SRPH=ZH6v5x?v8@caBc!Hqkr>9_man8yq^`G8FQ&@sLaJebya?rOZlV0 zx+ULer)sIkG8ET zGDKNsC||X%vfEpXX57GiODzY@`Mr@Yy>vSsW5!NeKoN5vuo7fXGiFU}nJ+?Ph@5wq z7*@|aj`<)S1aWQh_e8ts+Rhy!rq1|n?lBlpB0J!rJK@;_>V_^YN_U#kwI&DmizqW{ zw?yXVxoXcfW23RK?Gs$3qxNL(m~I+MW`*CIjg{|5uVI2mM6QxUiAyNZj(}?)eL-e} z5*J7jG|4NzUTpnyy1%w8;8uEimF$U?=gIj`iyISHsE~4*dGdK;SGbKM%Vqv6;Eh)C zZhCpYf_&zQaDwsnxuEkA0jqHN2?0G>%kU$EVuY&*oE^ z{NXdY49MnF0et>YEdIukiv;lblLqyutZ0a-#9L_0D3wITfgb8sYj>8E{+7flT!dl~%1LZY9#XBo5T}Zgo!obEhuhGX zJxh}>=6K@RRc`jWpAv|V@RYpYdX&YdYM!dWQ~H-sPO!9idRt}vPNPniK%!k%M!!nP z;a%q1f3VGOPT}C7^UGk2<3eNC!bn?Gf)cVcIW~;C4lN|kzDC_^2v#-z6N?4+BozB& zcW}8bt3Uf7jLi5jE(34%MAl+;YbfxS?ldMO-foS5l-@zJD^{jKr5fEf>MK$`AX@6Hcc2eiRs zI=3X+SyhC|kDfrn^vOb$cs2vo8c#4s`+vxQ^@?&K5#miF_j3ol`aZpG!6AL^g4cuK^ zdqVO)%F#|ALe%49RZ-mnkZk6{Bi%&b>D#%Wq@uKW#<%>@x9G?QVj3n9Z1|@(u+>j2 z^u}+O;>88Nw{kF@ydE8Sf7nk`eK&ZeOA)1Y%#;*opWnLG~`4wM@)yh^f* zDVQv6V9znPakB*;I66A9$a8sgPEkQcbey}gbocaAVB7#a{5Gb5*cdp|sJ{_bD|tT| z0m%6m@HcLnZCTK%9p$h})$jPJkF%){E>&b0^eL1wMORF}WJW6->fULYNFEGF2zFQE zT4uO{$oj#_k$4lHWJu6g$J&W9ppQ~WoNarM?y^|LBVE5c!Oaqc#G9I$uJZU|v)#Vv zeg-9PCjDG_RU^3%4b*%SZB+I7Vu2YSG&2w%)JC0NPL+;Kn6a5&Gdg`ARr@yvBA$w< zsj9UiR>{0}6;^)ka;fc?{0FQ~d8qnaYt+5UXLMbB7`1uv^dv(JKX-pzMT|iz$U9(@ zq_MyspOA{a2$G&t(%ERnXFCIX-e|^{c1y;@7i8gUR$A1&P>Q zvR<(gCElOKvQF0NzelhvkDL(gT71aFT&2@*(t|tYkiZ_?Jd;q@P5`N z$oy)DO5Z_m#;@xummwa(tyD+(zV7BvSqR`fi)} zm=vsRM&A|A92uK-T7mBpTGotX6}!decne|`ZsqNsl%y@X3!OVpB1k-k$pi&9FS;9& z3QP&Had|`IW$I4wPob68J^uoceo(n(S!3PL$a@J3y&6InN86R8RXxF=nbNI9D`*0=rk9=g!?Q0q#BNMt6_ASH!d4+-K zh=R7D@%z%+&!kj;g<(o6@Tb5)bce++KH4muuu(S(>88>ttCaVk4%ijfPt~Wcw}R3} z&JPzgA~uq4w{M{IwHY6kwi%I^z)2U?Z!Ws~+riSCJx2ZS72P#n*qIAhBAe;O*?S?Y zjdbBx;@cZ!7Y;Ux=^q;kxR3;_)@5=-Y zt(K8YM~B-nzJ;VRdNA<}RO*nZO0<|F=`&*AEl9ku3v4ek+R=U^DZ`lm6xH5Q+oC4P z4S;nb@S}fKl2}y-eHe^NeeWL1@ne@!(ZTaPzDurh*S^=W4!*R(h{%2v`b%HDCRH(D zE#h5R=X~8R!gkPhhdaJYy?)t8cRvret9%>x+ifLlBQCIMHf&a!Zf#nNWLy2IJ&kH(GYF)=?ujO zIYXyXe-i2GKJJ2V;a^hMHMQA+>w5oH2BnLjf6ZborR zSx&o5*m>HeczUb$@1&G6t{5=dO#@AuO(IuqLb`LXdn><(|nS7}y@`-as3)NGJM(Z1}-yeNNl+3Gj`c&GOWJXKE z8%d*v8A-Hpo!B-%otMu!_mE`$2|h{pAEN#qOk98nOD?0IqTHkg?Q$C)@8_m0C|z4` zlaBo&t16{s&`-U@$O8>CcKyC!bb{Ak`flxe714F{>jPe`DBU28ME%FhoLWbKi?ohV z#(t_ndkJnXOmG+NXD)ieUHIQr`(~O825d-vL3JX*OWsf1hbT-wMJhYm1MLTgnd0zc z94OHN(Z=UIB3UGn6pEJPL^JUiwgw&9zn@4WlSJ+a#AkV=4lt~XB+T!mlT!9UgdF;) zPt1%>6`jRZe|B3{6v!C!oZ8PsIoOQ>wnM0Kw$>p6?M*0>Whe_ihCO){se`yL5UWSO zE3r4eBZ*cim+~S#V^wa`hH*l0{r_n}yoM1^k zO9%VhIPzOm%TTIixR;!~pDcl#hMN37s-3{`q$YZ&;@zYyrlt=Ic{EUT(L zNy>*4$}kC*r`HgoT!bk<$GT(y>vTu_oyMnw{jm$xg55zP>g9m8gJ=>JT;WEIC=&r1 z7Ow>oQc}*{TqLT)1X6_ps%Bo%r>`UslWtH(-FCicE|C zEMv_^P+}hw-52F9Smm%@MI#V)ytFs*S$_LjqkoP#hA9%II=?Y@#x$k<0LxsnD;|d9Ck92FWGGehP0L z^;daG|3V`&6I^Aqe_>LtmRv^7~3ahAA6Wi*)N&E!JO*i_dr#i)Hl1y>Cs~`=YFZ`y+Hy zxI+^&rN4BaQTL?~g{k4}WSa_!^oVhOhN^Z+NXecTMCncZoPC68-b*swC6&z5F6r>B ztszMRY)5%urw)CTyt!6ZRw#S9uq(Nz)OkVC7@VPHAlK5N>s0S;p7OXXNVpCk8Uq*p z1p~vBGkWjire+^G^(G~kR(neOPpb79ivTJaD0m!YA{{R+Dp?4_ctQH)(* zU>wCt0q}%$ZEQaD1FsswUcmJh?@+)zGw=HzJe9pkaBi#1F+1 zkIX_2hzM)-Sm~m0?!xCT1bG%~amwXBZ)~NclTEp_-zsjXttqqf0znLmRWJn3>IX_&&wF`8{_{VjY!;O^F6CeI;V zo$61g55Z_6l5K%5DK!z(BBDpp)ta!d646Fo9d&4lK$&!$Ig6Cv5jt`bR8G6EEfUE0A9eX7 za+5W%yQY^|q(Uy54)=bOe;K(Kdg(9hc~?Dir=}Y1dJ?H0Kp-Q>TC8ll_%!Cp)PhL< z0l*-tYU~`k6=YAnY++5x5x6@3V0;w2^w(CFay9C`ivQmTM!DMHb5wn~akY$|WR?bm9gQT%IF&vjC56$1THlpM$v7oe{efO$6~n)9T`9+mGOB1 z;TVWcdqLe z>q+ueC37SgRy?gu=SN6BmJE|wu@o_~KVF5IkwEF~Na7VF(Mn$OThOTXMnN%Gq6(_6 zg($ja&c9%6p`pAVWqGG+K_&aE5U>(h5m_QzRo3>c5>r{Bq?;|a#3Xg@PWD#sVO0;D zEpVYN!gqDxDYER)_T-w|EO{V?qt((EBws=QNCsrO~#88kvW$Md=V|RysZYvw18OFRbNaJ&aDg7-Rdmf+CfqKmPW~1(V z;3ah^Oa)dFBhq0C_jVq}3wNH4U$%hiYZtMntuHERuzLh`h(MU!No4T#&lZV~Px+R@ z!c!5!T6{VhK2-%+w?Xk~=X{F3LXjaUZf;STRauon&1p4D9tN2wqN!qhJ(;XTVg`bO<=Q%`Cq-^=9VFNKc}OtbhH9&;xjgoUmCk5GJS zWn1zd;p?}cOTH%mHt;V)=1PAf6MDU3y;)Z6TxT&jd@|ns93_jIemNfuvF>6*N(_Q1 zOvAnVdW%atbBC|qj2<;pIt;xa_73cPW^9l;41)@k$qtxh$a=`k0sp)hnXmYk^mStTVsj~V^)g<{f?I1K_u1E?I0$tk?+@|E;|%`_iKr};QZ^NZWkX>MYG z(M_5?cA6iA)GAkrPo+2OJL&Wizm>+jK-y{i5%2o^>M^SSrjW0foQ1B|1^1)xkccN3 z9mre)=nGT>1 z4ri71`OTZcV%PfDA?oMoHwJaf{2{k2uH{}ri$r$6Q zTSoUOwhm-#GO@oU5hhp}1T9gmU*Bb-#tXlPCVVG{yVT!_ zld-Y_8{oL?vJ;K^);_QiLm$(LWpr#|mDYqRTqtjS!*^y~qU|y=0;-Y=4w;xMtY!!$ zT67J8!n2`5j-{w84D*rAPi_>cP8`d4TxMCQ2F&TRv&~86N?-b5J9xz11c|3)4?0)k zmd(N|GE_ufO;C|Uhhad;mE~0g$U)c{(q|Dx@ZPr_4z%wt%-E3Uc{O9lGzq{$w3U`; z)XD5#FlPAntaa8liK~tPWH0!JN&_erD-ROc9vN zPq6CGj4_E&qb$B=>3bX{!s>|9hT4Q!g4y$G#%8mS$`!!ER)2%EWmyr$CLdk{oJLM( zP(M=Mqib91m0V={;v11fBFAuTIYoM&H8J6O3Q8ue5Rc zmOu=Zy>BpfBi)Xqm_2VuEwgl;agS81oP5Z8ta}{}YNzCgck6 z=&C&{DVJq;jOSnuu@WD%k2DG|?x6RU5e7C7#*BMvx7f^^9Dy8_!-H}#RIqp?K9ZB4 z-~jPe$JKuD&+Czoai0sFIq67QDCGP^2Icq2B0dwtWH3HxZGiPs8XdW;H;G2*o|}|2 zL#+B6Icbg)naOmfbC+5ArE!l(sF=6Fk{;;=9oDL1^UY6!@$7fVn1hve&cLKgHT8_O zEU{>PTj|kz8tJzGKyDaGG1sPt%JsQqJ|Jsz=(*@IurHLpZbW1Ni!CwVd^5o^l_qRf z6>ir0Og|x*RpF7B3l&e5zCQiEBsFcIq4V!VWUKSvAie!ekTI5f$?4&f!Xpn^tSc%-N7}OkQmX#}MC?^qJ zFn+7Evh<4?W2F!WkB<8v-?esUqzS3xy-eQS5-%JPHy(}as6 zAG>63gq@17DjvE>TE|JPn-n2b&K=wXH_N8h!2av*A zbBO0`K9O>y{a`W~vgLZJ)2P3MYD^C0U!^F1m9gm*Eg{_jMwatWmg0hwcqHrMJe26^J>W+tprOY z7bOPcD4wcil~@5Q3ynyH60#p8uLNp2yQg)9%8%4Gfnyvfs|qEPr%#funS4swQB2&u z4y#p0KQvatY!Y6Oja^cm!5CbnnDHTmnQC>y3rrBHCU<-=`W<)jehO0>4r}u!78WIE zKq<9^8oxbp1uB9ZszqHHbyd=crS$yKPu6bDJtKt}>cdV&FoC;C0Vfj9tD?A-gF+*T zc6vyl9DX;mTJ#|4PmQ`03CV0KM|5eoYjBh0DfG2&8&6>qa3y?Q@EC?y8IuNBX)5a{ zk3!JXuBenvK{<|ztKO8|P01Z)w|beOyBRwg>e{4)L&r0Y=CC`!n5zCSq%qQxt;%IH zDPX7bpD_B>HE=zV6FG{wO* zVukLm0xUq_OZQok4bBDnO{A0bV#3?rr`U zj}FpDJDZiJazG-aX1GskSA!X?Lf3O74MB=xs1r9{C2p;dO-O#i22(e>>qH379s8cXsfsVH%sX%Xv;O>FuRqTiFGY6DmOi2Y z>0T(+5J|B=fv&dp8;rMRD?Z2=O;B%Yzo)o$LYE_Tn<}I06O~ts;8o+v=P~)nrkq@n z?H^4Lq1$Mz#F!ppF>?GCCpy<9#>!2Kag4fMh+Jd-JN(CnRsR`KyxpsUYeW(Xv&)!G z&U8ewx8_JVCPOnMYOS**g-Du8h-Y-1Q`ytmZl-guvGQc;dM5S9>G$b%ma2_k^^?O1)X0>ODZH~BB!bo_mMo#iYa8cvk8g&Qh*ick#*AF#QlXu ztTomqFFuORXm+7s2dmD#L;o+?L#Ccpy`mDr2V}izVLNpS~ zagODc4##qp`*rYU>qX?n7oN)kF~)x+D(GZ9_7W}6g2On(pY zVAX%_mJ){&jS(y0NAe}oSB$RP6Kg1elDE{dQ2BgP)ff9*wf%!O~j6kT_#A=!{8@DT=U6 zaQV&X8;K`L*0t9$R`E^bL^6?kBja<<=$&{Myz;w6@*fWt^)~nZT|AzeWaTxZWI{u| zT}>DW>P@60Ba{s3Xx&d18Pb0odDSORFn2RZSW%#Kr*Y3Z-p$BP4sP(bN`V-4KLKO6?se>4BbbKTPZM(Pg=iI?R?gHVrx$!r zSSUW^1PT2e$x!^H^LUq5x`)qz()(okwLzi)+e|*J8C?9|dFu1h%`ndJCdNzgTZwy< zat%g{Vng1%O1w2DUM8^Q)i9^%p~5t3?{t*pCdDkQ^|8@x<|5DvFA)VwKMfi~+eXF* zeIYR&fZz^!gW1@BiZC{$GALG1?_XAU_Hs z+lKN^q2zS^w(;VYos-k+U%7XsxBZFVoL;?p|5yG0tMI$VXU8jX_v+o7{v&)@Dm9lL zGc3L4W&FJSuID?&?-bt4fqa*jJ6h(B;G3rnKl1FFYkkjEpQ$a@cgtlOzoJaT;$a$k z9GdPIbM$wMg>Sn=f45oRj`A!JD9H|iGRzZb|73_M??+frZYGd#IXGP4i55K4f=65M zC<`*CQgp{#P&zU3cd`Xf0m?z*@{nFDPayRZwqFG5{Pav?xu@FGp{R1s41)Kzw5v`3 zRT`s6!%`OV*zV7FcGr%M)Lh%$f?dA5gz9L&%<^b~`?`GJR^Hj&nT}tFAMcuqaOe0t za;?KjU3S^T_LOLr$7lS022Q@kP2T^^Pu??Wy1s8`_-*527VXi%EPnDW@3wm;6ueDm z48SV690dN#tzqh7rewR{%yCeP~7DAyNvK{{{n}R zzVrE=#jmSh`};)PY(Ij_e7C>5;%vWP7fSxmBt4?J+rJa|uI4wEU%ln_E`uY-9LK@b z9SG35{G~*0B8yhFr}jP9o|>?dy#7(c1n>5@``PxC`~)ZNZeSt5r!D9N_WQH`2~NJ> zex~F1G=3KozWhE1pN+e~It$A8!`4r5>Kpfmt>1=usnm-3sZ;?!lb?LAWY0~aGeB%C}_(_@{B>ans`%k2A)(=yuhxo1L_ddV#9>m(e zgf#JMq zY2Hit&ylVHKV=f+YyA3mq1pU1?eA$||E1K?&r_+t1lrZ_>0jyhjlWE#p60idUmm}M z%TlQw{DfA1b$@ljOH}f|ed@(<-`(Mcb=Ga$y7{Nvi^RcADO}XoDuD*<-mo&2DHK-0X-}R zSpuvE{v5ao_-o)`jPH*Fm-cr!o&>H3J`Hr7g#91rMv-~}SOZ)S+zfmfIQbNZ<8@#Q za1*e~>u_uZE(dN07GUw(0bBt53|M@c!?6dr3HT+j>~s=L-j)G#fOVL3bAkQO;tmwx z0^l*gfkh6-3BWM$6ks#35IA^%!!ZE38h9RX{n-x3`M|N~I2?n43xJmbR{;IMqJdaJ zfRllj12+T500*Dza7+ZYo#Ak71ol7E;ph+VZ6@3xa3YH6Qu?L!l;>*TX5ePv=fHs& z#PTTLIY8Nxu@P7XJO~^M>`#9(4LA^32Mhw20H*<$1D68d2CfDsfSZ8_fd_$w)K7o< zrNO{5U=TPB7y>Q?E&whEE(P{ye&Han4CtXgCjtwB3xPr4a^Pg(X5bv)4&Y*79+M-> zf&GE&fn~tWz@@;?fdiRl%F88QU?Ffhunc$*I2IUWl5U#dfpzjdne+hjzD?gJ@D}7K zaO17*scrJj0`7yr{7^13m*8k;BRkF z?EuaJ<}rZie+TIVt_GF?r`<_7@_iTK0Q16x0}KLJ1Iz9v9N<7U%{PBc^vJ{)F-Y2ARHl8@PHE z>0%($AG7qy- zbPzZYxUq%w2;5420~cZwTnOx+z@NZY(k+m6DP{dg*G~8WIB^$z0L=T0`WHOr)BeYR z=WvA}aM~Wy0}Sq?eu4e>!!H6IsZ=%tuz|f(sfob6eB>c;H5+6$3p|lN>NwJW8t%a0 z+4M`m{w4HDz=IbdI~lO`zbutn4%|^ecnsuLj{*-|dIf!zd^45zFmU4-`exvqtMCup zjG?tZC*JOuNFNT&yFQhA8@Ra|JOjs#ljzrgtEbS%0fXNme&EEJ^oPK_yXXUvcT0g| zfy;r@fCJ|uhk={J$Z%l)yHlx6f(PyZ&H?sAJ}v|n0ha^IfUAL3z>UDkz|Fwfz`T3t zPl5e`4+94RR{;-3h#z^Hhu*XZI1t_7AaL4J@{L^0qrY(=PxBr}<^vc07Tpe5wj4bU zxmxyQD)ks};8WyVzJUqg=0A`hi7ugDVLa7+giafxMjq3<0P8DV16eTnO9* z3_eG{gdEPJ->Lylq>owxTnJna9QXqHMoumS4g&UXAUxo}wW-ts;Iu~iR^;c>CdyCb z=o{1*a3in`d0zGx(hFP-+yR{UHuY0P`hZIXz5^b(a8oKZVF2O1pGtiW+yN{&8+YJ9 z;K0qq16&H63S17H1KbE)0-W}D_zyS-*e32k_c{2ZKMesF0+#|e(}%7HF5O8w2I3z$ z8<_Vg^#oiF+y^XcBR%Ij95fB}IB&nw;hgPpo|xC$E%)G2G_(bWV_6yfU=}a=p5gfg zBaPfy?y%#s6D~RX{6b1!`}YGkTt>et2C-Q4%A&J+*2@rgU-zM;|d-@UlLVhja zN2IZu{$aigt~eCG8hlkZ__x6?055s49|?a4zh&TI!}KF~*&Xs2_?$FW-&xilywFQI zIIha~AnqK+;Fg0!6gV7LXq+G1Dsblsjz{PwfLniQd#aZpe41_=xCZDDS9I7KCx(-a z;1HQGtHvz`w+S5LL_HFo92k9jm+)4DYwi+WGq}xN!rKNe0gmR74sRd0;x6II4%;^9 zq)Z&6by~dO4uV58vy3v|GZ-)kocqf5)Qv#L@O;mGu2J~~_ht>x_r|l${Gx?9BlC+R zy(ad}FB+Qf9hzS-G~Y8c-#skX3waHGo*!bBwU8~$iD%!NB}dArNAkLukAktC%jq$2 zh2S#FNzURg1_!&R{WgP}MEG{OOJ25t8w{OYoq|meY^Whb;PUrLj8vh2WPF{tvp~9|m7Au03^e7yN4Q<>1S^ z;NJ#675u0!_#F~H_{uK$>}>cA{Mat|{@}gi=^rU4`;qhy0{_^A_SBj#cv&l1b!~g9 zop|g==qH08jQ;gZ7y3EiThOyE%xHK*zZkq5eJiI6emQst+ZRv^(~r=v2fy!@_Ec^f ztIKmU_%`r}+w>#!pG)|J<4a>TeID_ZgFn9uz7YIW@E3K#mw{gdetH-DSn&I7`ic3T ztMc87f2M)=qBlw#a)6%zUSvxhcoX~(!JGM>EBofVhv(9q#^k#ty$|C)75ATryY&cu zHTbqJ_(9;`20#0|?WxCUgnyN3v6Pb(2oVb0Q~kY_-R6qzT6dG2YzsfRofZ#g}g5XUjzPH zNe|@`ak&sGZh!9e{152b9*EO==pL(O9Af2np=)G*!94QojbyvVlS$;S5pKqc@x zRd*w(S^Y@*CPVia`u(?ru0qJZ3spt#<@_I+d$aN(HN%HW-(u)Dq5t2f!o5){5GlGa z%a`w+mrY_KIWzOUKD#1EtZUr(+El^L%U+ljx7wK0gN*CEbJ}I;X8IY+n~d`kr7WF6 z-*;Rs^4l-+ySw~nwdWjK0KVL;aqfJ##QXXiR>%@xkW4o)PPx;<2?+9=48DkTjHldn ze=#B*-v}x1;py@=^J}tB%j%mym@4Q}>?2f#OL$Kc-eATx2Res0qI3OKNd1}lVQ1Ez z|^y=2dM?@0OG~%gQ%90w1 z_+Q6!ZTIm6aRXJZ}@v>_;i@zF#Yz{yl}3HKTi%VZ z)f=$)2)*?PUhD`7@bhdWP~sQ6LiNk^?{@q*68~_Jmit0po7p|_P1}76?nAY^gu9&A zwpS08pXFlfIku7FjS9kuFkP7)tBb@TL8Lx|s z8kJw1brc@a3tg282$`dd-(u(rupfLO2&*2vu3;2^g{0p#RKNLDjf|8elc>wJ$QA!$ zN7;n`vm{?>|D*6VT*})uTEC6ToyAL~u)ql2Nkcx6Uq9?78#XbXk#tJA{fPA8?>B&v zxvscM?YDJ&LhpzEG3*<^*5z34s)2%Xyj2leU$=LpPwY2Eo6#rI=`-`aE>owfBG)xG zozfA9Bv9geTKr=#NvF@Mw_g(<-gRoN^hvpIgWiojrH`GT2O;$3x}F7C-KeUcJo@Az^eEbRm@~tyWKs*xOAm|>$9uQa*m@{8Aef&uty~6gyMZKiX4!L{1CclZs+C z|0m?S?gv!n$_MqQ99+KxjDsn8#`0T=f6piFslL*IrQ74flnHz=T-Dxa)!<{GMoHu| zw3`ahGg4K8pm-A*ERXQV=Y+eVtv&UQoi3eEskN$H7lDz8>HJ3r|J}YFVdp{UZgX+Q%GTu2T(-iua6H zJcTS3$BJCx(ttQ-^>OhUl=dt65;+lM@V4lvRO&@Z7xgvIg_06M539;LftD_6m``dc zYeL5tQhr0`m!xko;S?O7O1)`?6G7e-izX)ZK1#|-;`OVllJ;xnQgz}sObX0nDxRRu zq~3MvG(YVi+-0YvQb%>l_v6}cpu2EuwVPsu?)gd5>t|3p^~_Z2wT$$7T@Pu$iZ66I z32W)}&d_d}PrFTTkZ_k0Zp%Q*6MbIuKXM}LN{UNME!Ta#Q4$ro({y+v?EH_+ouV;H z$HBLfYdZ+9`kRz%Iy_Y5tba;)6dnSry~9&~{Pbtg?-`s*9k1m=kPkzW9gJmB3nfj8G17LqvI$Vmx1!K&6{MF%a>NVI=rCoL@my!7`&a7A`e~MG299HA6<%U%1Uf#95VO$Zx z4plCioh5EmELvaP0e$(6sYC31+1ULDgP$Ye@L;xg6o7je+^?jrq?sHeW#-Ej`_v%Z za?Y`BSYBoCGr<*3#wLurJVM8TcaE)Rci5r?R|BpLx;!D`5nLU(!Qkxiui%z|Ti(U* za&XmM{5F7_2u|9oJmU9la8tX`CBRMYf;-qHJc`HR!LTXeoi2gVXJ12GmoSUKhr0ML z16PxQ6PZ;7ZW_3kq}(w49WrzkQH~rbqP3=nu*ejK^qo*5x1}DI5#DbH*kxUu9^{(1 zt;VhSCgcy}9mhE7Z-yPwYv_Gh758RW#B%}*d-)l_-_sY1c13Phfg7lYtiHTno=ER` zu7$ngIrnDYmnCKA#yB{_j;D*e#8rfQ=)0-Z35Sd82Uc9(!^agOtd=4xPhH}gOk9g` zFPg=?zVs)Q;eD>EZpNb{WPB@q>r+|Xj*`gpXYlM`2G?Ci($XX(&oWU^5U|YRPs@c+v+H5Hmq>(lQO(FE0E5|2(f+mr7lNGJ_bvb9cuhY z=VJxoHJxe4)j1y$Uo-9pf52HEo#X4U?R1H+TH>Qkc3}}UDwTTjGWR&Khxm$cFUNf$ z>!o(_u4OQD0gL8k& zeLMLTdFI7`4gQZkOgbfw#n9}#pLtiQL%U1@I*x_CBE5r>$Qz_Mu*BtdE)I#~ZTvr5 z*mGG(UF?Hq+u~H}RNm#0y3CUbXT4aPAnbTm`Dk%U=3K84s~6|N&`!vRBeSfg> zm8b6PsmdNXGJn~TS;ItD1@en4I@^^ia?#(qY5Ph8l`>pTJRa80T`6&6w~b)XStoiJ zqbjMtjks6gK409?BIk*oE&7mGEG(8jgrZl7^FC;whE^^y*0iKKl0`kZ{%Mm+U*u)7 zYw6EYsY|5Ms=O)r)gezhY~_Azt*ETs=yh}U_UFdR_ zZtS{3r3e$g=F_L4eGJ-RLaXx0{DzD(z28k|QwnVm&MnaPUk0Cd&L`HVNze{cBT`1_ z3@)wmLAx~ZSHL83zh9D*D8N?B1|Y)99q`p%~bZ@bs-1Mr&RE@^)o_XWf^Nn~o5 zw0D``5PSBv?z>KZuJD}Ki_A>NRg>9mI?8b`$9>`NQmNZ{&#(=uxtH5@p;B)ZGOUsU z=6w{_wgSI;#Y-8#Ixq5@Fn-3ngmJI%?IC)Gj1{FhXKE6-E`EK(sep`P0dFk6SGEjuOj$VeKix-wv+9$sP0r> z)x_0?yR5BwCoQX#Ue(c_;YDliOXB)|_s!Oq%e30yVTY&gX)48dKG{_hug!^F1 zVj%B~F+7uK*j`>2g0J~KI+);TClO=-q6^y|QVx{J4C7B%g9DX!~r#*IuY6%PqOR68*$-B;*@nt3kY9^67ooyEC5#r?hHYURW>#yhh#13 zQt)%6qoQrxr}f8<`s$Fs46$9V{oTr;p{ibX5Y}J&+v!=+Ne7m6<k^|Uq_ytofsZ>Dcfz(n5^fL`V)S;p4yar90dRO6YcWwC>_W{1q7ij7upV6 zgpy}BXUMHVIF~_N4Xu(Zs?X}M=gige4!hcj-0v${z$<=)Uqkr2IUO&PD3)vyS-1$d z<%C)1hUk<+!mydHe z9nJ>93ERozvc4r*|9SpX0{lr|!@`F=MD_$q?y0^qyIPBE#fiD6gK!EO+Va<+0sGcF7~_4dwZwJ$0Kvb)*hZ z(PBj`{f;~`e2@oIn|c-% zYiQ#ad_#Xr-yo0MDmYs=u;V+J(&;ZaSJQoEeKVD(9%lJf{)(*dQr5R!4=ndp?sLx3 z^!u%EyPT|G{@1`Ns~)>1;KFi^8E(Px7Mx@jHx^uJ!A1+dYr&5#xW|IM zd=B*-Yr!)uc%cP{TX4Jur&w^N1?O4t#}@pJ1y@?I(Sq+<@M8<^v0$&ER{Rz`(}EXT zaJU7>TX2d6XIgNc1%GV8-&k;^1sg5+t_44~;2sP18fL|B!80v*p#_IqaJ&VlSa6xo zr96%pHtb?=(a_rOOq)FYw%bD9Zx(;ETuZ z`RAQ~Ua^{)(tb7XFB+>;;ML%e|1w%|IpnA#O~1mTwmQCMm(U0g#Xnkw? zix3Pte6OVjAD+I5hOFspz83xQG;B@ZV9^&IHa$a-KbpSHqGxd5?W5CQjiA!>p7EN@ zmULY`iNtg{ZqBpmyLUeu!K}mU>aOGaB<=3C;=8abq4h1=t;=yg0!xQiZPDA7w6Mid zwj}NvjE=7n0k8cpviz5HCF~U6P0ST*`euvXmc;YER($s}cdGrjeXaPud5le8e3eef z=~j9cey#YbkG1_*TmHX!*!;<&cAdUu7QI@PkDnh|@%2>yx;%eON2>jMuF(-4zC4eH zZ8UwAMQ?Q|jtBom{xrW`gxyj5@0u;0o(oR3-NRP+WU$*u(~mmMre9{!gXs3r@%N#* z9WMS4&$8W{t?=@?4Nb48a5?%6u<2VYdSx}}#*a?#TNo*<_^;JOhY$Y%#jEK(7Cnah zZXcb$b?66&%iqtaUhQ6Nh4-y)L$mYuTi8$2FS6)=a9H|@gKT>5L``_{Vd-zVz^1RZ z=*tgFZ(L;4FSF=J9hQFdV4J?pqOUwG{e7i2eff2o(CRi0@ulnY4YH@pXPHHxxmzOB zlcs;6%%(4|)~=bmC=R86>QbBDF-f~-?yfkL{ukwG`fqF3%v~3U(np8d^bOz9u9>?r z4y8ZGZ__)JK8YsXgMiWbeXYW#_k?t4wq~0dw!CZlcZb{bUW=X%t=mV_Z=rj(_}QYT zL3aCS`mBIWUv1G_3fLijG=0NJn|_5wfBqqq8lma8!HzmTZ5BNY+wG(2-LRvT{#s2o zy<2L#e7TzgEGkT{YeK z(e%l`+w?V7_*OL^;zQGK*kaSSS@dcb%^}owdYZS{^i}t2BDG7VNBZ8Mr0E~fL~0jI zkM!P8ZTj-ZHIdrQ(j)!&f7m>V{TOIz2CB+w_jV zYQkk29P(e&pVr%^UuMzIJA_grH2thTHhu9XjaR!*y78mw8@VAw=eOZKO{8|C^hn>2 zt5-C=<9$uEy&GCV>F{sy*z|1{{fmdCfA%PwzG1T_RJ&Qa@uR~(>XDwr=iQFVN1`jMxk=@Z&b z?PBR5OTTIQzq7DGhhLu5PHI<3nyTYP(?45i(^s`>H??b}gDm}~>1&DJs^6X3N$pxm zQ+2#(`lGmx%}NiFLY~8?XU{;JzN$^rt6eJkwbOqc{>L;w9ey>EMIN|{;9taqBE(~G7bG}5LoKU34I-65SQ^@pa9R@(HQb2Yu%1=1t^P2W6D)2m$>J<^{w)uwNmpy~U$x*-Oo(?9EGo8ED)rdPWsy78jv zcXP;wuCL~anqKXa=#l>U+idy-l3(?_S<^eb-E^lFzxkKuo-)}}9~q~uY%BYLEt z&W(3E{Z+&ykJ=T{BYpWjHofC^O>e1vhxpLtvnFEGFS6)+U{AF&%jGz+&Zb{x(KlEI zorf%Y9|M+dpJ`xSj+!rQ|JBdv^sWAeCf{%Qx5v{}SQg~5%p8se>)Rr8*fWcgkM5rS zAYG0-r?{;0>q=9m+}qOb-j8&8ZphY@1FiUu?CR0_*8Zo=u)|-J)c(~D-cH)k9qCs_kdM|U$HPEYSX&^X;W!Uj%vZ;=kYvU9%k-H;$}6{IJ7kbvyo zR|;^tFBK22-ZF*gboMG&UpaT^uMsM+-X>pJBFO5D3&3QR^ICtbWG(AcUKbd@6*^DO ziF`O+*{91_cGigsWPe+b?ks61PIq>x08duE0B;uExJr;v@&!=cg2%-k1PRPN?Y3DC zK2C_@(>RJM2Wwm*@C#)+7Ld7n@uek#Lc!sNo}*)p7WQq-tpuqZ#`Mrtruom zFN|Ansfur<^%hf`eh*)ZlhqeDUXKzji)g4l)J)MFZ%_U8yw7bryu{yhg;DgvKrQF# zrTCBcHGeC%L(VoCK3i{x@J!O##<)hAw`F4lH=*{LN^1J>TfWR3Vun^b7Ua31Wf~{n z4XwCw3e3>rAjuA`lvvz2mk^0({%~k$DN?9%l~(fQAM5*Co~+ckb&mC`YGKhRm20jo z+8yY|QHmBj$*;mejaNKsvge=d*Wm=(o5-T^T0F-)#V=5nDjRKcRmZJQ$t6mAj$cg; z&qWyT2zkpJ{M#Ve;K>`bX1goQm;Y^Wzv`c8@P}0M%i;!4R%+bf%gP&kS=`{uXqt0? z8vOFC2EQV1@W)y7m=@3RR@esbqS4ByX8gxC`1NsvKZo;~c!3+?HKJsLC-1Niqf7jE zg#Ukx@Tb(^cgGE$tkk%{?=ElfyW8cX z+^)94pNt!v(|E5X;L}K}(U5xdBGfvW1Eb_464gSsduseos6Z(t;K`ODI!T3DjPp&Vw_I;nc1hp|q47QPY*x6z2+_-z2W1q^`J*nxv|^I%<+KM~~?P z7Udqeh&^K3R*~LTR-Rk|SOFF@^)I?BWK<&YtTCsD4+hpWyQ{9>EziNx^pYtEF z{W=g){XfI@=T@lyEv%Qas&%kjp#v+iO<88=S ziJZYUg1P8is9)^*ulm|}SB3T9`uNKT`)Y|6>DfSYmOa~hz35^bntq>&!@iNH5;@(n z!+P2Ysi7|AT(#mr=|W-mDPzv9*ht%E&_p_Q3r;OMn?xzyjNPW~l6LKq(_EGJm9Gp< zlx0~iJDabn%1#ellsjlqZXV~z-xQyYqY7nJ+4*xk|J!^ODR9rpy@e6Cl&Y++X50#W z`y5-f?M*q)t4{B)SZ&+pa6ByU+vbc|-FlnWT&}CRysoObgH4aV*{Z8z)%nYGc{eEa z4mLfy7bSauV3c=o{o^S!7~?>|nd?x~qf-yt2p=j!etVYr{d{$PS8-LffDhAqsn@SW zi^frE3?OX3x^f(eF&hweK%N|_71?3E7PdzRd8|~SdAM($`P{Ci+1hc`w(Zy&ajs?l zwS$C|Ym)R{i1*9TJ@l#oSIWD5?Vvt8?K0IHZeD@G$eD74# zjym(z)KlVbFxv`B9cjosH2{`Mm62 zmp@sdmzvJ(Dz2LCZf0g)YC7|IL^vxByVP{%D={vM!(J43=9$@Hy+wr8TyrnYlT%@4 z{x+*KS79dFYG=;fCdY-5*<%)I7ajFr@;j-<&Z6-(G*o3HqEQ#u2(8jO64lMeM8Z4 zYB|sMRq!$3FDvFgfah10BRzak?xBlvt8l2}`Snc^T5GkMkSiLIUu7V%bUPpw({P;j zFMQ_4f6Q|R!JKwpRliVagE{TJ>M`}gA8k`lwPp3pgUYrjFWbtM{ocmi(U#SbKna6- zPrt@hxV~eHz5!mSM-BC99sel`i_#09GATEnT< zIo>*}x##q`QO#!_-|CiM7A9wnDnX zyJ2yqmG1&~q9(#p?|w6D)Z)af8eo!G9ZwQH?Ft`iz5FGLbc>lJTs@WV@rZJZnIu{e zVf#4BZSkDYG&_pd$X5O}m1wvMbAoQCxlb*creei!+ru;+_ixh*FF#tlRMqs9IrY%f zCGigKsq9maTzdX9=0sy3DykRAb2pY>H5Kp^V?iBsx5~+R^A_qQh_7@7@%>RdN#dML zwjjPg8pQWUgZTbv5Z@mS;`^gPVD67%>8=q`%>B{pN!1g(F}F9_=;<6Sd;aSjg!uk< zYAJsu?$Ga|mi0eDV#;5_K}g!af$tn&|L!2y9}3U&Yr^yWTiM|j_;mYsy#gf8e{sA5F=s{%3qw^G{>l>i+KV8h%G6(wcq_+f&OQg}=7{ z6@#*lUkP5f@l{^_jM*Kfmi_VfSFcJ}w*fe+wbHvi|u|DO2!^N(+M)Wtu8I0yOz z@E_z~OPPUw526k7dk}4~zm75o`wj39@k`nMq5cB05A*lq`w&0P_i+DA);-jpPTmo| z9z`1Ihh#s@e}XKf{v~7|Ib%?otRm?4pOO^w$)3k=;FLb`OXX9>q%4dN#BFH#5!NFPR2q!`q$Gg z6=%rg&%=Hs+g~zE_Rv&vQjN~q^lf?JELB8J`I~%Sm+6vfxZ2I3%O=0%c%>bbGrli> zHv48*WG`}|b_fwFK8%q35!|7)RmED_HqVd*@quI;wk}Ww#r5-_`7lB)wZpG*J0$Bb zz8m(dZyrY|;y}^=-8T=8MD=&1`=5#aqZnekE*+_jisd;K>ts+|I%W*rJ*X+?QXJ<4 zu1eC;wMQGl^C%Tt?=@Ph;vUVm)_?bC|J|egcaN5}N6Tr+pbd_yu@B=Z(tdE8?&5Ow zAMb7jdD>z9?nQcLHovh_rh^(ZsZB#=z{glDgPy&DFvZO?d3JrkC-s;>|&xc49X&_nCj-#eqv) zPZK!zsyP3>XU~ors5(5uCjcqKDx#&qS(zw*FLI zZ;HiF)%{G7tv*$!NQEj_shgVSzPM?URmqNwrnxVudM$Cxj_iS;ik|Q;){e|y9hiOD zcQn#hAX3YjW(yf~z6PH5WtFv|Rk5mo@(w?ZOK%lBymyF}>v%B!{Jcd&{^xY~y;Q5u z#kHE$6?gdODt7o6;tsz9b+o;yC)eROQco2-eDXK_!RmL6u&8!apc6Ntd5U&_J8{Dn^$w>c04zFDqdwsz9gkC z$95Gb=xuZ@_Ak52FjjNqPN6woOR9-?Ups|0wO?Pr6gSmOb2r-R*hqC$oaWB4`}NaJ zwRzqhTU5t?_g8O$At;_-1z|b6^MRVK77aH{MK$?SSZU?Wn zj<6SEECA&l_^s;64xD@o&*>a=|7UgJ<5g*s;z~=Zi97J5u(~?%daB|TQyrJt4&18- zwN>H1O7bN`O*ka0SRJWta-((YXJT7kAKT_<7)HOj)vCj!9CxeLoMGg;TdlHkJ^pC073Epz zoLsGN0haDH=}t*pe-Cd_f5{hJ9m&yZ#XkPm*I>OI@ehyrB4Qw);=~axi_Xt-tFj zpH!2nKP}V^w^iVOsJ};8bT^3WPb+M|S`W(WZ@*f}{+E35S0{FZ|F`v5vCdNWt2Qq# z?t=Q#_8_i5tuPvEDBx$(w*S3N z{Z%|jNWKW|8`Ym1L)-r6tba6&BMsw4#kx%qy#!rc>ptk8AIUjyswS_<@o#3eq%MEi zC)7b$>NQx&((|d6vp_h?YcBgi_+7F`d+lUzfWIeujJJ#Ikh^?8V+W<9y(@F_Ts8BV*Fi8!D^{0lA?8tX&Baq&o}aP4kY^sAdh!hBex?44 zR4~14hPN^&Pl~R_UI}vhBP8!xX}v3_l2kp8_jZ8C57x6F)80qbPPTWC!&J`m+>7ka zXGU^8_Aj`W@ki~Q>1O6f%8{m>3*y%Q8oIRBlWgW6{$_cj>VWoMO~ItD^7g)sr46{Y zRRgHKW$z2$T{i74dlr1EY}#A)dGOPLZEv|&W8NrN+}?6^;0{m|-35x%$Vy)KV!ZzjwG(TP(H+#ql%CcH7NY~b8%AB7ls`g$Z>7(`K zc}`b(du!%Cqn4+EZEv~SSLIO)VB1@+ahPM| zirZVRQ!wY4Qf7S1^*rVia+&ch&u>_IQCHmF@{C}X?E_qUOVN$k*MeL<f|ICJsMYuT>D}6my7G{-{jPg>k!OAAU9u|`fVI-I*utSH`n_v-ff(Wb*wz|@DysU zUx{_GJR1Ao$8-I&*iQn_TW(vrUtJCW2H`0Gz2$fk~~Fd z!tsy(QD&+Mo?>}=W9q69c&Akxp%5`Q;W9e#Vw?HZ6Y<&AE=Es39qwrBaL#{RMeMDL*c9JqOR9-4!EFl6W79t{IKBe$o&U}8 zjnJY-+?xjcgH~Hdk5EU6>vFb^da4+oZ?>qT|K00y+vawE`Ng=pDE|`|aQ?WR=nGPvu(VNUi>+Vp1#!s#BFokDTtpv>`bzt z2elEuzv_>Meo%fH;Lo4?rwaUcMgNtBdGuP@cY8uzjAfJW@1KzSYQ&KRgzN1JUO zbx|=s->Hu5OSCHN9saNCDAQo()Hx_Wb^fFC<)fW5qa8*NcOdnpKk7hRt(+}SG=lidvyY-} z?VKYuN7eOx+SlMqs*S7b`*e*rSs0wlmtM4{nyTC=*_&>*HRXEK52~rHosw;%3X6Xr zrh#B<$=Oz`q_+Azw65POH(Vtl~vk!1+Qf-DzD-@SSvALZMmHZq0fSrY{XwUVE^eV z&b0SGHDE7R6+fR<#TuMm&NZsylj17goKPND@kyzwD*CrZoKAtfa5fg7tyFRCtVQ}! zMcj*rH%?Y@@;?luTd9iwg7F^Ltp;7i?t7{6Y^D2FtXtRY_D2!_XprBkDt^^3)^IVs z_yvfU^PsADTq^$cnXRGjTEZu;;&G{}D*AmR&PhSuep&UHKO)QEcObS>f1pw;UVTsg z8-_)uQscKZ#=nxvh%+_4jc(YN&!f>f5@dgI690tsR^!=9J=jTu|&ElI7%)f%&zT;s-j7nz^E9)fj{ zJd>t+mm1F;tQkPRB#JxnF-?+3uE~F6v1@C#$;}fvy0C7$^fo$xEwAXb=EI5=eVy@a zrJ^%!{?AMnIhri|=_&s*!X|ab^*cRP<2e>SP9?1ds?0elb^Og(pU6XH9&J2@R9!CM z6y1+2Q?D1Tcp8xWA0IEPGPiblBT;^mS?tiaH>F6yc}yW z$g4jWi;rS>`A?EZFmKt>;lt%rbHzC#`L9tfVbd#4yxVA2_*Z@d&~VXg5-dH57zny?N<;B{>Oefjnh+O61X{xr;%0Lv-9O*$~P9In$#Zn_XcK_;1h< z6>Hy~8N7zBKhqE&+J<WJ;ziZjjt=8PXG?)CWn!6~TOOk5iE__kCNIQt$^BS&1G?~rcrMZZaDPgkGCXE3*@U$olsCllSQQ(h-Fz+QMLqE!ZwQxT z{_Ps#Uz$tuxV@@|(4bDL{Ra)vW_vC?fbxde2dlR{nT9wK>sSysgpw@9I!_+c5c1rK zbv@wPnVm~YwYCI>M*ES`xU3}e>j2Y zn8N=!<-b7mq{Mgv`8ZX}obxb&e3r^rx!+7vh?FU&qRCL_=n#-sJxwd)`!6 zd<`YNO%;PFS|eB2V2bT z_eJJq_~~Jbat~RQTaB0C+!vX5;_t=nsm>rh+It|%(^jm&x)kWOIIAP`+-I8C;yf7B zTUm0m%Fpp0ip<;jAH{kAR9CK>kI`nbMQ5{Z{*zHojd$>FWbt~C_X?_Pj?*e%4mnlE?ig1>&Xdsx<5S2- zG6rG%2Khn8NQ^c+@77uuX7Qz$z=g9R`{C{n!s9T8LJkJ{)c#I&{9)+df)VPi zPdQV(nlji|HZgyvPxIQC*FjC0;(Zi9_Np8C8!VkZ-TOE+RT+$F=Y109Yn%jQ+CwWF zIz`@+n96Hjup3P20Ig%ZN3`=kwW}S`9vZJU%G+q2ylj1ASjiOS4M4X==F#yw!Q}Sd zxlxgEPi_aTD$2`3a<$doDx_#omQp(v2W9e~{hhiKHQ-*dmVG?5Z3d-%FO?39?0cy? zi*7L}?JKGN?FVI8LUpZ+2W7G<{beuF*)=HFZ80d<#e?z)I^JZE8tuIt56XpDr^v&g zd?g-<7hx`yi^2J7?79VWrCf8o*W$q$a$)#qJk^ymbu5+2$G=O1^SvlP?>oFZQKTKn z+k|l#WC*CS0i&kMrGJN|^XVz+>U@Qb__~M^XMwOK#v72AWbA~I%T?0NGJ0Z+#TW>} zei$nuD`XsuaSP-I8KW@PKpvK%*T%dBc}2z?j6$wm<%0C_7`60fmFF~AHP8;hJqUys zV$6aZ0raUjSg&H~RtKvV1piTkRWDG{VEvTlEUdT(Jua61HI<{!^*d(`*6hovmHtEb zpp~U6^P`5M3VYDvihIy1|6mVVlD!A5sy%46LJii9b`P4UN`Ki;bpO0rUUhSTAMZY^ zv-{i_HI!#Dz3v>4#}1%{iuyyM(e1s@;!%4mORfdc=nlB5oW=@kF`t%;f&6(qYCp#O zNG=BQrr7lz=2vpf@xF)$a+TH`I)G@pw>kE7!fF9(Xn@{tx7+^+MgEUbPR+)2ml-Uc z3i4}X;kg)c zo(v7o>mkczXqbKh`AEi6j5=-ENDy9uu`{F-NZ)`_`)-wGi$cBp=TO`StK4H4b09N- zJ_V}jIu&-pYbmnTajgaaUgNs%|Fjb>F#F&#Hdw=Y7L#6H#bJGdQBQluf7k5_b^GA# zVV%EChPB-Xn;mcYf$?9t4=#|d%&;!3Fsv(89M+Zp!LTmM9@fPg)>Ue0SeK;CLEE%M zRrF_#7w)MU!b8~EtQ{hGo)TSHXGzzg)tn` zTZaDc`|FVBK{b_6->0v~^|h9-@GZW!Z_hD52zBUt3}liF9Rjb0+$uwd!T}diK9iwC z;)5^_0OpW*BIFPmIwZaZa)k^X8b1WNS4KySk0I}a^lli{_vNc4Va>5|O|A=61?Jee z1!Q}mPmKyAOnYW3=)=$)=%&WN%%N_IneM}|jTZlVjkB8n(>SXYAM73}K@7C#m{$`6 z?ZmhOM%aP2kmJz*4z#VGfo^4>ncq;R?LbRZrN3+?{jP+A-E=(C(!6e-m(EvZ7n+U- z+#z(oy+AeI1eu#Mlj570>p*p_Jj;gk`eFF_sr>X4km62sZs9?UQIOs;?!x#D@(ZY; z@~Ic}^`^dbL>qp|S6!yK6bOI8m50$D9XBkg6#S{eIbd<*#kqz=NUIfAdT+60}2Hs`^O#vs&LXirFYpiiv|V^GJy zTiwfP!N1q&tNlNXzB=*fy8-DMeFHcuNLSd+CI2t`jNSf+(YI|LI{&l({V?2S*MD-= zu$kRl=D)S+Xwb|2w>DY1Ui`nDwEIeBuK%R6uK#5ITif;&sibn1vpY<(D*a{GQtd@k zbn(JW^y!LJ#ILt!hDXejY@LxT7F6tm0!PzwtD^Ic-Ec`vaVy?hkBci7Hq1Ul?&js?OB9rmzORE&`s|M3R(pyn!E zsZ3nhwby?C*?UFi^5kFf=E-!c61Movz&whQRO2t}fbxJ|ab{3UlpiFQw)f5oijPOH zKQJh5=bdeh19<5E9Lo>%a&lF|`*mDgaUWB0jR)bVKanWPKB`o6sfXR&dm6~-6E zo5V-4I44uSFs`)1xY7#am-iONFYhh%)2!_k#xL*1+8e9sG`@I`w!)g%_*R`$lk0id z^`7M`Dyz%ZN8eRNThy&KZ5rLqgRD`M&$Gy>s2=`wv<81p^fI2@=vlrauj;N;m4dX^ zV`R}JoPIO`Ul9GRj)J^G*eLp;hJw6Jz;sl*x`I4Sz})B+zVo6Dl+2Hs;xCATdTd}+ zgS9F}VME4mRExEWqQ{6(Y~ILPB^twKltj~5t7T?T9}WeThFEoouu$WcA$WDtKO`Uv}k3L)rL(=myFcMw>`oDY}I3%Fz|o zv>E%dVa3sQl&=!K2QP`9s-?|%j-}P2zWA$0?+~Fzv@1etM#mGUR&*!TQakEQ$vTlw z_PWtsl&=@*moW9C&sf?ZIt|`1YRNV<;yS46N z)a@C3r=s~RO-JoWmlN&IcW$&lVe_IiVe_NqcnYHFlq`(euwj*=r{I;NL#dFW=v3kt zM@JB|N|Z;OlBf?7sz$rvuNHku`RdULMpO<}Q8PN1lC`3)l&l@i#8W5Q46hq4rcAx) zMRL`TzJ)i4y0HxnqZiobM$z5GX&iN+e3PgyWtv8Ju?^cr8`#2Tkx$I+qi$?Z^JpV^ zcZl9&-5sM@tlJ{04R0CEBSNcaI7?ecFHntbqPDEnHhP;f?V>6)bo=OKwxmO}jPH(7 zYsz$rMzB`rsDO++MH?vJC3=``*g5))wRVZFCEBi0U-EX1exhWzXae8eqh@T?Zc!`p z?jCiZOpoYF{5_+I#M~olLWDh|DtLNDPqTEdXjioKj;gZO-qCsZ_lf$k4f{sd;qMcj zOPs#Zf%yAH2eEGd=q>nw=u5U|zvv8N?jL=~_W@A@R2&$6%+?+hRbs0KMn2m)D7usg zgQKTe>)>cFA`FQhqlSk@>-ZiPHN<~N^e_>IM`KueXmmGQH6mJtXJoX1IEO{&@?9FO z;Cocmf~^`I-ORdUqM2-SSyY|!W20Rte|U5uOUFfJ_{T?A@jW3LPWFjjoyn|R=S#X| z(JVHwo(7y()Q5HIi$+BUQ@DX>ZqWs7brTgW^yNZL3Oj8aTLvF3!2Aa zCb9uL#9^vab34Xi?j>Ri(L$z4O0=k>5s?>qdoa%VnAnH9*nwBSh}az}_s5eLZtfWu z+O9uNzpKA9EA-gQtmmCjIsWS}n>~v0a0kl#?)9~mRpjVpiv{K%B956m_!1EKdFV_XBd zOvXkGUCww~#`jvME74>`qj@@^8|?{#Dj2^*zLn7cW4G>%5KuXHES<(`@I4(|ZVq!h zu}Aj%vE&JgtO5S+7;i#em(c;^3&`g(nqh>yL4;ZuH6hhOo`MBK@ifP327<8|T_Bx7 z&6yZ3bL2fl;jpV;-#;67ZbOOdsuiyZp?8}m(-66tgjPoI< z$~Y3EZ4Z_Le;USvkd-n9Ye`QgS&*k-!HIY-!a4&4=VH7Ac}d1481?s{90*on%!N#n zaVN%ukX14s#b~`JtqOt{F!a9%PLuIAMzvlj!$^%TBXp*99AluG&;3EFOCH;I5~b=< zst)kyVYGs@kTDrUBW@26o{JG)!dI}0ui?0cfjG(E#rXK1V=l?+Afv3HR;v_$a@$o&!T#|=^l%H>t+)t@{fl2ZVUOtzrP?AoN4j_@FAFe*YtE@Iz)XycU9(vwm zL8={>6ipQkr_3;50#Alal%c>UK~4a9S|t(v68sl~D%rtPo=OG>`X<(mEWRF?SdT*< zlcA=62l5s$fdjSj7g(Qz;1Z1AA-~E{s~2&^T1iK(d`$1^$P#^g&oNhBPX?(aPE|uz zsSnJSw1u>mAys=r_5uk(BXJ!H;v_vYNgT^DNtuU()K4zSRLV~VCdmTGd>Kk|0b~hC zB)JLK4IoZ3Ad|$g9Gm3XAT_d(P^zp4DgOX4NuGy1D?>>>hI|APNq)!mD~OXEl1bv2 ztFGsR)MGA5(LU5NFi9Fh>dR1)E|AV3kz_wy{Xv{$TqcQQF3H*;wPPhhNy$jc9|}y8 zsgTJsl;jl1Ng$ErQe2mSILWL`6323Eb*&3hXSpOdQT_&Ck~|1`K!%dM3V9hMl6-;d za}Xy{pV!AXyN#4&Sam*$8CKNiL=QCBP)P337uB zC3ym}1|*Wai|cLRHOL>sj-T=VunuhsFuuqAIWWVt_uiD@<9m*|^uGq_Zbd9mmuS|9 zqkmx1cZPJ7q4fPBeL*698LrVFPBJ=^#4(q|57SkOZIW4(p8-sglOZR{P?F`4WgwB{ zPF%Nxv?ezfIhA29hn?r1pyV20BEAcGTZYtq5BV0DDK57!0fCv~YC&qq&=j`=WP4ym zou|xW5_KQs9$-X019?h@=Iys2Z^)3S&mbFt6?J(=lw(fRZehA_6&w3kR`?khQI*(b z6#%md%^*!dLR5ELT|wL(uFoWK%q8g&rf+sh`cu9yFi8%D93n$CHw!WYB$AwoYaxh} z+?Pq>m`k!pn6A!QjH#tdD1R|9Np65#CqqfrKpq8&ByZz-6U0fL$|P~jCFvEWk90{s zr~Kc6N%AY?XBkRTy+3E|Ad#dsu9hH9@_HtTV=hVWF#WDe(w*{Mfl1OI(pQF(ltD&= zM3VWqjskI#Pclgyb4m6I)4g=ArA~Dwb4mJz>B>5;>5qB$6D8>ktqp8JtPtm`gGwOkd%WOs4!qV3N#-93?|Z zmOvJPM3NhDT?gVMqcTYxb4iAU=^QS97$px-{yt!mJPUbRhLU^)`4A+M{EF*m5GOe@ zlf*HXWJH*L%O$C_KUEA&k_M1^GL&Q|NGFg;G5}XUU}k+6IpsO#a@bjaBqa|8CgL2( zEE($7XG6{eX4bz9vRsB{{hJ{-%FwL;5M(v5qT=g9EVEaLbnzzsd>sTmxrk$8uVsbj zff4mShu8|;4@;y_9K7r?$OVU0Jj^fgkQE~+3rvj7Yc*wCbl;k4FQjkb;E3TV?X&x6j zy+sb&JdaZHVPGP@33*+Hn&)fCm%ubnbRa}f^HhVB$WZfa2WbMVC{20#1ZMIRE6^98 zwqdY;Z5z8YD|7@#RBuQx84@)DG8`mC&Biqo7*Q@qdW#%Z)G3ra37CkNLN1XZQFlS^ z07lf`AdkzCsMjE`$dIT{ARB-cRXHQdF()b=2F>c&*gvqscfg3sJBY)4U^byKq#;O% z+67k^U_`mdY0oj2!;0!d$$fx{I0|x@42e1tG8Y(83n8b;kf@6x7s`;R>maLu6;(SU z$}!jdehh-2oT&R);T~W_Jp*}4hFWSp)r#;794lAnCKspF85t~7p z%8;n;kgmXp8UX1hL!w4NhRcwsDUeCPifWb-<(R9*?}OlZt~{IA3s_-3FrvvE^^v)%;m76)>86$U?P4B`B;WT>77l#V;E5-7{$PdY658_L!vrB z+5s!7T}G5+t`@%y0^K+;vG-tw9>9n?2y%c7iJAx*4-%qIz;zrjqFm&(=a|c3MJ=Y} zdB8-x9&)V=iFyq32r!~vhOCpJmih?tp$v)o7V+a?I6Yei+QFZ)2wibLAfx zQ8gjeWk^(8NNbP~wKuN4fDz>)r#;794l8O1B?ki&aT;Wb42e1waxyTYE`(elL!wqe zu9hKD_dxCfR@8wRQI6$l8K2xR7|_7Rev%cQ07ldskk@2L)K`$rAR#LMU;+Rm%0*6l zj=3CGR9#Be1}0)hNP8I))fciaFrtP-hRKkqNstLLBQ2b*AR+2GT+aX_%0*6lj=3CG)Vq{?8<>dSL%x+EQAIxjSPwE0oe^$QDZZr9CQ7>WfmXo6xyWhHF_*)N8cWGBz(hO-aH#CFEu^&!iP{~~U4}&M2k8&2sPi+T9CL%XaTxS#YGWV93L}6Kbp&Lp z42e1oatcU@x*XS~z=(2@)1G54hZS`zC2s~M;!}{n$xusu0C^7>QC~y8lp#^!aEbsU zsw$)ku%a%{h;qy&Y#0WOx3jUEvO;5EM0J96kReh1AbmhW)EHc&fDz>)r#;794l8OF zC1(H=@l41<84|S;vH}=UcS3HLAyJP*9+RP#dIj0vQYvqNd@R z0*ojZIqf;-a#&HvQE~w=5if*XAVZ>Vf!qX)s7D|V$&jcQAuq^KOML)&4_Hy_GNK%F z39E-epB-%MuUX+sU_?cS5ek@1s0XP75~4cc>Hv%=7dh=Y=5kn3dr@*vU?Lt0IYfp; z&4SDTM%2lW6J2mB5O6DL^y22aKpQAg9Zas4F2?fP|>~aor1yC>J^HIp%U$QO{EHX<#CL z1o=>gMEwf+85mKOM{(^57*P!%^<+p?8%Qf)MQzTAa?B;H90pIeu(5Yzg>Jxz+7HrS zhD03>DFX>n3vkT`MwE-3_8fCLtf+G+c@{7cuYp`8L!usntOiDu9!+{)hFa=<$h$Hm z>PyHMz>4}gBg!#%2vZma%Ujym{%DTySjmVg!6*i16Iwua00~ih;OYU4C>J^HIp%U$ zQ3p}-0AM0cgp8LVQ71r-14h(h$ayj(>PpBJG9>DD$gRMNYNZRbe0+sG*|}dvlw&U8z%c!l6IIMgm4Ok}7}8LNMC}6U0urJQ zz_lMRqFm&(=a|c3MUA55VZcN@5;9kYM4bmY2N+Q+AeYOKsM{d7$dITq8xJxhllB9?QHDxSz$3SqOOLllp#?M zLLLAKQLo~985mJ6a@upu<*+UF2_-iG6VV$-SCS!7wJ>S`BWg!Ta~Tq~3#5w-iP{&k zH?X2~YfPWO+$0lo6UD?Z&C507*h5+2U|>W|fQ*wNQO84$1qo3X;aUoeD3>F>MGhLm5%n2lqYR1q1@e;&i7Fh=qjSKDx-=uoF(+z#nC{)d#;(r_ zb%7Dp2GUA~MD>RB0trzga194Wl#8799CJCWsHv2k3{1pRAScO?s7oQ203+&V$c-{2 zO8=W2y((^8JP zS}Y6Go1LhYtgr$YQFlUammyKlL7o8#Q5$fr2S${Ooc0`ZIjpGfDfulh5sM~rq6Cbn z=8$H4}jBg(N{E#q@!7}nm| z#-7ItbAS*Bn4HBZ}<2ni$ zQ7&@YbIj$iqRyh^8Nfum3UZ|kiCPW09~e>3L!OnPmU9U;v@LQzj#y91-hg-vHtxdc|xft1`In1~Y~<77zD z@sMMIQM3qht_&$!0l8d;6x{~71@JLXy8hV5a83~&%ZVM{vte4l?lHk1V~t0EQS}n! zMHy9)%q4j~OfPgvs!_fKm?Yaln#fR+ZjfC;BFR8p2Li9b zMovfQk4OWU~w<&Y#XP3^46i7gAe>+OH*KM;U6r zu8>`T6?JV!lw+>Lyc?tjb+fViu|gkUL>&q_M21Aog3JI3QD@>>2#hEfIqf;-a#&Hz zDY*=oh<8G6mmyKlL7o9d)Vq+kWk}Q)kk4gE)bEgAffaRIMwDX(TE^$iAl0I~ja@v0 z^G0AqHHI{lAyKdA~K$6Uf!gVamA z+1Tf^!eU@VT@6_&L!uspJOC1+Ud8n?Frr-KwC9-1VMTpH$qm3n^ky;t%8;m97&U+q zwIigt42jwW(nW?u?F-o(SWz!!L^=YjL|uey zDKMg3;V#@hT|Fr;wd)juYj!KIp&hQ9tI!wut_FS zegZH_=0lE>p(INni$EgD4Y;lYrsiDa)D}5x&8?>7{lG+g1@e*%)!Zh?XTa3l?~q?* zsOF01aApKd%{7G72Ub+E6%}}nIZ?NT!9G20>~^fs1{hI2AiK$ss3DNSAR%fRt|=g{ zxh0t-j=BEuco^shjz++-ls^WTBL~Veq zmmyK#LB0W2)O8tAj=6*nhrttj+SuuNTp|ZXR82^A84}eN(i$X0?Tu?MU_`mdT&u9z zTn;O02qgyt6LA`3iVTT56>>5#qArA7AVZ>7L9Uh|QTIUZ0#?+W8Bvb8UHpS#u&kGj z{Uj?q0gR|OAg{@gsIMTKK|)miksQ+iBg#cidycsrR#aU|)&?eGM@V}a64e*7FEFBp zLx#zas7a6sG9>C~$dSN`dN3o(F;|NZguxMe+1O{Y!a`s~Er%?VAyIciZU+fb&*6Fo z7*Q^A+H=h1u%h0jxCWJpvyNE;av)dR8{u%e#Ih;q!$ zM)!q5@7^}{{;V(n7*VB=kuoG|9%K$kh&mV7*}#Z$k-4F5v$-5r)D@Jx448=bLhhC! zQ7=MX07le@koRRs)Yp(NWk^(bH06O6^-4yRW3CqO34^+O+t}4up+qa1r^z5qWk^(a zNLP>$H3-*1z=(2@)1G54hZQxJl4F30cnsud84`6qWHB(Ju7<3XAyIch?vNo-Pe9fH zE9(7>D92pFyTV|z6ZI-9ybO$}Paqp)NR&69BN>c@s9Lyd03*sp=30f#=5kn3J5sVa zFcEt~c9$VhLm>wPBWeOvdp6xyWhHF_*)N`iznrfr%I$!ji}tm#4`qczfDttXGD(I+oeVhN{ z99Gm#l)M3$h)+P)$WTkY3wawDQJW!~WJr{^fG#3KqKYvp11l2U!cOsG^K0$J{>Tf-vaS*T(*k72XF%)Hjf?WJpxuu^beGgsAOs zH33GHi=6fxb2+T2&Xnv3OvL_>zA_}L3^E!RQL`a4Wk}S?kP~G{)cKIbz=|4^5#^Zc zey@f>?S3}))vT}*7*Tga?vx=>FF>9H2~nTm+5n6w7dh=Y=5kn3KT`60U?NsIj@=V5 zqIQHd2S(Jckey{nR3FGbG9+pkWC*aLre;Jr=Jp|HhruQ%Y62^a14h(Qka;pBY7yjI zkPvkpu2sN@a*@-XV=jjkrMD&D3rxh9ATP>LOMM2}2#ly-AwSEIs7l9k4<8s&^&xeE z6?Ifblw&U8nPKoKQ=^I9h80=?BWia@cNr3OFk}!&h?Hv%K-VaS6rBx)_>c^PV{_aW~BE9#VtD92pFg<-IisnNv#iWN2k zBPu)rB1lwSNNtc1)e%>FU_`mdY0oj2!;0!f$vuFHI2IphLO7>&YV;@wH1+kp}C1Z0g233(UtHptT|2{k|B{}z}x zhPdD<6WqNqr22{cY8jYVtsyOCNWxx_J%I^qbPUEC2#k)gkTEi(V>VGLeSrEJq}FsxX3BbF_*(O&+C+Y6_|)$ zLcWk8ZFwj0#2hfP>OgAAQ1i5a>>xw3c7^N=?1gmwUQHi%PL}1z4lh^{rcL0!l-?H@ zMZ+P(WJu9W$aIiUbOx@|fl=hbvS&&pu!=6Bc6?91w@}&42KC%P-hLA>UxhyQk4tf&U!FX2>QPk6`4UP8EQ>RfG&T z;0eFtE6{T}+u?5js$>UGnc%_BzKOLji}wa5Rw-np45ge0nFCDVK!H!kIu!&;a1msw z3?;f2at+X@nv&_ag8Bp*7Tb8%`^6^yy_CNjn9X<^@}vw&`VjIyNT~T0*N-5boglqM z0xPWMLQcYgiP#p>T84z}4cQAAO+z4qWk}OF$XFTDG#4@(=#x^)L49Mx&#)kN@VdO5 zwFoxRPi2*pfsu6~t|x$z<#MF9$YEt|pyYaBBK`*XMTTV6ID@(e zMpko3GZ~WA1=3lDWbF;v3s_O-_jRHiqZ^q|pD2H)fj0KRtS|@|QHMjyWJuHk$b66x zbpfs=AfD5f+a%nIu`I_X=^y133?h^U;x&}N3YZz-9>`rXGy|-KJP#5xz^AxA24)6u zk+~I<$YE!IpDFnxFcGVs$*2G(aZAXKz{~*MAiK)Y4A2kKM}}sA;gDg#*4)aBD95ZO z-i%oAo!LN7XXvwJjgjRl;m2-H6W4X5nK-e zQ*$nI;a6Zfsg}bNYo_A1dtGQBCg{>JdpG{8+`)vVjHmneeoF-MFWQr zN&{&z<R@6rsQRPev zDHs)e?LF8R~N*Aj4&-&rN|$0#;OmexfY%94m+&-jpbH$ssoO0#=w0jHq)UXUUMLYamyF zgs6vbtp-Mvi=5sfhZVJ!lFtJZ@l(jhG9=1h#C(lmM3rC^10$*lq>&7X>Huj6IDEXH zW68A##2P~HK6c`5qn!5-Bh;%b=}V!Wz<(O!BFH&1KE(JH@-@iQD!~_cPF~DF1L02? zL7FtDE#bidV2*-n92h6@J;!3369B*X@Zp5jX#IfV?*Y^2zJhF)p+1*?J{<@o`dkxS zjX<2FQ6`CFE=ha8xZ9yNNe9Ze118Cyke)J>2yxBg!$Cu)r@iKju^H?^xj* zU_|9yz`DR}LPJP>kPy`cS7%^ExyUKcF_*)N+J}<8fr)q+WP}VQo(q``jHuHfr^t|~ z3n3TCkf>FVtAQ1z*PiI(n-`$OJpL~6i#Lq0vF~ApyMPh(6y$F*)KVWn-UA6yKjHcT z7*Q@qYKt6JRKZd@Ffb9DLK@4EsIHJ*fDzRX(np3w4TlVqAyJbc69A{PI@0fTP%OdE zFn3V=`o1G=Prj zQf!J(D&M2%JHW)>4A~?@@pCUC21vwjgsTDIQ?m{eUhe*J#1#MI6Pde5Ier)1?I_X) z_$@GcLH3Z*3u8EB7|1i_55O}UYdQ!H#W)Xgj*JNyS3)iWm8-HrO>Cyn3nEquqH9YD z*_kDeQsiOa?})JuvQ|cYjK4!Z1$kN}?1?Ar&sQ*1>|zQ5geZJl8$RH*_>`CvL?(7s zN>ouv#oi9mM22E_gX{|OR6Y@VApQeEmF&prIPxUl#42O)XkfC;ha4qCfzN`R0ZiaP z8+{ell_1c@-wC;0hD1CLc?@Je)%UB$qW8j`@w6Jv?qhOIW4ASiUVk3EA2w;PB~?`N zgRuHe7{z6!bG`MUd4i+3Y?OHQ{)8wl8!cWZ=3~Sgis#m?!^#`N9OcP9PB}h~*Up`& zu%B4toCe0IY1SE~C7;?Qr)bHiVIwW^O7yn(5}E&SXW@x~AF7MR5>5;{mPyj$EwH-Kc)H_gJUuWPb04Zjp@s2++;^qrvLF_= zOvdHbcu19Zg*D!lmMddp`+T-CcU1|VRY5hAcd=HyHa5KGxh1tRzp$oPeTXWrEa2v) z`SD&Xuq;${brphPAhFASACF^BwAL}S5j^4{} zQ67CSf4lPP>+RPxJwVa-@lABE_Q~~#af&jj2N#%5bjl8jP+D3tP9bueaqoav;;BA?B2bvm;x9T9%DdKno>y^hFoE2}di+6AwuXaOa z-`Vu(u}=2RSwVNn3c71n&~8~lySbp<AFCFDHL_?x?9#rj+rUjhw>e`K6P_ zlue&hI`X+bP{AOpHViQL}vNnQzwndtUsf4+O%=wrOY+UK2 zvdLaf@$$+pP3visW)Yaeqf4hunK~sdoL(MHI6wY1IkSEIN1^5E(X7zHZ2IWZ8KXFl|k+fyWOdCrkjsH2XCj+zN=Z|bXb@|dxuliRl|_s6A2jfso66)6oh-mGw)$|Gm3 zW|Jra1!%NMvnGr$J#yA~j=p@3c|AXOa0iYg2GN7+A80QCPvZiXmWSHMeMVv7}H=wtqg`&e&pQ!IZO6%WG0y}fH{y?PjRfLHKV zVm(3+GwZZuar1bcVP>7~7+tjv|57M5o{w~;3|9EqABH`+yiio=uir%~9- zSWzbx!QxNerbe{TCprEqjHE<=*=HQVM5SJX@nUhvWqUEk%ZDx@0*LCvkllOg2o@CC(o(BA8r(*Gu zO21RK(JR{+m~C8Yjc>KibJ@n5*~R{zZS2NvI(^JKL$i%3)-YRee713EwsA|g@lLj} z(HbVS&wVj{Oth-m#%|X5OPAlvvjd!2y$IhhUB&998nE!)^H+nAVb9Gz{PpKV-W z4I|->>?Mz78=I_QYUj7?B_(IZn_~jhw1%kz-D}Eh=pNQE0Y+vonUZZ>kzH(Ew(&`J zv2Uzlk`{7DBa>uBe}=a>g`%{;i$&V&2gct6>B zaNklUldVmFd4ItKnDY!{3~+&D+rR*L8DJyoPMQqVq-fQjk1m1tYM1jUW-1a*nDfW z);bquFS#Mxcp|&l`s^h?W*e2c?4Jo;#~LQ7Zh>T$=r%~ki0^vX%_+-XXMT3E#o5N~ z*~UZJ>#WIM@>;f`yA}GF&G|HYiEdcvV=5%~yx53q)EZ{Vj@gE8Oz2~l?4NCvWgAyn z!&L9n*-PHZHh#!1R%cOG=vLXrZrR5E*~Z9hqGc z))MYv*qOrUUXpFxU=6d+- z*~OmCUh+k;Us~>z#c3swv(tmV2lsMtMt%u+&+Gd8D{OA{+K158Z*i4Rn!Jo?lYJ3&1=n` zD;DwcMtRRw70V@;`S+>0FRaJRDPWz~Oqm{>bYZ-1jCMV$md%~!g>O_INvWb~Im4Q8 z;$sYdfmdw+R&n@bPKnRMHDf2u^unk0n-4EPMTF;17Tca8Z(jrRYUrdP+9kamg=-!l zPpy|J<~R1W%KnL3@t0Mh1`A5P5-nYK84v1#f>B;Q*|jg{$_?;!s?k)z-ot7KYI}`w zHx0GD;koxODC{|Mw6~pNg*`{k@U{y@KfaP@ya28;>-u&jPbvU^DC=)WAaCroo_Fgi zJR_XvwHr|Z3ilvpGZS+UVm31|ufKzv-5}h9n6c~fyFITz@Rt#xnPN8M5cZ~f@m%ga z=dY##L3kiBw>OdAeS|s&xMo<_fJZqdk6mTf)nW}-%O4}uNNRX{1;76ZF4Y5njPo4* z6g%-Ji8S47ZW`wHXLwu!qz<4KZVl+dr?4R{*0Z*kIt1V2&et;bjnCoobV$4HOU!ml zRSu&CE3LcK8f`HK+a;B{V)#XrZKXzO^(+lTIxjVw*heYOXbL>y;=F@#zjkJPqH!3< zaHq={$77stjY>U;S040`=dnQLleG7_ih?HB^0nM^0VKPT1z(WNzn_JzwIPKs@@qC0 zR(_LXm2^wuwN~@_y;MMK6$LH<{$%HQ59=j)ew62Og4A8d6>s2w=sf+gb_e9#OyH0} z>6%)&UcNf_b4sF*hKI)XWc}?xAx#XucA`5E!co5&o2pL_I-5RY3*x`I$FSBvo*3MD zYlVrS(pDyh%AMFWJ2B*#i6J*LG30JBG31zuA)P%jq_Zc6q6!m3y28XzoINpA$(k7Q zG802y`NU9?H8E6GYkAckZAvxA6GO6A{AF#Z!F)S0ETQ%nf_ytM{0;sHD4!TAy~N{^ zc&aBR2F5-UL#SXcQ{*mSX9&5L#*1m!cttk-yy1uzLP zLzv)$UgrV>z@@1|k-ovaKXz%VkgNNfTx$X4Q$_JxbXj>aQ^hlQE(3O|&}u8*<{%i@ zsY0%TcRcUQ*rlmLu19e#0p(N0-S2Xt8kA2J`@FCH8IjynG3Wy(FAz=4^lxu6dG3!P^P z)@i_tnz4gM#aO4D%rC}N!F~f)x(LZS+hnpGiy|@u^rVM z&l<^E@t1v1HM_%!3#t9HfjgXd2L3oGpEZg$@T>-&8e5+=6zm0x+zRZhA=ikHxn~OO ztRdI5PdslQP(Ev%|0!b+l+PMNHgehq?5v^HTK%2FBVcC@xvs$cf8@Ofm{mpg?p<|q zCpyuABspgkFo6vMDiRe`RFsUU1PO`(MKNbYF-+@V&SM<2Gl*eyL_ zLpiVU72Ly6@ER)i^2bDHQ1BWO)qVncAVl&S65RuPGL-WgfBMvz7R)1;ZFZ{^!G(#H!}2k zX}9D>odU7fP~AB)JRjCP@}ZqDy-h?oEgzF^^V$IzevM z@0EW|k3y--$?|?rC-;6&sc~eEhP>aCWIEhbNwR)#Iox7NvVQL(xU(V7na1$j6wC8_ zwfhnq>rk}R!cLAYAsvFIma20r326D3$d@bJcRQ9blaaNVi&RJ1AN}8h{2x<$0jKu? zl=Zh?tY2AKfBVJyl~vfDvOa*aer09--hH9GU(7G2k{i#*a7rP*7NOk(=g70LU>qQz9ALCjR_ykG2zbdu{Uk@t%|Sva3!X%{mcsg#SE zenZMM`>qq$*4|2?@nzfk=P^+_o%{;rZ=}s1hhmC&Ix3IUl5A?WPsO$H>8N_fwcv57 zm=-0STnVj3q7-W;$>LghbaExM;&7?5#m07a)qaCBz6O=wLtj5Eb;S=|)j+1=QDkw= zdEFgP`}yFwvoHa7^7dS!>)TU9->wXOyR=dUSVhsdzals;ZEBh;Yhs!!|KB>zRs6y< zSD8P}HUCf3TvgsQSM8>`ns{eJ*Sb}6<5poJpar98?nj#cGbozoM*fqa7RpU?W#90` z5|Y|R)0|&5%T~g#nw4zvFGA(-oJgx?iSGU$_Xy=?Hy$Q4C*#O+vzq|DXTpY6v$N?# zVI^ydtochJAB^#kuX^X2sZMw~=HDB)MlDt1zhHLD;* zS~W|w3HEF+(xfKQzAdczLoKGVtLCw-t$D5$lI*IvzO6N7P$HVt`ggFVE0lZ#*fw4@ ze}=5_s=2g?$N}KScVk@K@sIOvVraO;2CL|MV%|}7*fDYxdf&|*&-p*PJ+EWGTdAR+1kyE(oLHu0 z7~)@=5Q5WaazaQq@DuB>RG7uZR z7Q^mZHXJVJN}_OB(|`|$Pg2TF2-lJ_E*y?+mr~y~pDIaJvG)@L#WZRZTYo1Fh@WUK zgJ043pFsGDt;CmiAq~ac?wX7ZgzE@J;jpS0TYV^TF7ty-`5;&+TnFAksWT{Wq|9h$ z>9md_U&DPaiDnolY2FnM@}aM@G0b;rf^ydx6%)Az<~vP(jT^|;?@41;PA9)oCLf{I{{ZEX`v%-AQ1dXusZt=f zO31aH;wm1dI3>S9^Fs)crZ|aq>0!+f$fH=7dnVjbk~kDgQr6R&?~pKKbS~1^SdCx> zR4@l{Sgcf^!nN9NaWx|^f+KB%G&YD!>#nP;?qy9OWNI#YK`Jz=|6@ir&|8bndD5#L z8T8buS!fMKScC(h!a^>Kh!*^A!ZyTR;Ezjvh;Q@Y8ZxUd0^iiD#WK$@+SHHeTS zARVV1kJaSH%VAR4%|#a|OO(Y^a??Ao0Z+=w;z>*nPdZ%|Pww1#c+!Lr;b{qqK2Sbc zDTk-)NM-TF9AwHzOH#2P*8-mQQEsDFvGwv64o~iu$|E%O1CYm)if@K{4T|ujO6B26 z#RHy{{2I-Vp@1ie`t@d&2YEc{a*u*rDv86BBzMBqLk>@>=fFPJ^o6R|fSfP6l#KAy zO^s-35?IYRB)RLWeLP%LZ{>3IBHtrT`QtIOJ7ij{40>*2jUw;^Q5J%psACo45TyCo z9e?$I*7-l?_Pc1tUszTpf>+uL{bmi&ln7pF5jg=&i8^Y;eXLay`K4F1@w8T=FoAEp z2PPw6DPi2MwG*k(u9!$~B1q+tC|c(wr&DK=-cd;{H{t5krHhi@QMr2LO}IFK?1Xzd zihkGnQl%WQ+)|`+nPAkxKWWpbX>9#|G-LC53?rTQS%v1`2Ob_DU` zVY<#juMaxiq<1gk40`d^%2G$|fp`LBietKY;$+1!g?e38t=I?&U21+drO>XcDir%$ zl!7ru>#-iwBCnhJME&X)c@r@ti1ivCL2Ph<*t*b;8pAsNavreiZtWq9UGbtEQ zmGY=sCP_n3ohnfTsvBS&sFd)aav~M-p!x`TSk^12E?U-$$$?5|E?pFs^{yIupkj%U z1=TSqdQhFClmpdmq`0T)K_!L8jbZD@(r^w`wfm5NDBUy*ia@2@qz9FfXOeX~gh-&0 z=mOX^kO!6Oek0tqk~mOF@*v#ZP*R{$H9ms*2UPf1x^qXYWI^>F0uL%x=Nojsl-{R^ zzvE&(s78~O=?6%m2vm{;d&w!9)1}7oH=z0}f~pWmrG`;#q%9dp$%YGp0Iq88(naWs=E6RR(k`fS zuu_QNVWm`d5={Tfph^Knb`liB;^1e4Di13KRUTG94XPAJnTlHkG(QP8ZXjE~3ytX} zK^IiDqw88ieiBsi?coMN(Ilu!3ZWazRx=e{23JNj9ij ziez8NO@fO#!}~C=LdnW9>W-BLK~=IP(p*rbYK|CS%}~fB`;xaECHyp~yd#j$S6_7czh#xsFJ+E{J#&R znl;#n3W527R2vMHMuB++wJ6Kmh-xyBD$m=9s&E*pyw_uB4(1xSime|-3pQ6^UVRk# z57JzBKsgM32=^Y8e_s|1RfoV_#RG?P11MlfqT^r}Lb*WdUAUJe$p%s#wqf51 zau`xQFM>H2%8UXLUvk|AjRL6x7f8A5%Zx>*9cRj+=0S@79x}CEUcnki1re_DV-6r{SD; zxK_UA(XOrgAc_@BP`{u_EK)O=XMV+*Xi8ufl}FtF6CBx0ZYosVo6L6O9~^hSQ3+u zB@MI)OIq#u-2+96e)qtw_9~zCSdv2HHn8<0XfGc>$pbz~?>q=aSW<4ll9G0?H9wFY zuq06nM5U0&lIlJkZf{Al9_L)R6CsBsRpV8dm!X2&=*6wE5@D(E$t;#sosZFZM|y7~ z{)~&wVX1msYbqd9Penc`SG7ErT*_g|r5u)2MUN$=bQFG=#nL-Y5Me3Wl$yU= zG~s<5u(bX)r)Ykp0y?hvqd=7AzGT+*ct6u{7L0$4ek|oSr2^*SA4!z1_FgMhhlOZb z^jiw?ZQ66)BTI?l%&*;)`gs>s9Ogca=q#+n0IQO59hmdNrc|^4v?*1Zw<%R7&uYpy z`?*g}sc{4OO{t3Prqn>Xt}o>0J{2Diw>wlBHl>l;}R# zE1+EbG=cYfMnSpw=`^^*prkgXW^sm+?XCF?K|xVDZGe?3&3)0PRQ1%?(cvJ|9o^CJ zGoPy5ZwG7oK)kk%LT*z^c_Wc;r@We8!A65vsy~M0rrra${)xA~Nq>s=_)fA=zj*>scQFsWhp#w_>IPHk()W&uEIxnS!_fksbd8xm_hCM0e)J1!oVsd*@ z3PrQwxESD+4adv5l4ws#Tj}0QoupKk4m=6#_UfZOPVLo~t7lBb2YGO6)F`&THw{?n zLMfR%hn7AK@^Dh|o8hj9qHtW5$_vL;JcLq8zJTU~Pza?YI%!91WnOh45NmdV zJe(w10e1-GLMg%iS(rzlf~^6>7hFoA3#HVE-2B37p$nzl^%ZQ7PFrLiP?Aj;%FQIm z)a?GIiqI4=nx))&?hhynG*ib!bK9fr#5Ud-;Lhuh`BLpuG2rhfa6*AwWp<}DZU8%h2EFDm*ON|_&{S~NPE`5($ zccT*2s&&K6oY{CYj6@(*S?}1TPl}Ds10k> zuUuI+XP>0I{Of+!zy176D~w;dr|w>6jtx~Oxb4V7NwBoqf?qZUK zZVMweMvN^rnZ2b~w+XI5L0oM3Qj!j4v2p4l>kGHqQdUEtuApTC!;qOLZoL~ zBsv9ll^4xHv#Tr@GA~3M+ZW23b)XWW97zb5WfMnZzsTFOdES@_MT) zOw~1^k<`kd&dvQeFzvdVbI=tO&BO?b&qq+)LN*7F1h}KZDET~>s~M6B+4G=C0`H?n^nAK~Dbi9e>0$K&y-GNt*(gj=*ct& zZ+|Cwb~Q}qFxoE@f0!69AoEe3(IfMjH%VsidX7aUAaf>#K&CXy8a1D-KZB;uA#)Ag zwF=51^Y3u4LQNu5g#$8`?7E9JZJ~fniS~z`K2AOx69Nk<`i&GR=!936j(HVE(IUhj)yp$mXbf-~ZL^G#$G#RI-r#`Hh$G zR$s$)OPIkk8{5&a&MhXvqHcjiSUiw)p(*f=QB2k``gik6O`$v^SbU1iBJUdKlT2v_ zj*VMR(q#Pd=p~XNKuDIwH!83xR$%KQoMJr+qEt3SKxY$P%P*ATFFCb3zfy|7?6`h) zu43_5RI#v9if3OMEDS5}!?58h7JpK8OeNc6^DjcLt7H82xEem%bzTJ%cAZyvAgfU0 zMz{6P()`Vxi`CWcOYe??63)fqM$Dsd_d+6>N4lR>~6ROWt+lrO%*5|2BwXtEZXPgFf`e)Dv0sD#8_de>)i`4w*Ll zro5W@$p2+$)g70-K&dEPbsxp~rBI>a0|pHrd`jsnJF#JXFq=-qM&&|Q@Kzf-DIfX?8l#${htdJ3i3jQ zLbuE5LIro4xDOSgy-oVmlxbQt<#$uJ5GpioK3jhQOs{r4T6Tvs0o}uu%7cTHLOfwi=*yi|2GO6p8YGfB5?k^YyG9QR3?W?f`(DK zpkb`@a5W)l7$;wnL#1=BZTV&<7XvCa<@lgs;DZwL$oea&`_@VkQYuo zq;+A98{O8wMh8`9z3?9N?@%ZgG&~4*8x%#q-VcYML4^aPl}zu!Z6kC8q$L`IXgGu@ z3L2KfE`of};7oW(EBPQ9cSzquTB5|BY&0Os1r6hn42N<-!U8;X~Af@%fAr zkMU~)#wUF0)eF$j(Tp49_P<-CrR5o8BjkCLnb>1Fj6YST^ftlsKCONVo-y_p2Y;Jg zM0w8`d-!>DoM((ZnuGc?4(j1i|2NUdi;v^9T9M*ool1|)KV7L*#xxF$C9N~=F=+y0 zasBGN%WgU_F31avy{~>!l?#FKdrG=k!w1GqVEj3caTRLZ=(c_f&7Z^g0{V9vl*9OA zaKD2ZVchRMsBplzl8;Qb<}N5;T%s;}vG)cAjO%L8fjtuPZrhpg7+11(Z|<5y0pk)a zhCLYa7?*`V!d)qei!~(KlfY^_DC@_+g!urfni^o%4J{qlVKL{^x0+&2Ysi#r0@DhJ zb9nELytB)@fxK79tFFnzd(CB^d5ecp9OU}j6EC7BylnE9G3 zwHUU^TkpT*%##XZVB0`e*f}uGUEo0&>yRIAoM}lLke}XI#06x8d!5if&8Aewq&KlrkM@jxZNf{B zV*1rpxgCvHe9fY#BVEOgGt~j}>1CLxaRJJcg{qu?Qbc{~A7j(mO>MXhdkRd8(Fjb- zuY~yyRz(^&yk88q!moFz(Iv)K`@p)BN=~A~M?i%NtUIYT1lC0gtUJ3UoSviX99Qxt zv@U}j*uAKDUkXC*Ie^Z(%E54Lq1M_fww+xEnIiXcnlmThcX8(ah}Lb81HKn^-jBz2 zAs1wKk-5X*_LM}xcM`KdI7b5TySfHC8No47@rQtR9rY}Hi>B!Ddv_(%OdBl-OBA>C zaSc`cMZkTDu-#8RX0|&23w}A&_*rBhO+g|1ulr%ZEd)PLIW6}FwNejx)-0pdSqN5l zb%lQx;J*|@C*u^(?)+YZ;jqIY*PV`bejO7wewEf%TcHZpJq{Ok+T^{%#;?b)0-^Y@ z=cMmLs#JVbhAZ{^ZDVC9yZ`H$u>I@Kklp_6L5Z;atCNN8U-yt#6glsRN|_5Y%G;rmT9xQjM@TXR=e@T=pk z7{9~TF71>}*;oGW*8z^ZY^HE{@bW)zNBq)8TfxS5Lg7H{YoqNp{wDkl2)A4-G=+Yn zZH^QQ-SgeCF@7aiYKp>!#PehpJD2M2L4=LA)DtijW9qdxl zjT_z8*U~}F{;Ip3v+2e}^u+Pv^cI^OJqL<_-4g z{1@rMK3%noouhranh;44IkMJQtF;*GL9c1VZ++)llCK}r>Mb4UpC*6rx!ibwJm^(? z9ozv>#)T6HA|ae8aFAEg0C^=h97LQA1;|UZ%RI0MdB|5&rSIY1kVN4`XGvBa%+ohe zfrGrW=^?M=7i7E%1;|Tu_#y1UL5Ku-nJ7Bcn%5;#2%*a~^Lc9#Vd4hBcQA!AE`(@< zu6EiIcOrfgu}8fwa~vglK&^V$&^kiB&gDU`)Cc6f0(sC&vh@OP7D68Ms>U&JOCfyh zY^=P2QlUV9oodwj-T>lJQiy`>SJ4sXPnt%s?J$3Ez<9g3^tfS^a(~P?hD@}${Lj$; zrO-Q-n-kdGYhq6DyL;iTbfY<;Aa73ai%T~r_}x8!UpnmWX-1N1nNIUO-!a{56ARnP* zy}Mf`c2;2*p>&QbdBY+eU4TO5EKye$QKb-~IXL`sYV|PejgVU=c5x;=vXvY|R5<_& z(XvEOz~1IXLbgndUt&#r$OXr8<^z|qB@HEC1!3CVi)-hjM=iSZ5Kn)Tf27oZw?7zM`f{D$kC5=l zMTmIhyRwg5h{*;#VxpVe4Ia6WFA0(NaU`QhE(GnAf8@d=`==pyrp5z%ytAE+xqETd zzvht(=eR50YDc{~xG&)}ZZE!sL)&^hb3fMno{sqqXXQF z*w)D%wtFdcH~CjX4%<#ru!1+TBoVfIsa|uC>;Z9tjaYdMk;C@yRF#_ZLfzDR5w;sg z^)qJ@uTCbv&7A?_x0XqrmJASYUSLfW)&If^3%?l7`z>$>aX(nJ6RrX1F&^!N`=~zb zgnJJ@-tQ8&HAQ*%XM9wjzZ33Y{{LQ7FA(OUdN(%ytEk?MlgfSR#xPDAH+ti!{s%hw zZ%{74x$0PJWoZ-HD3MQFg}j2WPJR0+vnlNW#0IR?q4<>A3nlqdVDl-|5DRH6ZbFwV}v6p6>Sqm zI1cTNcIo}2PCs-1vO$F7kX|u259y7XuW^JEJB9rYIx9CLJc#6IsL_njWQ6nXsn$FR zb@$q47<)2%ra%8PK~XYj_g7ex`y8HlDr*%{&FGr55TB~Jum z4I`ZQ5x)$X6%0)QC&L)(CauLpDaHm6@U|8#) zL^zXcJzjUvpD4nKHg!hKiQNCc1H9L5<<-{({^&Np3f{ly{(pLR$m2S?JLLWT(|Nf2 z$=#tMci-IK9rEt~CwGT}`+rlH>35;Dx1;#3Bzt$r?d{xvM(*xVw5cO$oqxDGdv_>o z>ZoG>L-+q4MSgZu$GiWfxk1sm(Hpz}f#RgTJ z0?506U3$m!=x4~ge@UK%yA5j6{hzRg(uyJmM!Adv4H})v zzm=d=#$+~4A}hR(;HwdCEs5z@SFL?nUZTkxG1=z~Yuqj7@F_R*&&>h>C^{Ati@i0*;|DTqODm_8G_z7hP&eJrBhG2FU@K>nzFm%XoSK z@{@|Q>6aNw-nfqE2ca-rBwBnqPc=X>J$Bed7FJ)u!#7aO-DHxa{AykhyefUNEG4TGwzw^x5A465@6FsT0XjjUgsbaFScqGxseq%+!qGacPR5E3$Jzt|Y# zvRG0V>1I}VRyW*2P%ss4At+9ZZXvuxGHgf53L zp4|ADAI8V0Tl?`*kydGA-r4b2jgMcvlaL!9P25RHoBTTonRLTD31M`&Cw~8LxRdak z9OxUjWaB#tkI-iupghpu2I!Ie=Q{~~ZwK;FfV_6w?}S|odB|7Oi}UWl$wMCUl6(sH z64d0KglTtLGX@Hfm(9Py{>h63d6_us_h>;`$S=E#TU$`#w02G2Nmzrp;hltc&|VL< zDsB&4e|jfj<=w1rAP;)g;|aKXC2^pyrWzIZu+<9TJ4axp(Vc|Lwo#5Tqx2_Qh(zn` z&-tW+A<%ulAzoPQsM?*;0ax_Hr*0nq^}7qkMP` z%A$GEW9;HWRi6hq)N(O8CZO%_BdC?{K;<^b6o21=#f|bFXdhnZ+>HD_sCsF~fNY)I z-$zht)1UZaD&+4YNYe9ho`it>eFW9(Ot@npPH+=e^f;!wk09`vni(UM)AZ9pj}!X~ z;Lj|K0RCD4_^tSB9nS=S-?gucSun5Bv6#&N@za-S@3`GQsUIVm25%=TWKQS5gqKM- zd`PX(KcuERPkFE475fJujzenPe=&`_SNt;ZoeQa@e>yEZU*X@Q+g&FLFZjiy=PTU% zeet?p6!CA-4cBR$?fAG?oo$Vt#IV7-bU)HFiBa>TQzXLOICqM~IGrNA4v>iM#`(7a zc0MOMMPj7R=1!5AIFzJ+&6HI$>&oas`wbdJUESTd#$96TAEf&;-vR?s#e?aab zZ4d%|YuCw6=PZ;2tW` zKjiA^Oz8XKJ)Maw$v6p$=|kgQwA)5&dP9ijAd-o8Pw{myMDAJrUe1J8+P$2K%}-nN zXUGjiFWUbvEC?Vst@LsxN;dI;hD0-X?^%ub4+QT+jLbgnS&VOCK7*1SI?*r;h?MSF z_b*f{C)1s2UTWf=s~ez(YWWN!5HiW0=<6TIrSuHN*6Lo99EWT>$z)?2FPn_)H?pCT zJV-XCP`)Qrbs<^qYyN|DaW0l}sd>*@b0CzMuOiC35bi8VG)WCo!{-U!tDV?-;Ppch?}BsN2$0QC7t3*u0(S=b$f^`Qzg%xcHj1Kmy_y& z&O6AGSy|N!JmvxMcwFd8HBn;#e5Nu8>la4==okQS>o~GF9Q^rv-|U}p=216~M=6Vt zR2^K@+fBWG8g;q?3)kttC*eDtwQydii;Fs4>HJQYkBvJ0NYYWK3jo^psD(v2_A*1yN~x=yc_shw%MA&~EM6`u+>1&TUdmC5UL6%U=RU33W0fHG&N+0gN2@T)rY8+EVP#iklT~10L<$np8?{vxDqUg(z zss4B9J~s_D8uk0&+tlelETND5u3YH!_2VhaaRoj^$FXx8cY5@Br(MZy^m(WKoGAbE zP7Oa`-|*E<_x>@XUH~YHM!nna4x`?GIxZUZZm&BU^(FbE-hVmHkNVR5Q6I&Z8ujID zK+?VMc>o9>jW5-I+PTtwlu4c_e3Z%eze-R1E4~`?6Jy7?AN2c2pDxt54&0Wq|IYKj z(cA*LodGX8mA*O_LZtqmM1j{}ACpL5dRDDwyv$b`pwwLYwkI0?ONHIy8z=al$vi-Y z|C)^kT&fv9;qStL`}UIR`4Yu9LMGFiyrkxuZju+h!iED>#iul{ zbW`PYq^4tIC#OoH`$Lk|_6T)p%At%wSJ+y(3~WtCyUn2mDn!7(Cw1bO7)-A`q%pPv zo>Ceae4_BD4&|5d|Z1jo>Z&aSNT6mP;vv^r;ooyc-nQ6?~TllWOzKOmdWk7BJ@qd;Ysy8 z2zHu8nUUnV*rl7~Cn7rm3V2eDJf2+2;mM^Oo>Vy<$-}UrFL1^bD|L5%t@i$aj9>Cq zZyg@_s`CP#Hcj?dP#k>K=?lPP8~RiC}ayV+2lue#!OR*aCtmTEQa4Ia3LQuotH`Mzq| zUcP5Cm0UvZdvU3QU~|4o^*oB=2SFxtICQ)F0_wiTIJf>po11)yG>Zh++{Xg_o0an?$uc5EX;YmIr!c*j{ zB0T+1eN{271D@m+8eiT|eAPZ{GE>%Ma=z+Q`eF+d;pvxsRrQ;ED;LW1RdujkyeRTj z@4`MLQRJ&G-ps#7fRg>YH}qBWrUX1KBO}jO-31G5^L*9w6yFG$%p>IGeASo8K8F5h zzUrUYXzZ&V3w3GAS1kuEn)@E5js4x6*9wFlj{W=57B<{&Q4fC z2~Q=8rGzK{l8Ab$(BIl&1tl(7=x^-=Jo%SI@K3rT{{{NtZt8F^iNwbx-Q4FsaLK8b&4Zf)`MZoA z;~rlA<&91<6#{D|Z$R%FCDU%&tZxEk(E-)@8GHZO`+!7 zc=o@QnZmve`NZLr(RVZRbP{ur9f&NGEw`Lg9uAr6?*fXN!rq@KBkc7!nh0hxXK(X; zz}}|)J@#}1N5_KYV9!4-_5amB;|rf;T}N%|qEE7l$$f@g2*`et)gd+S-?)@>LD6T( z1t|YCiE!$lCb^bWQ~!*wLk_Wx8^zW?O!E{4#IB*a&w`2qV(%b*Gvsa&y5Q|yxF?|K zTbCZO{xgA{WD4KMKWmLEx!`Z?7C^xrO7t1*`;fay*l8WrZOiJt736LbI?3K}y`f?U zVQ14rSjmIl1&a{>vriC~=#Q|gpjdr-wxeXTHRQVD9$BmhjqU!E-y+Plex7!I z8j7I&CFzf$9F)7f&n;Z&*FgCe^wvOmP#*9B@1sFEC{KgiUy=jb8`EhWU3Wg*0;owS zpYacNfT27nzX5v zI4B3@Q{h%Zrsn?X9-9rI{0t*OUb!9dviHiP1C;gdtd29#(6MSI6J!VlwucWZnXr0k zxUcq0ce=v|Q%V(}2QQ!5xDKX35BDkYU;K;IS(MKHi&QbZ>D=VMNM$$iS9oRZ6`QKP zJd_o?G;S1I{}jzrgjbfq`)TLjLxQsSzmk3i@=$gg&!6z|NhA@JRmJFjb|;w%URlXW z=#GN|lqEVI_DCoP<=fzHlq3t~kKq0Uc_=%Z-YYBl&ZoRH3I!-j)b%rh5GV)b*Wg~1 zJ%FIRax&+Y6H@||oh;8Q`~RKl+SGaFJU%gvGFM{9 z|L;`kRDHqrFk}+T$opf1yne|0D6eL(nI5I(`V&@9rYz$@{Tptd`G5X?SO9h1Y_Fa! zL7n5wRa~ZDlE?fPvBQ>d_@DCn=i!uy`P%_8zfT;Cr85%xZ*9k7$)i2D3kI>Wt2qBJ zwY9y4HVY-Tsf61FW;%AzE;;)Ba1tn&g z?ZnUe%9;^Sy0DDFD9IMMcO>b6q)d|Z*L;Nn39o3!GBuJ+N3s)?JQ7JuN$fvCyCjQe zFEQ~h{iHJ>>yFs?dl@^dZ?jJvo$2FA6) z%lT^WGtclk`~gZ)We zgAB=M_ReRP1jYgBjC|&beCFPKCUHof>CXAgPWjA!fpMmnjUE&0*#5MyXKj(wMI2?4E z?@#K=>=+oAvrm4`(!e-t7YD}m&wcsKH-T}rE??kHJEnhNT$w%cnKgm&wGNE0b$-qV zfpK-KSm;d`>f-hdj5Dw>KWANFTn+BZXC4iVYtd)(bN-&se4o!0F7hXJ@UIDsGrdD# zTn+XPjJt#-`8nqV#$Cca`8m($=X??vcX0y`%j=)9fpMm%1;(kZ4veeLwfW3lf$=UN zKj-;;=KXx;pMh~#YS^OG;Y@c7jA~%wE11eeaV>yF@#;=&CqVIsJF?jd#b0R87aXAY zGV(?7I5(Oc>3`UZ;)>KnajiZ?asB^Yk@<7N#{|2tWbSg?50`|PU_z+Mb$AvXZ=TvX zCdl}IO#FX-Oi*wV*KDuwF+t<&vi0Yx4MI$?iaB@*lngP!bEF>Rvdhbq`=*lLMGZeDZmRJn% zAPPczem-+cVAd(;&-pp;=jSAsdIQ%>ZB$^Kwf*yR&dSfZI-hwppZPj4?xfw9<<)IO zU|i0D`8mht=Uf~ZSDjb#)jrA3>2P?S>Fx5FJp*&QuIHe9=7_-DubdO}b1uneZpml< z5ST|)=E?k=W=HrMxa%2|&m54?TpSqJqPOMeypo^ud45jI<#`u3C@}6kuO2!igSz2v5TJ6^`{Q9P3v&aJyhM+?#5&?hlVz`4vv|%&=eKL=VXO6^`{Q z9QTFb*t)W(WYI5ilml0AfqYocw0%fd(CBJxy@D|OMzL}qX0-nviqAKddLrU8((glw z1*Uh}eQdAdJG=#yIMVbzlDYwT>G+ zZ4yP^T!%ryEPr-9%bIS!TT6aZ$7=A!njwh0`7S=dcQFczqq_Mn?&cS<2WEOtLvx&d zy7}(z&5I7EoA2{(zR$b)J}=p?FJ87=YdzjtlH8hfkLoE(zdV3+?^f=d9v8iDLtb`6LY#sY1ZmeejjR~1oxfS4K}q`iQ-K4ov_H*IGj&)6xX3v3PPLVJ6LE3qvxTx_>5rYiP&N;R`7`n8l# z9J2(rz0ohXT8&iLnP^tp5BY6wPor#=-HCLy-MWCP+cfp5wKI^nu%FYFEo}+uR(3Dy z*4i#dvyJ_h+HS=FV4L0kol@=WacH);^`tx4)>!ChpQThMTZ6o_J)L@Xu`iI@)&2ns z-Rz$HcDGkj!ya}s`aNwun!W6U)W5g=Gw14K2V$YGT}`^59Y?yqeStF$u=Dxd+73Z~ z8~Zuwf%a(Z46=(UJJ{ZZd|UerzuVdISl-?qOl^0txAD89)pp?!tM_(>+UwBY$*x7G z&VGo6VYU^y!|mS4ceV#28DVEp=aF_3wH;-b(|Wtur}*8~b|iNNei~6Z;cvD{M}*XJTg$I}Z7t_6Bq&**dIEwslzB%RYzB-gYbG z``GE!e~LYhv+rxi^Shr-@w>l0i?UPgV$OSj)vIg=+Bj#QX8*)lrrS$7`waUSxijsZ zNM_mgl$vc**qmbz;didR6Ujlg9h&p(bDZ~Jdk*J4#D0LaL(PyADdzC`DEPsUJsM48 zha$FiFz1Td573X>M<|=H_n?wAL-y*y#;kUrX7@&pr zc+`vRxf*VEOn)J^gGP^S&Tpx`yPpty5x?bjCAk%LAIetRN7%=0Zm%CG#D2-3thP4~ z6Jqb%R*3zorx1Gv7FybQ$XnS%1`DxI?jXdLaK^3du|tK}iKN@vow3>8E+pN-R&bV% zwvtkv>_C1y+hIt$*mKD3YJW?qZuVv*-R(O#k{)(1TuV>;9S*LSZNXW3+xgU?kA0Kh zzIJvGA$AeJ{q52G4zMTlyR}`-?>4rc-+@-Qbq3kn`5kN@Xqy4Cx5PKWxq4sj>u#NdJ=}iI@6Prk>Ndj0x!#esl;2VITO_;K zGHSc4?Sy1E+mGMTc1M19x4ZH?#!ljQtesBH$Jwb&OXKY~T<-)Mr*|gWX8i79TkyN5 z?ZodS+n3+Tc6)yJvJYc1a@H^e^&+iO7oIagtUnf1wzR&M$`xU=)tfl?u+JSXKYz^sowj;j>+rIoBVuv9= z)Q;tMzI`6a0=t>th4xOYEwbzRJXz7-NH4XENiVY>kv`mh%kL33Nk1;P zXCXh*o(sl~vX}CEw7r4fW9;4huCPyYrN`P=`90451<6YL8o$Te_xU}+e$DSHyM>lK z(SF76N%kM$@MIgKJx{TE5c^cyn%~oGcYaT|gZMqe?#%C*b^^a=*`eV1Y`Z(Z=h!$` zbgmuDxmMd(sM~q=7OrcJ?F2HkZ5M*HYwQNb z)V22S^z3!^3+i*dJ)Ydnb~vN^M>`++X4?bgd}?br?`Q0C^Y*)a7n@($X|%(ac0BU0 z>|MyewrkP+r~QE6Z|yAF=R12P7XD?wM(2BbKUegF?La*@+aX-_TedZ4eB18K@84`+ z&ijt-3FhCm=Yoy*Y$a#;#?GVlJ}^T*rRq}*ZsUuXor;6ACvxp|_Ic{R(tg9a*4rhh zuoZ{I+A8`hX1ntnw?p_%*i&hZq}`LVr|b%9p0-y|hm1Xna~0ShX`e#712r$Qqp(nH z*K^(ydoguxW;ar*)b7Ap%IxE4mfHiVbA=raqAKkzSZHpi(*jj?9Cfa?-!h_G+a&rs z*~wUIW9M@A-nJP%)!XhwJ^R>0sa0Q7@+_w;?_YsYQ_`G^FCS1wW0{gWxxDg0Uj8Q< zv3y&fzJXJg4|VBLYLD`rtsa@NPg4nFZ>-bBpTh}a_6klMw-d0Rumh-E(!Px(Wvh^+ z?Twr=E|+qmc4g)JO@K)~ zK*N+zZFLqpCRN2XmLJe=3rr^U1@J1LS*ETkOZ^~bmSbv7$!aQ7zOZBjWlC1k2j!== zJqTk(^ZD6Tr@MeeD`mIRNt|_8gH|qM8X2ZYneyyv zr)SQY4nzv1dv1C$>62XgqSTKRu_rrbU25QNFsHP5n&Q``x^Q9kOjpVEsbwI43q`&8arGZYBCoC`qjkZLw;d~`JtC!L zQu?`zG`n4|{x7x;47=S0lFCc03pR_Mpx`bag3C-Md5)OWQ83BX1*OGYZR$9>Bzayz z943)^j_ysaDPI7SF}=@6x!)u%rZzb_BZbsJ3@7(?tzl9#$w}_xm`v)FI+!VrDKjNI z(1FQ=Yh0I=_aJ>pjlfrOfyytaIh6D+Wt^)llRSBz47)0w8mcr!woVIGQp+Y!FHpFY zNsUrv&v5lCFul7XbGBBHn4uwIX3m*4+a%YvRMj)RB`-0YKi$8Uc*D36P|+gw5^_5R z_!_%`T3EY=`o`>C{Kjqno*DsZ+)L8F2i#IN$%UrP_=-O1WC`JdfK$=8R8SrLD#{i8 z%I1-7tIxDl^skYQDO$qM$V^4=WP8~3p$li4iU9>WZ&Zsxg>~fEV{r_|PNVm%{f>T* znSM`EjmIl`7T&0m?qpl1l{V>v79L?LwyC@y=G<9*%ID)B#*d#qbK%_ny>!s_@P&Rmt_HrjS3f=8#hE8&ehnU!*EdsOUNe@TR)p z;wwfZB{JznT+c49w;xZ9#NAYkZYdORN*z@PvwOMD{8H*Q<&1FyVRPy*+OlG-W45Fc z<6*{?ABwF{Q-8+&Rg8DcXQ{oVHo-BUrylABGtn_$q}~BM6??doeq&0u1_~8>#y3%& zlFfJ$dr0Xttko>0t{QC7CEbSbvK-%|J>70EsMb{%#M=!g?&p}eHRIA=k}fHw9~T*x zEz^*;T{ghjON*DgUzbzKr+Acw1UE#?tiNfWDLVVjLC|pqp?C{=q za_ZSd71(uaz-}a5&Elrt$7qyQ_Q^~|mg-6CR`#o?C2dlzz9#L{z?9$_k1Iwp~xvv85AJl-*>^x}CiCzM`IkxY8=ESOb}DND5mMwKU)>Xfyq z1!7KeOzZT4OO~3-lPh%2cB#t%K;sZld1&+(=H za`uk+8Z*A~+VbVFql@}s^ZFJpeP$i$8!A*mQ>1Gjx}MfDw(^Y3hvi)7;%!akSm|fG0XV7LBI%WdQa#MMcWA>p8%*BqG#)JcNiDM2*FP;hW z8^OZuYprFJF`|N(!i;jTb&y?RR>k) z22Rzy>M(Hh=z3Yzp_$uMNCT&8K^QnglU%=vRUMX@P#FeF)zZ9yQnf5^pi~{6H&CjM z$QvkC%ku_G)scAvrRu1>fl_sJ-ax53rbg%V1Ep$3%`_N4P^ym24isIwGNOT^%U%@m zY(veW`c)^iJqA&cu5LorNw<I(b`|^P7D|CA!%`o9TtB3tFn0*V#c2sZwiu48_ECaZ|LB0*{_B#+$OV`6S2)(yGqwHW zOdH)285wBTUo&I(gc}mK_SgK>!!x({*T3EBnQ&3tokmL;^LA9Hax*VuXCw;JRV+Lg zEnRG2+AL$fJERSb0Ml19t=a7>d>`Ck6+HBC>{VZAONIKL6m0b%2{Zp~_~bRsMBamg(p=C!Mzg#UE7{{*YS__*?`sEQyMF%~!n}HfnU1!TV%pumvt}4n zen_1Y4v@W55UhYYtl1i2XWLStaD4TqqdWbJR?2j-HHx7hM&n+n@+M3V!|?9-j!ZAx zQgeTN_4*a1mop;oyA!N%@Prm>nNc=fjB5Novc9J9r%?Q6n5}qtyA~S!D9qnb{j8cD zjmKa*wwT8)fM6zOzR`sgBrD>B5s#uo9Te{YvzWx8P{)yZ1rnx&%e4vHEgLXYPkmHi|j>3DR+&!%n#F>sd)TK zw6;TiFk~veCDPI{q00X)do&n$p6g54$+0E;PKq5vZtG6J_YHP5tG6CBRfFs?Lqcv& zx`IMwHwAm^8E(0()nx5?JtH8~!qx?i_Pz#S;TnwhoV|Rea<|d)YaDeQ1ufh==wiwC zr(*FdwdcRbW)Nu{m=0DS)azT<-hi=#WMG8~Lmhs@$jZcWxaly6>qOAa18MyV52UyU z(gun=pkgo5Oqql&$EgFL$B(XEd53R{b3>(Pa>h;ypfeRVt|7LCV(&nmdN3>`oQCj6 z1V2EvSB2t-BGl1?;>AmtAEl`wSNa2=FMw))7c^&hMU#@|()l^fj%c-kN=s(*hG@`^ znUyqzj6GjVja!m65}lnO(?*I#p&@xbGNh8>1S;eT%t3jU3YbYam!fs7^8!U*@BG(LFe^%zJh#2ayL`F${#U_j)>jaG^_}9u6Q|vVNt9 z!^f5^JTlJDyzku1@6o?SWgI#_$bqB!cC{L{cE4;2a;}U6$2SfeuTto*P{v{7oA@;J zK8ODls@#U^b)i6K2ZwJQ8e;#(AXGalSY`~nqg;v(4PLPdrBbMi`uK>T95a&yhHv7V zTLvr)Mx{Swx=QbcpvQ-@Ln_;|-*_nOgYI6^H)92b9kYf^QC z3yy3o1w2gNa4@MOV<=R+d$1fijcujqVB!_`L}@JKU~*(oj+u;L@K(DJz1AnLKklc9<0z^dfAq^(V9sUB6~qb^Xm$`ATSO1r_JF*GAj> zuy0B<00>=Bs{-xW&S(W?1dz;yl|slE_)(GxA(@K`)I-bQ7fR+l#l;Q+SHWK-+1H9v zE^ec$yaxZGWX^kB;$Tr^W9D0Ag~O@I_6k}q%f>93vnEpZw?$fb%tUW0RC~{E?I04aHZ%d`e;q)KOts zR3`4q*otJ_g=FnxG1CHaA=w@zMnf(nTZQSR*v*AxEkkW$=2G&qA=&YiI2uYSBy*uy zq?j%lLa?pkG1D4yA=ozI?AAmn-zJ>k z!}LrwfI_g|sP}|S#k=`F3)QkjFwigrTOlhgU!>@Qke{DYCM~=n{YX1b@E%$_Vtjj@ zp!Er%7ZkU>O?GaKU~`9iLvAq`C&z_Vc67q-5jz}yA2*t&#EwJrouJt-Uvv9>O|!$B zp>F&BtWLr@Yhy}p@Ywjpa}uZw;kp*DV-=R!%X#w6>Zy873C?@(H9$jnZ!UB<$ou7e zBENj3pT75r{PNL$P)>1!@*4CnQ5iP?_YHpe))kdsbB5zOh5 z2h^xQ!YopO2&gN`KS~7z)SF0L0~Jce0d^DI(~tw~77}lZ5n#)bG1ClkfZdqArYJ>| zy92;B*YydoZBcInnTii~_PNq`E>reH&=Ed! z*i`XaeU(?op6-_^8As=*$TQs(*^dhKg8USDW_%iYqu@tCmG{sNizwiy$TQs(Ihl+- zq1q#Ys4MD1}Lzmr@`Md}Tw$fa28aw-M3y*UEsnX2YV z&6_bw;un*%hEuMFO65Q5f*N;1QM30ZyMC+7k<+*Wopq{SS)}y-K~bmRNsheHpFs0) zFKp*(r+Ii-sJq6da(wi!*?6K`Ku*4B2P&$$@9Y5@!>Y0y!(F!17`~VD-U7L4xN8SZ z!(YMvLn2MX-CAfG?ww+mg8WGC;YRZQ@Owz+MsiO#k}rT?1r=V!x!hFRH#e1b!d#}G zn@azI&flTh+phK3#>D83lR$_tSQ->(Nl{a&*Xx8%2gpsOk#5{X zsZdDT`BQ0hc0S`6%tuq{7~>qzHbKm81s9arJ$+GE`b?V@J8-hWkaIkfNlcVLn-y=N zxdOGEAVq5h7}To&o)Bj3%tSBC2FG(k4y4bho; zUD+A>3L{_0Q0qU36U(pdOLoq;Y2IxSY#ixxH1Fo7TvM^Rfs@_l5%xvx9m!<{oJGE& zxnflWeKbtWPTTi@J37g)xtmQ0TQ3h$e#BrW?JJ#T8F~aNU(F!2$0S{tyC;HOpklsb zb!;-D;%i~gggW%DL+`j`xwZyls}HJt9lIZQXY*mEv8N_WrlI>JdXK93Qe#i^qEBG| z4%JRQ+IJ2+ZtnOUMRz>HoSiIJT)N{Tb562MSJP&%MQnct-J#Fns3w+mnLJb{8%(bs))+pP2x5&D*qyhXCZC{ zY)`BGl~x$=8l&fGe?@Jy$#>#0{%T^`6t>-AuT3_08-5>S;zQX~bop-*-$QM*;TIK7 zWK(pYFC2}ll0u%Ngxo~lhD0mK4Xs`zdWg}!Umb~|Vl=cSkQfKKp>I-YYXN$PLh;vNjwNvn)J8)%DwNKqvga9K z{fW*(_Y5eUrZ!u%GFq31A~RPRHl63CtACK5M5?le)M1WO#>@;^Dv{pj8c(d~P% zyql8MYp2tkm`uixK)ex+$D#NkFz=CgTg(A4|0eN0R4Dz}iAYL{=me4hgL`6W!?$@zoU# zhQ3*H9d{k<{z=;|%)ZG`*w$+v-dnd0_D{B)hSW~(r7fkY$zsP%GIepv_DI&q+1Jkp z)aA}^^hoxSPdJM6ErQAysmXf!QgJ5?T*PddQh_0>H0+{OdrqulNU|VC;rO{|?Si?HP<$24fh49vldjAwpU7JBrMWY9e=I%b z$Y37FoI5eGRzZB-Q4#8vs@1*?ihOrQExA)NZA$fN&o+?((_8WZ$@o-NYB#cupbpDa zsUa|@k~mpRf0&C%tc6UeaJwRFdVx&rl8krcgbuscqja@O3A-CetQRBfK11R!P@&2= z>~4m81L`R3o|CnCtZV{U2i70S{}<%ITFXvYHRQnBjYJp7fi)_f$l8e-EeqDc=njN* zw4M;orfF^%b#E}uwSF$-Y7RfJac;${`A}chT1e=WwDEzbGP{)}Z9N72OjnldEyVT# zYh}sNlA9s{Zs5Xx+>l9FnfIEXB^R4+^HE(3SLUj=pel4YnpA`vYwR}{P3cFX;py5eRrlYlgt9_eq< z4TS9rb$C+!a7MySrWz8p1FrA@qs`pWlG?b>!rA(Sd+KH%itQn5ht6KWES4xap@>=| zSOOKCwUE#`n22Rl(ct)PBJ(5762BI$6Hz|_ieCzI5s9@>><*ZlNn9=Fk1(4^JT2x) zn72s04w%qJCw^b<+yM6pQiN^RM(*uz$FMxm=>Zf=3fA^hnP4Soj+9SMXS( zR9Dc3uO796iVvTDsIh-axEP=pf*w$*!23$D%eU0kLmLwb-B|SAHt0D3?*OwM=Zt4lTZJM|Bnn4!VXi3;qQD%;HRUlRj)GiM zt|oDs7&YacByJa@rhJsdLy#$pY89F?vHG4BDIf9?c-+Dzt^@PF0blYBxS4XVK)wNA z_6=yuV&+E)T?4-28?X#vGpKYf4fsLmGyZ#m8t~0Tq7|0oXOYzwoiB}^X@y`bb0 zm^RzuE?Z@rK1yX9Zk%lq-xZZ16xaa@4fjAeSKLIYsNr5q6l!D6HQX2qjD}ppO(Sst zJ&CI!Qx;uMQw`?=nJt%lB;v4i!Dl5hY1XwakXes~KR_f zfZ3cx1?0{dSx97UWzQLf0Ugn857EBr%XH|GT#7N|j%O0Fhje;%>i`P$gIu=`Be4_Y zy44iiO3>Cj+pke73P9gU#AEoz_!EjJDKt)29)y`oVm4$7cHjg{v(<=FQG9-HBDJ#& zOhM~#^q0!O7?_htta1hvYo3&?L6nMum_H;EC(3}r&b8>DF9QlYuO@M&GoZ#lJ!>FJ zF&nr8195ibT|eA~{+%+QaO_bM4~tPa_B@GaA=eL)iA2_Hu$6NVe?#{zhz8K<|Cw!o zDAghqxib+vAv*o%6!;W!r?(XhR53bz8B8WokVEDVB>n|CWIBhlTQFixl**nq zayZ46Tn@x(wGcZZd)g?)l1_}B#wn#W1g42swfpsk!{R#P(2Ica6#Zx z*{jmi;`Z!Bas+XS3j(i1?YEE%0{=kbJ~0XcUnKE7RH!m82;2hq9^@eVQnnURs&%La z@!&w2m*-0J&C<9?Bc>gRt;7hj{YdnI9AqOC2@|EVmlS!f;po;uEMT6=kLpZ?cI9-q z{o;XB87;FD@zvD82W+!_;MGU5)a=BNX=pZg@zR_`jkE?d-;EuD#Q$RKJix3dvbW!L zyXW2sOkff)q?sY-j6@m9q7oD&s7O>$2}&>$Toki{5fv3MhgD1{s{-Ph6RW7Gh?o<) znB(gAdrx=Y8QA?l&v&2aojIrKoKvS_S5=2f4U~->ZAWytlPgQNU?G#Ij?rC&e=Cxo zTdmOAmui!vTTnKMpm8{LIH)~_afZO2l2}B*slXm2;%&reO1Jk+Tg~(cqSegqMYZhF z`krC(>(E#YZ1O)mO4{?D#U0l>@F!r{CjXm}c>>tve;dRrVl?^x5Mmeb3eJhtqg`)* z<=!*3&ASkfb0!qCr!xv`?tNeTrLQq#b*o>kis!2D`!*Y{Q^M^+jM-fEecz_V>j}FY z)Y8M_?gPL6Wn3|OmLdddO`S>LhkkvTI){LT3Y@%vmj^!b>uS=roq$cC>G>T<@*}^I z0teS&%?4^spFK^1%@lYo0V_e#i%4Ehd28OA8x(4mx0&!Kz&K5==3W^NUQNlFetAJ| zhGwQV8+Zk&mw?R%K7e>vjAjFWLi`~{vw_^YEX@H^r5;Q^zbCm$dz+ysD6&1Vm5(lI zP_PRVY6?mVk=s)p&z0_oN+WEuF+Mgp}4} zvO-8=Cd}(F_kqMbn3Kv9-XxG%0&@?i zpAr30x>{#s*JoV>tX5k;hxYVoI_s1eN2>AiP zKMZC(#8{A+3NstxI5Bfzu7J2q%$YFvK->Ym{&Ih42V8!}<&;x+)h<6p{F5MC(dPG? zaNB_S{XN9DV&r$C0k(wD;5ZrF=5kKn$6##)@kNF9s9F|yDWW-i1@vT(pPRM!b4`7v?}SHY;=7p%Fe zx^KkIKI{=*!4pw^7BjReZn09!-(JN?9wndf7AcPzfj_Mu22%%d3x~v(1OGSV{sFOG z%-1lVKztx(H;mtqYd%b2o5<9X!!X-(Rp?C-hP?bO_|L0<{Gp({tCUg@R9vEzwh`3= z6U~5^pQMv@PM65763hzno|0fGQ3Da`3JSN@yx^GmJJRNNORGUpu_rcv6&hEVdF6O( zT5zuf8wUQ7gj$a8LTDYZ9KQ+il9;cFc8wC=f5a%q4Pokl!nYz*mg9E&d_VMy0fYs6MFo0{&K*Lm`HN>;{M>UM6HB+ys!=0karlzL<|-?uWP!aCx~f zs#HCo%XP(HsGIjWdOxY#Hr5Rsd#=JSKAWM&z2vvih+dr6ALeDhp)s~m_=;a1Z@awe zH?VD&s*C7~oBfxxWw=^-t8F$m`v*wv2)<&x84qfktu20i1+J~2h(Wl~@}%ELHvdh) z2f!{~Tm3Q_?_0@!3CR8s>zS+C;CiF-7qPICz(t^_upMvR9!8|DPeF&0lgR6cCfsu% zvY9T`K}Wj+{nnJJ5XfA~{4VHNM^~kjn(?#_up7uhr$W7jR=at^OM%Re6iU#kOkpG7 z`+>Rx&JG)81&QdV&qMU$ZK2@M!uX<7aAYK-i;g;vZq`N}t8T%vQ2)qK-OHn0W){=% zf(vTPq3cQHT9BtD|e_)?69lHVbI1>OJ| zZ4-G=9(fR4RJ+<@UJLRK%wh+KR$^rFP>5k-Wbr77@qlaZG)lj%G7(Te!8J`(HM5DD zq3EJ>h#jWb*IN^NyTx8i%!P_=E_a?^8WsqjDn>Twk$(^3wneZSnd^W}3Lb*E7i3Gy zyn74oRbbxz2Jwp+d6(0Y3ItIKb))LwI3A_&bbj8zD1}Od%7LZO3!*E?mXxJ%4BQxC zDJ+FpEJi6@25|{s08$>VS{~=&Fr`rF@=uPFxDlz?Siu^wbg%#wH;?q9%?_ygiM zF-ju06_a+rj`+_faGx}tiI^hBe!-ZETa{Xh*47k-N-33XKxb)MC#KRm<10Ru&NE1L zLB116Y=h|!QDp{BfV(DbAg0m=j;Uxd2m@yzH46C=GH?#eB#4PJVAF!RQCo`}aL~== zFGjDr!OcTKZX3HTOfv~&n*w5P+@>>tC>1!7x+euYMM?(x3Bcmrn%n>7BmqIKCUYXiP z#yN4@h^hGTlHgzYmt)`B`en#p0<4+b4zU(kIxj$M6{B>%hWJX1()knOHxN2s6{Qmw z^;`L$?c;pCHVl=(jP`=)Dn>@fLW~h3qsKu^1KvQ*zpkU5?&vmzTxgzU^Q$w7oCmTM zXOpa3;cfsn$$A;$SuvVqeFO0&h+N#VpNk*mUntY*iGOxmW`)39Yy;6uj9eTFF<6XT z91Af9cx7_&!*uz^R8`bSgHQ6m!@f-sk3)VMuq-TwI1QNhYay-`qbcGu5L?B_`}ZN< z1zw)g8F_A4kL>)~Z%_V^p*73pHza?Ms^*>b+a7I!Yh5-}=~br5UBs6_6ExDR;!<>@^dgFKi2^*;VRL;NO?t!Q&{ zFWl$AoUGfP=?O3=dqQ*rk(cGs@D_XdeSUwI-_>4@L3D(4<>f+%xnkty6%Z@M$jfyQ zYe1Ow-jUJDs62njzh)n!4RAux{h;3qI^m~YJ#mH!)0|o^fZ)WW0X^`XczuCuZ z39|XX%yx(9C`M+FfEXo4W~V?*0-@P$`HQxl!MS{{m5vus9dM! z|9c;!-H_}cRT(`JVx$-uod&3|E z>k$7EBcmTe>;|FHrTZDZFaO$ojQ)t^zojaprJWMI>J}O80?|o~jP{490^xAGE;8!8 zm}(UjL~wt;J2V<@MG{2vfMiILHIommV3GujfOIS7rGpTBQ|n4N$3K1O>Z*-ff4dNjn5Vr2Am zh*QPL)AJ#g18<-%K%40Ar~bN+o$HCb3S=wJj6Dvw0hqD3A>I-rW1mBO3L+OL?dRg+ z{Naby%=hnz{wQ6!*q{qHl!00832}fJSse;77;yYM(>?ucCvg0mGd=IXrC}-H8t&Qd z$XGWgWPm=D$jmTdn&);acM%zl)X?zaTUXglmn5qrIx`39knT*BTpqd(P=f z0+s_?YdqpN)LP?1`0XJ36Rg) z)X0PQ-qJp6jfsejlb)K&5{T2qs5P&KxK4~L-VJdlVAMOF(qA=#Ow?FwY$EDWMHih% z>;}cwSZnMg<{ib_TH}ig!UAz#M`T%R)EyZO2z!wE64-zc^x;Wum~2UzcP$8M49vS> z5JSbtyQ3h+f+&SPQFS=4N+~>@e@2|bDG1F4mcoS)=YVWUSqguLyA@ap+aO*MqZHnU zcn?HtjfXFb^Dt5=u-14lPU0J+_5e#F*%uX%Eip@?6=|}^z#mMYjh}j_Q zZgnpSt1CX!R7D(5eR zI7f_}za8RMF*3RV;vo>OHEy7t?pP~3>9xj-L_P(w6=!qSAK|_RHfL>E#g!D;ob^D6 zULbOjZ;M%l#pU}^{!jb3I04bo(v^#6LM#v?7gs@CEk-Wh4sk08*BX_PQQrQIsH(VC ze3CzLH03MTHz5BIu(if35YGYg{#%GI#AvNidJu&T%=-=yZ9urzI6typz1DaTS2oF@QWt zRYse`lmjz50OBAqGI}_~NDvMT=kMpK)*2J`F*_aEW2G#!%ORGEk*_yHtQ8}(4?wI3 zq1nCrn`NzW|2}4)Ms~B5W%g5u-C|_cADHkQFtdda1t2Vf@)i5`hbj5Vu{DdJ8Ip~q zDx-rTs>I0XREWu9 z12fhYqO%wo8vt<-h+JH{pNot0|FMthBM}`bUAcHF#K~gh;*}7Wi;>lvA=Uz~@QMFg zYYdNOgybCeY_0JCqU$Bj{r2;isk>SUy`FFgw_4oly3<&2yvDdpXiF_P>I|hbB3*lU zHwzr3z~c^PW(&dvM{c5_Zf-n8z-(` z`|J^i3B%Yy0;fKvrwVTIGqNY+O)WlU5_J>BGCpzy3y$I3 zqqpZ3>Lu(h$e2;Q?FO3aE{Lm_u-hQh2$-TkElpfm!tR4CAmC)sS@%K85_TV?s7)t! zjE~}0dr}DvMUsT{Wrc$7H-xP;UWw>cFYbEq%>}-(g}Wos(pw?B5=CPX{f_#Z8-i^A$FEO|IvhQKR&2rFO9ihZ&s@UtdS zflY{N2%6{?>cgu;2Ln&CF2gUgd!{E5e;mkGsNFby816p6uV@VUEjT7Nn-iM-lgM8{ zmUh{5Zwk%wagKWvdSNgfn z$iNz;E=2x(8Mq7PdWdUepjBGcV0|I*3-|DO+@r)5S7!av_ee93K#jg-^d@8(<~HD1XfOGL7X8A zSp$jMoi-3tv4N?H+z!WLU^G$}A%B4kOoX`(VwD+CYulSP5L2;%V-vY^Wk8F&JCVO# z2GrUfh1g&QRMmXk*EIMWQL%xe61n%wfU@u`@=wcvs`^cc?Pfr|bVb@gOvMJqCUU=% z0rk>PkpD;qbT8}&i0{n6PPjYL24X5Ua6}@v`6LYNLCPP`3=D)ldrMj;red8DiQEa& zQO_6r1<h+1- zD==#9`&opZ2H|_5ind1nCPCYQHS#YZJ{O}#{yW6az#92&kztPUjHt-*bR(}bfqN`~ zpFI#eI=cQaRMMZ_HMGnrkt;0`YYIYF6m6~yCTJiqSB{1lCq}L;hByt_yYQz(hMgBv zHC!oI8hRK0e#GwsUg5JfN5`=Aw?!t>rN1?iyBy8x>DCrhHvudC*CAdOqtf3C@r@Xj z{$CLP0byJDGBV4!Yd`l?BKJL+O|QiAj$&W})>bMY>WfiZse8g#X$a=bJZ%6c2 z5VhU8kwUuku1nJ4=uYKxJ@t`MEY$l?Hqg8)C9qv)AD`?6m@uWL6=6W2OT2%VnpX|E?O z*Sa>ZR>hCGu608-Gt_%Y>s&qUK=;vum$x{3?vfW`Z;hx^rqAc&!6ASQ;+qM{fjb*;Mt0k^k-24fAkf@(=MblWgXyr zefy>K*kk*{)Mb*+9f^M`rloTfvfp73P4r5qxnjZMu+(cwh@V@{L#bX{xp$@OB=`7y zTZh`+S$UZ}AE7t7YWXW;`Ae(iZ^ZuJ+$1EoEehqEn3n96j#S-&Gy8If)cjokU-Bo#@(W$Bs2q-Xh;ndq-J1UrzC9M6>sp4wWA*mW zT-R5H>_&SX4rk(Zyt%eC(r%D0l2RV#HrI}K_GTO}i7uw~9q)Q6#*J>M`!MH3YA#LD z%uXkC(=rN%a z#ShSciS4OquiH3^YLN5wszoR^$k`Llq1YhTOJePn>A}vHJ6^ZvDXm`4*V`qbN-x(& zg%hguay?9CE;55ADUGhzl4;k$>cjszt<|BXR)SSH4yp!(kZ;R z=B2B{^Hpq1*y#cVk)7(strst>1!cQdQDf~|*@pZi!qU_{IIM$2C=k{`!gaUWJ?n4{ zj_;XqU%9jK$-QT7xl#9?inJ1JWrg&H zDKxgds~qzd{V8iNI$v!%jCs+)IuIB)Zu|HFcKg=4#Q+p=ilMExsRD%D(2ul6t&a z>hVZwv7|O?IQ2CNmej^-sg04;i`psJV74|yQX8tJHbhdjwK#jwq%@tCtp}^69*m^g zX}no)Qd*iyYQ0H$CEXPDcZw&bJqb@JSA^%MRN01 zVg49NS@F;BtRnYHo>f+7Tk-2H?=W(9XRF?>DH=;^W5UwDk=WusR4D;Xj5)W;n9fBM zzn7y|#!KGlP0%kUuP2FHZ4m!k=@bOKl66%~9oFi5w7#CTFFg*fRIFEWJ|j@Z^}0a& z+Qv}S^_6;C!t3`ULzmyquI8uP2rzthGy)K61JhKx&?r#*)s`kY@Feu!AYdbCGnXZ~ zYoN)%dEDz@VzDBrBN5$!Ut=1^(IH)!sJNSc^jfs{c{Qa zlmtFhif6*qn#40_V&=e%h8Yh0sW2x)%mUd`PJBzqMR4bW#P2WC061GI?WU&{YDyL$fH2&o3Z=D(dF z+KAEocO=B2Vl@9f9%4FR=3fVPeUvcuXulo}2(*W1_mA;+W>?Qb7h++)Olr<}0mL#f znls)8af=wu86Spt5ZEtAm+O)pRaEbzHdE|@L?dfKkJ_m8*UtI*f7 z{CDk1W@e7YeN99`Q*p{;(eMCy9;x7F2Lcm36N zMnahW4L0}na%8rol%c_2X7+YO_LkMKx2%goLwo1eu(!O1z4Ib_no*ZrNMrM-tFWD3 zEeLC3dUN_zcuDZ5+mziasmCS9AF2{{`s_m}IMf8aqZ5bog4HDAj#SjM419^6PJE-o z$PKg#y3y?nDbUQ5d79_#!ECZlg1R8BQs$VFc#Y1bt`cRg6`eO!DGODW%<904J!gb{ z`Ya^%bE}s+U3*8+3*G82C*$Oiu+P!nwgO)}Pv1g(1#A&qZwj{>fGyevK=c)(McXWh znZOpoADR%572Z*EcsbkY8;mL|6I!!SvJWa7=&Xjd-0+eg z41#HFOyG6P%&C{vko23sN15f+Z)x;MvYk_&u|x7?c_{HEa}On~!t6CW71@GXT?)n# zk*tEL)vbwW4h#^}-Iz=sVUglSjMX}T>j+;ZyNa}1=9Xr@FU7HVo?F)@S6=e?LtyGz zCkc|OzuYp5$xMzyC%1vc)JiVGSZ=w+6eOQgOl8(ZBwU=ldN@oIV@i{ga5lGT@i8P* z*K4+hsE)NJW^%X0OKv($7m|9KAb80cLt!QxlbO5~7ivu@kW@kPcr4VKYE0ea;DceN znG5B~j>BN48`Csdo6@dzoQbvc+NglFXQ#u#Jm+<5nKvM7IehcZWGHV?gPA1Sybxn~ zgEK9>AI{_rvEv%NHmbv`W9=4R$(baSH`JHGZXNRu$=a5-bXa{AU}$N0riGiO5sk?L z>rGkO9b4+)l{_O$BmH#*W&gyV!}joJN=b@FmS0wUInpKX(y;RD7vClNbG$03DEt|r zl6Ud8phcnYLx04;QP8sRCCT(wc-t`i7|a)RGPmXra)`j+A1-Nq*{0xJRji3WC@XfQrSFAnTD(ZG%q4eU74z>X6Q?nEmuu;WAnJ5DsP<3xjB z&?*eX6Q>^RZDjuQ>+IMKk46AkP*(co9SZ4{J~xyFGV zCmPssqQUAc@*dc6qJbSJ8rX57fgL9ryg_V>z>X6Q>^RZDjuQ=bQw(haJ5DsP<3s~H zPBgINL<2icG_d1D13OMMu;WC7zBw#_13OMMu;WC7Q)=<9U|`3I2LB?qXJE&P26mii za6dlx3G6u0;6=*kz`%|Z4eU74U{3~HMS&eB8jQuxfWVFu4W{Aqpumn34NjxQ2D_>GVSybd8nn;lC8NNO6AkP*(ZG%q4eU74K<8tP3hX%1U^wM=L}15>236P` z6I7AjSz>X6Q>^RZDjuQ=h@-->2<3s~HPBgIN zL<2icG^jvpdSJ(i1}Ea-@qryD8kA@9PEKIQi3VD7oEU`1iH?eo6MfqKN3GKi6AkPz z(ZCK94eT({;GcLL1mR(#B|6-$uzO)^T7E-mmFR@K!X6gTT_>Lva`KrjKHYWlSs^E% zNwnD}b&_O7=1j^U`7oU?*(6`XikF;8OtNV7vJZle3U}vW#uc$ql0?G6mE<(6lX0o0C5k&LD=)B4H z4Q02vCD);@g)bgLc*nY56Ygby&fje5Wq0Mz@MPDF12EjX`Cu%`8RzwWZwPrb+Vvhs z9u6=%(OdK39-=e--pX5V(Iq+Zpo>2SD{S>A`=q|ZZiks(vTrTr+-tTO(;bs7Gyjf6 z=zr_1>58#~-jZ#M$+Uysl5LGC=(E5}cFK4dLv{PGOM0OBSe|S*0HuM28YxyBMIl@koTYfYJA6C!#QT2?M)eEBP(K&L-3$qGTJ$8;<@*=CNEU#e$!rI8v zT!^sVr|DAw$*ap$9hnUkK0Oua+F7@!dloL3budiO>rjoVH`0I-!S~d$A3V)pFZc%` zF8C+4l?cA%((VPr^w%rN9R}UGp01%KjYW5@tE*tMN66Zwq8BrJXXE9nq1;%1+2%Q3 z@^>!ZMXyBVRF8{I(W_xOCEpOUtyrT&-Q*eAFZ!3olqa=uQna(>WyG}fn!N+pvUqx? z1e0CxtoVciyS5~U4T3o-X9J#PCU3;s;#ma`AytsvAZB)HX!{9c8jC6SvgKkM&B1Ph zuxUins|bSQ38}~odg@<6Mt%Ne>$;WwF9xHMQ7VsiWvxl5;U?6YwV{|Y{qoE&VUl9} z)&+_n{#E`R@EgVJcPvv;XZaq_Z|Qfy%@mB^sov`rst4QjJ8ptPAFTfZdVfW~W*x|L zf&Xac*c|NdlKp>#_LHB<@?)hp5s~Q~e?Fz_IsdiH>m+E7yirFUc*(oSwf~kK`Iedd zF(2mb!W~G}N@kNA|AUPGz!Z25l|;7wG@C;m)b%%KN-Al~(kG)twMpkcS*jdn_L(z< zrKEM00&m7WRD9U7{AZG~7_=kA3+_EcE$DN6;FX5G{=0r-2GQmjWXpfQuF(g}i`{iP z5bm|B;`# z&I*Ct$;bgO66QLWh|cm?MCE?KF3Qezi9+Ybq4Q}|u50dQlhu=q`c=5Z$CTg!>nI0z zcvP)==25@yfkL;r{4dbS{)Rs@$)=aeWd~IQN?W7!hv6}c79z6ybfx~IE70XEy9s|z z=*yo_!KnUXWr(l6B>5w)K?hQ}1?~@*c?w)*uJZM#E4A@=^yCGndG4=}%k-KpB_nlH zvomi)vd`S9UTSW>?09`z)swq#8rPvH+p zz%DK)RZzPn99uOeKKn|+uPOdn2yfQ+lvT-niQG?Oo8D}FwJudR;Z#-my4kV1X%nvJ zD*U>Mdb0n{6h0^b%5`swz^)SbU~0m95oB~#DU~G}t8(k;o?xneB2UJSoW|44AVXK3 zRNsUZSI3uNZY*NXxcyKUQ;7m3jrT*{q_$ltMaH*hDyJOS~Tm}6mHf_MQi zy}r6B53NQPtlkt@o7wj@MEEQ3SnS^du^ZuCAnS6re`0YSo!rmGR8FGtpSbC6M(kVU zzn0FlqpEd+v4CpMto0XIPAJdN+}|de6nh7{_w^J(7#u@>2Kkbt@HP zDgG1#E5_y!O~t4fyFqjTr2i%ci%Gu=>H8^4>=43-0KXXKc!Yt0V1Bk9m5!G7`W9|5)v}#PHE8t6)X%(;o^*2FnwPP#z&*46m zQ7`p=q+g?D&?eR|ChD&L8KvfucS6*j^riP%_>TkW8LYwl8ApH@hZq#Fs@q4Ps^u5anC=|&wjiZS5_dl8~I(poeT2= z#CO174O8nxZgv1Ks}J03QKBwQrOnOuvvY94jYX_J3iW_H6s9#qOW;q1=?_r_ysVdx z^t{g^bKEJ8C_naYo}aTy=Dt8|6bd7N+XXWnVk)Ss?8Rz9no1ksc9LanKC-7sSy?+D zVmYv^T?26ypbaZ;dm|UBsdV1v`zg!UZAjk&EME^pJP0gbFGIWtxGt}w4o*?SxEW#> zVed$2Jr#+9hYyiXKMeL13-ud5C9$wW3%p_-_MoE82nVn^IOQ+5_<= zuvYXt#IJzbQMI0sE}580J9CwvWmTFzi?IP%snmn01FTf4RjR4|N~I;T&84hTIRK&? zuu>TeF%a6IJ5LCCpYSC(H zKS$3);an-D+u+r3SAy`DbudHaC+WmvifNwx+#^BeFpZQG5W5wvn?a-FU^KP!vOb;Q zc|S$kymlT@rLp#7LFy4{|BTo}Xg&b?W^e(FC4)2-sd#%5J!(a>2d)TNe!Q?@eef(+ z8boo+#!|Z%=laBN5l-Z|))PEZB zei5uU#wYNq;nFa{694CRI~|B0KKfAZvSl9Lq(uKKAMXR}ZE&L`&c|gXsxz zfSArO!ypEWIS^(t#Ic~|FqmKP-#-LmE+MleawNV#{&vE?b@hk|ZEU~%Nf19fe!m0Or2)`?BO|MQ5dJC{eEfIA-MV2Hk;NMimHLN10| z3jB*2!c;WywdILsNsYV|fv3s%qZ-|aWwFh~f zbXSO!m844BmiEB7u+s#&RV9UiP)x;W{j2G&q8;a@^wEdh|(u_!5X znNQGIe~3hBE>+}XS&+4}5&z#IY7lAn1@0A?g%Gnrk;ME@3Ar8aCg6V$vjbwAn7?3x zc~lon%N%lXw&ZI;bRx7B@aw=F4KW%ts)VtucQAsi_klQ@s3npe0<#L@Qeczy5xc{( z2)vl8AS?R0qk_W2<-68VTan))9Zl9_$z+;htg@db>pi5S$@;&Le-(JCSy3XItW$oe zC7#N)k|I+RSQN$bs7w{AM+Z4q$%LA~hp6uY)&%~7_#G678!F*C-^;r@QdezmK#|I` zDzSALbMVx2H3UdKSmgC9i`H*c-Y;S0rS6XUXU)N7G#?<1hnV_0H|ud6e5^4g+zzIE zKE8lPyT>d%FVAqYA_15m7CgX$X=$FuOreu&N~RHUxM6Qk+Pyb8j}-1 zrWJK07jQ}!@f7bhl2h@Xh|Emj6_1XD!ewe<#D$9y)*)PPyiSAL(B{%@vs}hD2Z1|+ z;H4Nj4HPNVKbDY(;BEo_Nia;%yA zCYVVwxd!4qLY9HlGm(YU+SVAvT}KM$l>3m26JYXezs#T5GrH>oXN>daVb5LRXMR!6 z|Ke`!|6=oc;8HLfAsz-r67wqvc^B?&koS+s#PzJ0UPqaRpzmd--mDQGg)zx|2~U2F zV!B(>j87HV)QLNLvG+MhwfHb}Yx#-QSqVP`oOx9T)>%eHfgsvqz&T8B-7h8ts@pqg)z?|A%L;iotYQ!FwSfVm0ZtzJ|6qJ#dHz%rYwadTu zNoon^N6|u4jUwy&*SCfU+LHaUA7Qh&?EK+4v&^;--GeN-^R;`p%r*E=656(I&v7la zcbhYnjHk|ZC5p+h{lw+2T>FW&=CV8M>PkCfiOgN#Y{RhDoGD)FLRVXNwld}`A{A~g zmTLPcV~6Zr>FgmfuT&dr8CQ%T=vG&xBuceM6bjw#a>vW%f!dkeYO~Affu&kAW{S=?dKO8C=CjPt5sz73gz(EBf3 zKH~Zngwfdv%V37?Lb3%|YSM}lU4eVA5S#$RVw%J=?j<~&nJ#%j^x^Dw> z5$k!fz9!K9318Fbd>L62w9uUfJ0#xS5?Bp;Wv7!(H!F1H(O7g=&{(48)U2S@bQJ>) zh7ZtZOQt}MtPG0eJ-=*~b=h;Y6*t@r`I_`un!|%GX+y(<)vMu4y&AqWtoEf*P`6gP z_!NXt-=WwHm^EGrmgs8J;zCtG{i{BwOufo8MGUjAq2_1Il0Ht^|Qy zOq76s0NuVIY=8_GpT=~Ou-wa3@UQvxW$8)+&j7hrIj{S6GpY1+zOkgh>r}qmeJkH3 z1fB{y<{!vJ9M#+*1Bl(>Hcsi67>zJL(}@%ucxNvpTV1V z1lfl0^O1cHu}0mwAm?>8yn`mPh(N9!czJh5LYgi4q`*}pW3iX{mu85;$~8-DEifdyeJ(OVjmyNFeyzK+U?GxDCfR77Q%U9-^9jYp&c z;l@(%vh_Geb{2)w`UZ_<5BaVS4bZy&Lw-}yIl530(fuHpj8oO49`(cf>KgL4__-R8 zv7*OYvUTsh^+Y#fmt=FB_tvKu^#(Hn}z09@$P~_t}0jyf)n7 z+qI!o^JOJ+iXV>p8Ovl~fo~UJuJxM5wiqs#miqN{(>lw4^!KpF!^_30*t^!vHM+I% z-8^PLmc15!nS9DIpIZ3c6_H~jOACL94fQs9vDeT7eA~7cKD${QgIez<^-S~BEJr9br_UU=L({@Uv>bzH_>iKzXQMG|J za|zcLw8HVF{N;Fji7DE@{oH4X!pTz7O7=jM`T)+ljVx=#IGt$C;pY)Pvqqlo48t>) zq(|vh;V8YRdXyd!C9LuH?f(z?4{OLLsX<)@V*BBJ-fH8iDX;8Pz%_^9YN>FH+VWy3 zn(|InN$pJ3nu>KBqehV7Lx7D@$3sk43D_9rN8YediYVH6SZq5J;TW|L;rUYVS}#GwqxuX+H9}P2bt&-yBR*z&UW_|DIgv~+E!;590-0U5l=puXT2)GdxF63)5$0Qo) z*_J2aH(D59i#aCIMPc8=e+}lnH3HjH5`~jUBeTwAw5BEU_3%uirHtO7&SbQvB`Oql z9Q*{(;#{@=>4|*##2n+)I-Iz8T*97~x{LT#pyMU1-+kh9e^3UGPuN=HJ7V^NqI-yK zakENrW};wHXl73CV2y?+B<$Js?6b)_$bC&Sli6Ya9Y{cTP-otPS)N-SN?uIBg$mRg zHEwmHNWR`gz_p;`JEWBC%p)O8oaZD;v^?2F_@iJz`Npu6FCtLC+zI_j>*Rzj&^|=ym!IJX^A0A1u;W?X0 z&X{23=laj2=lnUE^yDw`YUx3?vMxJoaZi1=X{B$d}mMqR|e)& zCvD}w89Cs*&LrV(OJvyAeVt`|>jKz*duNFDVzl2r3}P@C@=K)o+gw>|qfuXt&K`bR z8on2y?HA+!@>dKSuGmrknjpt5KbXRCsMjb($OajLc21t^x?4RH!F~zTm1?fZ`RQ(!$hZ$fr+5p z60ZysUqry^ppbMc6Nf0>>tL@`kk(;M!c?Dt->5JxA)6#@>HaSK8$b=ASt#b6!($2x zqBff)vUTApC#Vc`97t=r9+$%2)iGf!u|CA~0^ad~bBgv$_iHbe=B2oArTL|^#{2JUDBlYf zSUY>m)losX(L+!3-g0eJ0&Y~8_qSYU<^5%3p9cd*N2Xgvro$}09cFP45+4J5dTK@_ z>AaY-DFk}%9TyIb4bNwv7ue9)4x)`14UGdK`U8IbThd=1x3?~5Sf$RV=U84I5Btm{ zmn-OW1s$xQ9k8!}Tosv)I8(N2UBGidAh%Ejx5*VLus#9xK#S#v z63E7-#$!Gi(qh3d<4Z$+oZYecA*xK@TP)|EbOmF3@?ST{+G3+6usb%fT!yz&amjXU=89Nh%-QOUFCpZb@6Vd()Wf{{TTtDfUy4W4L9M_^?z@; zU7uJv&MN4HsQ!7pQ+}*>*}d_^viS!#egKxu_80Ml3n;cG7FST-iW+0V3>3Kz*?2HI zymgatvm4z+>lZ7Cn`;Q~Cz!BZ9-N}&E9B^ak z@;AB?6^0wFlDW|}5i=HFj{q6g3D$%qlVLqzjk6wLk1tG%65w-E@s&1CYOV7Jt4Q?t z!dxuP2G-G+LM#@ej=mD&G7ya!X>&97H@D9DX6jaiZ`dV_b%PtMh1=*MfyjhCavXj&XTqFb98d zjOVS1Jc#?`NiMaReXH8P6X(9=&w6a$3+koFqF=XImu6o zVzQw1bE~JPO}2B*_t0u*VQwbqGBc8u1o^X%fq18Vrs?LVX3VW7rj@z#X~1?QnUDaWBGeAj+_Ik zWcdxVd>`!JLEZzAw(gmy&{G{`cJ#Vzio#_p?RUQtE}}25h@#^5=~kbYPVnWXN^lF7 z9|t{^;LE)!-rc0lgk$2pJKjC8>5VM(zs9K z&YKx2{LXN+7;jYIT9Wb|vq@f;3u>4%Q}IhssH==r3jNL@?VI<$6;?ZD?B`Y&6C76i z&2|g(2b|pltlDq2YOiw{%U6(>7p0?W|1YQMp%eE@7f(Bx-&R(y zOY&9yc0boGX&~A=@-Rg6{Xvu#Y(Jehl~>{G6U-sCIv%TsQulgy0n5TEfKUYBz)_G6hY(t|NXG@Q;Ss z0CBIFnJ^zgyenoQOy`xP3H)U+EiUK#QNUjTa}~s8AoHsucxd(M*rS)^(fmZPn}+0k zI`%`zJOFC{f?SbB7ZCguK~I9ZKOf0eEv+6?)QtW7dzn%NW0{X5yA7lkN3wS^jiiOz z<3Vh^79ewWx?Fb2i*AWj32ml!L!wg4qqz4XHC=ZzD`2imD}5uS-iegnade%C`hfJ_ z2VUbp$neHUkg1yermH-!H3Y$!BHstY^aFrc_yj`0W#^KT!@ddUg~SXLJcljlaPE+oK5l zvtKjy$8U%jORkJ`=`! zHuSAkB;}t);0|K91Jbw%_CV6OMcNm`d`I{^7muzo`X>=9OKmxDx zhj0fx9~N&_WWoQ4z_CP+kcgMLjn>sJDn#GAU52(KibrD1?N-A>>NH7ON$!pW*&vnR zi;2`F5>y+$5~0gM>Z?d5E=l#f#=0YYU*t)<$diZU@H!&yL~X6oXuKY7XC&4l64S?O z{D&2R$PS5RX#mQ6A`Hq`YMS~x))KK zpGOx-9v+RT)}gfLxAN=Utc%a>h`H7LxgWjzK<9^QD5k|9CU7gUn}EL+=3R)l04Zr? zD^HKcF_q4I8$Y#&l$?h1@6gx_QZ4rz)>QymUm+1kFknO?@dVFr>*tIjyY6?y_>8yr z7lzKzr686J(o~d7&;O}`T@eZp&jW0OYABf$jfl>BTQ%Lq53KQ5Akh#MpBTlfjnJ>Y zkH+(8j^*8_OTV_j{eyhFx7QP;ZZdCW@kCVC&WovZS(N)3zbFeTiz84N1yWB(^6|)T zmkZ_V<#hF(r%PN5KfB^Ely!-lhTQ zDa6H~^PCz6(rR-FTubcrz+VKj8R7{*9#w*U(HA+-h zf6Jo`t%=47-ZocN^z~Eqn7ox7D|YC#hhnuHIo4o|Jz z&&a|8W|jJ~Ls_PJ*lP(r8R=5=BF&FqOV}hb$0m=jC2aDTV_&m)Em5VJq*nL->xnv= zi-z;%cM{=z`D=}I?<5-96n6r(?f*Mrljm?IoDAoewWfQ1zF(^JN`J=9F!HDHwl0%u zYhJlw^TJV0gUcGr(Z&fGcHcAp%hAS(3e9$Nc5CX=IMHrAWs_qrlqc#+GDo}cetDRM zj0dlhzGWX>NbkAEsv{Hbxoh6*J1v#xO&r8t6k$2PR6DvRA4h zEDRbF2WhwpgGR(bn%9IuhsQyhs)a$5;~-7T!k`&(kY+kz(46qrt5>S4a>jA|@fU}s zSE?CBDD<~bhzC{Y%=7$n!n?9~scBItaeiz@^P4c}vVD|R#!6A0Toox$N0#WCC{bPK zL;YJ~le(sdLAS?2x|WAQ_eMdH+52O&Uh6AWNB?nd#i+1~|Kq|cPL^7D4~2(S{HLpD zjJ2{qU3)dmu)hCtwmQpbpjyp{+TTVo$*8_ZYL$|Tn0^$#pINht^Fp1-yX?3Svei6V z-%pG4cRJVX@X*7ZuF@J|4>iJ_u4lD}@3}H#tP#HFI>^J&sgGRPDQ2k=ejI*r~gnQpLA}15N>gU8hjZaoW23WYF$N=bAV@8b5|aDr!BJP$F;Rr`bmPb0;Qa1* zU{{Y5Q9NGCQzDu8>cJT3W$Ee>M=*(wMEuAT{}J{|Wk^?&voW#+@G?I^7e%5NSBj6b ziqm57N(3$iPK&_}5bHsaiD(}5Cfs&l7uVa|4`#5{>5ZHj>x+ zEY-qV=Ne&AL)%){q`DT@x}G*Nm8jlsi$38q@FLaJLZ_IekA*YI^P)_;)!HgiZ7p<7 z%?=L~Wq@*Pr3+nK4fvf2Xbp;{A3*Jx@iW7AFp1D{AlF*Tm0{pg0v3aI4@RlwN2xuD z&vmAs=J^~GAa5>1=n~*%wW1zZMba)!6~$Gt#AS8GPuB~v8&Oyd>UN$`t>gc3v7cMrpG&^#WU6bV zI{6kOpMl&?>fXnN)mr~L7T=)V?nu2LQtyg#xVthttd34dbpXYCB1zjEFLsP``l&y8 zt+jvR_i;||n&;0J*;h4`^Nt3dzOL8?OO9 zlHHuWRPkJ7znkkIGROAKy19O`?`7*dTV)%S%l4tTCO>kgz3W~r-rfz-BcopHy)xX~ z`5FZz)!cQG(_ZWE`xDXF`D#{*XzV&zMA>-dpl)beb3#SdbzQ>9o1~TJbcuFa7$`E& zb+d$b9At5WVHVg*L1%Dj{MP8esJu8u1G&vStc2n6!k;rFiPKo@V~Yw4Q-10x_ub+{2@erYEk+o zg`z&S3hSTOrBLyPQ1Otax&|$GJ7W3e-9&~m7nsbj*~~#MjM90PbjIDF_Hb}j^svwa zDrJ6dwfcWgd3$gd85u?sKM;&JPe+-BW$9;jIWi#lX!Iue( z)+Z6d=r0qkExM_WEeOJ63-lU76CGO+{H7yWXsj2LkK~=Es<#cey>J42xo$f)I1cJC z-E#MuuB$?9t!Q6w`6_B{X&9x`4U*UEhP1EeAQW|drM}teow)<8j7RN-LVM0?^~Ky? z?W)_H8IQKpUC!+5IcO1dy^btoJUT$nuN(;99`u~+Wo&G$8`yWkEmMfjcF5S+K~Dr7 zyqd`XIIBxX_JJ}U%j$PD?_ugY^%+mt+ejVwPJPCv0=1Y93%Yt4;dkmg@SXaMEd@C1 zb>KVo8BgY2WF!LNcj`OLL*S{rPK0_5KO#R>3%rbH%H;UnBzF@SpOUlB*3Bc2-@xw% z?Z!P4Uhm>d`sKJ&HQCE}v4O;z-M|hI@Y-#Ql>xGm;|aU#gA(1X8#JrE(J&H zcK!asA6MU~zX_QefUg_%4?x@_MmOyL1@W>N-LU^0;#0t*H-AM}ZhPVC-{7`?BtGhz4uC3P6#0vffdtSlImhVmVq5h0z zX0I*Tmc3;)>@Dk}(9mA^q5jZb_PHpc7MQ{t*=Gd)OE66!8iNVy@;%ZsvOx#%Y)aE(QP;;-dLhsQ*pL?s`Dt+_rXS<- zsK~;8xV|u}A+8oP4CYCQ$3V-m zFc-nh|p@n4Fx- zRjK@m(7WDIfx6XyI{|&&>a0Y!064vOeHO%gF&D$!1+h-dwJ_Tuwt+^s!sr#pMh_VA z=n{W3#E(RM1?s&7Q}1p5*oNfNC|&2pR3ikH)=91D1 zU_}(+d-tdS_9n6)kg^J(7mD41SN~&@eVpjm=w<#FFyjdx12V4~MUtDtGH~7wnfM`* zU?V;X4>68XqA`b>%Fw#+naP61y15|S(@Ix!WIQkZ!J zpR6dmwR38^)H=$>9@nr9f@WhGLT8(x)>aosf(frmf?qrTlxYagC+bRsRseTA%>58| zi_yvS??SvPW&}*h&D`byMKVzDY(g@%Vu%d~-jOnVkSp3H4|MyW8`2$s-9{J(F&ebg zZ3Gk5t%JpcoC56D!Fq^$02}w0V*W(gyA0wb!kz)CoTy;^ldWJ|u3WX`%AkDZ`TVU6 zUl3{BwW+Zo*Ti*gTDeH?O^j|{BN&JAm7R?J`H;TNFz&`8Es%(f{$DfZiosdPX4*`>aXE%>)kRv*y31jr zb0K~tOO-M3$qOj~5(IH~58!)B{mW)GXV^Z!2|b8dH^BnlALp zd4nfxP+9+J)TL?VJg>4z0Zp^Xk|kkq7efC}F}(z}(2H86B%>C&J)Pl~l;L9E?~G$s zGef?N;;d#eb!Xt^sAW0teMvh%qld0Eof!>Blhwn4YX&nFVzQXJFsDJBA|?ms28e6K zcrcqF9tTBctsf!(f_oA8hr;{_@ozEXVd~$;{wMH{gXs%#fS7qOlOc`*HuT0G`rgm- zFp=M^CU}OjIGBs#UiNu_j>WM&p!oP7QzG%?ggV5)|2Fq$t@kh<=@E{FIVuuf~D>a_O|vIbbE-39Ruhz8BhvZq1w zH^P1Zskfp`M6I(~je$_(<2>F=*>?bqM$9HP1pjZbQ3WP8FK(IPSZSkP=GL%~so020 z_vPY5>N90n<43(a*wq86f=DXv(+kOy4Z01Z28{4b^C++!Dp+pt!PzvX}ZR zY6km`l`JQ{yxGxcrLkuuW;A*%Av~VKDD@fD;i=32FH;BU$;gh;doyQXT0gg1ALMbF zZFtNNx4HpW40AWcRlu)+$+?qt0Zf)Gj7q7K`#y8dJ?Gx!CS*?t zYr>9T0=OWMATCu1E>NomMMbLtYg>60uqv_yajV7PQunyjrB<|Rt5#fEyNTLbTi3p= zOJAwAt@ZQ8*6!{1|35QxmV0jqLTv5#k>Bs0%$a%4%rnnE%bD{m5*v6k05{9;#6mTG z-e}zX5sBa8r`|&a<2OLy@sn2_%J2m*{we3rY-XHS%P*$Ms}4s2W)6Pxs>6CD*5X$^ zcz=P99nP?qsRBc;Ry?|^S2CYf@NkAF@WkV+ViIoNL}D|3&cNM)(w>050e$lJz|B9A z{T_bup30mMC7Z(VQvTEwuk23N&G%HM-Gyll{B%D)XC4Ach{SZS?56;X-g5f{AZHSK z=)P$09s^x$D&Fri(31mEATK%DQmnRwwHgFIhIv@N`*0qrIu}2AVd0BNT+SQ5uy8vP zx8esU&$lW!ni{Z1U{bZFO?9l7UW<0sTL60)PdvmbeukT;kl4g32J#4n)6ky_K&T-{ z;D+I?_emsPMfuP1lhaS?p0iB6l%4;Cm;Es-=IJMY0L*V$SHBVi#F-|A`XFom^62}B4^y5 z4QcxTyS*#Er^YiqM%?W>4fr|EJ}dTx?=d@$ms+}CSH2j~pGVPjK<$N}x)L`hAaN9L zZpO`pNUY(_-MHC+#BKOZWpztTb^28W?olSlk5K$1;rZ|uB;Mu?tHl=woHG2R!O2KW z#838`Uu$Yo&ehX_P?z3oJ`cr*5&j80d^QqG@v9<4xYztE=D|%15WAl$WZC*&^ZU&M zw1>aZ4`MGrt4`cGQwj3C z**K2PpagzX3FHfadYb)UrbpHG&xe5tT6MV=i2}oP?OQTubcOE^P_b0$Yk@rZr<0;kIwiS zfkB909A9JMYb<713Tpu-8loDE4pMK5Uc2BxF4@*4r)ymO=?0rMgN1Gpr{5p>7qNK#rQ(v zuUO&1-u-yE_(=vN?d6r|OS%r1v7>QGvGx>GPI^49yIhZk8p^$+GpFJ%;2el|%)mQP z>;yFwxMLdv6l8+;&=K#GnWe!BQ9+b?Z!^|OH$K&@bw)ZL4;|$@~)HhrdW|9yo63Qv(ck90@ zbztPCTT3y87qnQM0oU&D_X#8)v&Bx+P3*~8A_kA`3;thsC1TSOeiF+)Yakz62;HBD zU&#Z6Ji9+RcOUM*%sg_?UEW9z{vP*lGLORw_nhJ6`hNE!^24v>vfYrkqLKYyhx=9d zRqO+nt*%Z36>ojl$~um(+)g3(3n+dPzskkPnJB!t50&6ow=bdA2(u2w{ek#Zo(!-x zgVqCwPTVfXuVMux^IYk*xBmAlnqMMsUUP$Alm3WRr$aM;?j!2>E~@)p6(9B1|7k_V zeBKV$x2slGJjUCR`u2jA*^6-Ne!p@(D(UwosI=fMU0rG}s(UiKI)&ouB5YCs2G`-| zmf+^ANZiF67dMX~@d$7Dk+>fs@icy!lTAH8%EFYP5+ycN@na@V+1aLW1Rl2qa}lR2 zo^kv-&Oz3=bQ&=CMBwsNwiKNcTwXx~KVyr;=HSh^<6LLO| zyF>8vp2y8{Bs%aDAv)I9{yt!*6is676r$Gv@B)59tiks~^zjp79fri-ydkmfLgIG( zCQ^8gH+4CcAiTn72Fh96Z&30AKz_t$Ud2uJ0oVB>ZZe$)ej;@6C!iP!jImJWFrz0l zPrr%ff70X+LcWWt4Sap;{+hT|$al&vH?<}ub{Qqs!(<}HX=Kbj25Ojo)N%L8UVthi z<{S?!kHgO+bCw|SS>BLI>yfyGH)PU{NOa@p^kc2RHnplIP@paS7W+?je-#jS;U{eW z4ib;!*FYet{$<=fi=R~gS0w(-8&+TZAn1f2`Ze3sO{$olgI2qlT#WOaS}?@Yu+az} z4fP&jK9rgU$;k{iq+E>RqvFM28;{C<3lf}}6a!kG0GbFOT8CBxL7$;pMhh}aTJuR- zt0o%HU*OSNvA|8^X@RR?FPqv}QcD}`Huoold~)2K*pKEd`zspEN#fre9A%56JD%PL zE#P%pKIRRDfDU7gyRyDvTCVzKkkP*(d0ZobG2R{u~l>!1G(9O8rD z9Ve8NUwfYLDGxW$^}wCFOC%BaMW`cej>Sx zkT`)iRE)JqtlTwYj*u z4f*a2T=rpz=KQyjmaVLg2kqQHg;jGpk5Aw0TyCponMwB zCdYv0#Va5_GU^y_B=1B+aDY0$Eel5-!L>8+#EW&50W$&_wv`dc@XUw>fE>V!8H;Sg zFr-4ht)AMg{4qS1ngR2g+G9A=PHG%)_LQ59ll~&If`>r8)MSt83i-BrP@DM}Sdikk z)dTZw_0+=IxG~>WPx0I8f%&$2ir-cb%(vB3=b*BnL5`IvPko0a!-`g;=IRu`tsV@Q zo7&WZgK#sV`a3A;?*un}90qEl?8^_9L&(y53?cOfj!ExV^m$-XISK8hKQ`z`$gHfK zh4cX#$=@4xBz<5Bha1kI3vjs>&?8CP(N1u78wzK7#~>?xFD?Ua$7NLdL0m_3a<*sz z;G8tCgE#cU>Ms(F>ZBCJL%ztZehQnQ)catut8VpRZ`EWJrqo-I317(;XivP!lZ`ik_*Tk}q;A`69R470*_vKjB)bk_hj#ctSZuL6g$0_+Dw~{Y%EBPX~q7Q9W z>(TB6bqLZEm3)y~eFp9Brdm<+?rICtd#JM!D(3%~tY7ZY5vjR`Nw|l>u&tD)}O}k}qm3)y~$rrhm ze34tp7r9jO}dDe6@|@|jtsEf8D|jH{eC3J ziR-OehsX9C@IDAr z@HSA#-yU*B)^%S2U^BN)svqpev^u;$X_qDC)F=9rbhVP6+n>Z5z5q|Ek2`7dV!|P; z^G|xK# zFasaKViJDdRNU-_#6s*=j1%@3~2!gz5){TBVX<;qx75& z$TRUP+lWWcv~}A=ovHht^h!MDy#Sbt0ek^|-jlew8HpS5%bp*$rAHUlpE4PHgLl{A zSLFq{nxzb+U)g!&)EPmsP}vVMTu@8egq z987r0BF(e@A4wmKr@e0ES3inp@bfOm%_&Hn%$tjFvl59j@vD8uJo+LsIsJT&vl~`< zhw(@+{h?SK!OraiLL3Y1+Qa~juJc%$o=k|d0kwXG&5$?`Bk>S^*}s`)I8A>#9;)4B zGN_W_gm}QVhJ#eQ9Sd}B`HhxEWfP_`d@Z7=?0-=Ew=IB8z+1@QmmBJ!3Euw#<|kk(nH{64nSVe7 zzrwGWCUlYLTnpw8cgIiE@MOt43Ae}NS91qCH`U_485OSbe+fLir;&d)0M5kEdmJ~P zN8&=>JcyfXkhluJ@_o&tuOUOc|-m`iNq85 z)m~s8)%@qQkyAay5xC@XZc?IqnSKyqIR{;2J zsbdtVDrMhx$4I&nkA2(S^FG#*X00XZNe~bLK8(OE@DC5U!zI0T2Gf(~A>T>UWlTx8 zBR!R`6S#lUC)m*lP2>9n?q6z80Gu<_MH5pNAVpy>FxUN4`U+|H_^niV0VpDx!K#dRwFKvvo}4}i|29AZ`4r1q}2&yB91RmHYv^gpW}lIat{ z@PV@Uty}ltzCf@%s}84Eig!Dwn_f;gQ8UV>^ijKq*Hx$-5~YkqH9o-C-3HA|NSlM0bJ7EfrN#5}769KNlp7xz=JQ$@L* zvO-1@7xz@SBP1?}MkMKm)0VbYokQ9tiLJwv1igW_CCEk?qRstSd7RFT#gAP zyhnm*7D#ekujY{%S{b&uZjbs>`Qy#xHU2xO~k`EoT#D>?o zQwmA0XRJIGSxz%F2?u*xtg<3j!WW9vR{4@o@a+iPMQ{t!B2p1|8ze$zVj91xO`f)J zG0Irs{isVs$c<1UwnBxEApKwae~$hiX^qU6fIhI!T6wmr`2{Sp*1s2m|6Gfl=Fax%^=HZHTgZe-c{m-SC9Rhkk)#ug#x!}T*%4#h1ld8O&dYUZJF$rd0NX?x8}B#XTo%*w zyfIV0(8m34Pf;R6-Hcvu;J7{%+ijMko!D`$W(R5a)`!4Z@U=IFhP+F(?-kO}JaLn{ zIUeXNk4a^3lVUwCizWQPM-{Jp73%}fpKAmDa~si~g@eq+j~HXDk0LN@CGNFLOuR%D3K(M8vchx&Eyf<@0Jg%)Or9{XnjF>y-Fu=|_z>{|=1)RlG^? z0xJ}|I}`0JJ0{;J!bABvHZWZMj}N}}fSiH(8)>pnPe)*)DBKcP2RQ3kxAIEt??m; z0pXsEI0L{HGHV<8V37WQju|*Jp}c@JCwHfS4V9XdRR!skP-?Co_s#kNoqjrMFecjA=O2o>ghGy!va5o=RYZ&z zD%0(aE`tCW$>$Yh86Bke#Na$8>m*Cz6*2?f4U zNqE!fkrK7qLx!(K)P)a6K~w)Q&yq2FWMes>wjmRmj#oNBM7gnCzi!c(A?QH6zhRKw z-!NF{1P365+Pj{lAENb>4-D0!NE#IBuy0gWv1GJ%aGF&HQF1E2K}ZwO6dKFGHxgvn3QWP=((EaC$dC51 z6|w}O`U69@ok^lh(sN%cix8Mc5@rL#xYUu9S-U6RiYDH5aFpHbRHz5>;54PFk60%$ z<)0TjaqF8M-N$w~5!>8dPGNu0PA&@)dtFSA3L_ihm0aYqTxh&SuaJNMg@f({!Z~4j zZ=0ceSd<`3KyAG@uf*^nPE1Ehrigl@7fwPI;(_Rbm7-!JMUCU9G{TOG2}Ll#hlWk`)MJ zi6K0^*}E=f(W_O2sW)7bwx}%wtYU00uPpz~c0k$#O(~opKvpFOMTe>jpk-&B&*Je7u*Gcssx_^ygSFN8&NsGcOtfhLTzthk=q5qCaLoNPoEa zo+X}+K8#8z@d$;fqHLo-mnir)NWER17(j0gr)_<8!9kvtaIOO4mGiFJ4sDDIb|3S#l{6B*2cyHYfQ3Tu#4y6 zXVoP&At ztZfZu@F$rQe{i5&n$NXEZk5tbH-EKW>Z3U%5X-{poY`E)~f3v6%BkesM-e3^rh|D z0sXM7+iBh@ZFY{@7_A))E=gONFzj`)I2q^s=QvPn+>>_H%nBqX!V0_3JZsa3!F#^> zD9tq=2@``^vO?wqD`hkvIi@9PKJ40XJT$1vjn?e~rZI1cX)+j>C^(Ewl=V7>-Da{e(m&uEbhsqnVG>i zb;M@y1w_x_YXj5EMwA@elFy6}Zzfpgv5mvD9yH-n>p;lR$78}EirYC?rdR3hl;uD> zWw{N5*gXCeU`Sz$WEql9D$Gv)&P2s5?-ISBc>`Pt3`Tg zOS=e4RrGX5EPJB?lc{dIsRCd)m%*-o$zxi1k}_gES^n}@5)A8K`6G(>)Mz0W(svU+ zJ;jwnu$(94v3S2#94N7$1u(eb6+LK59~W2$YJ z=p@>PypiM8*1}AN$-+`dws2uq2AbL(>w{0ip(?!-Uye*rYFnp?Ll(1r>q8f{Llo+;0EsdBTJ1jd<87ZI=@V3Qs7^G6Eg6?aYjK zSTlr^K{2tk>SNVI&jyL!$iaA=4T|fPURIa#kncr#>S7OovQELC|Ac#S3i;+gUpL}2 zorQFxPX`QPv;>}h2-M9X;zz(Z<EdJca9I(exZgPi><@ti?vm&e`_=o z8Ir`Fg{=(5^5dL>R-@nqL=J6@Q!wwq7^eVzNbaNBmfbozD8Q58^roaI!K#T~;MZG5 zL$}jwIGOYWc8gBT6I6ya&Uga5xyqP+ItCgMbDP*!r8`|4)&Mgu17SQ8ACwg`a`k#v z1TPEpyb)z!!g+u!gW?Lt7oG>}g|Q*qp=nX(8-bkd(WK=&-q`L4_;wp@<-6@|k8hJ|f~DUaHpP5%=)rOR zc22-+U-hsaTt{1RW2eu;iK*DXHNK_dZ^fW(E8(sE$dhg1h~6n3QY~C|kJ$-*@<&J& z3WYwkHO#^g*|Ml{g4xp8%fwz7K8W2&%%*DF((8gvT%Zu?Ce2;Kl<5kieWJ$PS%YTm zTdk?k+~#Z^()%y^Yukk_?LFHreBCIrfkyiA5)^s)0is+>PmZj&szjE(|E?(4A{sLO z-y9c^&OI8d4-^C>M+AB5(~E16?fiY~8A4KXt2Za6{>`MeW4ZiYy1gzwpeuR2(4m(Z z5^9~@=^|u&Y^+FP-A5+HdvOtYB+l7|yA|KO)2tXlG%?Ut&C0gA;<(G&Dd72T@&gO_ z2O@l9Vo@#HIGqzr77T%C?D3Y&2U}ya_S*Px#7+65=_E|ACf(p_VwAq0?Sq7d<2Nm# zb+I^FVzvHp7_77Hd0^51*y0NgG6C}u{j`+l2&ugxB{;kS`g&g|9fI?} zb|YUebF2BHypzCs#n^~VuRA4sA#g07)SIgFC?4$+5b@|udUcq?^|MIg?TOg=f*gNe zDJjU&Ty-eOy>3BG3UVL4KZZd|uP=`!x87lM`?kHm*AvB)wl~i-CsiOEt?i_E?=R~0 zMDZj^cgi@Iy*NEkE9J7rw#-$&G{g8k^r{KwB@x!=!I&n;#HMS$jyto}wie-^0MZsxMK(y}x93Tzw-QLQ<}5W$WFmAy$H}oH$9muw#!aK7*oHng{1X%Hi5W;%vhKj&XPEI1M2kO@>xUp(P3`XMaLNv?P(ZmsyB+d zOZ=A0#p4V;<4QAL`W>1mK<%neooo_dTi`g&M&u<&bBSnJv7eB81mr{=T^ZX)X`Lka zL^bZFo6aO=PIeE^vy4l^9{Ti5R>o|7&gGs7{qP-Ua&!RaUiv`hDY_HMCZ;ARjvh~= zaFC}hQW9a6Y=WMzx+ z3~Ldt65{0K0pg#EmpjfWrU@e=pN?@u0uDWr`%IWEj&N3$&i*W2v2_EZ>(5v6Vd+yT zM)>}X<19`-7V+u7k_E|?`O|U>qvuW!H895=F9{iM^~`aWHia&Vw#0Fk$vMnm{uyRE z1)X-B&n25{$Z9^G8TQR|nFWq>RtSxU<&a{b`q|0s2;=2pL0ApBbdKqWWNJIN!U~$> ztZae?T19Z2RjNr0h&95ijDoJC{1N8!%i#L(XM6gzk*q`*5}iOTrN$rj(@l~-_FzNva5>E=LJr1D z_Q-O4oR?dc6mJ+r>{9rN;YmB`5u(%?U0mBW0AR@F5I3C&roN-)6hUyP6wld08W1bov z=Q8cy7>&H+j5X*Oz+FPkiahT)8;K!$APT!i0a=p`$eN8gG1W}4q5s4@sX=65$>BJ= z85_Y9J$H{|VnksNW3)`K_Dnjm$#K%KS9`HK$I)?s5n@x*)FdKuLiscyQe!|5Qe)ra zZi)AV3}_8)ig+LRX8*`WIFsoKQdu=5@ev@1Jw#}seN((}29OTTyFqRrDs~)bU#UA5 zd_|UIKTUnd*+0>K(sm+hFs~gz$cfScq|6!x8XZXGFrOuxndc8@kxaUu;ZUM2jxpi| z)2{BCHd+UT)YZ>=cKC3x4i^q#cn}L04wa~;v7CW`L<@5?E0`f-J~t7IHC7NXU`IZ# zo#bIQzN2G2~^fb<&c?rqWcJ`={28@?T z0sV&a(MHA{XMRLnpT)(Fb4=2t9~%v7r#9A2YlJC3Ep2%jYsgulbclwSZzLdIu+s;mSk&L;x%(XvDI{O^ zumQ>n>-1N$1tE(Vw^ho>o&K8RY8>o7u`2H)osn?y4E)%qUmaz`23H!;GM)$_v^TJy z0mSDd@`Hyk3dj%{jY*N68L{6CH$KD85}DhQ0`9=PD`mc5fNiRlsQhjgK3HMrEOcYh z>81MZsz@gJ;Zm$4nDd3-#12kG2pfUNTWvn#}yI5PTho|C?##Mm>EeNE?C&#zD z7PJ;kxxybQN^^2aG(YeNKdDC5pA=f)aLQ;mqk^LPR)y`ngeY)aFA>U-h3(y&@sorWb8P|MzMpF zp@mFFNN9mCy6A9zXhOX#1@(BaA?~4z#Y6Y!cF^ zyHk1`N6+m^OG%FhMY1w9x<1nH$?@zs$`b3FIChGj4VfB;wW>T#gwb>jG+&LM?_`|a zsIE%=6)%0_A24(9Lt?0lq;olbF4AS*MJ$oaEll~!DRR3$fh7LCu9OOt?*+4~s{C4o z!l2Br-P>1F{9*g}LuUK62L>&^SM8Sw)EOwRaur(n6dvADB<`_Dd<$54v#b2dpvoUQ z&97YO4@RTGxnXN}_d#pV+SXQirpu_l!Vg-}($Q7^0O{cRYCk|C9UNEfk3?cbt3UE2 z+_w7FL0OBx%iaLq+s_J!x3r6QrT2hiA#GIg;<`WqqA1<5=p zsPTg-z;!`2h*;$h(s+}e)l>2*?GFs9S~SpXq!ln<)&b0GG`Uad1I)i=F~6qR3P>p$ zs@v$7cKDSoXdR@QE!xzkCWXS_%`yTS4?u-I;~LHmtK2xvTag z#lX-%inV6<+JW!2$m%nEyDYvxa15b^UcNfdkg1>Vk7!3XfX_V!wq6t10DdYC2=p%O zOZ1+#=>0N6kA6$YcCt%%D5N^ilIqv=dI6CZ-u5Z2-nGbViI9OL7Nk}Ak>nhLzOB+~ z9U%IQl@m;p-si2(RD%?TR*zE7IwFEyYzcOW$kvBLumu*Qt0IhqV3)^bMhn-ff&}|q z91zSt5dlx15$r~b-YQqi{=rqGn0J=)ck_yx;Bw$Gk)p3Zqs?#r6aJso;y2IH;@1W< zbhKY=6gU-J?hmFgc)xKvP~zRaa$}+}famMKtDl;;Z0bQX{gQ|LR4_fL3NEEFLnJa! z>{VtFvgsam7DKUAu+T4-|2Oy(@ITE4+x$#$X_a3Qs22g?%~yVZKGN!^ntViYsg`6T zZ-zC}D1PupHnEYx43_Y8aFj$vK2Yy(I$wk|qpG0UA27{NwfaRtX0~6VOL^Uh$Gj>P zKG+yO_AvRbBcZ^DX^lw9~smz2P9syv@CPZ(*vBfXCA$TN&Yx1+m zX$dlm5c^gI<+_zc8JZ6dmhDoj4ez_E@9jqUQDO=l@7^3WC}3kJ7}%}bjVt=nZp^Xh zJw)_M`)@O1d(hW&(4vEYcHSPxQH{g| z3rsybGVobt`}K|uPphG0wZ6(UijO^`58AlVw()n5w1fQ!f~J^?%%kcuRGB}|uZ3Wm zpmN8^B(ai^>6kv~!n3vucfI z7PDuF*P*c5^lz2je*X^oH?~}f4BVBp`oZ;*L1#I(#V>)U#7);l>xM}HH6ah9<5bS;qq3$U#mYN)+x@h?5MOf%ts~o^WF~0O~>w&l6woGB{@Ax zYVu1i@M4esBiW7q2u@ms*w>)G9mQ*b#cOq16jW&K1)r7yNuaSKaU?lC$|VmlU37#l z5W-auyZR8j2l@cJ8!dKs_A~YgyJ_{HsTTj;W_`OFL=+p#vNn)#eW02UuuMQK!oX#= zP}Rl%6z+?I15g=9g}c)(%QL(n)LZJN;K=#{^LPKnn6Hu0<&i$1`3zcL{CN@0IkO{l zpADidO%M&%A%KdJ+6NhGH;2?N#$YTp3AekbpshPWaCk$!S_Bq)pX=biCf5b!yG@9{ zBX;>li~r4KMrOn_tJ$+>ikJjJwN23M&6AnH(%`5n7#hrg`j8MWSa1I82)!DHhjIPd zDhXmG^4;KPA!#i-HeEs+nF`+b8}M|dCDZCROk*J0Fwf6)&+;2)`k%8pK22Vs&2oErEPxp20ueA zG80YJ>kIxD=%8KK4iwVC8Ggz;waefRWOr@#GpjeF4=sKg$*HZYQ7aOHudQ^UsoX1X zm*{3li(k8Bwm+oJA27=ga9u+S%s#{5wt=Lzpf5rTzlPX)M`k+x(ii-CxvA^cU1`%@ z2{MyI>;hQFw3k)7#QU!hZ$8*!;Jsy=>;v)aCkthI)d|=orix4Au!S>4!*1^b37xS( z6wL8*By`@qBdmnNqn_FB!zWt-wf}Pk^#%%RJHU<-epD}^K~734hNA6$wu?d~lH1Aq zRPf&+!#9bjzT{^%Tp|)nQJsfMAgbgbq&X?7H$|cf6=OW1WQ+TOB)*$)?l ztxmvpnkHArVGC!9s6N#PqUwCIfT(hgEqDwvcLQ~sj`Ss)kQ|KMA4ynKPP@{oP9Ps( z%@B6AlbK>poBZOYPA!>C>Xev3u@qv_4$&O%$TT#8z3cT|rjio$lHu(dU3UPIE!N?G$RVIh3z8O!<;ckjucej%)YPD8J+$BOeZ(B zwRLp2kpafIV)*IJ@BH5_e(CX6UX5v`lU1G4OJ16cEZuMM1w}F^C&ahQ77-rSS}UWs zY)K-&!{o0onCn$1Y7rzPkl1V~5^6PqkC9i9`H|0Iq;%&+)14;W9g6*pvO;VksM#m^nbuhw z{f33;)-1nbIsy`DgeJZMnQU^Q&pES-rG9;zUnAj14dRYi_#q_omb7bBUU6$UUzOVN z;nTNH&XuV8wkoek&z4rUL8y*_WAcVes!OLTA!amx+KsZip#Rzf(G7`}692P79U$Sm z(LETuVZy3Z@UZ!xcz|=#UO)IMz(Np8XW@Rf3>>ou`Na^bgZ$D1kcS(oZ4sq*Hs}>6 zRVORSZZIH%+PO31C~0NM9tyY*5iP%*i1CUjJJ3FzQVMazz@6cUjdai{+|_5`J78cb zc+`kT7y-a!@_r*Pp>$#h<5emXi^9aVkrfyecKSmJCjpJ5kgvrB5q1VeZl>9=ECc$J zgc*=G_Mc|mn@yYFchybn;H->Lp{C6*pC#mALwbpm#1VJm&F!SYfFVM3z)S(*z+LXz zz-q5$qi9`4r#~Lu@!I@~cAaYX%i5E+#VBL9R+q17J@(FoLlaf**3e5(>(?Sp-k>$v z*rhIDkg!Ybv`apAdo;}QGYB*v#y}_*tySP&^I24RjZx(a$N~BjsxlzDEwYl zH<}9i62T54m^ouRG33e@&;q`p0g1v;u^>%;J*F}`yPLEPksw9F6}iZ*T)Jo^*zE~2 z#t;L8xJ9VXBG76{Q(IG~{5qRD+d8Gq$__2Ge9{A%$Q5I6(ruzt3hq_R%zFfxi{a7M zV8kA^^C~qy?lj5lZ8;XZ3|pF+(d65lCiIN;HqlJ@B?yi~IAk=1IY_}$bPJT2iEh8F z)41+7vh_^t87PiN7E7^ zDQ7(q@>hZ@Af?1L@Pf_64h%z|p%U!ZLcy2LpS@xfe9Ou%Z4O|(y(UnTp@{0{MMj?9 zh>oZ+RxwXJrESd5Yz7iK9}#$yKcq=V+OU*hpVKy zt_RYs>4U}>*v9`6uFKFaa_PDlVLM)E%fNzhZ=WB08?iXHA97mLI}4M1LAzT5H2Pw7 z@E(iC=fkOM9l%^Fij9|+{NO~t4rq+P{=9B%w2`^zzG(Vg+w|(3reBVlUME``G4Y0Q z$~4^<#GkO#$n76X&E%`FTAk#>Pk5j^jhb?YRiXFS!6Ha}G6F^i1juX>3iJgU_gFN3 zl#Qd|VXFcW@Pj)cnB{)IgAl^dBw&-%GM$>~tLdT#eSyZi7LBzzXxwPfxZa=v3FrY0 zyQ6AHX2d6OB^Q6*Tj3T&%;>{-#aMz!b{6Pzk}OvWGFSl1rOB62$&OmK#TJ=M3{4Kj z*sablKL_G*sO*EO@8n)NFSxe`J3tYcj_&ex)|jtiJT(SB&6`7~`ZH5r?vAYV$NW^v zgmqW;0h;AhMErSM49yM^T`k81PlR4Q(Hn^=4#^^V%|Px94)lSXl!nOZmF@4M${nTP zJr=cR;_UQGCirS5Y#5n(a}6k2*5sF+OkCUvR5uQu7D>?JZ$R=>RLmyiuN7HLyOSz6 zVU#)nGYv~#M7BF&fJ{_iMlS6(Rr=}&esN~JJE7bi3k!Jzc5CctKNir{;Lp3M1h{!q z^teuW2V#`qVQ+96|CfNP_TCHLs`AGNUMXfzywkZ5d~~&OB%h-8U}5!)8kuq~_Ao1* z#(I>uq!VCzeYNpdeleZT!G0rXQGpqNah`4WKh;G7jka0nYv%GVFT9XCx~_YTOTw!!!~*i zl+p9{f-j*2do!uJd(r0HT#ac`F8_Giz;yYI2X9UPWCa1*a*Bz}PL^;y)D!BC(STxa z$&wCkOwf~HX{cFnku5~?C$!5=+N<9pjhn|xyc62oI+(7$=>LRs4AK7s``;IPpBh$} z{{Jd2bTFuJGaYfU!>H4U-Ti&wF&5iqA1=~i-BL{6n{B%desFuUP@Ptme#r6|dNj20=~B0D%)J^MjUl%wl`qM!YeLk!1yT z6yd!PIEPxe9t!$=&U0M4QpV`4)O*gs3pbE7ioS(n)0hk7gR-d(HG8+N?AGf>u;cJi z7=kuPd;NgZrnkS-A^SgxK?3 zklrA3@l?_qgdfs6#m~;OH0PKS^tA-f&f`$CRSa4N`@LI&R{ZqPQ#N#v;9z|R*}Hd* z?^h$>g0eD>9Wi1_8p5**}N+e^`OFBRp#i134eF3*80ebP>&gmeV17Lr53K zWzovydMm1#z5O8oYK;R;54wVd1q7DH^d$6iHgv_vZ78o@e(53@rT&n*v)VgaIF9fR zQxKC45|VzsFMI$8=J@k2jYdD-k76trgAP@{$)!5Hb`IyHEw|?fuzbP;*!Ct`UFH1( zoCBwyUeQ2Kht@nS1D9T1u_T8{OE_{miWR`POj;7wB)anUIgHRlZnbk7k#-z5^={F{ zmJl8k^!~PDNkIlG@9z$H=&fGaV9h%$&Q$Pn;Z!_V*vM-}k~6gP5bbb7Q9dWqBKn{qmk$p=aDnJS@-yQydn-0nVZO&J>c}*Se!kiVe13MV;qx15GxY|AG|v8W(21*niEI~+`*6Hwg5E@| zy&RDfU3f*{-2u;5?7al9mf{{w5LR^VRt4O-oe=E3lW$ug8<87~G&K*Cy#Pf#Mt+=4 z0s3F~&XjkF>M;9u=*O^SQeZP5uQncyPf6fKW+S3Q1H$DWte`i@*J$cl4}3n#A2QJ| zo@_OD4OTzHSisufHQN3n4xA^&*qxE3Y9m(IM%&8{;AJe~I2K6|FU;dxSa=&uIgm1Z#t)U}>OIZs6VTD+D1r z`vR%|!MyqbomWA76Ywnep1`XVf!BRbi!^=hIZgCGtoOUV(gWm*Rw??%SuJ>=NFE?b z>ml1XO{^XhvwUwsZ{)znWSB$mkIR8%T~(D=gN+Q;I<`qaIE`@LK(r@9u^xb5JX=VC z*Scyr-_Kc9?-%HN1aWx*h&K`tmllBdWdfqB0K~5VF(~kEMiKmHirjgM%`$;ipb*0g z3q1QySo;m=!}&=FanSFE5DNjZwgAMI1jL#ogwWylAp|M4x&Xxc35b7cwp&^X9)C(e z{Jj9gUkn6~xnQpi8N&aZuT|b33OtF|5ehzGt)z>6kjndA?vt>G7vV|T!!$^4LYh;g zs&sH;HKIEGW=YyChV=9w<~Fo>%6lQV9r(VR(GIO5qEQ1$jDBn(v4t7sP%4;z$O2do z+`2>D^ioXJ`XdJC-u~ZY;Pe`b=_9I^zHY;R``nFu6^$LRi1gI3F1O_1-Xh&;{Xc+1 zQQp;C?KGBBq^%U%(r$#$TyCGy$|;WK4MWmE#NyHd5Wfr|SVva@h+icj))j#Gbpqnz z0uaAVKwMM+;&%y%3kpE|9}U4})n=f2UICE*4J)A@pIZRpy#z#O0f;{s2rGs5e<1qt zH^?BIL8P^i+rNf&&O@tzDFE^J1jL^TK>RaoRaj~}`FjgmQ276bhjgHFZqVKis%SyF z+x$P*{MSN~g2iCDedjuZdmXRbZT?>>|5KPKiNmEZ;Tpfs1OhK1V7)N|i}VJzG^*4( zp#OW=c1k0a4i*LNkU*3%1h>Xx(K3OC56N~xdo<+TfYdWDktSJW(-)~5ajEi3QWg%CtfmW2{0c@UGNty@rF9@;VKKG|(j) z=+!f@m>D9Zi>}VdOMRF5lY*!H46%B*AXcDXCz=0GpyUfmGM@T@-;8BJpU-XX={a95 z;!xDvfJdo8B~TJq4^H4@F$z)MtzxTcYJ$!THuI+gcTAl>3geY3Ihi2M_%iUGf<^GX zTh4)7^w!xBB+dXjPH7CX%2+~q|7&evKG-su!evmRy!UzrJ4nD7{~(m?84Nu{qYR{! z_vhZg7?COOuf2i2CSVtDwWkPm}_t2%PB*uf2|zZ0mXansV`yVwE-EPGND zY-MN%jQ0Z))4L&QA56U{zF3-ltzfe##%>M3K2xw5V)R4Ef;P}zKkRPP$w%Zra6TSc zloZ&LtIq01T=X7lOL?o#>E;-=$}c;@PcP~cEHB4ohw?7ASmyH?oB=woNN3jd2KL_q zw!Sy8mj&#yp1~kBX!Jz^yP{_>job4Ac4g0Cnjb$Au&a9p)3rS-VAu8r#`ndQcYSYQ z)H&tdWZ9TcUX9x(0sB(VU>Y|JQBd0tdIP&uz<$~@m~Iq9YSi|#p22jZ>jdnTp22i& z974b`^bDqJ`;vfd>J98B0eh-vFfAe97cg!8IDMZp3J_xkLBjYKWw-ZSo9@E51?-OA zz#bE@yLtnAM8Lk<8`wqxySHaBO}B3d*w=akdqBYM?+xth0%j%`dZycb0`^ePwM`d2 zy+^uv#iv^OwpmjkoD)f?Cy0`~2m!8F}&7qIX426n4}nc18|1kn=m3(@+E zde(YF5CMC*XD|>1X8Om*p~Fl+v7Bc5aUAY~@KHSUwLPN@T<~`GGlKH90L|Qq99J3)pvhu1z!NHUaxy zZ(z3w*b`d?13}IeuwV5C)+u1W?hR~}fc>^-Fh~R%{fE%)cRholrx2XWAvi-(V;YeN z=G4!?ktnj3d@FVdDDU1vj|bkPc%a(*&Y4)@M`+ADj@$+z2N9$9O>7PuqjgMB+|L7P<=X9JXe-Dt^p|KVtNMC{P_v6PFG93dK)Jd@g6CL%C(G&H$m_Rd*4{j z7ccO-HuM1*#oj~9xtw*WKf217fd73P%OOa^gdbjg8-X2=g7Rt!_Woc2ayha@q+uHVOA>$;^3paUsZWf+g~>jl{k6 z*a3_hu}?DI@_B$m?k>sPH+Abdux|iM0SxQ>Ucow#ErjJIx}hB0PZQ8xldVsbOrb|a zqWkpoIRrsd*!&fZH@x#SMmZ0!!owruaQ+zY=jdbfAOz{>S~!wc7$T+zm+QeqETxmE zkO{C8jPq{5s!XC@?@6?u;NAor@cV2xNf6Ow9cTiHigoz-c1y6O z{+P_oA#e&hmli%G;WvU?8h1ScBC33sma{;Krl#3GTDao`j(t_$U0X%|Thhtn0C(F~ z;hxZ)#L*#P%NWdKcqV%Pi=J%{3@gAc0NToXqh~OfD2|o{?77~+E)%eqdj^{>wXGAd zS9%81xLqt@uWc0!zTpu8`)bc%M~RsJ1WVB&i6|GpbwoFeU?AhT_&wghdKiL2ww45z zZP9<_y=K`0n=4aBTIj9}`S!8}C@7p)2;65t{2yDmG}gb2=-w)&STyyXDp0xsCy-B+WuY!Wc%fAD~XE4cQaInFzC6Ew>^v&$T^U|xiF$5iUq6Wx6(_4|n#^LlfazNKEf zqpI~gggA%>uNmS%Ih>)B!7;B`5|TF;+q-a&_YC>wqF;+6pX9KDyY%~m8<5lBV`FA* zw?Cl6=leLFel3W|(_wKq4Z1a8qhyOw&2}Ls!lY@YtpQ)-!0VOXqRoB-6W#tK{@bAM zs0?=_ToX0LTk-Oisd*EAi@rk}l+BZ*9Vtg!lGUwWnsndPuWkZAIimspO9Hp0o?OJK zD>~26!`;;qh%LM0J_t!FB9ic})cOs61)8b{G2|3NNCaN+#1*eS*JJ;82j2+*6PxjuIJOCA;bb=Z#gDbS zDO_)LsHV`7O*7q{I1dOy%zR~yHQI&o%Bt0|KR?v=(%gpug z3O(c-rz#-|$JGeSjNU6L3zK5~^1U2mJ6q&WOP#ek6)A<`fvWxzaB(Y3MJ6BRaH9QaE(S zoCyUxfju`+2W+tMGwb~n>X2i1u;4;*#BKIr=nIDqc+p$83jxVZ@JtnR99RIIlU9&{ zx5FRh;ZW^~KKc!KTUdJX9`m84s9)=2+6v!$rU`qJkXH`6%CS4JZMFCf+3449;<+f1 zj#3Ea#K8_ZoJZ?vCy%%@C-G#u@OCo0FTwzAyQ|Q-RKE$J_vN|A@{NwIK0l z0sXTIHsaP5-wkTi=|xvYDY8|a&$9e3xuK)_2q4s5;DT5p3~nRC9!2G0k!Ug`pm z?OkBf%=x^ezaZBxkJVl!X?tZ0S9`p6OMbT(usZxBYWT;Zn>}g!OH%u-I`2#J(3d;} zGG6K{8sJtBpR>gck^7321DN=3(9hiJ=^NIGtXRqYx`O*Lw$s6;P+O5R^GWjg8*rH+ znRCgT+>0)e>wK0Mynq=f2{No}en#pRSh)w0S*Rbb;y(s*>M|-g(EYIrCgb0QLjDUO zc1%at&$}f2I+sLSC;9d&1iZ?X&VJS1gnHfJAH0%C-*SU$sY!~zC6z~4HwCK)(6n5L zWf6N&#M>-W{dmkxKi=w#1eN_!mz!#4OU*&10sq_zUS7myMnDU67770tzJtLl`|y|| z6Kz`Opk-#+e;IQ2F`W9uEBW`BBEd#1l;kwIZo%~@`iU2C0*{w^L29>Gp~P!Sy86D+ zk|0w_+yp_&-9;`i^itn;w;&sxyQ5S4jPY5dRr-CEm{nBc74c zXNA(w3T+`@C{0Kibl4^O&q}?#g7d9X2eY4%=bmwwARBv2aUCU;l~lm9B}R#IyQvAH z+iq$SGbafXCXo}9X}@%ebx&d3J_WSkmE}_iKZ1N2A@?I#^+;$TuPhtMj4>HjGlogL zs>CEO^T`8AjhAPeS$Q*orW4^A9FOcn!P$p-`^c@-ypKq`WJ*19AvSZRkCA%m#^&=$ zohf=^pajd9)v+9#ADLvYh6NEF%8Vy5k?og?|K3_;0vu78%cdo5x`0 zHvQDy;-ByJ7;oGyzWiR3BX!?vowQVHtf-fITFc(k5)l2^l|uYuBhP6wjekOh*~ogv zT`dK~5?-IJhxT{|p(3%2rQ~_d?1HXcs?WJlKIRv6>nKnGb;-jkZyejGdDwP@`(1*? zyt1N6l!1uPlLUjYVn?(bB+IoNj38r2u;;gU)W=&uK4j3P-QorGC$|VMaYGs>qMl&0 zw~WkM>uOP7>(b?}h4+H-kk-$pY+*Q#>)EXL9MK?rxX`*v@sj7T`BggdJWt|GYj#QU z3*h!C>*8++d4VFL7brmp|A;~YFI`HY^Au~~Rr-Vr4gAHfi12NQZM@W1#pPb8*?yb! z;5OGpzxQCJQIz;TaR&G4nCL+_Lp1IaKl`9Ki-#oqeMpNC{xNF(j;``MQvY|vz3cQp z#l8Pi1Aj+q{*K$B4c~mPScHIq$zQaTu66?&m~&kU-nr7;xi(Asey+P2C18+9lSCu8 zc$#yfSyGZqB9MaMaxun~;z1uFb!l11G-$M@lMHatJTYs3k*EXd0S;sp`XWYWuDJ9? zGQ88|OSE8(U6)5PY%7v%ptfZQPQY|Bz>P{_Hj~S_YOhVA*(!1rX zZaE)_U37`LvYzFve0hsTeYr;6M3V>i|pr_c?_-85oi?4T5=WD{h@-gd8r>t9&RWkJ+lGM$V)w?t;SPgN7sqb zSSMqX14)JhNr3}tVs?|LX*Y2yQ<>W;E}p8>Dzs@8h9K8V-Ksfwt4PD``sNO?Xm<&R z?~v<-BEvd;hbS$k4^f?VPHduCLJiVfJEuuBb6?Xm$3HT0qgc{M1@A{iVjk7T4*%G_ zM`c{ILU3Ebj7w!)cBxQEU$2&&t0hNYuhG8c8W9(JWiWG%Ok`ZCtG?3oLfW}f+SXUC z^6T7|4zcS}@e?u_{h?5KlMF_|9#$FhipcfEt5^t9p?dHNws7^v!kB-EOfM z-9r3sp@z*NYr4U4H}z>j==6XMEu+XRBOdx9q3bdMSVo(FtAq}>dNh@HYwf&SsD~Sh z^1VXD`^`1OkaQ#^W+a8~B{4*=Xu}mZuD=xH`b!qT2#JV^8>1Bhmg;)B(w240na_y5 znWRVT7EX18B(PL$X)ehHz;VHX`BOLIHafxCpH{icH%Fbr*5Jrf#%~;fecU z$;uNOpih6&Ey#9Dd*lhSld_YOL35z>{ktF71h}clTqEk|$T73#$OFzZCAuu5mKoyVFOx1`t><;)HdC^~{>F8R znwy}`Ns>W4U$GTDEa}Y>`F6H+5K(lB!9cQGuj+2SF1G1)S6n^z z1^@~uvYj~A_vx(rlnj3(WAdsP6J5vuNId$wxv~a`C+o`*PsU|eNE~tn<5hM@z~cS- zADcz=H)F<<0wGY_Foey#5XCEnPgja)UMUbi6I#D&xn@r>}B$7mq=BY7~Y2^=L$)8nKaEkb1+pW_BC%7kLor? zE}0Iu94jtoWIy=&ODon|lw1m}zs$H}JJ(-=ZiIf=*`SwB#Bd`*tOUlOgSlXZ$4%kP zG%+W>sJblAra1^oFY(S~-~9aqh{-~8ee(WJM%Hienp}5xutZ``%mA^H0!;?(QmZr_ zR_asfeVO{nYeZ#LQEjKpfiIo65`8zAW+ z`bwiVL_A$X2V;i@0dHV(7_G!Gd1M%C8YY>Nk_U7qaTqCeO$ahXV}f*d0>zb6;kdF# z0wJcklEVTCzX!8mFo`pisiCY&Ujz`F+n6C9alKgn^OAnb}{j zmCa^R&TSeP@FtMiM*P>0%COCXP_d#C^Kfh>MP^vAx$7)hdg0WMn;I`Y!*{^lR8xRy zcl?`$3y$F+f@ISYmbsbhaV=)VRLcA4QbsbDF;6Z+nQGp$D;y6|?P9bc#~N57v+8Do zoiHOM+#1bhN6VvQ^_8$=u?K_yh}zh;Dn>bOunN~=CL?4d)hk9h+{(p>QZP~i!hB?8 zd@)uA6fy~AaD0(S7Ik}Pc6ddMB#Sf+DRCrWiwfM~)Yx&%A0Ir#D}yQg>tgfc+cQgu zRi+x389@uy;ACQMJ-z`0rhXA;Xp^-}Bdbzz;=<#DUeoFp-lT)OcqJOMSnVuQw2cft zSg6=WvK}lPKU}bHXIVQXv7HQT7d>eg*m?TOy4p1?2Uyrt#4B@S7veC3HY0*yodI$3 zgBy6I99||euCHH|G(J#`e14|7VgrF^2QTr;dd?tfGH))IGnscL`Mlg6K_X(Vg!60q ziqS7=!kaZhr8P#$=;7AuQKMF>bvj622jZ~=ov;sD{n0sedMIYlxn9CH+=#!lU|Ur) z;FmkIDQbuBGarUBnHwk)N#BEnl6mXNWDaL2aAjw=^^%oGN`UxI{Ft zL>OE`Au~xfCrf*xw=#8rGoz4MK>?~@eHGGxSP*>?k*y+-2GvR8TqAa*iefhk6MVRm zi*ngTigsmtO*+z@AoVn{o+dHa6Qtn@G%w{rIj^i!F7-?e5HT)k&O&u(qLsSY)_#?V|sU(y(cS#A?7Ia_D3xQh%zZO|UJ4i@3R|gmek!P4jb5n;!DY+F2943s7nTw-HjHAe=W3a7> z|5*L;Wb26%v7E^JQ;GJelH%nwQu|aP>}llKGMUg?CO9qI$ll@PEL>-_X*0tG8*CR@ z$_w)cUxLe6O#s$BA=9RsnpiSI4A2O{wKQ<|AV#7h11KZ|q#g0@uCy*{*BI=IvPSBO zOP7<5g)&mnB$DYF*(8ymQIw~_xpBE$fdDV+0 z)u>Sn&XF2q)#Er4{5V$lX_EBQQfq8-VKU1nvwVM+>$&zl#j5Pd+&zV02Qhh2rbW3~ zgx>47BlJF&p|@NLh2Hmky-!1LDgYdgxnRGP#KCFo`eup02QM+HA!M3t;?Z3Aml0`> z0&PTeDB!jzv5Igu+%l4Efc%sgKP@S87STSgJ;m6oQ6^0wlFu}WGzZwQh}>SBi%2); z>H(>*l8oKQas}Zym@Z>4bu*?OA|21rwMDtP4@ik1ts$fGC}rBFSm~+@RTqJO;FNf3 zvCwjnTiNgsZTE>{T?u%jTaKcERzx}4MS-c7JeKxjs+ygw7Ea33H7u#)Mcf`f%EY9; zGFdIXu4h-JCP~$c*69ag@X=EP*!%z%4Zzkt{$m9LggMw8kMy{}#BiFF#7YD~3T3n) z_BAVWn%ZsAs8=#D6vVe-EF>0?J_kde$ktJa;k}umZ6fRSa73XU74ET#*pCaUVf`i2 z@qmidrQ^8#FbL)?yDiSED&r>Xfx4?{wTI$4U<)$BdOIgw!?;)y;Ov;7H^Z zvmh3fk<`76PGsy}C#;EL4+(F27&Nho{Ba6fG6NLK+1M7&yqHDI?Ha|=-)#jUJ!TT& z%16cwnh0ZX67)K2GNkHQy34T|OG&N6)sCeIP(%SJYJZ7ir*R?-6q@k?{XRxfd~!VN z=BKLokCjg#+%^P30y2p-oJ67DLx(Neqf-)FC@yOT~b|@p#t!H5*RBY4=k!eKXFh(UfV31cz{MH6k#5M^2$*Uxz3rRIDFVdUTdMO^OiEl3T=iwFECd&P!x5^-_xB(JPRUsxWG zb#;0%UEWecEhYNPB>q|^lvzgGGmPVj)8517RF{c5XC<8R8>OiET)RzZ*@M-!RiJDvrjTKhKk=%jTB<7JNZbfH+vwN!OU+k+8 zN8%j_3GQ{MMfpkT3j!Se2{4W%gF%SH1r_sH3pF=-RRTA7`G8Y86n&3!>5E{?U_mZs zS|25niXz$)kyY8TRw}_B)Das7Ws>nZ(O!LhLB@yJjY9Q(S+9J)EamnpCI3Ss&KE1V z&V&~$Xza1&i^}vjsR0|m@bU)l2)7#1F|34ak66PY1aIUa>_8_X-<1`g^W;^A^JL-u z0(s9uzguykya^HCwRy3?tdr|{*|mw6SW%C?vW078*X9k9e}jI{0{=MEhkpdXo=iS- zi`0%inasOI9>T6Onlj`O;7&p7PANeaecfFe`ZmeC%Us!0Q(*o*peuMlaKIHz2WyNQ zq#DW+&If(vVy-Z-L}o_BV(O6!{fZ1%$|+Rk=ng0Dn2yisF?X;GEOw_X?JmQS-DwT5 z%7_ayKPFR4E7_mNWohkkk$}f#vel+ZnaA0c_&-_Tv~X045fadhAQmN3Kiey-F}QDO1QU78$x2>cn(({j#jEep%FPzEnA%J<-!gITy?ICB2IK66_i`$6zlAwgqtX@)DFq)}(RaP|9pNU=#nB zy*Gigt0>dP``o(c^z9|Rq&rDxA$tOWq?15^m~>~6AxJl?6QvUqj1IK>--S*HO9C_? zC<+~LLBwXqEux~#FcB5?6N3xNsBs+!HHym&196#gnels`=dE+kxl4EMZ4#aN{)Qjb z=blri>aBOLx8ACgO1VEcQ+Lkw0YQs!y(}hb%xa-#Y^(I#G7GowdO<3 zQSL-kI)w%`K|~KpDR1I6?CBIS*m+lM*Xj9I#RFeQb+(o8UPy54 zNE+TcgQj=}C0wRY2KC@-dl1LG_{Md5 z@K!Akr_yrYsuk+Dnj*s=RxKyINVK#RHMRrprk0*7J{Q1qiIR9T2^nY}Zycqi#-l_S zjZ}-zn>aXK6>lbC9%}{*Lu8k=MB-ZhUP~U{BrkI49@P(eSMfga%> z4jDhFAHOU{{<0YSI*I;u@>&o5CKB@FB=h|g2(jHFm2M&-8s=%M)IOui)7~C&EKeKp zpAN{DmqD(Vk6yA-fFAxDdBG-3hGSu$-K-UJiV#Mgv)Pm~u#kN-nlNko$w{Q3 z-;ToO)^Xr|I||8s7M~Ysf#l~|Vu+Eqqx`##{@+EaXN-HFQF!^8V8}A`xG9B5NE$7= z@+w3LNIseP^GdzUsB;Tt5p-NJcbMfDEHUN_;+-#wT^{!(De$oW6s zbBHi5~Rhy8XC}Qz~h09?4@{M&?PdOg!dMG*ipAW%rP*(gfUS zxk|)mZ)<4W-)y@?-Ylzawwv6e?I!n#LifP?gArh^9k$<+edEkgk+uw(q5-F^C)Z+= z03CyCwQ=Km)4jVSG`l1;ajAyhZ8^sGD}fZdIY@>3<$c^QLCLfL45iOu^F})!Vy6f_ zxX($Ltn`}%A5rU%s3*I=TrTeA#*L3ih9A*;D7^yln>+HNBKx(8A)o&;^>dZwI$t9g zuF>1rMa>0vznS_PEWZg!q9&X){xF1*XMjD934canZ=jj4s_^=XoJ;!YJsL73>>9u) znHO>rwcbgrrXm#&D%iGxH@_nv;5*WrztSY;SC%^WE13^-k2#zxn4iKwn%YR|UQtjA z9@HXO_(L3BA>a9mz^95{;eA5f&_gS&J$hY{lp#F_Iv+MsfRq~(eIbHr9cW7CO?B8dY#rb>No%Bal{iAhXM!<-=JfnE; zNVCx)L*tO%|9j0ai&PYyhIm@WCO~7xSE&yaYOb`XX~9EY zrMwdpGjhWwy7JSc-f?_7uL;#OZWZAy{7W%9WWtMvb!auOvc-y3FF^Q)Hi5iFOq!mY z+4T*g`TK?Y`&GW*C~xzo_e%@DKiV~gqrXHIN&XH`>46l}pYgTLN@VV6B&*dA`t8tikafQ&Ss5pGeMTKSAO zRiA-!=f2H0mc)uyQ|$Bu{Zu>1mw@dzS;HT;`bkmgVcCX<)iw@epqqm~9E2UifG{Y3 zSUCBeVr!hZhC5ac2Ye&meZc#-i=4MxxauwoO5LS5;!-N?E@|JmqzXe_|A)=sm;FhX zSjZ}QB3*v3IO)CO7rVY!VEk50{af+oEjH|1yez2sTQqJfjpuh|uo&dB?=d-e58kIs zL*!y&hd7lIzi-oPcSsEG5d!bg9V_op5AhuY&0Y2pA>fPR@vkXO?n{FBYc?awa~KCZ zI)@?s-dOapAh*}dDz+rjqFzb{(r(0FDMzd%F)+!pmydd>l5P}2ZWOdP3w|4RtRd)Z zUuCGrR#?tPuN9yyGuhXx;nz#nw$mlOguHhNA6;TncZvGDgebgMuKB%=V)apL@}oin zeh?=gmvlVnOG(;;((OvKe}NXzEa;%!7*=*(PH7X5Tbyy0g^qv`sb|xPzboK|h`T)RZ~$LWFV*u7{4&0d9R-|_Y~ps1 zWRJ*ownv2F6Gavv*tp`9;Zxcz@+n<^U&Hyncpix72-Ar7n=2mI^T)+yaY=$9-Bcyz z97#94l~x_t!$Z#s=#|i<@%whe@7o2$k7JyYThlmwLOT}H0R7NZ+Ke{yp_k3k8Zxxd$KnR#+yA$ zQkKCoL~s@ACe1RWA90dy@mAe2T-r{PhlJ6Gq~0Gg{Akw+hh^Jl3b8_fj4!wui`Qg} zTco~iKP3;^Ar}(Esr@4hX-1P7;bQ5g#hQXjX$zy2qD@_y`Wl??iFGF~p><&u3@bZ- zXd1^qIOej3-{A{_8F*e(!zHx*kzU!)aIH#(4)RLY(rUiW%KkJj9GwvDDmu)o9p7HY z>0MD<-@XWGyaN$((D5Jc1-w8u13n>SHvl5Qh?k+HTpOs+wKRk;CF4@d1(T#(kD&WA ziO(m|Cki6w4?vc};B?KZn_i0FikB<7IQ4#;Fw{}~}+Zsn8b@#piTk9+vL#}W!ol5RPPK$=VJL1(W=42Hq62IT+2&jiS3YQixA zMxM}iV~ZF-zky@rKdr!_7Ms*r#}l~OK~6Zxi05dqYgL|PWslp=5w}HOW69R>EjgCf z9BES-0@03W_5-|e1YZ;NI(Q|}I*2h3&~KRCfbQx^{n1VY9uLvx-vRxfyI*kM7N?s^yQibh^a!X zJV`M~f16xy^FmU)gG}1N8DA=ULI*D?weJWvGEeQzW2K|io|YJn(p}rCV%JBJT1V?P zQvYzhNc}17& zMl9T3ObBhK#Buzs2!K)R7&6_8AX5vwfs`nrd^zw0-Ck-|U{K7gC^I8_4AHG3ktD3O z)@5qGDjc zT6D>`J?G4dIVU+-w4C}KRYnW+ulFr5A}rV^qL_(p!7bCj6i9JVQh5DK4K^Zcc@-;; zvPxe6YC(LGjBHBJTC(&kvfLE5tDRNjBm$;=sR`%FSJD!cR&kHAoX?|F&Ff=zXsY;V zGIp$fX?~aS#xiwnOiw*tpn2KLG-b)nQbV(NV-7WuK$|1+oFiR6N5Gi1o`{7)z_jZIC*RRG8swT`LnXcvDAtwnr6 z=d)OqPvDDrKNZ({r=CgiOB`R^pOZF#fg4` z*J_wXUqZ?zT70EIUrF+2`NYf!!JNwR%DzmZxt}9f@)&g>rSJF55spsUU2iG`bGj4l zqs7kCqL9DAN!ur$Yu?~l%DRgVlqe(#Jwg;~m zaz&*&0{yIz&7AZ!vbp5}hh3rcc&2WiCSIT3AGPi6OH31X&SCzE6C3gd+hdj!GgE$s zS+)kuviw3O3-rFio3GKocF$>aOPHEUhY@}SDw3%*ubepi;qRw(7U$E2QwYtaIdd4* zbeX4_<*5DqA+4u|()R0psB_-(&rLQMi}myosrPm$J`hLC1H z`<=Q%R=_Ly|2;2DNEi|!V7MgG6%{0|i-b`mgFxzv-$ZV&O*;JHewjUe|X zmZe4hvR2TW!V%cA`R~zAn;dmvc7{YZwcUFL37XmkL^8|}2=QSTM7@r-SR%qDCeH#n zID}0tXQL}nF)@1vJn@e&JNp>TR6>)OLfONaYIP0`XNgfHDgFL(98a$wc8gG^4unnC zR9DJQFDCXD6YwQcIcb=Qd!N zy9Z=1J4J&9uqj%B%8Fi1E>a;bO3Xu|w=REzuPP20xYH63$)1l>!Joo!QY@pI zIe`yWs!;f4B_(vFfL*DER;ygYo)u#|k9Ad?&#LG1=gVz7_RD$wYIgTpvMWLoE_*qQ zBvUB3UT_82jwM>uAFc((M?3IV;k9bv1PFVgKkvaWI0w-{ZeTaUsC$)wi73H2QU6|Z zW6>?*y+L%xAim^|ADP#(U}BGdcOTx$9f0*i47QCmC#YU+^j=g<5(+SV^=rn4`S~3G zc#?2}bgj0FiV`04e1wl^pp*@r&#Q8U!b0;^QF`#`;qETt@#t1YypE3OHfeeSCEvye zmNgynGU1cdm8BCeRf|idL6&OM(Q#2`-gvxZ^rk!}l%G#wGU)gqfpY;_qBDpd<#0Nw z-ScwY)i$g&w)yYr?x(-~N`-r0$#GuD<}Os95w+KUno6~2QvY) z?dR+p!hW5-QNQ`6Z3M*xdBEiOWZX#0ac;k_T`b2G_>tv!Jmq*kk*TW*Qmcnhm>JSO zT04;QLd-kw>84g(Dv?LYiY|ohwyx0?;Xvi*67@uFq8`ruN>S^rxpy5_nU zH>*z*E6(FA^sRGtRLIMl#fGdf5Ivw8jb0*gHIZnh&c=CWbpP5AfEUpCr9q~{=(*@! zxSeCmvn2++5`!HTMBU`Oqp0zZ7Jy>kV^~8)C$G|Y>QDOd0RFdX7`h|)lKAY`y1Z~= z6^M_c>yk>K7M29pfksI3GBo$h#QJ^^JZB8N^RHwU72=)W7xB)|#>6`lE9M<+q!hk` zsAXGUPY*t~B-Xw~ex%i5NO4%R5xx<=95|65i=RD3pLnA4>w& z!1}mx&Opo)SY+a3NH%%aR|8(xfOm%BDCF_nXUwyB49qh%0`sU4^As(Ft@(MS%yX70 z9?HgU0yZN52*G|@k8wx+{a6*v&CEd-4tTeRtV7Nrp^bC0GCA8>D z?9&=Vr|CSp*6UHDUDB=KhIy`h%^X*Jf)UqN#?c27#i9li$uScL+9RE^)flyGjABsr zo=@~2PGT+iQ0B#)FUn<7>SZd=fb8N!yFE||GT+9Pq9y54stb=U? zr>JhsI>2Fl)on7w-7T@EIdWUHon=W;FRjPaH(?900Bbac5!qFUevxo?yp)d;eh z0UOadmU6@lB8GEPjFBa2eJ}I|XB%eJ7o%1R3jtZ%pBHlUc>B@K~W}W>|HV5Kd&6Fco$ULT2iDgu6r#?Ae zw2;d0!P%nwq67WK7@&Ejwze;-QoO4-uhVEY%%l>TW>RI(Y?b^7F~Dp>X2<%BYGtEg z9&;{p-||Ad(gIdGkQ*q&ONARbX)YO~GIL4BDrYWb7)Q5`vSzZSi@Mr#odnx{^}u0` z`i2nT#jPN^@>Fn>_(?yByTCFNs1c_}|E`j+Pp5>~QZrmyC2Tc~fvNVBsZ_|tD`KiG z2Ru{x%&jX+a)F@_6_YSS))VCR1^v;)bx=tuo+9EBUF1!5Vva>hbNMV9znTt(aaYlN z0S%G6$Py!Rmu3Y?p`B4GQcIrZ;j~|W8X9VU1VwZI#-2ekZF7sZBsWvztQOe>P0+vB zhktP2JQZ77l5s5B|6AZ?E#>IFpM8=Zg04y7T_^=~MCYc~ z4e%G#XUe+7PIM|sEEheFjz)G*D4wIZC%#97h4P^+8-J8rkzfFE0)AP3UEa9#D0XyS zef2qZ%SlHS<);5Fze&kB0)Yhy3LqH*j|?~Jd)>q2Wt=yq&3)Lm(FZvfQ2`qtL=*XE z9SZMgn-;)t?503LnRm1z*g?gut_vh{2*fuEkHg%x9sXL%9WR>RpO;hs-8#A!)UD@1CA04Lq)H1J01wK(E| z%jE)Lh0wB+_TJV<2~vDgBd!Etg;ZEC0a{O(*6TeRq*38W=D{iI(Yujd2ziw@%<#1B ztS}rE(m25p1878yqB&k7*N%K(xTYDuf)#J36asD%;FAS&DUkT+BoOUa1SjKye`Z}5R`DA%+CsWBS*WKk(c*}`Q z1a?^xY~LU`TfciX_UfTsFZVbB5ADskfdxC6omo1be|SBgzT^TDSJT2;Dqty$dOOIw zo|WnqdP(7VHnCdneT!+eDk)5HhWP>{>0kwY`IVx_Tv?8}>_C1|Qps4(e^P?awQEX- zTSdG0z&;*9W=%o1fRNb?*9a{ZsUCqKHyScETu5o$Zv00U1Z<%oky352DH7Ci09{sk>-$P<`5aREhu`UF{oF( zSB7s|qGmj3qJf<8MHF)ck%hN~Vs&line1)Ku6m5Gm!T~++;f>GA}(1-VM$P9xc9>+ zYKpkX@!iGuGOytE0ZlvY zoz5nEPCJ`ofyx|Bs?X|WD|R%);FT#0R_bHwAze(O#k-gU*0lJ1Vm0wDbyfRq+>^m* zjWV4yEAk?|)4&%JQJ7z(+q^BHBx>{8N?skQn9|{M5j8}5h}2bu9wKF>*f|udnUh@f z=x{iS%Qbkp#_MHuNbUkFF89#R$?T2(h^JLD1LP|0LQ|tA?bC@7e&`p{YiXA&NG&;$ zS5xi8wy9J$^3s$QsGmm=(j(-^SU9f812nZIe@-7h@~cv0kV;)bks?*d_@va~GP2Jh zcJfBV&r8u)d3#|SqZY{QOYO%~EDDlXJT=AQJ)a+mZ8CT%G z`9vf8q6_3`Op-|0tODoWL7#9Sv!%Tc(Ojcweo?FJ59zd84Y@wb98E0A7jB|lB<4Bo z4HTK@IXd3ZJ4%Fn4vRzKDA5;7@ag4%^p`_8n~%$RndY3s3z9QLk}f4gX{*nhjdCdE z-j3pTQ+=j{FY&v3$xGF9iFddu!|R17NxBc-%=jQ~ryU#oY!YM4VYEj(OXTIupo7zB z;^7^1VgtT8BcTl(-blVVsaFnba;iu1&6jh$htcVl<2Ed97Pi-MYa8n2&W(ZtSnz%hY?SZPL3G!+WX za$G5AlvF5jMme;@>WoU9d;q&}VT&Fqt-E=oQijU@UoIWHyi62~Y%>-KCn78+SVgpx zURe^?F?Gs@bXA*Mf=lV@gGD&YnO_du22!2*r8HTDldUGI7(Zsdn*{+pj1f+*D^uVl z7srUSi)~3WS${0LT1t?-3(Hgy{7S{I1#iBn)DMZL6==(*KBQFvHq2d(qnWqharJz& z-e=gd?R6}K{k(ag(`p^iYb90+qnU(B(M>A-lOP~WpG zXN+s<)k@qCL41;*u#c@$4fkM7}(+hDmBYKD>MNLRBOz>xd2_KTQBX=GfgU|O~ z`g|(n^A#mC)L?d3iO&b!>GSPi+XkcIL1r|_CS(Y&?aRkvr`^*xL$vQJS?YEY7!?h~ z9@e|LAF_I$Y{9lNsN@YS3T>h|bOdLQ+fvf5p2Y>ob;|`dE>eyG?^h35>=+y@-e`s> z6HP@gqxg7>@i_MJ^m*BlRu&vwSXoX0@os8w(!Urr-V3bWdB5Q}4t39}-ECYIE9%(c|XIUSPI_25>aFpxXKaUGXEl}2lvncP_Qwk&i zM92CUW&D@im066iA&ZbOW~3= z=bT0B(&c_0;m1Iw$F6CDgIF(}VL*Z1GBrbY%N(9QFvpYH(u$8)%q_EFl z51=Pwo` zOos={k{SRvay@l)=aPhNpr{DtGsLUj!{ptv9;Ueic)Iiuc$h6{XDn%q1l(()Ja^+( zaT=q9ZI0%WX=)|8VOTX6E9rLE)KnmN%E!DQJ(rD zwu%Lc45osHscc4#ScE_1ekR?S*w0+S>zAWY^wyydm<7en>s*tl; zv}F7dn3+n#hxzE?7?+GmlRvwYNmG$`GLt;HJ~@&Elrkhp;L|Iu~h3Z21GBaJk3rn3&IBX}sb zwfuGAxY~u|v4j~X=HU6MD0kS#?O18cdw$sCG3uUauu`VKi_y3YG6kv9GNn7GaW_l- zk%}^s2aBSNk@hH$Il#DHiGD4dWv$=HLe0goMs^=>oaVLP!jg|KGOZ&WU!+Imw@cHx zION-<(>_D7FQ|0pV!&yy`eq8=G45wUKH;cq%~UOR3@s6~lKvb%K3Ywf3zg&Cc+*o_ z4oXj2pVfBhsHZ3pOXxMau(SQ#Q@#UVxijN>kPbch3uwjXBwDgsUfFBiLB zg)41Nw_H*mI;o;bq0QdQCL=nf%TGTIoe5#eG%P>c zw>=@{B0RQ(pxg56OE;TvBquXCcrv-BM?ql=0}^Z1Az!E%li6iNV7(UNihhCH^l_Q1*73$TAk24-ZJ-CEqS_>SMq!JQ4cFNSV zj^$B5%=M=gSYvEF2@H;s0XRd5Fm5J!e29*mqUpWupwi&e$$eaz_WPFjmQpvHqz}q& zGhUEyZwy;|lJg7vDw^p}D^1I46|M0KKqCHW=kSvaR_!ALmaLZuZ-zX!nILb5_K>Bw z|4c4yKE1t~)3+3B`*(SFDT!3Ny&5pbEI6e`3-%rJHilcW@Az!`tY`B< zst`D5tK7s#<%)CpV2cW=!Y$HVc7u$*MI+myMz;8MMj7~p2%K@~3;AwxKI$SZAuLiW zi}dayy<314xFUEdQfNR*wbG)F@2N0tQ%Au`e@=_f&9u)wjgR=ejz}9P&L79;Eg5NW zq(fjQ1cxw*6xX`wB;g>v-kH*BYuHW-y?j6USq{0nmtfdPakkLPm$wkQvnfzpWMD_e zI;te=>PrYmYTbLYHvMd-3$?5TMV$A?OLk+0P(Jd`?E?BFT&vs3Ad9Y~O3z^*DmL)e2f< zKRB0~bkK_@vbE$}<*WEW_*|k;a&5+uS+`!Vk@2=zw;WfZ($uhRz|XJhSnAMX z9XO?(W17j~OImnyO)=pL$2}t1$|{1hX+-Qh=EVa}npL-HObP-8E4EPaZ;?4WC}#E6 zx@Yi0FU4cCd9NNxr1}=wD4cAGOOwPxOyU075%(oYhk0I2z|3Ebh=NVxn@wce>c?zK zAfYQUSB7~eU4i@M#dWdRonRg-l+I(5b2;4cGFaZO6TFm=AI;wqmt%Q#tfV(}1d;r% zVjH3p!BUN!ViSY2gyCuhhY))aGBTBrr8asu>a2-wL|FwNB@L6@E%G;vqfAQex%BJV zk%Y1W(v3)c&oETQro!0RW`+H%G!0WNc0`Y~2$U0Y^pvEu9z{M#`CS~W?GMFqvHS$# z8Dq%4+(J)Pg$@TTIxe;aE>g8&QMpoOoYYZ{k|Y_60;6TpFPETJb#rGjLOKVNY*?7z z#u`2>nw;!ITbquHYPx)waxUeJj1m~NI0(UHBsW>WE1Pji&YaJ>lKZ5nVx^o-6mPyE zqZ#5U!8Q#yh(`S>#a(m;CSG_};n3o&!a|A)Jr*it9Fz}J9_I*gP>v+!aq{OO4uF=> zSWr`;6fz?Zjl6nL&S0;$^;XaAm29&%1To=~Ae7mlLmrSRvh=sv6wl&ZOTL=qT27~5 zs)*(6=izF$pO6rj&pI3`o0k+ew^s*Jq3O9k0r$ZvJ17;=xAC|aN8hMGtH)`{+B&;Q zD3{3O2#e~ij z64SHg-<1ms#%I`&u{3KtJd5mfk;X)Byk~)~LhXy&f7U}ItB#N!&Yjs0Vf3ShQzcxI z)9P`)UJj<6mZm2BvgKTQZx#}yxFjLLLz!r)Pd~R(=2WB)gCc{b6%5B%5~-z8)YQ3G zQ*}$Gly;Y*ps2KJP}YYoQ;k-37EduG78M|ej}X=rMlG1R5w)t=p-uP4$3Dk$Xyn!O zC{@U#Ec(#Y)5oPe%Cb>U=+nb;Xe4RpV=IhlNEMHL;hpR<2G^266u6cQ<5%WdQmt1n zS+Q#w2HWLWjCAa=9@42KgayG{GVy#4H}NNR^*{X3$U-YfBb|JhkIxDnLyBvW6X>Ik z64#TCIW>}cBk?luBNZ#`jh?`bD!BnTNN-h=a? zc~uVkNO`XC*hos6k+8aH6~jLTjL}UiS-OxroOFDsxonJ*K?dQI0oH*6Xwyo4`=#?2zY*g z?x_m7r$sw}TChPRAMhN-J&nKFJ=Q}biFEIbk`Mnf&L}Nhdg~9(kPlA8*=y1&DtZZL zl^zG*Fb_i(t|;fTaB`i5QRs!Tev%m?5lo6I!kOgw!yC>#q(?;($BAhdN-SE(4L^LS zdFC;~>0j`yNKuX}s--h_b5i3V=37vwdY(CCLCnbtRw;Q?8AEp75CONdBY73)9r!&_ zNiJpuSCU{;c4gBw?UBVQDe-B&Na2(DISgkH6@BEU)RP_c>_~Yim2MJ0+%Dq(9}Xbu zRgk)RIZ1C4FQhSetGY-fx$|Jo5bH<@|0Oqun-^R%+TxAjqwWPF&yL*#CmKri-rOs2 z`*24pM{&+ZJ4cd}nzZUkh^;vTi-T?n=Q|CuI}NNq%BkTckxJfZpuc%I&yt)whRxt# zr7NgHu3*twl20I;s4`d3f87SA4_{&)9Q`OsB14uXm6xepFP+vs-JjNtN@dP*Om;%g zh%!=)wiZj;jGdnuLr#1S{K#C6BIzsfyL6Q#kIg3|%ooJ->95U~q4S|K9^a?mfhtsB zOjYGz7S%;Ovurw#EECogxLHqTU3GdYOfT`Y9PqVg5)qA&D~=)Sq= zWVEZnMV6ATLqliSp`lW|P0^ot*w!3P1%&CMMKSw`Sg%rks)yC{hx~9XGmjn&Huqpb z^b!CxMvwE*9$=aJb|sZ}0lUiMJU#X}cj9sN*s0v(T&52L#tX_xW<)i7V|LrdBuiFe z&P*0?!haC84cZK#R(qdpGe?dxThA_=EKEhRwkq+)2PQf1Nr^@3!yfFbdITLD`Xi|2n*muxS^RTYp= z*#@PCznf{PKKRO({f8ox;42ySWD?Bpr{bhTEBu~|`CPvb;fjB9G*G*2b|V?2QNh32 zx=~IoPNr=#^a|Umm|~*IQnt3cgyTtl9&EazK74DAuwPDpEmeCZ7lg>2Rv-Nup)tEj?s;1xTkTN7y^$FF*~Mm&5`ITk4#&!%J; z-AJeeKmH^uB2AdI;JNhn0*y|4e`fUQJ1Y~tpH$6mmS59PQXxO7+`-!)ImJ6mM^YV^ zZl8uX(1!0$L!Y_rc#fR|Fc1TKhUhHORNx=L52#NQS0T%LsL|Hn<+cT`zLS_*BC{QT zRlJ`cYVl3<%hSjNd!pS%J{a4{T0iW7%&T5Xd@zd1bkRBG#_%3nP%(r2YtE7X?3R(7 z0x)(TOv^@zRYS0c)XYRY6*<|&Cnab_wA)?CHA}?G8p(&FyKD2ciE|!DH%|s*KF2d( z9+Ww7so@?zudNrIs`c{QRt3%sVSjdxVra}RSzbZd?Hk#~*c^a%Wvn1m6s!2eF7v(0 zg^~(3?H#WD4=ndZ+?HzhVWor^>rY&|tv%Ssl)!bsfKOSC6NV?daBDeP zDU8k~f-Ho?-V+0BK2$r2DULUZC#7bawqo`9BkQTjG!99}>D{VDL`@yFHq&(^frNAp;gNe}b?1=3ma1+t6934uV#_&cRsko{@NMB@ZsJR|sI^K}g*5#P z%HRxHDAC)R)Y$UHTDfy;iTwrAo!)hkRHPg*4;vxtm0vR!{@~k1pKOTwGPT$}@l$v( zVNI^)WCVTSj+E~6%`zJFubG;fmawKK>uPE;!Hi6EI2j&Qpkfm5$vfhe3xk@v%+TrH zF`8BC7|9NN)Q! z96=Kg`Oi2uvAd+u-kb@%w-|02AIYn0BB&Dxi6#e4nk!K3CW{FbbhKH>Y~?>nT(ptV z1EUt{4&t3TYI08gI}CHyg~6Ik0NOr0MoOoT*Mb=v7On1Hi6K%b>Tp!2{im~NZ44Ubqi^zq8tq$ zJ~L0l(Deq%2P0!q{U}8>>ZSoRDoG^<@*G0p2~wZQ78R2?9PFrY**6BT$oykJtb~-g z>`*+7;rnx=l^gbuk0pS1Hh+GX=T=Xfe_=?*4eC;C!cp@Dzg1 z>W$SNJXmFG6qw5Oco4T=j_$Uz<4ZK)ONUVB>Y#6fLP{@|rhPFVzl>JgP_Rkw}k!98YTXq`c&^ zyYXbk31Ya>B$u_0Br$aELP~Ov&8!`R(3*ZIkXI1f#APeB{EMZ$jRy#(MC6djiR3g= zh;P0a&19Ws;$X5)KWfpU?%d2+(x@s5By9j}muTK77PO75D4le5fSle9m_S#^rK+~5 z*SZ8w^!tSeWo~P|cfSX8V?=>_36<_ujq&S5u%i$ zhtY?_ZmT-phUfT=w2_TCdn>OB2v&&^r+gZ&(ng%U&Sy(i2o6piGK;Wm++7@1i5>!#eO6ElJq*6^tb|sLgXB&p&U!|oRZuX zwu-ufV;jNWAP6+1qx)KgfEtKs^TqTDOq-D*i@AXJVlK#JFOBx@LPu8z7pUaJbG5__ z?}6WbRV*9;>F`Z=?r79ta=$nwfU8(A`yR;(0@Y9wW}qC8%+1sy(h zBFT4VY%_T%!c7BWtj3*q(422UaR!~0Hck`EBpA^t#W0S@?~)Ln*(0SmURpHJ7Z2TF z5;tp(a_iYN;p~)Wzf6={#v8}0P`qh8FWb!YsKIr-ktzho^C(EClHRA%6ts!)%2gky zuGSF;PsCeY`az(OA(*MkX1dOz1onTOuT@SkH~k~5uMshJMqpIEKs8g10p*BbTh1F5@OhueBuqhVARb(!CV|9Yan z^5o{Q8d$UmmqNfK*se_WZ*=2i5$Of2q0ERgMX49E!e%1xJQlAaF5jS+p6vyPZS~PSxzJf_9P-UO+zFs%E#covk`4E*>Io zY$E#3QiZdi``1WB&q9#Z4ytWazEXdnMRV z6(qM)h$Sr8sf15?x_a!m1;0Il+3pLKVt|V`5dAMC7SAw=jd300=bI>s_C%NBm$(LT;XI-!(r1x5I(Tyjf1aiZ zzYvcZS0?)cIlM0*&d+B}^>v;g5X-zW8pzZ^BmBv`Xlo$EBd=GKYJ}%fBd{pe2oJ+Y z;lIPH5lEvkXav&HGy>H?iAESOU62wV;_pxjdz$(rTPz8G6?P$m_Oq<8pP?CiEPJRW zJty`zWkAY~K^gG1Qf2TK+#OYrDN!lCeWrx3jLx`(6)Uqa9b662k*s4-2B%5GmzDk< zdFdZX1waAH@mm9_twsgBf+~PTu?qOeOH(RYJ$Q%{ZDCA%eto?P_m| z6m&V0BD<0@E>i6$P?M|n3GD6!Wy!>1jT>Wc^aPF;?l4Og0&gQPn`kPgN}HD-J}b$y zJ&Qn{#g^Lix&~^iC6i$NzHHX-pYXg}<6f=)R&!9wHCWB}R})e45dKDezQ_z}d&KW> zzK9My{(@kzrtkrAJ?V8QMkbH-e}+I)k)u^Vi@iLb#otmj%4V*ndmtDz?v3*A6%y~@ zlWu{Nf_b(43+aV!SwR1c4X)P9&nK)W&@(nFg@_necl#13GaeLC_3{w(xDJ zp>=yVL?L)Hf8NbPPVt)aJ^%l#nMlErypwy{T%Gb3HwR&@o3k$*KNtpX^B{lph2s$@ zbDMj@2tRtl+MaOy5S9VM`a8ntvamL~DXd2`+343{6a~4(SKx93FZ(y-FZ*=atGi*t z<}lj95{W_n)!coRy_=hfmt0d{I1WA5M!yR2`Idd*xISFp7dGHtJ${`RHf#**djQ6_ z!$#bjkQ*OHyTis0h0*zf4L4sEg1w^M2k_#<6@VWP>5AU4;iF;nIfEW3(7>M!^!p9^ z+YbhPOCI_bLf-%i>;~z6hXDj(`~I-DKWy(JZQ4Pn-MG6^IGPN642P2k!us9eTrgEI z6dubzIZQSqHz7CB)}}!Op9t$$hQZcBjMVk;ec*C1oWZ(-`Kx|*Q$J+4f%Lv`Dl6;@ zbNG|V9En-uYBonE$bkuPNha`@WP)7OtGmPOr+4_z)6dG`a015Dav*HjP2@}fF3$k~ z8+f}vZ0RE~XM?wpEfqF`TP6*&T5BHVxZfW(_C>A>4_Rp;#_RTl&4YF|7&iCW)ex^1 z@M-4g@gd!_Z06+g;d6MgPuR+2PMjR(-1{%foD_u9aX-invH!2sWI6-jCkP|mq>2k< z9JHSHLBXp>Qh`CwFk0C%95;d588V9U>e)W5hjvtR!GAFl(GozsQoG{mu zgK+0&g}HC4b=)273v)S&&IW+e8cxi8D9oJ`PTUYq-yKfu52yEqQ}z=#6CaI*3Bj~J z-sjUEJsk!n8>5H9W{ky9u{=fvE`*9fL(QQ5>L*l-i=b*VsCo%2p~@UX;0>%+f^~k_ zx*?oA9Jcm{ll#I60;_dbOj(f(_xZF(Plxw~Gu?%|4KcazW;&o!mwVr$8kPEwWmM`} zZ}2MhPOnmX+ZcFh+7}{-7px1TO%S)AP{*CW0av%>ufSqaaL$#G)%)5Yimp8j$pP-7 z{xEzKYJP~nZ{R!MmaK&5g|!>Q_Pt?=yx-IotB?+I4!(>N*6$5<4W@-GPtfgoVu4i? ziPkHL#F?36fa)8BTcR5Dr#wKI*&#^-2BA8@o~{d}E9&-zlhoGQ@nL+8F6suu_L45d zW##%Hk7X7V_W?O*$@Pcesc<@)^c)8)7IB<7G4&K2=c0)vx~$%vGYq0PXO0ToHWV0x zeuQ=CAh#>bVzkhB=um)z!`gT;Xuk*TSAW<*WT0XLs#8`oOC|^-K+V?p;$`uL-BCLT ziW{cTTWyqE3M#uT`@+fW!%aF6_oCmv&mrqG%A7bZ|0)na2Zp#IuxIY|9{;8Xa$Wuv zw{1`S3OmT(|3ulluD!5!r*Gv81+_gwq8}IB?NmVaYj7y4bw_?wMs{j0_OkQACUZx6 zAvvufy&rIBu*k0PEnf(~Med<+>LcV-cLJ+RNXsEy$gYGLRKl9;a9F=S%pQwsFQsQP zF|1#Q_m{=*OSi(k%(a29&`-u~A5_BCseVjH<8RryhCr2TmH9V}rACFksv$p&DVQ4c zDg*Dp-xz`(8mTchAYW}(gGDpHh34T5qz*X%I6z+{Ef}8l@b`;?U zP~;v!QHPAfnmP^d_Lxd{*TZEbi82?qMQ&dW4xDRin+OMS_l0=S-iE6?FXI)MK5*}? z3*e84;UBoLKRkw4oO0cWr@WnUU(K=J#c)sD%ue$x+-vuPXd5BU)H^w-#wam_3k@x#0_CrUf5{}lXxY(eUCWC(Vj;TZ!orY&G=QBT+z)cL25xPcrT>@*D zr!iC>2L`t`a`yr)nYNIx&KcN)pK~ch?&iy=G63%b1~0gBF9saB2X~UubEk*3xtl|53h6_@J#vo#jcx*10Uj0m z#LT9R&P!MaNeAeth`-(8oCuk@ zIaf`!?%-P3EzO+yXj@60*T;3b6QCmZQZe%xn8~(lw%cN80Xo8C`onSEVbe*^TPGjD9C0(o9}H8CU{lk8g{p-o_j|v=Bnk+PK#7#~~U1fuG(C~wKD0B4`q-(vwKnMM?CC^@AmZ=0_oIzYTy{g@Xs}&C+J>-$J~S%4 zzANj{uluK(F1*p~?z^mum2oq0Un1kRW3MQTQ4>13D34MBOkOTeyp14@^?xsv4LF8v z_Qn8^@n5cyTwY=!P;3DfUGzvB>8;g3^M@3yv$s`y=13|FM< za>(WCe!ooJyIHq8-7Y{|-q$A2AMOXE9<8hq0r^xR^V|)W^@L6CnVK%rG4rOtj155N z?2QzOqOCxxy9P}+yWiDx4aD_KUbYGaBxy^ST@^OETQBPaZoxvItx05NR06{s^o1?U z@y>Bok^$m+5g@J;BU1U@E~3|i53spt2+%xuSr9{mu#1=A!vqc3?rt!}tTOz0dw68j z7EW0SR=pCJ5J&fxokN{5XoxUX0$S)2Nx&I0UH}XEpDHaIohJ6aA2c(8W5llIXdvkA z)%TQUbZC)}@66K|^YKc+hNn?2eN*aMULJMQm-1R*VR*P zZc0?qtx$TH9*H;QNyMh$=7du!)Noas0-=An1pO6umq!0Ffkcf=Zi8qJv#UYXYcGpM z?Xxvw&>}|^Dv{^M8o7EMk|2CW4C+=7YD+kQ;<=UL`L-0iSgfd)3o!@qO0P`Y*H_=R z2!yFm)Sg%0=co8Q!W3J+W1~CcS>gHBPC&DSkA;8!A%JgHQm*RI|aYNc)7?S zhyvH|fc#}nqif#eJ^fp}r~lJ(p8j?6^lvzXp8m`8o<6;F`uwl)p8f!r48tj13|E`8 ze|O5+|7PCVzaj1HUxQit=+6FbXxGE;?1QndEUy^zzWz|mvj)%TzCI?k)e1%01>%go zGGD)mOyub6%j@q6$6c9r&C9v`_6lA8QC=zZ{OuKb{(7aWsF(j;6M(y_q|N`-2|zK= z9>o72gP#7Y|6ht@Ml<3VK%x6T2CwEX>HfzSn7)5D`iZy1O<{Ho9Fnx}U$LK4%>+cg zKRk)=?#z3Ie_==eV5Cm@`4!Je|LW&gYX$%J`uT9vIxum?svVtVbMIw0yC6?L&d;U) z>fry?!LP`{pLwHq@IT9p*!+^@_qKN`jC)V*K1_IS!z>+-0{0Cbi--xkW)=ZUuHXjl zp4#-Q7LvgOgid$Y2FeCuCJYm=xYEXOZ8U(jfq%Y8YajPU!)W8%TEE1jI(IM1W8HTz z8nAdMesg8$iwM`M%|>M%X2Y<<-FXq$6YAX$us-0TZMYY>VKo%uq|I=CAK?QSKg5r~ z-E|3mOwi<*i)IWkMsKnd8wPH%)W(%?-%EXeI1LpVH`;3M3XYa=ejw}Pupa>Q)%f37 zg4vzH(lr`&K5G9REbS)i2nxh`pS~+>88xF5AO(YbAo#~!w@G{M2OuUOuJnDP&CLztqXBh1yXKS zi2#_vP2|GD%MgplK0U^OntQYWxDYvzN)O18kuqFUbboMY3{~pf^M{oAcw8V_zsG33 z2&~IR@{dG4xLxnQcF_>QNc!}4El3&h zua3HMU4v*rK`i$Wm>7p_v>6B&18o-%aM13*v52H_KXPF`?%)7}wMJY3I*qXdE_k{> zQsB_6t%^3@s&S69VO?D^tT4J>Bm1X|2K~r>uaSKiv!!@wi*0Dx2$0=&QNSz$<&3!p zK_@2I2JVlb{LCO+&929jlI|)zYgiT5ooc^NMa8Xz(S)Dvd`*6A2jNJCbu_k5 zq0v^1tp1{~fs$2=HOa8~3{dD*$n>IfI}NnPD#NTn#oLH z=G$w05#8RdMKI)fE#|9zWV4A_I=yHh|DaoKHey6w;e^R}zCDW;KDK>tj#>ybeIN8- zgtk9PW}48}HU({CHFFo39=nbwX+;>aAI^^^hwW$?O>EOBs2*ow7>n10Z7aiB@8-q) zRpBf!8tPog)WZ3QMJ_%q>~vV;T}*s2g8_E~+^Qc4=by&AJtICp(m!8P`u-A!Wb3le z!!~rf#34T$kC*g>ZCDj0Jn9xL`PbgigGx;V>kR@6kK1sk__2R`YlzRDHzCKtUC@SOSXpf7lum~*eiLv!kiLdH9~|OsVn`%MvGs9 z2ccDvCFXvHJB{uo%z~fMH*;oZ=bnD>w{subK70U=AkS~CdiSW_hNQh;*6i689zi&O2@D=sbN2&W%r1U_TH{XT zegtrysi$mr*!~=v3nX%X__pM|PwKtT+I#N5b`r?%mvnx2apxB!56u0u>g7j#SJ_HG zD6Dj6Q6*sgoxz^=+mw{WL>?w*Ep#6Q_Uqg?gRVu)6WD;e{meRI9sn!gR$F`#VO0(F z;m>+5uW)smQ0?EpaG_XxcD>f zIbkD2W2Ml;^tmY77#+w##NA_X6%dqMI0VZLRWyWfEK+lkg7X~Q?nYH)7P&9g?2pCv zkui%cGXK$BEVf-B+%SgG=zg@LYY#?qYX~KTd~3KA?&p`%9=f-?E+(@fmhPUubjZ`} zsY@%)i)ijKw{7j=P-X7~x&!wvaICagKE?1u&qj-VNgR(oXBhIgc4As1Hm#VKLgyV% zFwwPw@3UiuuZUc7(25w7x$fhsWE@EbM*cZ+0QCHKFOomNYedpKktBeA9aSAa2fa-;8gvCMFD& z$oF!J=f0DH@o0!)yH)+|kNbOp_2*ysyk7VOUWj}A+z#yE!c2`=^kDe9pH|(^r0RZB zbw5{FS4Qq1Rrd?|y1t3O*X=JB-~O0xek>my z>t0IJGc48uRK{07%JWrISc?-N)`QFFoFcnD=0SQ}bjaLyQ5XD&*+(td7NZE<-EpOS zgWp$!-K>a|=~`Fc)yIE`4rtBv4(|y*0WHQvSs48(xtIB*@K0Qnc^k3}rnt{*5}H}w z*5<|sa2FBFkjCH@Cv{1ZHCM@$h>SGYR37$n-$k3#{a1k?b z-SLP$xL(Tkg_gE#le2I!_+cruyL)OoFbRY8L%fr93%TtF2ScMNB!UXO_m+ZpT&0RU zQG;Iuc#ur5wN)iO@Kke-}1m!3=POg?Qa_ zNbvd(g?N3+;2aS$L-&6GkAZt5XT+n7f_uFp_g|`em$i%i0!-l#Yqy5e&jC;MQj%U? zAU+cnsDvI91|P$)f!gaXf;I)YZy%kaK`$d9*z8GPh(5|~np$vwt$UAHM6La+1&@-Q z69S6jTB*?=Ve15~C3wmvXNb%og0c74>;-~diWxx3Lt4{i3#FW`W! zgV>^wajzy{P&b^93e+9gVwHu78r*h(F#+6Y0r1n^!$D6rhiVV+9LT|Q!qD8lx;=z> zH|~StU)ct{pMbF=Oj74wg4Vcf31LU*9Q7OBH4UuU18f1|f^b`FgP zLb~>kD`16CF5s-&HwpKDXNs*1_is=$mF(m(=_?QkjXXFQZoglacrEKp>>JdX1}{Tk)9`t&$7ilxly`)+CUkJUZ}d>UIidQ-nCe#*Q2iak0k`)_Bwx(G)et{ceIxH0i??wd zl=%N}VM6>zb*M0cbdnT2NfZAjz?e{;A)lD+Kb5%_1yXdsS#OsRp6kPD!vAQe2#+nf zO3-(IN6OFc-%X1RF?dP89m2qEE9f4#49$q_Wug}pBVVKLpnH+x3b8Kv;sc&9p7zTF zy%tLxm)E~nZ=Wf`{Gqlpbw)bS=NnE)diwY&nb%^Idq~iUNf4Gom zqi7wk_<{C9`USKrzy{DPwb+?CWkuL_1$ZES{3=;Xpy5?ohMPt+ zwl&N?9G)nxvH`K~rf?Pd1(1knNx2fkIF?x=1AH=Zq~nTk!YCirL7bA0%>1=1FjciTJf9JdBLxg=ZrwL0d;RN}6h;w~I;#BtBkjkmCZK zIb4~}q*MBGvLRZvjhyjj%ZkJXZ?z(wlVSq>_G21nFlPHiuO9B>NN^CJn-lDTi8*gy zpBx<=p=|4uR|@+3N=*u;4JKjDZVk}EddIreL>_Jog!R7)n^)@I!!`TuCPP?ef9|gz z)UC(1lB?o-xqjS)qI_&CR}0xDn!t_BO|ZMfH$4QZ2p8RCOv2NNG6Qzd9+@-~K(k|N zF??Cj$+mclfdl+?I>E=W0ycnYUd1-40$2^!!Rm#yDB(aBxVjG;ZF1Q5#XI#;r{4S^ z-<*IY0(u@`uYXeGb^Z9&Fj!gxw!&&OlI_}ZJ$rD}I;8t+_j&35Y`hZ`(w%2dd}Igh zxsQgy$#!*I7`)c5-W~=g`YTrHL4l)66`2d`uL+w^#4#W^+~Ksa`L*HrUx)Rl^P(Q& zhbO?~^#PrSG%kZpJ=_k6lY}UBBO&z5J5vb#xYxAQ7apNs%q!ItLam-hDC*V$`4Kj^ z9@r1+<2ML}%|$}mcjUDtXvKZLK+4t+jaGxzV5CXZQQBxI(*{Umy?D~}?TK=X)pIqi zlor>J7LBf<6CG{!njxXenFUnID~m=Eq%n6<*!-?A%Df7$?|M?9$8rl89;Tb+qD@G? z0ILf9S@Cm5he{#CK>U1vg8r;~Ot^`3MkY)ghyIZg^n*36o;)NWvI%vug$?M3^M~%R zQu-;6{!gdTkFzas9~GL2{^q^vVT2m2)@(40P=jr>82SrpAY@{_j5o<(9(wLw#D|Zb zJbk|xk#St37FBRKfg*xY?oFu8tpR+%t-g>g8I0|@nFPr*DB?lzq43+CAJ(t7tJmXa zA~8P=n;$_FtGq~T6dlTm0UJs2`#VYy4h8Zvbz98u?@RMLf&)V5J!yo4R=AHI()>O` z4Oa8~K0*y5-1B=u4e-1567xIG45BJp^Krv7Ljd$QsT{C_6Jfm2)Ge_4q3Cy>28oiw zc2^?hw;@i&(bb({aEo1Sr+J&QS54JMcS6M@G=;fq-fgVN{>5(NA+>)WWV8TqXItR2 zv?o4tL)69z)ScofOR}Q71X$Y%Zc0U3=2-2pZ3k; z^RF|}ix!=+3x_=hG;^>wVAv;iYVR+W1N(Fwv5$Jb)AlT|ni~>B;C*27V^GXEvg+$N ziv*8+XWG?`X`O1{2)43GPT+QJQ(lPf-)3i5Xp*Ft&29x)b?0;4$9521XY~gwoc1IL zzKpY!cr466Vz6b8P{DV0OHV!FDJMMj>dLDFg8H=~G_#g|NcCv?8kD>^5?um{(`6_>Yiv`a(33=K{_yhc``8f*2 z5tPv&J;H{~W2pHSnPrzr*Qc4xS&K5*(f2 z{-8rtIN`_k(D6$l06Y$|=|EV2Cj#2htF=(t&|`UXn8aZTKEM-$`)wIb@t69YrPF?Uv9G3lO10Lbauu- zE!OyY(OLsMGZ7f0>+A&oL~i2{a!CVsJ%cC=n!SaCZqJ8Yo>s`^6BLX+7M*_@-ZQo; z;;njj75+Lb@BHy1<_C6%wLmAjVQxV)@RPg1GN*9tS9Gkpxg+l8DMZs#-CPgML*X#4 z8@d0un5{RuH-$Y4^!~@i12g@NzWulZq4+6)h5Pjzum(edi%C)N+Da7PbbxCJl3 ztL$RBSmahS5MyUi;Fp~p!o!D@n8DDhTeR0Na2NuWbWw0}ggz4r32nFl)mPzNvd*~} zDF1e&6V8&bR|ei$MWLK?1;M91kVAwGm@?FoYnbMx6=0t5)`XCsDgS-F_N<$^z!>S zPH|`Fy&+~(M-U=uE2LJ1(W8>shf5@u%kJ}06(BzZR!+S|=b0M19TQ|M`nZV{T;?FQ zct5W*Y`z!*adD51n!^bsILF1kwksTeDgJK9(lJ)@sP%cAQS>In^#IjuLB&-Ftgusk zUx~$em|PtYh`3dB1(Lb8;oO}zha)aqa_MwcgxycR^@bXbOsAIF(Rxs6CQLH+^qOyE}{Jj5mcjUjVe@%06r8R?>N z>9;g~M*Ajv=ILgz$qpO@A}+ADkzRA#x}T5*|zO7raUKsrPwMH!^#eG49AdP`bGTV)P#69Dm|h$ z`bh!pTF4yzU46q`e|25)O27s&-mk$oI(7<}doy%TctJk0@*fvlp#04t&H|hf2E8*8 zlrc5zetWTTAz5AT)w>P3nZsPxM?=?K$_S>?WT$n9I1r}eRCEnSgZjnAYSFDWx}9{jE!fMAhZuog~nI2LVi3Hap}@m9vvUOrb)4ik)}=;8>Tp0gEL zoe(a44t+i9+jbs{$XtQ^dYQ5qenu6?pi5amfgPMVI~8rVySsJ_hg01@X9lc6cRQEM zX+_`Uum<1|Ho=mfMgj;iM}-I3;NH7)PdL5{=3isjkMK(^90g833DsmYeB?Rdkj1KVWq6GKA}D+W)|6|bs}(Py5~kq@R`t!qxi$ez9A`{CyK)oyei+V4 z=+?nsaM2_3L_(b2)`gxG_UG|IJd~5Gmp7vNr{xA9ypM?U=b<>As~ZHNIEvrObfczR z3FVR0M!yjer@NLZ*^_{yH8Ay%pyN>tZfVsPDC{$nW zt9YyI2q4leJP3wv$Q{W`I6Kn)0Pjq5Z$`crD*YDW^}t=5ADBkO@$L0vp+INQj13h- zdNgE0V-8HK*Mo4akO2MBX_tMboBA%voHQPFAm4RZVQzR~*#4rh;S}7#CR+{+1B8Os zUj_sD8*39WV%$>z)8RMad&+e&i5|Z?cLRg&%kBW~{*2rSp3q8C2M4PV;xw2ivd!Xvq-;AKvUDG zBD)iNr~xw}ZkLecTJv@i1C?T47}jpHt8cI}XY&)Z-fg2C(u5g;jJD4Xm%57v1^H8e zGH_qzLk3e%!e8Wvd)yn-i42`=v(FP~uaO3Xe$?N=AvQ=vFazXPq6_q`U(X5gHO0Z) zZXB@75pkOUY<25t;gP3O6K`Nvvi^d-6@Yj@03r|Ra5EgRVoJT_gJH_8tuce7T;#T$0z&SmshOdU`}*A88hlF%UQC;oKI-ckyJlIQGnV1;)R8#jN{qEF#S2^&z}mv)F{dDln0`KN203CHmXmjY!&}_7 zT$__5;DE1c*kGW94HkvD?h)Bw|2`aC%!RyzGr^Y%nczlGIV2TxRK@?+%8C>I;v`b9dgOm}w0cR+}W{6g|*1sc;_Y0w4uYt~I zT7B%`)}AYr$yhifK#V&(oQB1B_F!zSbp}w&MK_C`8(#)anA6!mla?Nt7u50ca6vR! zA@Nl^UypRQTnmQD$5P7i9jP zy3kC28fffrm+#n%3}>VwU{rzoo~lq|CaOmY0ixR)>&s!!g`qdj@~ZAaYg4dak5)Hu zzxLg;=@Pla3$Z?LD2LhTi~g|YwHS+!KN7G{tubBVz$n09J@G;JB}~dR4d0VNK3$3ZFcuE zKEgEaba2FZcFVfqdgDZ;>hU1;fJYnBu5$*)0a1~1u@v%Tn;oFCP7ND1F1U#L3ufkD zYNDlR;Xh6JD6i@(z)OIfS2WhHoqFyu+yO)5Fy_d86w*|dw;-b9_u|nvN93u&gr{hK z3Y?@*q@ASqgEPnq&n1Y1CK}upceh`Z)cjvjFv4Pz9=-+Y zEWkdeU+j(C;e4oNI>SyPZ(Ag{$!bR-hI%*&{=QuqK0n z?7?~HVGf)9Jm7m&%&4C$!rZoSiC)j$ntq$AY?9^PK^M$`?xKlz(-F*!%}6a~6tke5W`u>6Lw!-&uw@@7Kawt&&~;Apg}IiOzsrI zfDo_F4Cb5-qsBl^L&UEGKtDvFCt#BgW}aPpWD)gi9TfGYK>nc&}wxd38t$b5nO!xMfs6NqnJWv;FX&FvY7{A zA4qq1lTz5q1BORicPErspQ3~tHxM4-F2LYY)NlxXMaVv)RueSm6%i!&JP$hWYn7gJ zbh>|}$oMY1L|VPQ7lD`976^kK#C1{bX?tRtk>C|Dqp<%A%;y`y9&RCIyw*+K4EIi6S+Ti#}jaOlmmKvn3RO>mE`yz4&)wcp(UL zYuYeSV>`G-cKity&7*0Z&4abas2j8zZ#LX*$DRsI`S5w#VT5viOcihbyBf5;#a|%bNPiAB?Gfel82`Yw&ipmm5 z5JVuL;8OuXR%LNt08x-tKoJ!Y(f0(w1!ejE|5MAY+nFT1_xpa|_WPpDy>+VU)H$b4 zojP@@>Neye*FLVncxQ-h46g7evBJO>R!Qn{2}6>pOT{0*j}g!*Q&VfMx7!W z)r#vMs9{UnirW@y(=ru&L-_XtB@_B8k@Q6jz%^AC!#E^dl4_9tx=o?tkUX?Nu|XMr zOz8nICODv&Q!tqdeuii#b8kW{w@jLh z$MF&+DS#{`;x~p{OD~8OLa>XiGU^zn{)44>l0t=+O&5R1;g4*yZCL-L4|ov#>dYt7 z&-30i_N=Z3t5{C*Alb3PXdQn21)R}0xts1)diz~ZVC}WozkypOa`N>dy{m)WYfP8= zv~qQF{&mT*40eikf&YhxfYft}g$ zBv+Ney}^SQaNV*VH$VLo@pcrD-2hGZc@4hg5CCy{_b5%;&4#qgKg2l*a^eyUb8`=g z_ys?2Z$^t6x54PElo5gB^BJ1S*hW(!c_g-O=>6k%Q8KwgS&=JA-kM~^+GG;e0*L@L zJKIyBW$FUBr@=4J;=$tdF>C?UBarT0B~($4MSIy;s|LZR@qz!b+t79Z{HF0&JY!ZFP6!%+&ozHs+XH+x zj^d@T+#H2vkDX-$6y!%^_r|QRJ}Q8F`8bilIYur<$oC_-r=;xs+jxV)7@OvY3$TI_ zJ1k@T&^0t`$h%ToBC)A7MT{=ph0yjJK^MjswnBol-8rDAhzc14HLR1^$<-qBKBK-O!hn#;Q0@(z~z)A{Q+3V;P1ZjWZp6Y48}a4JRTm^n$#6jvSMLh zllx97J#A_O&WQ@U4O;4qG#xKoT`&c|Vs7eDpHgWa1P_Ccf)$1LD;BQ|4~Ztip+GR? z;~$dW1T?BDcKqzK_#ztz%Pq4gYVd*i4ib&l(A zjG(5YTVX!A+cpV1UhsV$NTz=Xf=)bD>FoW(`_S3DaF$N#Y;xwVxL7|$6R4pSto#r!Ug&C5CiY6iq>r^~YL5SpQ*j><~ck z!?LPU>CZJsqct{w@w*o1pvAjBBIN%F(|3U1CISN!umgi*DwYQ_0rQ40G-1)8PN)}i z@u){OZp2SxKx;iRa^pt~Cp%2rPny%{hLx*zj%I~4l_n3uCW&I*g{y$W9$+{L^5zFA;GFY|VKS{D$?xn0!I2c}6i}bqhCSA?K_#ab zxNz9vs^`w+H%)mu2K0k?;_oRf%_T;f9K9-UK<6o*%8^W zTno^r7W5B6-bW93XOb(o$y4QQ!)2W50(`JbihmbhKMQcH`Mb(t#gqGxijmfcRB`4sn-NxhuswaM^ z4dX#=E|{?RN%A8;ygCPt#pdZ{E|*P5jJV8Ir9KE(zC|2>h$A|%O8>YF1+24hu%4nz|?0PDnWmT&>ZW8iQ$Sn?qA zLBAouMZm$gq$JFhl*Dq<5AIfrK0o-QTCIZ(OdYAo5&B0^34MI)nXsKPoC#_D2RKp+ zDiEYauoQt6elv$L2osC&gF8(Vf0nL@R1XnFuUaPr2K1O$;GqQ=i) zm!|4)15Zvn-K0G3y_|KGtKE7r{V@D5u00VXDoK4oD*w<_-t$Bh)KTsxvoh$q3zgN^ z(t`jOsz^4LpBSq6#kw)jhtVz2%yffn$ORHLN~! zrVoc%z?2*AOY@-ef08D2Ey}=|+JSb*V#g)kv?LfOnU$s_PlSsZ)BPwk9OA0$N%#!fW8{*JTD7XeNL$%|zl z6`xh&-2tvKrrpKg?ZO7;=#4A5hYQBPh90E0kHxZc7M>HjV8(9*0~hhPK)B#NM==*RW3scIgm{?&v)sdGckf36fq+S4N+{K*MnmqXA&aLC`p`E<%KnFK!+??|g>9l8ZAVY)bD1)`heF}NLHoO_M(N>4)X zL2JH%lXoENi)Y=p2KM#{-fP;9C36>*9RxlDj{M*<@kt$fEMib*1x`FM%ihLStnyb# z-Is3{U+y7a4N4V`PG1u5*%O zz5>~uc#`sHab|7f3at+4Dp3o?8DC{9`x01x=(t49UIiAgDvM7bRhv$TZAjmU=GU%= zuH&SjL>VzrK%v#ig>mu$SlG1j4J*_uj?0=-7t2|sGy-{0P_8=Q#$dEeq;*4VjaD_C zx?5V|p~U?e3eqkgFhK8c-qwK(J@?{6fgC?Z(qmFPo`-n(lcx-Ojv!uKK*vDQ;?-kC z76=?GjMCgLQ>mjaQJjRof@<`pi*!lY9dm37hi##Uzvj1j&BD2dTJaZ38(q)B!3E^` z_bTyc^xON?hcpoKO+LvO(r>o%jKHT@1*zd1aQYw>e5C||L9qVRSt^hvKa{Ea2=sEE zoYI<{h7WtjhS+3$hEd|tA4mUk_77990y&7BsD2n8Q8M)du?g&4(xD7e*MR$%#bRSY zMn8Zt7rK5jFYr4Js%vpjo9*HQOJqwO+(0ML?F`O^J*Dv;9h`!_!C)@K+2`QwB}*!e zl#iEOBtKW7gOIb3$snXEq@q@E9M6)S4IG|&6{{PZixnq?c)$hE@?)`Z9BHCz;owXj zY}U#vSljg^=2MXK5~?z6%kb?O>~>)=;R>OO+=9Ewsq_uOXFn)oV>;#jJY2|s$2yB5 zm2cX-de(hXJvbMbd<0=*iTK42`*6A?=7#tpHpF1W4|oFc*N8g4g1XOuoxhTu zX^5H8$)5-#2g2$nA0$CE_2CTJYL>f=YHquayl;oCSSaVWgY-g{N>3CWT=`8DnUx9kO^x`oS}zkJw7z%qCUYHl@w_TULvTXg0w zIJToy2kCulpGs#z;nvoxRI$%O&!%DRE)&CY5OK1a=x&Z?cM!(If9O@3yJ<3s;seois1^Y$ zXKpIbRM@2{FiWeEFBv zc%|Grf&M&*Rc0$FQg`3*f~g2aWJ#4m8mdjB?kgNr+;e~`f= zF?@VU3uknKKb^G#K^z9)u@078nroc9A-BOpfFH^?45`GBoYY~+J#m75eO%@0IR-t1 z^=ptrSDBMFL+BmoI#;d;7)_DGpYPyQ4P|hNzXHE(k3|P|Yh{J{ZNt$DG{89i#AL~w zr2igX{o>vSb!0S_&h&?5&+1}~c{&wq)#r{!rx4&tor)U;*iEc1F zNl1hl{LuRgko^D4T>MR6M9TCxdFi3;^-`9M0!G#T7hdMVI)$N`hhNZr|2nR_(hlOf zJvV)DO`+rzB=JpNQGQHmd~%2GNpAtBqOVBoioa7oHu&O4cpmF%yeILQhV%kX6g(V- zGtMTe#QJ`4KI0bnyjSuBS{9<4N#4acZ%4G{+FGP^I+dw~#js0yny3{Xz<^yt_MXKsGRiG zN@HkfB9xf%ns@w(b@_Z=7gu? zKnQ=cSPlr%joRNYP0RP(VV?2Jvm_sr0)BV|Kj(nUjdVy(q6fyMl5($3VLvcDastEY zV}%lYkk)%5F>ew`0m+wq#7FFm;^pZMC0>eb$5kRY>6|t4s>)xEJ4`Ii4a}&ty2!O+ zkJ6vhrSUxvx3t1dtlX2vF9Ff@5hqa&=t(b5!B^GLPpVZIW_}zPTc2a$n!N|F6EIW3 z565CA3??F^PJS^Pq~M2zs(Tr4gZfYlFJ)7Z%nSPYJ1EhOU?T|L#{My+po|;s+%`3z zV^z4ltFVK&%mWORqs$EPcQ678_p$CI#B8y`KaW$KJF}9d*LZ&%wSNtpOhZ4sH=AuQ zZqsJZ8sm8kSo6~MS5d=W+#b7vHeCMjx^{xMw*y|t4(fPQo42w9-pb~cadr7RRdnxq z#~<`ie}-LA53yo*k|LPG?!*tsUbdTrqjt5HpWUI7CE(xftsp zqE!~>^B>{n?kI$GX^wElM<`NTO^A3TQzxS+qLHM)hK$$_w;BE50hfY1A_~CN{i1M_ zyJ)Vj)&f%tjR&`fQiQA7 zO8S?wD}q(6vb!uoQPCZS7$>l@>Olsy zQ4Tw9()<;wdG&d`C$vwBW51I5pqt5KJg9gJsDm4VSECJ=NZH@IWqA$;Wf@8x2fdrX zt9x=Ck2J^!-|)OxqMt}h{cBZy$l(dbc2JuUMV`jsVia*@Rt@@ZP$#y~I`HCW(@Wlq zSgMg*I-W2}_C*XqpM`1x7@g0t~CAEQTat2L<>#7FTd364`9 zgH)aL1f!X-IS*>$#_+45L;R^8ImPoQJ)qvBXLFf8IW#p%ItFu&o>n)B$u-lgoJGgb z1$By{ftV*#Io!X<)L_^GY)@Zd@sj1GLw)(4 zO?|meucLqSKyPliG+W)<-&RHLNz(SKbXUGoP9`wO{_LQj9+30*_Iy`}TJ+J}02JA2SdZ%_NC4&dtQ z>z|)5v~`?j3il5JIKXB`z~JS(`v(h__fp8hO2n(&Fw}>pEdS)DRB_|ARMr%>4&*$6 z$sa2d;xIDoq6nQr%u`Ler8LfLwlBA-(6hwrXd4{t$>l*wZ@znwlFfJbbQQ84{X>0) zthAfWw&lA;=<+6oIyMhv2Xlp?!9G(45aMfTR>WqrLmHCJcJ$`j2D2S)9o;#!)7G9p zqZ1|7TOMz1?GTJk69;lSJ5gq-e#_f8(wEKVhuU>6-&8K&R4x=m=kiVE@=fJ}ZNS(D z-xM8uW4o=(kI6Q-)K_K8Dk@s)T58LhtFjF(&8r$}>Q`mUnpS0^5-pjk?E0#T=7y$h zMMM4is-|Wqv$87FoUJaaMVZQ~#%whTt*WhvDwnNqU2^m>*@njEn!2*u2o6BDY*kAH zsA{aKZCKS(m2Jt8gt~_HRZ%9e9KCpn0JG&AvyC-X6;%!qtE%d&n#yXkO;xKh*$ha@ z0GZPmk!9-^XV=%1WgD6zv@F_K*3?|1>Sn7OEr;Z$s=l(SzG7okzN)o3yL{2o<;SvE zrn#)%l?0Ks)R)!Ptg5f7%w`}iNVJ&kY6`QWt}$CyUzsg$Dyy$pon7kmMlnT3;3JX) zwpt_N>KmHTeq&ipQ(1YfBfGkqj3G+okZEqBfE$|{vPR}glx0mIrnW589O29~*48v< zYa4WjI3i!aq_P_Fu577ju4$+TwyNr?rtIpn%1Q)9ta71|eRXO>O=TAST3uI`tyo=F zQ}5s}tKCqxF$2v)2W0EYnjxm;$3}=L#!N+7b2A!U#x8WKDj8O+WiNo!+Nvxt*YDXE zW$W2~TzAXsYNQd7MR{gJS!2{Dn5k>PFj-e4iWX&%m{lMVv@9v2rLn9Mg_~QNV!TPi zbyans32MMGQ(^U8T84(SR;*qXHBnPfPFw2H8!QRIfEbR-s7R(_HM#>m-vf)ErRm2M z)Y;PkgJ=6gvAnE&BiLY<)wH_9hVYkQ1=QCXwR+G>nDM}^s7rfhX}R7I&9bXP_(6xEqs z%-B&~9n}#m+IBhi!X5a_ogv8Ldir(drwE;68q{o&fkAeN*4-0LO|=aW^d2*l>b{s! z8nHQdz8q#OirsZGFRrdW^J2sz9ckvt`C%IX;z+Oz%lmoWNR} zF-dDHQSVrZSQjsa=TKD_5zzp*r>0t_dYKxc95&467KxY9>iPq?@E!B?vl~&hP!~1bZtk`;>ZmO-S7m`F}TWOxN*-H9{ zwKW)~*@#^FBuaOw3=urDx~95$ag^ReNB81BIrhY@%z0c7k;tg{uIrL)=g{Fb%>wM>&&BZ|R%yYs48>*^WC zdN|+J*@KEI(dj0J-9)~tpo>LssNHG7(Hp?S7HrT?9XSj=vF;#%zqW4bcJu1T* zQ7NXR9JuGA^#8t42I3pJ;f@>@{QZ4-iG%dbhg$T`kDw6A5cu2N-&yEchJ?JZYh55q zsAc((%_l6oC5sVqhKCpoT=jhapadE6UUgL&mJKi`6}5C;%IYes%jy;%yE=Okm*9&% z&PTPF!i>t%~|)r{UHNT>Ff-9#b`!Ivbm6S`9yo;J~jg zt6zoHpGC8@65c_hO=6y`tZ6!CS-FL%cB;X4Bs9o0*K12~!E?;A?D{2pK$h+SS>{oA zQXk=&&BNKDd>aC5q!8v-WNlxU#6?hFwYsbpo`vQN#VmGV!gNdy18FdqU*dJQkf?5FJ%IEtWS%@&*K)>Z+Qe8jVfW6{|PESm$#H z<#8Mo33o$zJ)&Cc>7;B@MS41Ids&>Y645#~N!YGja4~@uD^BHV#;9aD6*Gf1>6`R z5*c7AVaPgqwL`P+hscych!qvuUEJI@+?zYY8M2LQTZ-CR?}{HwTH6=3^kX&GYTQR^ zuccthGM)od*WI(J+iN>xn2w$v-lhc1OL^4o+O$vsO@XG$Caf&7*<9N&Q54rNt87K4 zRje!+vsHmD8i7sCb;L4Uu-Ofnm<}DMZTnIqO#|SZLdDifVCy%PL9ZhNjn?Yn{Yr#3 z-nXj|XvE@MX+btJQXFg*4RwvRRpfz9fIMg~`z1E$YM^Pb^w~^R6AYpyKuK$T;}RBb z${TG$X7;rs7YCBwI9S!PsJ)Yeo%1JJD016JuWDh%Px<6+vZNu z6jKgE7g30slOkJZM6m%YaZ_r&ayQpG-W!cXOd{*4)24!!*aizw?plrMqzXu;tR*rT z6}8pq{neZ^42nkBtm>L*E>tMji$`P1044**q&Z-e#L64$>#Gn<<`8Z>qphcx&YST8 zqPZutI@7$?Hc6gPeL4HA)ASjAY4}8Cup&zIW{_#a6dR=IjaZMxOl;JSjOB_-qd~_- zBc`!g1w;);nrk@Wwss3zh$PpXX|1hnZgQAd-%6j|7LVi>If;5rfPKWCO>u(Dn-J&P zK*?$L8n}UBk=4Qjr^SLj?CFdd?o3TBY$^wG9O3G+jG$9uX>NikcFZD%RAf!X09C}R zXJF2a2ZHrf-%vi+$%#+B9Ze(J-&Yvy@6B%MAMErBxy=KBb+#4SviV#ucCX?4pxcK5 zWT>Ds=XK@!&M-y#5q`)Okji%rWV_q?I-w)61ze$RQy$s59%AjtJ2v#i)s9F)u92 z3b$LL7zl>QOIODh*_Y3IU4v~Mi{vkag-~*1S;g9HQ`v^>5(X1IYP@}_h!2m@y}up&V>Adxc4lW^ZNP+5q3ci_d;VWmJ%->RkwC6r=0OXA3=jTfsAED|F$wz)(9RP+Y9ppp^C^qR|@5#u~~=OYJ=c zDBGTRI@p2|0$NmbV$ToztLr?TBJrBY0{%D>g#*=4UsC}lK!<8k=!n5p`O2F02!xk4 zqX#7Nu-Q}?A!EcAtUy4z7H`yyoTU)1iqQ=kf{Qcy_C^3QMkW~q&`blhoQN;sjMQKY06}m!F2R= zoJIx0@DP;5Df!fjrP&U}rK!=3DxLjX`lM+Wh&Z092*fOwKaG~E7`O*yNEI5HT=Hr# zXnK@zM=0KIx@zP+@FAyc#6m!c6|+Maj2Jky8aPD6a}O$#ibiWP861=-tLMoYp&HvM z7>@A5bHi;oKZR2uI_iorXQ3+}1w17&fRjkD(VKe)+qM9^L?`n-1=TsSH%??jgIXZi zD`&d*Oj8yCPbtkHYQYVfngqk0ow|q$gqjp9n7}H~n1qUtk^)5yBcGZcHH6_aHJS}8 z5`vUtq85u$q|KBZYq}I%6{aCzgx%O<2CbtUS))pDsJn|Y2Y!pCO0Y*Y8QqR*GSZ}Q zBC;&KWm2l|R0`2zh1H=tju(MrlqlEJhVVy{%YnYVOF}vdQ@cBp6x2*&sCCRIs>yUZ ztmhg@p!bDBJ<&P|^jwrv=n@tcfG^G@HlyKpZJ-Hd_yy z3M#u-KWT2FV%#p-0%crc>Y=;)#7)i(=Q@T8Il4FnPU5pgO1EYEn`WbD#>HwmGLhg$kIY+P9+AgdOaq3JvMq>9=#sNuFnqUHH5UnWJ9?{SGopB?3t|!Pxwxy{8ac)FkvyE7?R4vXTP=SqpGz23!pm`bY z%z;ZQZc<~G5Uh_ai^^IYa=;lJ#0C}nIO{2c#_P*%i3A4mEH?ef_az<%KvyqU8>$+C zrf>LeI*0G3cld6)hwsa!4T`28qZh|bBOxKXCl8pH#~Q|PgF#m4CF^P+R+;@QBQ|Q5 zW{++rQkaM~!*wFB^$j?vQI=__j|g636c4Q}akyJzRF80@d$2`LO^3ZD4tq-sdwuAv zEHn&8=g#v2KxG8n$Wp&L%?8dh8LJC>TH-M42nF@&8rz$TH2$>wK(=rJ|!{f6*DSS z_q!@C*+ViD;q*eGr)#U*D-fj=7}jm5*{m?Q72_ouCXoJS8XKA5K(H>%A&OJ-oE;EW zD&TCY3c?IBK(INN&klHbXQssQd@=i`&1I4Cs_q}`$aN}9rja{JtN85XvZn_pfbuYb z(F}t(U4uT0u|&Dkog&m=+00>0Ax*+RIas7kz}SV88`_9g)1|4#QKNyuo-;V+7TY&C zcQH3W4WS`DgcON-g30%s$)N#N=zwPRP;t?Q?k&uNsMHW_Fv6IH_O`r8lk__S9f8Ph+dD#bO{5(hemLob!9x##F4;ib^HefL zsM8y2-wHca=*Ci`SQr_aLocg!6Wru;D1s^-Fk*eDjbydGT(@Y1!cTdqX`jSHzYvGb zIRlDb9S0Q^9kZ_We_fjam55dV3N1wr*K+{nJ2_Ede8B=!U67+y1aF)MB}}0FAZs+t zGQ{+GO}ZJQPPaV#)xm42*%c&PBGOC)ZN3yucQF{sq#j7*trLz^~Z0uo1$h#l3^feJWCilEIs z@a*Y845ETpQB#FgLvO)M9(Ep7;xwGnc31Uv^!Demwyj3Qo*pH1T_5wpB`$gbx?#Nv zaR+8s&miV8gAHBV41;O9Ru*#b`}9!5ydW+f?x66}15X_{NAlt*#%(wr>?z_?lwMV> zZ)n2ZDV|U@?iCEGbw&{}7a?PgR8P#{nE84($--!e21bdBZGosWa^iKWr9fq_eQ49_ z{(dtG)U@QXX1LtIT^EH&VviBdBGYH)Y$&!WgEsR8s5iOHDleWoA{GG$L6yqD5yHj| zR(84)3^M0Atz{b6Imp$4(HF-N$Ts3+RnDwFAf3f#DD&N{BKW?GvBrQ#TCIS(QF z(1kd1rw7+vIqkMPo7#Hvxu{RI+C=Ue+%Srf9*-gu6B88E1a(ZdBN*%ZkbTRtB#bB} zVbn_!hH*(4&Lv@3mxSS65{7vQ0*g;YT^X$}oU;CboSZA}gL35ignarGdrr7Ut>bd= z+8rmJI%RAgSW?jhu`KXjU|EdoqpZ#HA+LY1XOppZvI>G5VcLOhtVLjNsBKd&Gmz`( z>FVj=G-&f{@ zw8Kv_0hSum=WDPOmg=S#+kUrrjv4{x@aO9U3tRH&%u`L{MX405yXG zuVzT#)eH!{9^*loO~@lcVa5VVz|hil(RmbtXo0)x+yqh?bx^$}GtjajkFjm1mN-uF zSfz?AilPtYa7DbM8xAvPG&#!}O-Y*(0mi|? ze(v97PlGn&0re5Ht;4vY8X~p!!eB0!?V}=NhUB>(oXQj(#2%4eKK7z2*vAJ9V06@n z4D@J?#sfu`zJTk`n z2t2`_J43HkF@7<%bn`?i&Qlk&r9vg5jjX1rmsL1NEZN}7aZt29i;ETL(*AzgAE2nP z^nioo5RXNdv@-~c$hp%95ehG+7a=I6`M4BI`)KmDVP?l`4ejX0$1|`V@rEObb@n2A zbDLVwqN6zk^H2~FfkK!}JOIEF;@$=CjGq2pS^VU^_MW~r%rtoj5@H=rbORQjeiNQ4ragphR81F`9eO1?-Sc?4xrHFR^!T06Nl}%b)29?FB@0Wz0V$KF=vJg1N*e4* ze&7U$d$^Cexu3e4!-qh$p5+J751#faWoWVEjgVygjX*z?nNd91n#t!Fn&FYOAsN(K z6d+vmkbmHTIKmS}5UI3vbPQq3L@HzQ!TMHSowD>iibfigqFtji>MNqbI#$htvd*Hc zr46;}5;LFQ7M3u8%!*lQiEJ=CeSizWw3e(o$BRxc#xO( zln19w7<7%Ms8<=E#%A1n;=T12EY#J6fe;5aX1WIZH^Yh#wrzF0j+|k#5K|-49c=6D z8J2#ANztyUUbBhg8B?r1Mvan!KB6cFE02vNM$K`K-l`P__+okyFcuQ5V60v@1#QMT zCYlE|QX4S>)eu%wT&#H+YU04Fkz&@?R0d<CWbq1_RU zv)3u>A}uAc1So2l)K*-L42J_X6#<~7*(zpFhJiK?Oko%Z1uxRs zg3xWAwpR9pW1S;|ydO6YH)AgA>ChB3;HaV^5PB>_daE#IWy2QC=-HvKt}?c55Ri-rs;O{7vuV_3w6z}46xEV^+~WGn2i#3N{vz@TDXRngO# zaydCn6jmYp23?GYYE(v% zvGAMF?~uC65j!ZRpJF{j9VK?MFp8C#*+W~LIiFRFc`!4=$n}(}$>|EXB}K~IR8`T^ zl&QfN0-(^sikdg!xYDnI9+_dqeej+ReFTw%1YXUsD1;b)YVH80FAl3cl`Hbq9Da+{ zj?+JIsI??sFlE9ih#`y2o6Jm{)~y!^HE-MGSdR{zaHYrRB7k+Ho`Ns6f)U3C9x`VW zGOL|C(Z}||{$WWkkyr$J58)7_Rl zwgcmKMMYob7))e;m9dhTXuNb;Wf|5{s^44=aT0BhS$n8YnR(;QL4~x+z;TG9mG7TG z72rR1Yg0wDPOZiGp!+CJn#uHJ0;NtCa3cZ|%t37!t$d8D=XX0a$1n137sfQ-~zdD@^ugpJ*=XD$_B{787Fb$C3)hIoO-p58F6u% zoB2&Z+{)mp9kVVR5vM^(`5NqYoJ4jaOxZIQ=@QsRLJI@@ ztGX~!5!evXGovz0cOWJzt0u@EXq^QG;s;(1^B_(^&@h^J@nxW^a#O`>9|{|p0E@1s zJuI81HDk~7Km}#SBW|hzS~pwF=qKH;&D_yaWz>|+8#BFjYBwC|B1%6ZhS8uy6&uk7 zjqhdZ2`!L>JQq?3oq6@3@4BFWaMQv9K9_+HTEZEaFZsDaRbU}DZ3l)5I?>&qFU&{Z z%!lb}>z%K@b*b}IC{)!ZT6~qVZ)kY_roN$tIM#ql-hu@S)&EsmgO@DWBL@o)R#>v2 z+f&HjF|{B9-_e{uUrtiY=f^PNyUt&-Xvwk#i;kYZc;P;X%x}$;?1e-5!G$=M(mRAn zIbY}mDIlYmbfiJxLfGf7o=ppTmM%ZW;~$jV)ZROv_nh)0)N+b1C zy!U}YUCh`=0drwvA4T+ajD44odlC7beHG#XdfvgIT9)p_dm?_JIkG2$ooKy$_|j z+j_gGv*LU2LtRa+PSH5^soi%_eBTOckmB+NNKvUAhtS?crHaV4mHl#vuORJnbSPYM z&1Jv13?QsGz<{g|^idDtI7r)kJ+9eFTyaghnuRe&-g_`(N~={^z=!3+3AJ z|9%n{a%q^~!=z)-8)xu;FOBfl;hhh15sK(2j=}fH6r?+HfIeK*siyyVEg+tO)jj~5 zJLl`d`JsUUpaYDn$oV*8(*}pm)>#1Ny^b9Aq}VjTSRkMbUZOo|By?z2M2Mv9KQCE; zf|@44BI1xh>;*%NM+TUX8z0i93nZ(^Ew;K5p84QqerM0Xe_ezBJ8J=~0I@OXu)*fh zv_p>sYJ?%g3JUY_A0;+IC=^f?<(aRpOwKogQ$N$7qSX;1Mq7v}Cq57&Ivq@L>l2R^ zvcyMYCz@UWa4$m3f1vjRAbBsj9V29^1vN5e;n!~XJ2~cG=uJzl+nH|KnGUCwroIqd z=P%gpom^DN6vi*$c<=Jph3`7TZwilC6n?>9w>w<49FXNFs>F%aNK~)#n@}a3dLn*v z%J;(QM~yAuhk3@njepbU?)En91%2opj@pMWRHzo7fW!%LypluIlQims-ZOT{qMo!6l^SAshV3EqkN@Mj#}@UGxl`bz}(&8oFZQKl;=z%&zWf>S0Yt=#4fa9DR_8@W|ci zk87e*8t}i0s6|v({0qGU4N(WqF+`oWf1(yc3X7LGQYc*vDG0<$YBI`9p5m18CZmi% zP=-Uv?J!8{YD?7NhN#1T6B?>6w*AN9d?foLJ!K=A@Vqgp#+_-5ckd`9T&yGXX{-nL zP@Ark1fe(8sLiFeS*1sw`Rzqj4kDa94Qx$&m)`4%T<7SBC!j(_07KfFD8X8L`H8GMUk zSb+Tik?29H=^2N_n?7K2V$p7ITC5IQ9yMU; zJ~3hX?r`l!jOmT1W=5(s@zxg}{4D=lI(^w|6_sS*yA;Z<5vR1TC`?F#^yPLwS|YT z^xG6E3Gy3GE(MCx*~tNK@x0yPY%%LgbZW_3NCjm|SNZM#Gtkv(bgO~xVjy;@LsI_> zy@L`LdEr5`6FX*wvky<~m>nLzobSua`M$P^?@i4pV|4&DPM8foX3y1>&m|wr%kgJz zvovav6YXq4J4?{c7PPa%@-={a=LCCF6?zx@5ZuN79X_qt0Sk7Aclh540NNg2k)$;B zztFqH_uIou{2xofNhtVZ7Hn4q+ikJW1b#<&V}PshqP?O{A)dw6Atj4Q)ii!yEv zUu4FMU?^6@G-aRRA-fa)2g2RD%}EQ;<`moJ2W*>BhP3$s+h&v@ZGOPEX)|nF<|UkS zW5PczywT+|YV$PP<^h%%Y4bGOW|Sdqo@U#$8Kx=w3=h9M;rE7j>ozA7^(4z@uWd8R z*dF5N<9hK&$u`Q6HhXQGHp8~XmvHgUz|Vy{(c$Uq4qkBG^aZf9-wFO0OmlixXix%2 z?jM6&LsYpnyf2KZ+((tn#Ze#s_k`aS{vAq@zHY{X>t-&1E&NXK=Lj`~39N=NZ{aGs&)DE_D#e zyTf}!=H3VS5KIlkfc^A{cb^aW+~@y? z9ghFO@;O!B2r2Zw!EyLlfUFLc$HjS$8R(x)@ZAKZ;i4ZmJ)v}E;q|ME?O`8#GGd%v%00ZDs^qh>X861u;2D`$& z564|-303Y7|DcAWRQUrnvn!6e@^(%Mw+rf-3$A+?hvSREWfAHx|*D%Sh;kcs?BKcVOgbc?&Kt2Rh z12G&wliYJ!j4@S|2)!G9&f%~6S0xfV=7m=!wg-tFhlbmONAR2-J`y|~68?1f5}vd1 z_bogR#ou$1On)``^(5OQ*YrFCC`?y+_NRG<9TTKddXWQBb2lC_Vp5azXVScHBWQE(}Un`ZunT@7xH{Q z@h5p+9{dJ8&Ix}LJdfwB@cG~`lKxAu4Lr^Qudm@bSMxeY^Ewf{ejmKf4eu9TCx&k( zF92_I!#lz22sDGYd%)A2@G0;(LjJy3d53wS_d9T08vf4zgHOs|_5Uua7yKNZs(rqgd@If^Jrz2q$LGV+^HV1w|{hhAx7X@0YaF z&_$5)v!HaPrSMXraHT~hKJ~0{O&67=>jP|3It+gHNdLl0y5QlOQ&{kn&qLJ?4L|Q+ zD=K$+@&+*sHze=Cb5?i<4FvLkn7mKm_mMQff6u|QjYIO;)Dn{7vEa5^pB_ z?r;Z27g9F_Uk}V$=Ig=hAwaK(7bgwq;^gf~f0HA`(0ew43Fz6x3kkSSJA%J*I{K@w z!F7(*FbkZhV(7h;fK&An8Yk|_D7l04PDgl+rhuTY2M>e*J%H)JlzJK%L9U>6y)iJ+ z2^24dc)Y|i9jXkyvGBl+eh;6^FNEL6*zWT8V|B(=E`jBkoXhX^v3S{mw*3z%@Y-+q zA{Icp1%jgakI`R#J#jtf^G{M1?hGD&_>u&~`ex!nx}FyXJ209H!1?_U$@5uSwJ9Yq zEr)G8Ln&5*_lpW}nb(7)z4e)*A@nMMtOB%c3cull=(g})|7oV4_W#1vU;N*)AO95m z5*-!(GWk4O4WCb5lSb-}^qZsbxP0^-qhp<@*;9Ob4d}rJpzH?9x43M5%YTs?@*<`& z;QCYICU(G0$=itew&ay*r0z(+G767ZMx8wx6{GwB4~Tv_aV|W%DGLAx@TTNFNdWIj{xTT> zf0_Jbs%QxXq(5Q9ZQ(h=pV;jWq6QxSNZwt7$Ja<{TllF_myW{Y5(>Ue#O!)w;p4>6 z9-bRKO8@VVMARPsoQT@Pb5cJk!Q*x!Y6qgbM&a>UBBFx*G7G8^xn*} zq3E4ILvJM=v6ZBKE@;06&pF{O{%v^90uR3sKkXO7!(8w{FJ+GQQa}?1E@{0mxI`R{ zOM>h0oEu)J9giQ2;{o3^cuC+diT^e){Acin)H^3U7qXZa?g%fIr;^EB$mH{o$(-=+ z0lGEE9?hUtd|I6H(3ZYj?XR2~FiCW!C zmb$w6UZ5OiLk^$PTJ)TFYtQ+=k@Ro;=l#h4+xFJ`6vFU%|20)FagNC4yaY$iY{=^d zEvHX||2bM7hW|O>|1QX3cK9oCYiC0a%A3EZzj;9^^$&(=eE9u~@%i_kBJ_*qYRee_CiPR_k{ zfmuIpGiDo{&k$M7S}+?=FmrSA?tPa$)RcTM`BRvIS>aEU506{}_<;WqNJ-;<7)EtO zDon|FrsOlpe**XH@Sn-;vb_0r>ihCMKfM!!W_I|aQ4c{WW`_@rdR&y_@lj7RJ)-2p zyTtYz;bN^U>)2W0{W5}QX`#)6=qR+=aJ}!A#-ETmV0L(^*tJ=qX@vfkREk_@0f3Sav&5Q%zjR3^Ek4O!9r+bHSV=$a^sI$#g8mksADpUJqhaTzdm(7fo;Vf5* z3AuDB9Sg+my*=|6Ab-KJCck(i5$J&90F*gkk}eZYnuFw={ZyHRGLt5oDwF3TId{2f z<={Q2Jdl&_yHCJh?<;#`PDJI2pH(r>6_ktm7fvigMp;9eysh{W+@^-J>7ktMf7Z7! zQ`id+tA}*!8?b^n^)hUBg_ofi)(<}YgJQ2_@pvI^{v)_xd4=17|n(}2C6gHui772+n z26!<|GoI1_H@^7~Qy>bphyUS!j1Kdc*?7C;P*>DH82ug8wH+v$x+?U%ZP{=eWZkI`4z0W^Y4Y;IgG7W`a=w;D1wx}xMx zv4`U(Q+X#(FDZCmlHopmI?eI)d1HoT;9`QpG=10-H|c%f!DTz|Af9AeeF{|S%#Ty) zKJK4SQ-DbFL5Jf+!|DI!6Ah#?eD@7h*>Cwj!Gi$&3)i2?WsHxUc9o+PN#_o2g;xB# ze?2wndK5C6bf5nqK@S3z(U%8N#mMCUcs^`rzFdK!W_ht-^znNge((=_ zaOKHFliR|-=b{ZfDF-in0EGCh;U^Mkq1790(5^h$&!9N|oC}W|4=IrG#zu_Ww{T?> zG_k1#TAu62+qv*Y@!-ZG9*_Ia(5OD=@1!-ohUTgX=IZ7IfH$*M9xx2gPkcq*Tw|?z zsig%&=6WtRVL6{AwT-HJBdR|@oDU=(BF=~SXvX8QL{Rw1ZiL5yoCl_W{&D~3toL(P zZ#E&S=loavvFVjtwH3#X(<}b9wEWkiW-LP?xeP>%GNpHeqh}IP((@9a2NbNcZ;jr& z5w~>pCiEs41(GyC?O(jlhP?@7o zN?m;#BSc}}w-e^|VIo&xGNk(M=#^$bfJF1UFOsOOqlkI=%F)6eTqJKd8FZ6h^>-zp zl3z)Dn-Ahcr;$5*^dyubCx?$RZ=K3R$7s6v!hdRPptTcQFa)4z>%x|kphlcI;xZy0{$uUj8j&zR!O(0A_9GboMuC zu_Px$S{5vJ2a`L9%I!!YcC4fPtBvKvOnIMqmrT`0SlDWu&kOvUtb=+7qQ(aPcmH4Z z{gL2F>y(nlu=X-Pb#*nvxI&V;UJsFkPbMA-AQ`Kj4LA?A5v6~Sz~iyRLxKPP z@W}vd>vNony4c^asSiEfw-r6G^<3)lxjv9I0?8|U{LB9$HQ|eFZ(VqO;#LlzTX~A9 z1(*@)ZVZ1L{3lcY$#xnoEwM(B2%Ea0x!tF+9eAkC@KDjt`&B#5Xy;05_LYe*sCHgp z>aU4U1bBQR_!ReKJ{5c)j3|CY-JeUGtvECMvrWWf!AzsDK?M-X8x@X5;P_&IVf)44 zej>gfB1ei6;uuM2;xSSv1lE299mMCAiL1%@)f7WBHff#=099l5dic<>K>~LgXgODb z>z@O`C^NElLx8UaQBsPa(2n5qWbpIBmjnM)!S0``s!L8Ia)2@G-BdJ5==%17Z6z~l zOgE?U!_vbH?64z^VLh_az$zuQPoE>R$-p*kw-$K2|7q)Rd>RH$BjBRN{x1yQO%atI z!M{f~@o(mAk=l0X5Rh7so^+s^86H!Ms(VsKyi&9(Dv_d^L*JoOj5F z`=6i$C+NoM?}m*pJq&M$9f7waEAdv@gtyElylujoyGaN`;?w?(bg6FTSZftA(X)!- zrJO+|>XaS<4LpK35ss|JTXhrOGC90$=h76Tl2|edOU4^x3~_pz^;^Ot*Wyp+ zEd1Gy!;pCV7-3XghS(b2vk=Ek^%HUEf25(AIGTy0Im`^>&+u7bf|9|zo^XEZKSq35 z@V-5LUcs9M@5+CY_WenccakEHhrqFY_A>zRW{0nG1>VJI6V8&ti8~9zYEcLp6@q3U zc$Lk)>c8QO^Y#We$&B3z-zB;@{6V1jAS#OoM+t8CFDvrkE+aiBD|#?kMV-UtO_865 zI6 zTea1|wtmpSK6s%Wo)`Mp*>(wbC(QOEmOHx;t_JflUr0O>isNX(_I{HbcbG{Q15 zL4n(GiHrV?*Pp1W5%In+qBs)TedK%$;AK^KtE$4=Dw$8W;tf3nAUg-c^*a9%rXKNe z9pTcqPZ_l|GOY>$6NfizAhc*rbFgdF$3_yrb~#&LU25N4Z7>Z~Hj-y$%Pz;juQMv8(mEG6*FH zVMMg=XK;=_ZWi2oLKg;lbjRcyPCO8Z58TO4-|2szo&I@lTQ^}#_^))){>ozyO`@UU z#NX1?zU%{{@7u(1F%eFd>0||vRsbn{4D8n8aSNw~jj-kqvl|}vA0_Zn|F`rg|C)G< zUeH?!_+uO4O94G^A&mSlJn#sO8=ei7bsd!T2ULO|unFS+6lL`(?8`yXKA*Ubsq1)< zg`VHPnEDr6YE~_YdG+8ZB0A6*?o3>Pa%kC19`O5)Sb>bXe`7Zb`QV928-WxOI$0!C ztskg0*L~!@Is6f7X;tw+>~mC=e6&V-j$+pfmA)(dQ6_6Q%3c^mNi7|? ze)p24d$C)GPWw>;$7XfNL2??A1B9n|x%NmkUgK_ZJF%L|^-7)_m&u!y&U`kmT?ihF3I0)pjao3MaU$k;p zI6#N#`f*Pdw$y42Zw{tA*6El!RH_B z7CispFHA>)FHC=NI?KN}{WT_Eo4##EFfeg>b*Gi&6=N`rZyWO~KK?Rx`#3zdk9%ZX;+WmxFUH+H0m&as z_~aBkE}gPtDjqwgo;Qt|=S@3r8j|NtyKLI%tzP9k%r<%xP{`7Q!Z=P|_43>Iq z#_#0$%8WPpbb2)PZA*gOnY|jFSQ%bF=H@XF=*?rcLEe?&1yi=o#M5EIU~sDM9Fx`r zpr6M);iUdB_Ld1z-aQkxO^H$$Ou2Aclsa$v&!$JIf1h#dj41Wv8NYB+FV1)^N@4O& zw`e|QrLKO3!@UUh{0OioM}R%B2aJX?aPTX`na=6O}jYjJFF*kCCx{(C6f}ra;LkT5Kijr1ENt2?aNm0^N z)V9;!Q=`qy%HeL_*&*hgonhYD5$2tpVBXmQ`rXcTBd8)~Iktia8^D9O@gQ0}h!GDW zi}{7AOKKFd&?~^8ecb;toxZ2XTt60%-;aHjk2@#*WfI20NdHpPR+;KJIf-a{>c$iv z-%j1n$J1k87=y<-W3S*e`>S!+j>qGs@qeCx$F7ODO~m8DiBIxz#{u6s0KT#NFLl}) zgL)!}nD|x3Nwy`wmc-*Lsq@%zSB$!L6dtdSI%hPzh?1zbL=#lf>5r)x+~HIx-zgB) z_r@^wJ&NoUC9+c>vU?^FO1)zY;&F=b6eYq_ln75zB0R$Z|zjo z+No&eVQKAQw3a%H%OKt2sj9_ORg0&p7EhHe54SD8kJR4#(caI5yPr|*KCCc{R=i)a z1{Q_2_p659uNr#4YUusmXh(d-4XU)$?0?ElrT@fOnlR|Cte86;H|iHH5M~}Nc{M#@ zxClrkrDQn1`1}Qix|KQ7cTW82#62+mbmHd@V4=?)@XZ7E$o%F3KbpkMKTSGk@*bJz zOg@iy^~3WfU%O}KO|T(*5O@zp${s+PnPMPj`}DJV8j*R!O^*&DlNpCwFCL?LkNSVK z_+;ZIZPP{g9C!?$g{!r1`X3hGQpRATbP+yV&Yf{UpM@t>{baRLfH1L4@Y!?`J_jD7 z&%$;6owZt_g7iW`XaPkC9e50(fsYXcDKBjRDHlN;9w9(@&XyMhX$Jtc!I+B*B;L0h zHYT2iNS9iXED;WD+9kj(S0rLvKB4NDX?!J#a=~ZQMfen*; z2Oh&`;R#j$WXtc-g3qRl@Hy}pJ_}E%`rQ^E{BFp}ri<`7@EAS|r{9kHLzdrTwvSa?F!|Cq(MT=3a+5k3bVqtC(Gn=ZoV zz+?Cjj|&6d`orF@zQ_2w(s#z(o*;M+jgTY2*85{Xda^UD&fJVgS68A zYYY#xejwGRT?*pxhyt)+%j^0#SbUX&&!&s;Iq(=h3s0!}cUyc_g3qRl@Hy}pJ_}E% z`VUxq)q>Bai|{${7(NS6sQOP?d=N0?WYb0X9C!?$g(p<~7cIUT!DrJ&_#Ai)pM@t> z{l8m$s|BA;7vXc@F?<%DQ1w%KIZ={WEBI`>2%iIw;j?gEf3n55M)28m5k3bV!)M_M zRln5YYY==kU4+kp$M9KrLe*bm@hLN9(?$3kcnqI~Csh6Q7GJHN?ZhSc!U75x8(%^<~j?Yj7Pg^414r}%@5M> zav-~)UdcEWq~Yed0GEU~JR$+zY!JY)2LjM)05Qit4v!Fk2U}hcfCmepV-FspG)zBu zh^0Z<_=p8~<}RR!1zXVdB*B7#g9RV}sRmHYf&-7SVBvxQELebMAxguS1Pie=C_Y%T z01pJyhYKiT!4@oH!N8$O%0V!IVoh@3F%~Rb5P$^>(3%vb;c$Qjn-0>43CZ)KZorxo zJXk;x4-Pyc0o`EW=msD_HyA*1H#qPJ0W`_N1p#=l0No8y8cqp#h^0aCqNp1%>$-p< z7Hq+yZZL3k0}y}(11RnW2OeX=!UX|XumIf+Q5vfNuwc_c`UoL;Rn!fK#{oq=*n&ko z7&y8C2*85@6nBFIkMUsPf&e^NfbNDU4I2a=VrfXdNwW|JX*g#tpoj%qu!sc%A0-ID zf&qjsf;c?Jf-Nryz=8$nZiv!Y%z_1*4$_AT$tOqM0EZkrSb$4H93GK?(O}A>8&o{Q z0E)Z8fky~18Z2B8fCmfE-4LbWB7uik8kBWKqX9mJ3n*g27A)!p1IK6p0*KoHibsP3 zkFj9kf&eU7fbNDUeS)xH(?NQ^kUSXifC&&hSU?dE4m=_O-C*G00SLf@0TlD#z#|0c z1`8Ji;K2ek4^di;2Ah`Q1Cq~*c!00&0*ZLB1&jK@z`+9$fCmF8=D~r-c(8Cm03Iwr z^AM%qEB#>8LHa#H@~0vmplsm50*ZKW;1LPv2LlHWKmZ;LpqK{-9w9(KShyen4;G+# zh|(Bu7!Ecq0Y{Mh8O;M+C473<1r+gM3l{Z*fn!Jm0eCQg;vwz8V?0>6AOH^*poesn zMsx=}#L}SnnurCpy=nnPEI9Cp0(66cg9RV}3kFckf&-5bpc^b)5P$^>&@4o0IQ?KD zmIezqL@YqpTtE>EwqQ{&7&uq}0;j5dumy`)FmSK{1Yp4cidk^rF%~Rb5P$^>&@4o0EKk8gEDegkt62zwbg8gl0Yxl0 z@Q4B^l7XiM0a!4Az(o*;M+l%uw!9z!3l^YRh^4_oEDaVO&@8~qfH&v@Tnggwhyt)+ z5Wvd-0+4C|#a@O3j}U+b3l{`n!2+~Niqde1!GcW(>A6DkqY)2kiEIHyJUH-(1n^+s z-~kB0g8>xt;J_mU;K9NL0eG+g%|n!i!2l07Z8YZ>5f5rvZ2?6*IPi!B@L=HJ0SLf@ z0TlD#z#|0U!NLUrc(4G?Lo5v*Y&uBKl79G|<^fZ~QE@;K54K>@h&FJHXdnO&22ebr z9e9if3l{|7!2+)~0a!4AVip{DgaF-O z;er4xSb%0BN-HlTmIe#|(kx&VjUb{6C}P1DEb0XVhm!#WV8H;2oeT#aW5L1&0a&mA ztw~W@P0%(iGdkkVM>Xh}SCC#WaXSled2kE5JRp#7%7X_WfLIM6#zPz)@qm7`oEqX5P{e}+k4OLy296012tca=6i;vtJVF2-EL;$P2Mf@{ zAxfiP&`+^6q+1)YFh{z<0*Y90;1LC2!N9=+5P$^(C}zQdM+m@zg$n|(U;&ziD2-za zU?G+U3(XM=vxNl&E(pMa1!xwcG-8KfA(jRU zr$j8w6BaC>hy@28Q2-VU94r6(vqhnnA*C|8bIz3{v}$;+HuUsdf>yK2|2UAu-O1i*p~ zSS&bUcyPgjCkz(GTP#4Y{=2YX13ng9xKjeKpmFqP5C97r@b_mI?hpVAHZBCff(>vp z27JU$Sh>aQu=vryLxaP^uZ0I2@bTcnog#n-je`de01q1Q^WefA0^q^Mg#dW40gDGG ztQ-M$SUd;dVWGtXEY@EN4>sWA!G$|T01q06r33=tK?8nE$%Q)vz=Mqo0q|f077tEX z`Niz8cI7Q|+M%cLU;{oLT)0yN@St(D0||c0yk}XdLYT0^mUd{&sNT9v*C52!ID0uw=;z!vhLgvcsV$JeH89Qw|Tm z@&Y~{?1VlZG!7m>06b{G&w~s1@L=OY06f@$)ecTrt^C+wy;${`#RF_5H7&IP9}h0v zDFS3k;~3&V06b{GKg79khX7>B#)SZQumNj`bHZvS?Fpk9t~uS|326o!@Uh^+of3cr zjiWn&09ep~zdN{ahX7cxaUlQ}Y`|&;C#*(kI~VhIAjR~z=H<-vgE=Y0^q^Mg#dW40gDGGtonl;7Bd$-yy@&&#vtehd_33* zeeIxecr-u&JZQl0(Qx4&9&B6)fCn3})RGg1zX9#w38Td892T%u(GB=muoL=N&^TBC z0kEI}KMOA0!-9m0uwdgt z04&&m#ex%tX#^HLVU+l!#X>?*HG&=Xap1z80-zBzp3MBvqo%Xs(z=91}&ESO9qK+Nb>re-sW>EV*ZNSHa3wMfuX3#jA0R&K14fvbEg*yb$ z3^pzVz=I7~&ESNw!3KKV6Gp9{a#;A5G=mNJSa9J^3BZEJ!2$??1r7LFaN!OCuwdgt z04&&m#ex%tsDcGg7%W_HSWqh@HsE8yg*znx3mOLtAOIFL;Ag>wI|RUjjSB&=U;`El zP8f?iV8Igx3lBQWgKCCg13ng9xKjeKpmDGO0$@P{eimG~LjWw;xDWsfHej*fgw>?R z69x+jmfH?)igFPo8}PBB1sfLvV8I3~7M!qhpL)Vz zA)fR6m3Yz=I7~JUC%kF=z%mEZgkC!!U=3D&fHf zd_1^trwHIdAs6lx0aHVbrw9R*RRd8s0WaxQpOU=x!7gK@*4x0aTL)!fpay+~EPd+3AG<>fHt`!E(Z| zEy1H5mMwjzeN9v3N7mHe3{^o`gR?L7Lr?fp>MeIT5bq84bqdxS zoUnR>6IO39)l70Xr+$KKv%OFLnah zXM&x;Z(=86=Yic8>^y!GJ0H6VEad~%^G*CFb~8pPEfH*S>SDP;MM>)q1lyN-NU%d9 zwGRn)DfNY zP?Nw1r%1#s{Fcy4h(pVZJKRKE>4iX|3m`W!cbL8pUl;>mH*0+;sL$rxfSVh4LN_-u zqU#)H07mZLG?+hr-w|i9a(+dF%6gD7@;|{~2h7rk$M>v2p1Qt6X zUIpV0y9?mOodRGu*ZIdAB!Dq- zD$B-&07h3EutrxWtj z1qiC9po#!NwE;^|oiLpC5L8b%5RNX;JaBc^9d@(e#hn6#up(#WA0YV?qm7!LI2i&i zz(df3dkAU*F9?CKC!td%UfdyIy3#8@i3?zrMBQPyIU>F=SY57J;3Y8bu!jRzI=5(| zN_76;Ge1OlwM7rz_}{#Mo1i(5Oq7f5P+!LfVC#? zgjq$dc-hg9I@o8|3H&PVu)73a+$jO-MCbok9-D_$9M$y1k>dtD1YHT;bpnE#z`qCq zh=T?KZUSE1DG|iPPOp4XF2E8KI{}LfG4X`K>RHVKm&4p)cLBV(Qvh(F^Z%o@9Cyp2 zhdI+@A|^KACI~Tc;SNEFiN-Mj1ObSH20U{nFYXY4nAqus0K~)wEHQDymz=NG$2!ID0uz2uXqEkYCqD{f;U9)B>EEt9AU)(Iw^^fZU@{9~9?9;fPhqBEI2pNP!( zH!*9Uc!FVkOKrxJ67JiV;l2&1;6Dh^U^K`T-wpVvU1Y`(!4~2xYfQEY_kBlZj23JU zzTQCW62HtdW3pfy@GU1|8-nlRvj(#Tdl$cny^DLiBQqKWJB;7N4hPQwTP4^Tv;r~J zsH!YG`4L=Ts&O|_ExGzEED0WbIUanUfm`Dv(OheXS3oXla)sSJ%$?3FEO!2E$sdarYkKPPE*Ib- z=)s*H2ir~)fSUtbCoo0OfJYO1afbk=4t9EVd6x?iSB*OiO^d06D+8=1%yyPKJmBP% z8}RX9C-hZ{##3d@3}w}Tp9dH2;laj*0H!}S0BPa$#|dMV4m^0mDDeu11vs?bfR6<` zp^pWP!>bPhU_k?Z7F@W81sfLvV8I3~7Mw8G#NF`-V=D8H)(kFWT?=Q2Jsh~w`8d$& zJ4yan^#65cgmBEFN^HPG(1SZ1K{9pv4&sO zp>~nEF?aiTac68ufbNfID)_J!m191r9TU}v@m0tDkXJM&LQ}d@XT)6B=V$y-?(dG4 zT186X9fz=!?68|fFYd62V(RoAm>)LD_bhstimO#i8*meJCv@`-n?$FFa!{+58t_cN zVUyT+8ws5v!X~j3vS;DsiW7#Zi8}LypGKXb6!sWn27ZIji2UY7d0I%hpbd8!EgKN1pWS#34-Qt|5K1h9tm{AK<<90 z{c@Nae}ym0{?!H(cP(!)KQs9alni|2`ZIHM1RrMD7ug#{%--ncWF50P`Cu}>m*M=8 z#N*~)o#tm@P8+Mjp|MK`n5)8X@)qiSk%P$$JE`%D8UB~~S4dEU1eX)>xSTjG#usGa z)ze~I_)UllNqA39tBk)e|7va!7n9Eoz8MLPT{Zv|&d21(`Pj$2@tXM<=8Jy#ccHN} zoXzWy9fqsE6fIo(B3*w&ZU*gM@q&%OLPel+u6`7PIVMTeqH!t2Y zmH8e=zB9ql*hK@(Gbman-Z9s_KeGsg989aMY@j`Q)Y)K~{kXe>fE67s)`{Hc;9&1XqL1s{_mp z(ankSL(v1WG5Hnb#*cAA=2b5lb2UE~{usVLeli9*KN&k4L+EU5UJ^p{k`_U-ATf)Q zz=luOuLQ3%L{vib#y}%<04o_#pC;kNeATe@B?gsKIXLK z*OD3gTJlB)HztouVO~}OzM{DCFXJnmX(rg3W;WSE(PTEK18Pnmm?26AjXf}9XL~?9 z+wW{>;Fq>F9EwA`5#s(j)S5#a7f_4svEW9$pR3YutbIHtRW$`@Bd;Vd*((WK6Byo_&?4cMga%my`Y`%ZB9gzIxS5Z46GzAJ z7#({}7Bk+B)$_Xrvy;y8QJY+w%ujk1!%Yd^?0|3K)R>bB))p|;+JeLQ zUXgjK;8cO8bE@Dbp_>I)3z`3FVPg?fo+`Rr1o(2%4*B}w{*E^~7L1)fU_vlAeYmRC>M?1g?}`Db;1ZQrbeOrL?XSY1MU_*NL>}bz0VmuUyw@yWod9T@w6Kr*UOS zKCWzS8I$cSyWN@3+nvXkGx_-P@#UKG`0}acNIJEAYdMo_Ek7$sTFS@X$E0KLn|UAc znfI-b@QVA6-sk!3h24kjALbtx)Bb2OCxr2s5dLF03WXYpKHRy%{A2jD1c0AGsL@D} zX?{YY0n&@mYq1;Y2o1~_)DDk9?S{2;@(pWO*A7YQ+Pz+YxYyCR2(2xEnsD1Lb`iWk zs1Ux%U-+9Q{3gV78uRJJ{7%Z^;L%XBRKo+EDNqflpTYb9$KY^7*c;Ihu8kmqdchm9 zJMv)^5g$fJ$Gj2j{)l)j)|BjxV0TBvR`hmn#G&NbDTtVz@^Okcf|>vk1EB%D5mW|@ zfG+SxY{?j$iHO0_3f_oKnO`yDtIS1N-UuoTM4ZaH;)}SFH8&d(R3zRU+p?$RAYw|+ zj2v$Sl?x)MTf7k;?GQorho_h;%5GW4&I0b9hP@M#PSX&d=V!)j4MFIIDFB| zn`2S|M85|_|E4eEX2HlpM2svPQ|QexrtmFByj8f}7qPwYFe45ZUiC#>Eu2+^h*?FA zMP5>kMSB>r2P)DVak=PqeyI2Lj>|eqH81NpxESHV#p8>;3C9MVt6g1SSP8qP~$YI!3g z8q3#|OTsnfEAB&h#eL`R^CZM{IHLP;+t2?K8l&wuePh)8)*oPN{Q+91tI{!DMaN)P zDvG5mEDh-3uA+m(&j)^hJq9faP%K?xML->Qg*sjwvm%<3Hzfnv1T|tIhmzN%09li= zBPC(%m;n)H-;r{f%0yI0SFsw9VoS!fOgyGREm&DMWsc1PGB&Fr%R(Bm7GNOD>Qt=*k1WB=Yb0!=qWCrbJipwyyyRpFD4m4;WYMmJ$poaObZRFHnc8VnCm@?(_gKjG zPK{+i8lf&MWNq2#&Ok;(9axAc1T+cyy@jlSxq~!k%P*EgJT8`BDgU#?<4XCBa&UE{ zyrul>xS~OYH_@K5-c6GVUtf)*LR!yKS|e_q5I5FZ{c)$2-)-V?skF-O0z}zVC}o2W zsl%YbnusWy3MiZN3Hav8c^G`T&j;G*XO{M5`EIr<9M7vdpc8ix} zQ|1AD>&-lnc{bB2$yv;q?pcy~Sv#_jYX@{;Ji?+JE0l7qfO5Q)$z?O>_M;#n5u0DTXJ_^VgXS^iT%bX{j=ulgL zq_t4Y@sds|Si~fYpsC{}xmhr#&}q~$h2sm6VLZ&Xco_~CUSN_7(CqP&TrIrCB)2f# zjhBRao=i~D$4j%UV{=E4X~y(8UXmLfUn)kDmoPZQOESK=o=NJ9r^HP%rFcJ+>@Pmb zapfomllX$xTs*i0X$NC;xtE|VFFDR+$1$?RD_C9W_);Vpk4byHB#TSmXOj0zkCj6A zAH&!bFC_=8cyUuZt?L9ToVmwKvc1#5G9(#THna@i=Nwu#t}I^4ab=5`auEi80?WX9e{X6z1lD0v>-u;x6BQVuACRUetDJD|+cYv6p{lHq_` zGB#x*`=(4xyK7|H?ZnAo(nqG;4k&haADLA} z8Yo-%`ud2k523Y~8aqjD7Hls>oXmA=WUhjTf*>tzVNjyWnqJIg+2 z=<~9s&ImPi-rpIa{he5W6&R@kG6yTa*dADN!oN!+s9`uBgcJ3#0^ z2;y7bA3*4nMd%%86-2$ah`Kjoccd|jh(_^Av76`|qbc3JMY_E?M8bPpJ}GyOL&!WK@-Vq0MC5y0jwE-) z2RV@G-j;vJ9U*hK-XhT6m&FmsN zCyDd>o~g}0EHJPZw7{4c#G2y7;H+Q*mW0I5Fe?bBMmNkeAdB7c58R|ZF~K;KfY6zQ z8wueT#G7#gs@KlblCUF+=pE6+Q5!oPg}b9CCLEB^?y@9;he%&{<-Nd)D+Q>#c_nLn zHXh@%XJ*^EXJ*4m*-e?>-C=$=*V=OM!v19(T?p1k`! zFcxayg+K$e_CoA|0sSpcVpd7qomeEYJ50yB9g*s8$I-=Bs?jh5 z?NsB7*Aydq4XimUy1DpDaoaU_1ula&YHno7*phEtb7Nr<{uwp5yyQ{|igyVHB5Y1^ zJD?s**OmcXTehLhD)R=Yb(^J~W$0MlVKu>%&|NH}HVWOsPS!mTX?uR{M%~@&n_5TT zI9*#E5=#G=0sHcp@re=btEbHK+1D!3*S-kj@dYfMc5!5`LG{E+^u!_jHvN!k=^8jU9 zTh|Wc*0p=PT|5GBx7&LU0(+^ADwQ^>RNAQ0q~>9r$-r2tR1eAw)f4X})J5@2d9z?f zAzm`0a2BV)vkEbIRzg*M+!2qDJ6`03_F~7+JH{jaIc&oC#BX$j%BvKW7momSV0;4O zi$5-AL5g8=c7@5gvjm}?usq{u*+WHD2}Sh?lQON9mr@cqP~a(=|T1meSju zK;Slv*Z7IXQPEW@MOP_`4y6^vRtW`U1Tdyu%fr{r#mURBoCmR!C5WvU-Z>sZ&Yo-) z7sh5s_n)(!4yV=-5e&K!oKRt6bF+bM7S+yV4D+sPb3gSG@>J_7k93)8bQx#LQ;pfp z()N*O1JS!!*xiFY3!q`mZDbl#t*0^WiA%LJaXH0RZJDuDduA-vddAYo$w@V4CG{nE z$aJI{M&6ZDgt$+Dokb=h)#6bADie`vj9Qq1RI8~6YKL=1tDJFO91enNCm(Lrw{YblZ*! zaYZh)RyOo=D-BtciFa>vSr~1Nr9m{ZFrI)`?qMEFypaeeuHFl?Ch15LcQ71DIwm`v zjwRJ5Bf2(uTCy2y!IH2@g<2dpu_TWp*U9h#_f}ru9;WPt=>ciEoCe}k@qKQ->s{`N z!Lu=AWd`6i7)S89o3TW;!Ytv|7`#)2sy7z4AXGh+Q1yUNHFI^l8T(@J7Hj2NX4SpL zVT-~o;B>mT`9?pA6R0A#*C7h@F~!fS^bp zR#(*oL!_^_$t^M}F?Y$}eQU8h2DG_JMpezt0;~}CmRU1ja+M0Rx3~pC^G3%h#R%1x z)R!nK+~ERX1va=SKm-$fLogK}`&K}xt`p|UUDS3J1V4nnQ3U^meY=`HjvpSv*kbkL^26K4sxZG4hSQCnYak&$7lPydkJ0SR&drn2ixDPA=q6g*fY3ytSbx)bc609|DnXYYAYC{FBe@c zYRv!_J&|c7F~+JzWNZZJSV(h;I2m{l{r-R?A>9ZZ7 z?&1<^rPW2KDhkn zUMrNmV#$SKTj??yAggK_0;<&|tJXXeTYKl4A*}Gh%hsGOKR!;!Zr#VM-MWb2wq8a+ zge@6%EIy5R1o)c-%!Sw{3)^dZcvm>|A_nJOboX$}AZB`SvnGFp;{+J7ho_h?20B8k zV2KyNNg8!EYN&-999vOU^o8up{DoVbgd6E`-U!REjkH&=>~rrDwUjTq z5BV2iYeVb&Y;LLcfQgos3}WP^Cu7D&;+>g7*M+UKQ-R=1fJm z84LLU#KNnBg$JKtmCP4ZH0Rvv*Iw9ih~joy;&7m(T3DHb!(%o8ll=5_l7%XQ2d0Ub%Yk))5S+(`N;*L7Uw zOcItT&LCcnt4Jh{t2nD}KGCw6w7He?=k{k+^#8(uG<{(-1Q}`wz7s<5gU}lhJl=>5 zmig`Agkf?7WLUy{3C~Zskbvk536tbR$fW47LA|E7@s$iKI4@3 zvy`cFzGG_Ys}$Z>)260{`v?9n*iKW^W)fMFwk92qUFje4F*9Q;m(M=W0@J+%ro|WA zO1GEA5gulLn~y#yKTh*uMI1%PL&^=Vq`ZApdEp+C@*+{<5-xw=Nctn!wx_1Pmx{-g zv^nW`49}d+73)KppYm}m7v<>F#=D(TIKRvvHv}Wt>qSpYLvSV*@AcV(>*laT9WyNP zdZH@^MbR-!V)K)nm>W3SreoG7Z^lBsrnWhGrya91`7zL0{-b>Ya-b=lb=ERUY z9Wy6=c%~CWhIP#B%tNji)`5=sH1k-llZVw}#c1|D(+Ul`pXQH$n)EEjKy{*}!Tjl? zsUCx-!F==?h|M$o!_VHm3#Hfpgn=N%)1U6TIUyrZwP-2+xlIq15^lQxXtG z0rp4DPgupM!;oo<4&~aP>PFJ2WGkAo?2i`d>1GZ|pO|i?I+S@PQ~k7+vAZ68zg<1k z{8oSTeRfMMdw_2QM4T(Z*o6o}7b4)LQg}f~WE-Ob%8bw?Tn31^^U>>k+~HBJN(lN$ zPI2UvOC{v}H7-ufOWw!FzT~6Hm}-2MGBFj|C*t@O)qt>rm6UXJhDvmX1};P#%KQiq z&5fgLe609`=8ygr_Wi$B%XYH=!Rb+M zq59_~Gj<-jEMr;649`hlqb7*xe8l-);_I?L;o&y@wmX>=a^HF>I2dec8JLZe%9aoGn+8w%%8jkDv zJa=_Had=D<8jfxLPK>ljcOJfi+SAEdOFH?yWGLwF<{wk2<8%U%(C+4YsYB8oa7g;N zbe(IQs5hNl^f?ySMYCz}Q0D1Oo&0p>N6>6mjMnr#U1@7Ha|SM*5wln8F5Wzo>N%9h zTWEIk)|TB^(hNT%8tl5cIP6cgC;Tcz*?pW(iA!Qq#s+R?#o<1xC4Um?C_l!E@L{@( zwvrX`=D1MvC9b?aE)s0?fQZba;dvdB3y|WAkf_JUMTjYuECM3g#tgEZ5S*8Mp2xh- zCtsq6`%>~z9>{)y&T!z%*T z7+a0#@3CquI%F#t%3?Fxdl;+;c2cAk7Q2|&%@)qQTEY|24DQr)G=c3Z#dejKZ5$xC z*#@IkiqS4$bZaU?Tgh(^;TIucw@R_wMX}pOv0DXpVcsYQa!AXX#TZKXj;u5+i0+C) zT~KYg)y0*xAv_tYwM9LKZJx(ub+VF4nNP7;dfAo5AKJaAK=T|bB+^ftDer{z?{BQUSkkQs1gSKDjK! zKdB!K*E>SPO^DjC#F<=jnVEQy1KmU@7Uij55`%45X)cM4mO0HTSWd|F0dy`N@1?$< z%1mijIKg670$fAOvy1s@#z0v>5&7(9zLvEPEu|W)o4GY>MK(g0vs?J6&si*!i^Vy& zK)L<*kDeW zZY8(y{74H2y`HMptEAQ~((d|(--qg;M#*rMIX+`@1|;wU*1jdmAS|L>CADwSSV{d; zCG~D0E!i(~zWZ{{D7#XT^Kj0E927|^6+x-ZE~*;kb_wy3$;?$m*o7xk6+8ddjVoL@0;ivS@L6gLx~Z1;Iyjuq9=1D$)+-tpME|B4`-% za`-Aw`CJVTkHBv~KXqYh;xjgxg{jLCw8<<-5NVgwzI#+!+Z2sZV;)Gpg=+{zH!7&o zJeqtR7BODN(H||nJP6Tca<!hhf*V*8CG=& zqis)f9{Y4J)$G^@L;AhY?915t%TmrTbcS8eQdG2I)@Op4^r>FZOwCzRT2(vF+PSVR z6lEVd*vB&TK^e|5bS^kBgo3dQWJ_LKEw5!z-utL}Zx1D0)#|<2yuiNvDBk-Cb}i_a z$P;h+id(isOOX|nx>DtadxY7rO^4a6E@)*&wyRti8Xjt*j+1Oy387ZTNhoH+>Oj`^ zEL{Jj`YHaEYcIsYWM>yGk0(_)!yiCR^SL-@r6_}~mQt7n%?#Y~<0y^a-TZ_~9fmQr zcX!JRrAVPB7|9x8ES~l@5hU0}_uD5~OSz;zD|ZDCcdsFh9#$eG*_)1DCiJTm{i?tZ zGurk6y~df{Ymt=^?eJL{X-EOokg_gCL+erwX1jbqucyGedL8lN1CmJifbzD;u2ea)MPaE7TKxa=sx3lMQ zzu)BXW`p`2Lg$ZJ8TpdK#FvpV3E_SN2mV)sIVNE*kBw6n{fXJyh@w3Dqon1#O3VAB z)+X&wvS{x|oreE&5MM*l^rsZjfT@%^B+be&ByCKZm4P*A{bZG?1m3xf%e`aOmkTk! z)ejxL-&P)cU`-$(vfNwAa&K_Fh`yagQ9N+QioEuQU|;2fEF(_1>@8d(G>f(ycGXb4 zdn@to4ZfPGP@3(Y2WiKF3Q9ZNuatK9T`BFoy=A{w16y(Ck9whN_v#mYR?oxw9p{}R z$Ac%S*@dZYim4uoDOf@Y^-*N1kG_u9gwWd)wg&Tca@YeLe$Iz**iCWRLvh#*94<{l z+}fn`d`JiGp*ZXY4nImsd^!MY;BG$btB=4i3L#C1$!+S(v{QxO9^iK#=bg%}<8|+t zhmB@&>ogmKIe1HxbT{`y9yuS|op4%Q;RmS+u{Rr4O_K#_YEHl%3PNjB z@;a9XMJnV`lX^m8MG_u0sU0LNRrIJyogm?vG#Vep9Oz&+!%8*VX0+ocfzho;HHFg=P*qx}(=5PfR>*>Z}1C|J@nY{-KpZqA1Kh-<%dHUQNPy}-m{ORT<>~7^6 zVH+jDqw;J3{*+0HD-!*gR$$!n6Tg^pHN~IqD)g!^UF$w#_h@q%IeQ$OL2!C1!RZO+ zcGGdfG658^>8V=man(^DhltkGVN#zuT^uITAdf3ieOz_T$6LQPzAWj^xd?75i0CF` zzY+a|?x6coMmXZ13FKQ2lEcO)v{*&ndZDxDMrq20s@=b~(FC9~9>xM}6uA_QK1MaawPf z>ACiVqAqn6v5zMX2=ooyCqB^6LokVIq~%=rL(HoI^{Rl>}H?3e2ufKFr!WoID5SG~6B2Q_rP(sGUo_oQecee{k}o z9b^3+OIygzT~c!(vMK$p)ZpFpQCyTCl`)3e?rl}AD;-sRj|xX6pQo4UeCQ%qgr%-~sJg~ZXVi6=t|wfU!XDQ{b%!2iJ!@4B z$d-G96=@}PS0T1NL-r_UlhRR#J-l@Y_-^`mx{##ua8DH~&!WO=vwk{N^fLWOFK3Oi zs|XYhXIMB(@2BO_)hEK{>j`ggz448NjZ~cC|G>!T_J2HU6B7SN zH~2rG8GFgV5PG(+XDtZ&%v=|II~c7PAFUi89iMK#9o!Ve4XajY6HG9M`bgZ#;Duna z3aRwwh2Wr2k`>P^<{|#glRT=D z9}FI0@*}}v=(0NbK^lgY=1C^+sgn0Z*Ec_c2LYZyDB%b0rzQ??pQ?E|csZDavsxMj za3%PF1xV_r1Erk?>OVsuSNOXnidMiJ6Z&H)`b@x0d_rhK$o)!lLTFOx+g2H9MY<_J z9cWE9jL|2wCX~`Y@KD(w0woshG}g)l)MfA?Yy82D+Nl~0MXgbZKLRNlW*27N;TcHJ z%F4^j&&N((keMKZn>>-k2`>#X%hC{@_&!g1W@c_K-|8gCcsc=vi&}+K7eY(JsMg`& zK@t-c>~X^>_)1uMyBpzu43wnKxY)#edn|p_!VX| zzxuW19HI0)_Uqu-e+%5a-<#0IZD36T@~lbNpP*eT`xBZn05xUov+s)Cm$9%NpoQ(0 zw9~1Uw7XjFa=fg~fR(luS;X$`6u_tyq#=IkO z2{3}ElSYV*G9q~fmuVIzpWvf5r8b2}n&kG_lqPzQno_oi+h|J)u50dQ9!RMZOSvv} zb1L7pIdyy*;5liNxS}&DeXXph-%M}HVC=q(b(w(IWgZus<#^`EEW)!nqM|H|v$ykD z_4e$Olw9*<_8kWAWH-p-PD9S39Hw5B(?mz0S)aEk57CSAw&XGRe%={*p2@qO$7I*@ z-fu^IB2UR6yr=zf+3$b6{b;#6b~H~mcf+xm1NoZ81NqH_n)A;~ipCBbWvkl84kvlv zS{?4}A;CojO$7`Vo+~8XbA`i;5FA%Du1M1vSG17O!lI!anW3rU=8gz%#(ilFzFa(0 z?u?yTyh6eoi_aA!`dsl`*{nCW z@4QK(Hg(?4;P%evBzUg#EV&VOR{7F$zIkc+iE>1rD8I^}?jYW$(mP)49wBq6v6pV~ z+aY*8d4_KZFUj5$!zJ6*xMce@)!gaWXADtR@t`E)aUi9M=5!MT5J@IM(hypgd5z0m z*D{CLYn>D_K-aSt^K>6&439h6H*)Z}k#jdkuZiB}S=$<%wLO}Ln4^#?gwEvc%IsP zJ?$dqfX6gD;Z^Zi2d7Th3uhTBywY0lBeZ`2cI<@#B zS~^YSo!b-3W|v7}W|uYk?x$@mJH!-+ppv~WI|ZeT2Jk)oU z?NDfCq@YsvW<61Uo)PDvV>}UmZy1)OQR88B(GQTyHAM}txq9+CkXq}$WF@t#h z%OL2O@H2A!PLvC#eF@V!Q=A@cjM}%4Hb&newk`TfA|9{6HnO8+l<6s>j75hdjU6r8 zq^BBha2%224MG@i>=dFTdScXpO6Vyn0Ta~OS$i4Ui*d(JC@P?*8hv_V^jV&R6wA@Q zt@obCKm>$ike#PCcV{lJow)~dtyBkdk2ujsa);)@6SFAq5D!Zp!gyt+I+J&W$Q9T_ z7IHoBPCFoX+Kp^)Qyyh3-E}7I$l6qSFp2y#0m};SXmDi9C1<|DHu_R64wy+E8R68;bT5*$)k2As34761fX&%Pz>!jtw1wHFR9!U`sk; z;O!}9n3eP8;z`9oCP7zN$jsup5+HRv(5WPVkBjt6LE?ynxW|vXB$y z9~1c)eOe)|Nu1ZS=jurJ?#kXtO4RG-Qt=Dx>$6z}Ekr_$LEr4x@FwNQLI zM|kF~hj|Oy>0Y-)F3N+M-9yamc<&KChJ9_&WATz)&s)!n5!S;zccfwrRay^GY4P&y zX>VQHKRQ2N5>a|RlwpsvUbwU0dBbwp_Z`}lS4%Ld4>@0|D|nBWE4){*pL%gW^kTf% zQBmHbQVVX8&Mg>R_o~#o!l}I6e=0Oxizo0C0~4UQ<0Y9{JgNjqMnRLuOCqWr z%ivJ$@rxt69-mf#u8&7&Qt3v1*I*-*e|$PSN-uUoqKg;};?d#oaIa=BDgI5 zma);mK8EkBmB5WgEpPm*4Ppk0rN7WyCTG@|aU#?ZUKQ54_J@z#zmJDch8+qg!)NF< zn-;+iFYG7g{1b|B?*kxr|PnY($c zT(mCEq-XD=Z)zWvZw-|1A|4z+llK`tQlCLnA+$t02$r-5xTO7hUURy>{bq(X!_GoT z+yylrcR>x@MoZ~ET8a+mS*pB81-Eq)=$n}M_aF)1`74I?1wc;vc`ifW>v=mgfhVjd zp#HrP4ZIDv3YWp{$K2kXL)!Vrx^qzBAwo@--4Wu6tNOYdPn{mqmQCIE;eobMixMp1c?K3U z<{58skJDShEkXNQ$t~1**i#qU&aWbSHiJD1WqqIh4{O=LorU_Cf+I8uV>p)uV=&A+EtmTIJlCH4-<69?I^Xm)(dN4 zLIOFGdm-0*|0hnu$<3)3U_N@U(tMgH4;JO2Cv+D_JsvIjQ##-=rNi_N7TxJMWNsH? zK?nF`aXm3R39dcr@D}g+c?))ymH0@(<$|`~ei?RF8*i^GtSe1w=Ye zcUUf?dBN#uPAgUX%wkL;QL!bnd5b8=`8LQl(gTEJf1M$q-IZgwyL)oyh#r5rr(){2 zM4g@u~zf9uLz3e@O_?%9I65$GnFos}U z>ra8L5P^+<DIVB{`jRjayBwB@Ib-zNyfZvYOixSED<` z%O`yV8$QuT;w71s{syJK(sow1$nyZY}4&(m)Uwp6LJ0gVcds zw#49!ulI)c^TsL}k*m!aP+9<|q++107A1nXN$E4WLb)4DlL!q#e@1ATTAZ|3C2pWAFhiJ1DTa#dgA%#r1rpF5%Eok7}Ff9V|LB$r4^y*UPL+ zpJeTpm?^tIXJ%m61fpkVzL|;W<(b$#fsX`dLSk?!6(xp?iL0~mxRVWu>7^tFDWN>^ z%JW>w?u83Qo2;8e>+|ZZn?yy5s+1Jr8?lfgzKC-XK9@T)5ahR}9jm!t0t=y#D^+?f z9pWw4Gf|AEqBF!6bYzSN&?n1VjCz3PR#-aH%MO zdnD8^O1}EV%@FDr6%dlk9HLTn3iYXS2yH9Bxh=?$eF(i_NLLV1XIHQ{g90%Oh;cTf z&>`ecs~cOqUERUPAnUlNs;7ArvW|xq^@HYFu8y5GZ=3ng6mG{`4E<5^g}!$%&48Ku3j07$>@6A-!((-9D<50A#YpuCBpH#skoTL2 z7Q{LgOeuh))38sdCnSFlAA8d3EXl9se&^Z@ZwEM=ahsPhzQPW`ZMf(F-IdD3PHc1l zky)HgM&$CG9XaV_8ruhP->9&TFD>Nk%Sjc4`~Zb5x-aKIPO^n5L=qX0D7HSU#*eMW94 z<@}u4H{-_g*6eaY3)I*}?nOWvwIGM_7#Qo%eNa9m{Ug z8r@O;9v84=L~*VjKxeUTMAa9(aGc;s;s3L`lK;yj?n$=OnN%v>nXFsGg`(YwoQ{R= z^a}S|PYxa8C2${vn)zsE=fWq9t#Ra_d5SAmr_jAp+|7zx*wCZW+z{{oyluV|PIGr4 zf`@S3pxEAWb*c2E3e}VF0b%welmUZ^kN)b|Mox1!vX5Dvt3q`ytbFh)p$Zvme7U6C zRjO_WLy_GMXRp}p@Gal8+H{a|#vSxNj8t~-Q@v1nAC}cJXY#s`nVCy?ig+daVx{Vf z6{;`dnt<%LvH>nReb5vCuP4oYyJEF%IT8H3yh&~^8LeLBs|@m%vYK(4{Wp_6Im3{!TN%%F*cd#u9O!e(Dy zoOv|iVuJd0k=}A#&jiPK94a(j<3x$AT4^3{f3iKO;Tq19oJrxc8t`x{@-oQd3j$X8 z$+s%GQUX#ByNyqcI4}6rxM&Cm7io-Figl+5)8iE<(>lz!<3t_LJeg^glsh_bZx=0b z2zI+&?G)Wzw6$SmP}wUJNa12q$GLgrW^jH8;@3!h(#w1+?JZt?@)k}3skrGcOU3+IY`fen zxjpH062lyeX}WGsy~8c;=IZRX?X9*9b5AYP$!1!FN9V83C(hXfaqev8lY3VAoQt_4 zpSmAt8D9z|k9@G{RgbQ*wXDnmP;7DeGwTS;PTrilL=K~H*DrCspI3^2FumKvg)}yu zs*58d$?9Tkkh@FM<8JIvyffuw_H}Ba_w(M@BX;-?f$zgtIy!$QMQ<+n!X*gKzz|8o zg`6xDsNgRD*gm?8WHU78;i(6B?nt&HQ$vBKQdGG8`TIC<@W6MmY*nf%3)|#(V!r6! zCJ(ZKXVG7u9iTU}4>%Tb?PY%!-rr{j1jkmG#IcQ?;_mq~GkTHpUQcoN?|{(;1EY;U z84RvAwoCZ1F*QhhWAI~nj#MWSa5CW-=Ra*xedu2j`pOB>2J>H@LZv)4AZhH+oK>Cy zf8zaje==4Z9(c7ejD2hvu6gv}bAwAcT=IQxTo^I$X9c*~cJpUG^c3p+sR1c?yX`Lo zqjLjLn5ig%FJ^A=1Y=I14ty~m2T`|j^vL~Y-*5Bhzedac8r9IlRSozS zSp~jD#%gfEt8tbSxE$hi<7@}uESx9(++Y*&rr<^|zA<=<_^sebRGW+b$dG7@`DjKD z&iaLrscvLGf6T_vLBsl z3rO=Xp3>dGrO&6{u`+R@OL@$?`!nN@tUU+*wDO^@2PS21z_XQpCi~yLikU{ zjG#_EBiIt%NF^F`~t;A)r@I|l=Rf7aWA#0*wTSos}L!!?=&tdNQ=fjWuKV`6do*!o<%YA8`$YcTr zK9mLDIY9G3*K6>I@gYm{AuqPW7nm&B8pHQ{GV}fZ%mKqSV*#sm!W)}`AIA7z*&uz6 z4ec?@NP0Tp0JBh*JXd{rQ==996xbZ`GKiriRm^JGZG2=T^$p;9FY6H928gv+`Z=6+ zL37tUiGQA(NwHXJoW;ZH0Z)?7b7#qpAZ&Q^p(ph1&zDJTymmn zpM1_E(a$YmeJ|k^2X2+Q6simOYS41yQUEhxc8~(^Vu5!VCoDPc>&UU^y>c+)9JebLA9f*;m+@}M5#||8z`lTa4fo3q{Z9_BA2k+`L(J*Hj z-Ec$-QfFQ6WoG5*`#5ucVzmynVcAboVt@Y#4yjwmWCIB)DL~6m*0PCG)TmfufLNTe zZ{t%g{rXx(X)wS2lhS7c9iN3@bp(M2zWoWG3@GR{po~ zmT&(gt2G54Jwnlpu7Rbkc%+<`B6sNvipdwSfxH`5+iMlmWGeas>li-@9w2v+1rK)x zJmys(X)NXnWH(7e5)4FU*_|j__D5EQ4>4l9}HVj6OMv-EowG z*FF9R0*h^7k* z!h}+iE>aTJBx-a~hx^T+_hg#qp5wUk+;g$%dg3$Ce6G<*mrJT-9N_?iqdhjS3oY6s zLah>6V+P7UN;*C3Ej9@Crj)gm#1*D$w+4H#`k7?;3^l8-5|>xv>I9r7XHj>7Eq#~x z&sqs4Ytjrnftr8fcUm}qH-xP;#28^{WQ4I)+d@cgK1RB~8^C`VgQwLbskka?@qi|-dhHQ*d!KRRN}qwLN@ zX4vrf6DveZD*-5tZ%g<5jc-T+HB3lx(=HynP$<^v5#wV9KgL&EeW^b&ZZqaK>ctl` zD2O6dND)+XP+T#P{OmDOBxS@N0YwI_g6aaAlTj(eR~h?^j6s3E0ZYMK@;4|rO(ba= z_>1<_XZ%+oyLb4ZBz*@R={vtws`Q#s({kGzzD$MWw7F5V9 zjDr7YqxDpUnE`H+P5&v#{S?=M(U9m?jX?ElmRa$dv6z*%7~6T(ut#Y(le>}S>Wxy7 zR@DZXQU$tI-IBotTL|h14ajVaBp=@9AxkMp>urxI>UHk+NN!a{tty3u zF^jsPDp@|%c=tB^M>JiZd_lDAPa4b@?%GB{@~>Dz0C>1x{9qqFf$Z}utqsi=X4`j6 z&knsELKn66Sic>*t7BB%wDGU`U?u9X@>g!HXTH$Difu5Oyh^eO`UChU#$k_=G!JX@ zpx$F1nDwE#A*7#+LV3u)9Xd?>FpB5KU8PfI#0#UTR!0Zda(G@Fyou^DZz2@cM4@detV*oD=$9t@`Rw`R6?|r>E;O>8JU!@v;G2Yh6FC!`5>k(l;3ev+ws4 z8rUyTJpYID?}v+jMsR*GVqG`Wh+5LW*WyOnwn}gqIDCWiADn+bmMGC^s1XLDkIcUx zPSo?k&utPdxK&1z8x|T73eO4Lj|p^rXm;q{WugQYwOQ)#-)EJZN^TmM^U5~5OOtU| zdgfhYvNl=F$!z_eC~ZCag84RXgy!=yMO%FfCXmoZ&l+JsRz?_;l?n4;1ID=3PVcIf z*AViT!QBexgTK%t)GvN%_qJcYsKFO&4I7nd<@-BB-o zl!F5;k59e$r`i>A!;9ZAM$zgR6@1mJ5ME`YVev0`-HTrjTDPsQj^FWLM8i!s;P8D6 z4&Uo?Ia_csd;a1OTF`D?t@&%X%kezNffP5(DJu47l6@hVOJSA)mc#gEa8jH}*A z4U3wg6(NZKbGAZ&R9f9wD%bq^FWc%utKp1b7eWW5lYW!IE`)&9g*F*G4c%=HqjvT0 zqiEo^$ejm=RvM+|wF;S!%#`z2g9pU+I)K7!17E78Cm3mvuF!yb(OqGM%fL5mM4A%h880U=8a|Z5OF~-rq-yFKaCHX6%J0X|Na3?g{Oo{#1 zBme1Gee=v2ycumPF8sygL%cZ=H}zXK#;4(zag@~Y!fvl@JVneHU3bH48gIFn6<&4T$4_@YTqe9>fs5qcg1w}BpBY~2mr4&xd>+hr@FVzr7Ami`;~4Yv)L zpW(V?hz4&#gsg0(;)Gdi19OA>say63KS9l68it#}$lEyck0yj`ikzn`nA? z7A0pEAGo~FAOXL57VmhrKcuKXlwemlQc?J> zQuuH`5umL`3z0hOZu*HV9k#uhbNG-#VmZ{j#Ly-4nkw=X3cD1!tCZCuwU&qPx@w84 z3!>h^_a_$rU$ITbaw=@r;U!wbIjpek=EMEbdtc%QIJ)4D`Zc@^agA01H$&fzKHN8u z`4^EuR`ikYgeJ8g{!R%pzq!%s>T(n2IjiM-bX6n}9 z8O9F7j@SV%?U*B=r(?Lb{@*B^bSjfecB*@sfBDzuGnerdy1)K0c7gu*XDZNdkdYjn z>zdvVGR6Y=uV)<9KLhm^ir(etptQ5(ngD^QT6odRFP2}@{MKLUqMLvDceuQo%svET z*If(!Y*twUtdsPx93XHz8AFLYqg^6gM{Bfe1OB&0`0Vj$G7|8qq<`RPm?jhvv zEum-rq9L&Uo)S$@Y|q|WSbJl^`!OtdFW0haE;okI${G@UDX0^@gk>f`bKp&CTcRr{ z%f0*Z-@bE2qL&++MWQ!jw~k+;FTycqNwkvBZ~cQ7-QLgZU(4Z3)4!R)7HF%w%<>8- zq~6cspXKa>_#_VfZN6_^wEug%oqNCRG|1?jJfb|lw(hk?Z8@&TEiw}q`YXnB--jF)V&~H ziEc}t|30SVIr?n~w3q8q4sWAS1gCv6qPaD+}0aYY`L9cETfPv3vSgyYHkfK

3HXhTSg@`lh6LPtU;Z0JO2 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 literal 0 HcmV?d00001 diff --git a/user/apps/test-backlog/Makefile b/user/apps/test-backlog/Makefile index e7c1704af..f65b0d520 100644 --- a/user/apps/test-backlog/Makefile +++ b/user/apps/test-backlog/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" RUSTFLAGS+="" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test-blockcache/Makefile b/user/apps/test-blockcache/Makefile index a4c3cfe71..86a122015 100644 --- a/user/apps/test-blockcache/Makefile +++ b/user/apps/test-blockcache/Makefile @@ -1,6 +1,6 @@ # The toolchain we use. # You can get it by running DragonOS' `tools/bootstrap.sh` -TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" RUSTFLAGS+="-C target-feature=+crt-static -C link-arg=-no-pie" # 如果是在dadk中编译,那么安装到dadk的安装目录中 diff --git a/user/apps/test-for-robustfutex/Makefile b/user/apps/test-for-robustfutex/Makefile index 1b0274d20..d9eb6cd1e 100644 --- a/user/apps/test-for-robustfutex/Makefile +++ b/user/apps/test-for-robustfutex/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" RUSTFLAGS+="" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test-mount/Makefile b/user/apps/test-mount/Makefile index 1b0274d20..d9eb6cd1e 100644 --- a/user/apps/test-mount/Makefile +++ b/user/apps/test-mount/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" RUSTFLAGS+="" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test_alarm/Makefile b/user/apps/test_alarm/Makefile index 1b0274d20..d9eb6cd1e 100644 --- a/user/apps/test_alarm/Makefile +++ b/user/apps/test_alarm/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" RUSTFLAGS+="" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test_cred/.gitignore b/user/apps/test_cred/.gitignore new file mode 100644 index 000000000..91773eb0b --- /dev/null +++ b/user/apps/test_cred/.gitignore @@ -0,0 +1 @@ +test_cred \ No newline at end of file diff --git a/user/apps/test_cred/Makefile b/user/apps/test_cred/Makefile new file mode 100644 index 000000000..18ce21000 --- /dev/null +++ b/user/apps/test_cred/Makefile @@ -0,0 +1,20 @@ +ifeq ($(ARCH), x86_64) + CROSS_COMPILE=x86_64-linux-musl- +else ifeq ($(ARCH), riscv64) + CROSS_COMPILE=riscv64-linux-musl- +endif + +CC=$(CROSS_COMPILE)gcc + +.PHONY: all +all: main.c + $(CC) -static -o test_cred main.c + +.PHONY: install clean +install: all + mv test_cred $(DADK_CURRENT_BUILD_DIR)/test_cred + +clean: + rm test_cred *.o + +fmt: diff --git a/user/apps/test_cred/main.c b/user/apps/test_cred/main.c new file mode 100644 index 000000000..8e4784d20 --- /dev/null +++ b/user/apps/test_cred/main.c @@ -0,0 +1,42 @@ +#include +#include +#include +#include + +int main() +{ + printf("Current uid: %d, euid: %d, gid: %d, egid: %d\n\n", getuid(), geteuid(), getgid(), getegid()); + + // 测试uid + printf("Set uid 1000\n"); + setuid(1000); + int uid = getuid(); + assert(uid == 1000); + printf("Current uid:%d\n\n", uid); + + // 测试gid + printf("Set gid 1000\n"); + setgid(1000); + int gid = getgid(); + assert(gid == 1000); + printf("Current gid:%d\n\n", gid); + + // 测试euid + printf("Setg euid 1000\n"); + seteuid(1000); + int euid = geteuid(); + assert(euid == 1000); + printf("Current euid:%d\n\n", euid); + + // 测试egid + printf("Set egid 1000\n"); + setegid(1000); + int egid = getegid(); + assert(egid == 1000); + printf("Current egid:%d\n\n", egid); + + // 测试uid在非root用户下无法修改 + printf("Try to setuid for non_root.\n"); + assert(setuid(0) < 0); // 非root用户无法修改uid + printf("Current uid: %d, euid: %d, gid: %d, egid: %d\n", getuid(), geteuid(), getgid(), getegid()); +} \ No newline at end of file diff --git a/user/apps/test_eventfd/.gitignore b/user/apps/test_eventfd/.gitignore new file mode 100644 index 000000000..fdf3c0f82 --- /dev/null +++ b/user/apps/test_eventfd/.gitignore @@ -0,0 +1 @@ +test_eventfd \ No newline at end of file diff --git a/user/apps/test_eventfd/Makefile b/user/apps/test_eventfd/Makefile new file mode 100644 index 000000000..fc61e3624 --- /dev/null +++ b/user/apps/test_eventfd/Makefile @@ -0,0 +1,20 @@ +ifeq ($(ARCH), x86_64) + CROSS_COMPILE=x86_64-linux-musl- +else ifeq ($(ARCH), riscv64) + CROSS_COMPILE=riscv64-linux-musl- +endif + +CC=$(CROSS_COMPILE)gcc + +.PHONY: all +all: main.c + $(CC) -static -o test_eventfd main.c + +.PHONY: install clean +install: all + mv test_eventfd $(DADK_CURRENT_BUILD_DIR)/test_eventfd + +clean: + rm test_eventfd *.o + +fmt: diff --git a/user/apps/test_eventfd/main.c b/user/apps/test_eventfd/main.c new file mode 100644 index 000000000..cea0dc0a0 --- /dev/null +++ b/user/apps/test_eventfd/main.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + int efd; + uint64_t u; + ssize_t s; + + if (argc < 2) { + fprintf(stderr, "Usage: %s ...\n", argv[0]); + exit(EXIT_FAILURE); + } + + efd = eventfd(0, 0); + if (efd == -1) + err(EXIT_FAILURE, "eventfd"); + + switch (fork()) { + case 0: + for (size_t j = 1; j < argc; j++) { + printf("Child writing %s to efd\n", argv[j]); + u = strtoull(argv[j], NULL, 0); + /* strtoull() allows various bases */ + s = write(efd, &u, sizeof(uint64_t)); + if (s != sizeof(uint64_t)) + err(EXIT_FAILURE, "write"); + } + printf("Child completed write loop\n"); + + exit(EXIT_SUCCESS); + + default: + sleep(2); + + printf("Parent about to read\n"); + s = read(efd, &u, sizeof(uint64_t)); + if (s != sizeof(uint64_t)) + err(EXIT_FAILURE, "read"); + printf("Parent read %"PRIu64" (%#"PRIx64") from efd\n", u, u); + exit(EXIT_SUCCESS); + + case -1: + err(EXIT_FAILURE, "fork"); + } +} \ No newline at end of file diff --git a/user/apps/test_lo/.gitignore b/user/apps/test_lo/.gitignore new file mode 100644 index 000000000..1ac354611 --- /dev/null +++ b/user/apps/test_lo/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +/install/ \ No newline at end of file diff --git a/user/apps/test_lo/Cargo.toml b/user/apps/test_lo/Cargo.toml new file mode 100644 index 000000000..cb9613c17 --- /dev/null +++ b/user/apps/test_lo/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "test_lo" +version = "0.1.0" +edition = "2021" +description = "测试lo网卡功能" +authors = [ "smallc <2628035541@qq.com>" ] + diff --git a/user/apps/test_lo/Makefile b/user/apps/test_lo/Makefile new file mode 100644 index 000000000..7522ea16c --- /dev/null +++ b/user/apps/test_lo/Makefile @@ -0,0 +1,56 @@ +TOOLCHAIN= +RUSTFLAGS= + +ifdef DADK_CURRENT_BUILD_DIR +# 如果是在dadk中编译,那么安装到dadk的安装目录中 + INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) +else +# 如果是在本地编译,那么安装到当前目录下的install目录中 + INSTALL_DIR = ./install +endif + +ifeq ($(ARCH), x86_64) + export RUST_TARGET=x86_64-unknown-linux-musl +else ifeq ($(ARCH), riscv64) + export RUST_TARGET=riscv64gc-unknown-linux-gnu +else +# 默认为x86_86,用于本地编译 + export RUST_TARGET=x86_64-unknown-linux-musl +endif + +run: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) + +build: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) + +clean: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) + +test: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) + +doc: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET) + +fmt: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt + +fmt-check: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check + +run-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release + +build-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release + +clean-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release + +test-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release + +.PHONY: install +install: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test_lo/README.md b/user/apps/test_lo/README.md new file mode 100644 index 000000000..cd96a78e1 --- /dev/null +++ b/user/apps/test_lo/README.md @@ -0,0 +1,7 @@ +# test-lo + +lo网卡功能测试程序 + +## 测试过程: + +通过创建一个UDP套接字,然后发送一条消息到本地回环地址127.0.0.1(lo网卡),再接收并验证这条消息,以此来测试lo网卡的功能。期望发送的消息和接收到的消息是完全一样的。通过日志输出查看测试是否成功。 \ No newline at end of file diff --git a/user/apps/test_lo/src/main.rs b/user/apps/test_lo/src/main.rs new file mode 100644 index 000000000..a92493735 --- /dev/null +++ b/user/apps/test_lo/src/main.rs @@ -0,0 +1,25 @@ +use std::net::UdpSocket; +use std::str; + +fn main() -> std::io::Result<()> { + let socket = UdpSocket::bind("127.0.0.1:34254")?; + socket.connect("127.0.0.1:34254")?; + + let msg = "Hello, loopback!"; + socket.send(msg.as_bytes())?; + + let mut buf = [0; 1024]; + let (amt, _src) = socket.recv_from(&mut buf)?; + + let received_msg = str::from_utf8(&buf[..amt]).expect("Could not read buffer as UTF-8"); + + println!("Sent: {}", msg); + println!("Received: {}", received_msg); + + assert_eq!( + msg, received_msg, + "The sent and received messages do not match!" + ); + + Ok(()) +} diff --git a/user/apps/test_socket/Makefile b/user/apps/test_socket/Makefile index 1b0274d20..d9eb6cd1e 100644 --- a/user/apps/test_socket/Makefile +++ b/user/apps/test_socket/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" RUSTFLAGS+="" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test_statx/Makefile b/user/apps/test_statx/Makefile index 0239a0625..127c6ccb3 100644 --- a/user/apps/test_statx/Makefile +++ b/user/apps/test_statx/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" # RUSTFLAGS+="-C target-feature=+crt-static -C link-arg=-no-pie" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test_tokio/.gitignore b/user/apps/test_tokio/.gitignore new file mode 100644 index 000000000..1ac354611 --- /dev/null +++ b/user/apps/test_tokio/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +/install/ \ No newline at end of file diff --git a/user/apps/test_tokio/Cargo.toml b/user/apps/test_tokio/Cargo.toml new file mode 100644 index 000000000..c559f8d2c --- /dev/null +++ b/user/apps/test_tokio/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "test_tokio" +version = "0.1.0" +edition = "2021" + +[dependencies] +tokio = { version = "1.25", features = [ + "macros", + "rt", + "rt-multi-thread", + "net", + "signal", +] } \ No newline at end of file diff --git a/user/apps/test_tokio/Makefile b/user/apps/test_tokio/Makefile new file mode 100644 index 000000000..d9eb6cd1e --- /dev/null +++ b/user/apps/test_tokio/Makefile @@ -0,0 +1,56 @@ +TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" +RUSTFLAGS+="" + +ifdef DADK_CURRENT_BUILD_DIR +# 如果是在dadk中编译,那么安装到dadk的安装目录中 + INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) +else +# 如果是在本地编译,那么安装到当前目录下的install目录中 + INSTALL_DIR = ./install +endif + +ifeq ($(ARCH), x86_64) + export RUST_TARGET=x86_64-unknown-linux-musl +else ifeq ($(ARCH), riscv64) + export RUST_TARGET=riscv64gc-unknown-linux-gnu +else +# 默认为x86_86,用于本地编译 + export RUST_TARGET=x86_64-unknown-linux-musl +endif + +run: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) + +build: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) + +clean: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) + +test: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) + +doc: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET) + +fmt: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt + +fmt-check: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check + +run-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release + +build-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release + +clean-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release + +test-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release + +.PHONY: install +install: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test_tokio/src/main.rs b/user/apps/test_tokio/src/main.rs new file mode 100644 index 000000000..91b06613b --- /dev/null +++ b/user/apps/test_tokio/src/main.rs @@ -0,0 +1,17 @@ +use tokio::signal; + +async fn say_world() { + println!("world"); +} + +#[tokio::main(flavor = "current_thread")] +async fn main() { + // Calling `say_world()` does not execute the body of `say_world()`. + let op = say_world(); + + // This println! comes first + println!("hello"); + + // Calling `.await` on `op` starts executing `say_world`. + op.await; +} diff --git a/user/apps/test_utimensat/.gitignore b/user/apps/test_utimensat/.gitignore new file mode 100644 index 000000000..f49103416 --- /dev/null +++ b/user/apps/test_utimensat/.gitignore @@ -0,0 +1 @@ +test_utimensat \ No newline at end of file diff --git a/user/apps/test_utimensat/Makefile b/user/apps/test_utimensat/Makefile new file mode 100644 index 000000000..985606c7b --- /dev/null +++ b/user/apps/test_utimensat/Makefile @@ -0,0 +1,20 @@ +ifeq ($(ARCH), x86_64) + CROSS_COMPILE=x86_64-linux-musl- +else ifeq ($(ARCH), riscv64) + CROSS_COMPILE=riscv64-linux-musl- +endif + +CC=$(CROSS_COMPILE)gcc + +.PHONY: all +all: main.c + $(CC) -static -o test_utimensat main.c + +.PHONY: install clean +install: all + mv test_utimensat $(DADK_CURRENT_BUILD_DIR)/test_utimensat + +clean: + rm test_utimensat *.o + +fmt: diff --git a/user/apps/test_utimensat/main.c b/user/apps/test_utimensat/main.c new file mode 100644 index 000000000..441fdef63 --- /dev/null +++ b/user/apps/test_utimensat/main.c @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include +#include + + +int main(){ + int res = utimensat(AT_FDCWD, "/bin/about.elf", NULL, 0); + printf("utimensat res = %d\n", res); +} \ No newline at end of file diff --git a/user/apps/user-manage/.gitignore b/user/apps/user-manage/.gitignore new file mode 100644 index 000000000..afe59cbbe --- /dev/null +++ b/user/apps/user-manage/.gitignore @@ -0,0 +1,3 @@ +/target +/Cargo.lock +/install \ No newline at end of file diff --git a/user/apps/user-manage/Cargo.toml b/user/apps/user-manage/Cargo.toml new file mode 100644 index 000000000..c3e73f109 --- /dev/null +++ b/user/apps/user-manage/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "user_manage_tool" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "useradd" +path = "src/cmd/useradd.rs" + +[[bin]] +name = "userdel" +path = "src/cmd/userdel.rs" + +[[bin]] +name = "usermod" +path = "src/cmd/usermod.rs" + +[[bin]] +name = "passwd" +path = "src/cmd/passwd.rs" + +[[bin]] +name = "groupadd" +path = "src/cmd/groupadd.rs" + +[[bin]] +name = "groupdel" +path = "src/cmd/groupdel.rs" + +[[bin]] +name = "groupmod" +path = "src/cmd/groupmod.rs" + +[dependencies] +libc = "0.2.153" +lazy_static = "1.4.0" diff --git a/user/apps/user-manage/Makefile b/user/apps/user-manage/Makefile new file mode 100644 index 000000000..82cc81f92 --- /dev/null +++ b/user/apps/user-manage/Makefile @@ -0,0 +1,47 @@ +# The toolchain we use. +# You can get it by running DragonOS' `tools/bootstrap.sh` +TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" +RUSTFLAGS+="" + +ifdef DADK_CURRENT_BUILD_DIR +# 如果是在dadk中编译,那么安装到dadk的安装目录中 + INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) +else +# 如果是在本地编译,那么安装到当前目录下的install目录中 + INSTALL_DIR = ./install +endif + + +ifeq ($(ARCH), x86_64) + export RUST_TARGET=x86_64-unknown-linux-musl +else ifeq ($(ARCH), riscv64) + export RUST_TARGET=riscv64gc-unknown-linux-gnu +else +# 默认为x86_86,用于本地编译 + export RUST_TARGET=x86_64-unknown-linux-musl +endif + +build: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) + +run-dragonreach: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --bin DragonReach + +clean: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean + +build-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release + +clean-release: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release + +fmt: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt + +fmt-check: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check + +.PHONY: install +install: + RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force \ No newline at end of file diff --git a/user/apps/user-manage/README.md b/user/apps/user-manage/README.md new file mode 100644 index 000000000..510189013 --- /dev/null +++ b/user/apps/user-manage/README.md @@ -0,0 +1,142 @@ +## useradd + +- usage:添加用户 + + > useradd [options] username + + useradd -c \ -d \ -G \ -g \ -s \ -u \ username + +- 参数说明: + + - 选项: + -c comment 指定一段注释性描述 + -d 目录 指定用户主目录,如果不存在,则创建该目录 + -G 用户组 指定用户所属的用户组 + -g 组id + -s Shell 文件 指定用户的登录 Shell + -u 用户号 指定用户的用户号 + + - 用户名: + 指定新账号的登录名。 + +- 更新文件: + > /etc/passwd + > /etc/shadow + > /etc/group + > /etc/gshadow + +## userdel + +- usage:删除用户 + + > userdel [options] username + + userdel -r username + +- 选项: + -r 连同用户主目录一起删除。 + +- 更新文件: + > /etc/passwd + > /etc/shadow + > /etc/group + +## usermod + +- usage:修改用户 + + > usermod [options] username + + usermod -a -G<组 1,组 2,...> -c<备注> -d<登入目录> -G<组名> -l<名称> -s<登入终端> -u<用户 id> username + +- 选项: + -a -G<组 1,组 2,...> 将用户添加到其它组中 + -c<备注>  修改用户帐号的备注文字。 + -d 登入目录>  修改用户登入时的目录。 + -G<组名>  修改用户所属的群组。 + -l<名称>  修改用户名称。 + -s\  修改用户登入后所使用的 shell。 + -u\  修改用户 ID。 + +- 更新文件: + > /etc/passwd + > /etc/shadow + > /etc/group + > /etc/gshadow + +## passwd + +- usage:设置密码 + + > 普通用户: passwd + > root 用户: passwd username + + 普通用户只能修改自己的密码,因此不需要指定用户名。 + +- 更新文件 + > /etc/shadow + > /etc/passwd + +## groupadd + +- usage:添加用户组 + + > groupadd [options] groupname + + groupadd -g\ -p\ groupname + +- 选项: + -g\ 指定组 id + -p 设置密码 + +- 更新文件 + > /etc/group + > /etc/gshadow + +## groupdel + +- usage:删除用户组 + + > groupdel groupname + + groupdel \ + +- 注意事项: + 只有当用户组的组成员为空时才可以删除该组 + +- 更新文件 + > /etc/group + > /etc/gshadow + +## groupmod + +- usage:修改用户组信息 + + > groupmod [options] groupname + + groupadd -g\ -n\ groupname + +- 选项: + -g 设置新 gid + -n 设置新组名 + +- 更新文件 + > /etc/group + > /etc/gshadow + > /etc/passwd + +_/etc/passwd 文件格式:_ + +> 用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录 Shell + +_/etc/shadow 文件格式:_ + +> 登录名:加密口令:最后一次修改时间:最小时间间隔:最大时间间隔:警告时间:不活动时间:失效时间:标志 + +_/etc/group 文件格式:_ + +> 组名:口令:组标识号:组内用户列表 + +_/etc/gshadow 文件格式:_ + +> 组名:组密码:组管理员名称:组成员 diff --git a/user/apps/user-manage/src/check/check.rs b/user/apps/user-manage/src/check/check.rs new file mode 100644 index 000000000..36754c928 --- /dev/null +++ b/user/apps/user-manage/src/check/check.rs @@ -0,0 +1,908 @@ +use super::info::{GAddInfo, GDelInfo, GModInfo, PasswdInfo, UAddInfo, UDelInfo, UModInfo}; +use crate::{ + error::error::{ErrorHandler, ExitStatus}, + parser::cmd::{CmdOption, GroupCommand, PasswdCommand, UserCommand}, +}; +use std::{ + collections::{HashMap, HashSet}, + fs, + io::Write, +}; + +/// useradd命令检查器 +#[derive(Debug)] +pub struct UAddCheck; + +impl UAddCheck { + /// **校验解析后的useradd命令** + /// + /// ## 参数 + /// - `cmd`: 解析后的useradd命令 + /// + /// ## 返回 + /// - `UAddInfo`: 校验后的信息 + pub fn check(cmd: UserCommand) -> UAddInfo { + let mut info = UAddInfo::default(); + info.username = cmd.username; + + // 填充信息 + for (option, arg) in cmd.options.iter() { + match option { + CmdOption::Shell => { + info.shell = arg.clone(); + } + CmdOption::Comment => { + info.comment = arg.clone(); + } + CmdOption::Uid => { + info.uid = arg.clone(); + } + CmdOption::Group => { + info.group = arg.clone(); + } + CmdOption::Gid => { + info.gid = arg.clone(); + } + CmdOption::Dir => { + info.home_dir = arg.clone(); + } + _ => { + let op: &str = option.clone().into(); + ErrorHandler::error_handle( + format!("Unimplemented option: {}", op), + ExitStatus::InvalidCmdSyntax, + ); + } + } + } + + // 完善用户信息 + if info.username.is_empty() { + ErrorHandler::error_handle("Invalid username".to_string(), ExitStatus::InvalidArg); + } + + if info.uid.is_empty() { + ErrorHandler::error_handle("Uid is required".to_string(), ExitStatus::InvalidCmdSyntax); + } + + if info.comment.is_empty() { + info.comment = info.username.clone() + ",,,"; + } + if info.home_dir.is_empty() { + let home_dir = format!("/home/{}", info.username.clone()); + info.home_dir = home_dir; + } + if info.shell.is_empty() { + info.shell = "/bin/NovaShell".to_string(); + } + + // 校验终端是否有效 + check_shell(&info.shell); + + // 校验是否有重复用户名和用户id + scan_passwd( + PasswdField { + username: Some(info.username.clone()), + uid: Some(info.uid.clone()), + }, + false, + ); + + // 判断group和gid是否有效 + Self::check_group_gid(&mut info); + + info + } + + /// 检查组名、组id是否有效,如果组名不存在,则创建新的用户组 + fn check_group_gid(info: &mut UAddInfo) { + if info.group.is_empty() && info.gid.is_empty() { + ErrorHandler::error_handle( + "user must belong to a group".to_string(), + ExitStatus::InvalidCmdSyntax, + ); + } + + let r = fs::read_to_string("/etc/group"); + let mut max_gid: u32 = 0; + match r { + Ok(content) => { + for line in content.lines() { + let data: Vec<&str> = line.split(":").collect(); + let (groupname, gid) = (data[0].to_string(), data[2].to_string()); + if !info.group.is_empty() && info.group == groupname { + if !info.gid.is_empty() && info.gid != gid { + ErrorHandler::error_handle( + format!("The gid of the group [{}] isn't {}", info.group, info.gid), + ExitStatus::InvalidArg, + ) + } else if info.gid.is_empty() || info.gid == gid { + info.gid = gid; + return; + } + } + + if !info.gid.is_empty() && info.gid == gid { + if !info.group.is_empty() && info.group != groupname { + ErrorHandler::error_handle( + format!("The gid of the group [{}] isn't {}", info.group, info.gid), + ExitStatus::InvalidArg, + ) + } else if info.group.is_empty() || info.group == groupname { + info.group = groupname; + return; + } + } + + max_gid = max_gid.max(u32::from_str_radix(data[2], 10).unwrap()); + } + } + Err(_) => { + ErrorHandler::error_handle( + "Can't read file: /etc/group".to_string(), + ExitStatus::GroupFile, + ); + } + } + + // 没有对应的用户组,默认创建新的用户组 + let mut groupname = info.username.clone(); + let mut gid = (max_gid + 1).to_string(); + if !info.group.is_empty() { + groupname = info.group.clone(); + } else { + info.group = groupname.clone(); + } + + if !info.gid.is_empty() { + gid = info.gid.clone(); + } else { + info.gid = gid.clone(); + } + let mut success = true; + let r = std::process::Command::new("/bin/groupadd") + .arg("-g") + .arg(gid.clone()) + .arg(groupname) + .status(); + if let Ok(exit_status) = r { + if exit_status.code() != Some(0) { + success = false; + } + } else { + success = false; + } + + if !success { + ErrorHandler::error_handle("groupadd failed".to_string(), ExitStatus::GroupaddFail); + } + } +} + +/// userdel命令检查器 +#[derive(Debug)] +pub struct UDelCheck; + +impl UDelCheck { + /// **校验userdel命令** + /// + /// ## 参数 + /// - `cmd`: userdel命令 + /// + /// ## 返回 + /// - `UDelInfo`: 校验后的用户信息 + pub fn check(cmd: UserCommand) -> UDelInfo { + let mut info = UDelInfo::default(); + info.username = cmd.username; + + // 检查用户是否存在 + scan_passwd( + PasswdField { + username: Some(info.username.clone()), + uid: None, + }, + true, + ); + + if let Some(_) = cmd.options.get(&CmdOption::Remove) { + info.home = Some(Self::home(&info.username)); + } + + info + } + + /// 获取用户家目录 + fn home(username: &String) -> String { + let mut home = String::new(); + match std::fs::read_to_string("/etc/passwd") { + Ok(data) => { + for line in data.lines() { + let data = line.split(':').collect::>(); + if data[0] == username { + home = data[5].to_string(); + break; + } + } + } + Err(_) => { + ErrorHandler::error_handle( + "Can't read file: /etc/passwd".to_string(), + ExitStatus::PasswdFile, + ); + } + } + home + } +} + +/// usermod命令检查器 +#[derive(Debug)] +pub struct UModCheck; + +impl UModCheck { + /// **校验usermod命令** + /// + /// ## 参数 + /// - `cmd`: usermod命令 + /// + /// ## 返回 + /// - `UModInfo`: 校验后的用户信息 + pub fn check(cmd: UserCommand) -> UModInfo { + let mut info = Self::parse_options(&cmd.options); + info.username = cmd.username; + + // 校验shell是否有效 + if let Some(shell) = &info.new_shell { + check_shell(shell); + } + + // 校验new_home是否有效 + if let Some(new_home) = &info.new_home { + Self::check_home(new_home); + } + + // 校验用户是否存在 + scan_passwd( + PasswdField { + username: Some(info.username.clone()), + uid: None, + }, + true, + ); + + // 校验new_name、new_uid是否有效 + scan_passwd( + PasswdField { + username: info.new_name.clone(), + uid: info.new_uid.clone(), + }, + false, + ); + + // 校验groups、new_gid是否有效 + scan_group( + GroupField { + groups: info.groups.clone(), + gid: info.new_gid.clone(), + }, + true, + ); + + info + } + + /// **校验home目录是否有效** + /// + /// ## 参数 + /// - `home`: home目录路径 + fn check_home(home: &String) { + if fs::File::open(home).is_ok() { + ErrorHandler::error_handle(format!("{} already exists", home), ExitStatus::InvalidArg); + } + } + + /// **解析options** + /// + /// ## 参数 + /// - `options`: 命令选项 + /// + /// ## 返回 + /// - `UModInfo`: 用户信息 + fn parse_options(options: &HashMap) -> UModInfo { + let mut info = UModInfo::default(); + for (option, arg) in options { + match option { + CmdOption::Append => { + info.groups = Some(arg.split(",").map(|s| s.to_string()).collect()); + } + CmdOption::Comment => { + info.new_comment = Some(arg.clone()); + } + CmdOption::Dir => { + info.new_home = Some(arg.clone()); + } + CmdOption::Gid => { + info.new_gid = Some(arg.clone()); + } + CmdOption::Login => { + info.new_name = Some(arg.clone()); + } + CmdOption::Shell => { + info.new_shell = Some(arg.clone()); + } + CmdOption::Uid => { + info.new_uid = Some(arg.clone()); + } + _ => ErrorHandler::error_handle( + "Invalid option".to_string(), + ExitStatus::InvalidCmdSyntax, + ), + } + } + info + } +} + +/// passwd命令检查器 +#[derive(Debug)] +pub struct PasswdCheck; + +impl PasswdCheck { + /// **校验passwd命令** + /// + /// ## 参数 + /// - `cmd`: passwd命令 + /// + /// ## 返回 + /// - `PasswdInfo`: 校验后的信息 + pub fn check(cmd: PasswdCommand) -> PasswdInfo { + let uid = unsafe { libc::geteuid().to_string() }; + let cur_username = Self::cur_username(uid.clone()); + let mut to_change_username = String::new(); + + if let Some(username) = cmd.username { + to_change_username = username.clone(); + + // 不是root用户不能修改别人的密码 + if uid != "0" && cur_username != username { + ErrorHandler::error_handle( + "You can't change password for other users".to_string(), + ExitStatus::PermissionDenied, + ); + } + + // 检验待修改用户是否存在 + scan_passwd( + PasswdField { + username: Some(username.clone()), + uid: None, + }, + true, + ); + } + + let mut new_password = String::new(); + match uid.as_str() { + "0" => { + if to_change_username.is_empty() { + to_change_username = cur_username; + } + print!("New password: "); + std::io::stdout().flush().unwrap(); + std::io::stdin().read_line(&mut new_password).unwrap(); + new_password = new_password.trim().to_string(); + let mut check_password = String::new(); + print!("\nRe-enter new password: "); + std::io::stdout().flush().unwrap(); + std::io::stdin().read_line(&mut check_password).unwrap(); + check_password = check_password.trim().to_string(); + if new_password != check_password { + ErrorHandler::error_handle( + "\nThe two passwords that you entered do not match.".to_string(), + ExitStatus::InvalidArg, + ) + } + } + _ => { + to_change_username = cur_username.clone(); + print!("Old password: "); + std::io::stdout().flush().unwrap(); + let mut old_password = String::new(); + std::io::stdin().read_line(&mut old_password).unwrap(); + old_password = old_password.trim().to_string(); + Self::check_password(cur_username, old_password); + print!("\nNew password: "); + std::io::stdout().flush().unwrap(); + std::io::stdin().read_line(&mut new_password).unwrap(); + new_password = new_password.trim().to_string(); + print!("\nRe-enter new password: "); + std::io::stdout().flush().unwrap(); + let mut check_password = String::new(); + std::io::stdin().read_line(&mut check_password).unwrap(); + check_password = check_password.trim().to_string(); + if new_password != check_password { + println!("{}", new_password); + ErrorHandler::error_handle( + "\nThe two passwords that you entered do not match.".to_string(), + ExitStatus::InvalidArg, + ) + } + } + }; + + PasswdInfo { + username: to_change_username, + new_password, + } + } + + /// **获取uid对应的用户名** + /// + /// ## 参数 + /// - `uid`: 用户id + /// + /// ## 返回 + /// 用户名 + fn cur_username(uid: String) -> String { + let r = fs::read_to_string("/etc/passwd"); + let mut cur_username = String::new(); + + match r { + Ok(content) => { + for line in content.lines() { + let field = line.split(":").collect::>(); + if uid == field[2] { + cur_username = field[0].to_string(); + } + } + } + Err(_) => { + ErrorHandler::error_handle( + "Can't read /etc/passwd".to_string(), + ExitStatus::PasswdFile, + ); + } + } + + cur_username + } + + /// **校验密码** + /// + /// ## 参数 + /// - `username`: 用户名 + /// - `password`: 密码 + fn check_password(username: String, password: String) { + let r = fs::read_to_string("/etc/shadow"); + match r { + Ok(content) => { + for line in content.lines() { + let field = line.split(":").collect::>(); + if username == field[0] { + if password != field[1] { + ErrorHandler::error_handle( + "Password error".to_string(), + ExitStatus::InvalidArg, + ); + } else { + return; + } + } + } + } + Err(_) => { + ErrorHandler::error_handle( + "Can't read /etc/shadow".to_string(), + ExitStatus::ShadowFile, + ); + } + } + } +} + +/// groupadd命令检查器 +#[derive(Debug)] +pub struct GAddCheck; + +impl GAddCheck { + /// **校验groupadd命令** + /// + /// ## 参数 + /// - `cmd`: groupadd命令 + /// + /// ## 返回 + /// - `GAddInfo`: 校验后的组信息 + pub fn check(cmd: GroupCommand) -> GAddInfo { + let mut info = GAddInfo { + groupname: cmd.groupname.clone(), + gid: String::new(), + passwd: None, + }; + + if info.groupname.is_empty() { + ErrorHandler::error_handle("groupname is required".to_string(), ExitStatus::InvalidArg); + } + + if let Some(gid) = cmd.options.get(&CmdOption::Gid) { + info.gid = gid.clone(); + } else { + ErrorHandler::error_handle("gid is required".to_string(), ExitStatus::InvalidArg); + } + + if let Some(passwd) = cmd.options.get(&CmdOption::Passwd) { + info.passwd = Some(passwd.clone()); + } + + // 检查组名或组id是否已存在 + scan_group( + GroupField { + groups: Some(vec![info.groupname.clone()]), + gid: Some(info.gid.clone()), + }, + false, + ); + + info + } +} + +/// groupdel命令检查器 +#[derive(Debug)] +pub struct GDelCheck; + +impl GDelCheck { + /// **校验groupdel命令** + /// + /// ## 参数 + /// - `cmd`: groupdel命令 + /// + /// ## 返回 + /// - `GDelInfo`: 校验后的组信息 + pub fn check(cmd: GroupCommand) -> GDelInfo { + if let Some(gid) = check_groupname(cmd.groupname.clone()) { + // 检查group是不是某个用户的主组,如果是的话则不能删除 + Self::is_main_group(gid); + } else { + // 用户组不存在 + ErrorHandler::error_handle( + format!("group:[{}] doesn't exist", cmd.groupname), + ExitStatus::GroupNotExist, + ); + } + GDelInfo { + groupname: cmd.groupname, + } + } + + /// **检查该组是否为某个用户的主用户组** + /// + /// ## 参数 + /// - `gid`: 组id + /// + /// ## 返回 + /// Some(gid): 组id + /// None + fn is_main_group(gid: String) { + // 读取/etc/passwd文件 + let r = fs::read_to_string("/etc/passwd"); + match r { + Ok(content) => { + for line in content.lines() { + let field = line.split(":").collect::>(); + if field[3] == gid { + ErrorHandler::error_handle( + format!( + "groupdel failed: group is main group of user:[{}]", + field[0] + ), + ExitStatus::InvalidArg, + ) + } + } + } + Err(_) => { + ErrorHandler::error_handle( + "Can't read file: /etc/passwd".to_string(), + ExitStatus::PasswdFile, + ); + } + } + } +} + +/// groupmod命令检查器 +#[derive(Debug)] +pub struct GModCheck; + +impl GModCheck { + /// **校验groupmod命令** + /// + /// ## 参数 + /// - `cmd`: groupmod命令 + /// + /// ## 返回 + /// - `GModInfo`: 校验后的组信息 + pub fn check(cmd: GroupCommand) -> GModInfo { + let mut info = GModInfo::default(); + info.groupname = cmd.groupname; + + if let Some(new_groupname) = cmd.options.get(&CmdOption::NewGroupName) { + info.new_groupname = Some(new_groupname.clone()); + } + + if let Some(new_gid) = cmd.options.get(&CmdOption::Gid) { + info.new_gid = Some(new_gid.clone()); + } + + Self::check_group_file(&mut info); + + info + } + + /// 查看groupname是否存在,同时检测new_gid、new_groupname是否重复 + fn check_group_file(info: &mut GModInfo) { + let mut is_group_exist = false; + let r = fs::read_to_string("/etc/group"); + match r { + Ok(content) => { + for line in content.lines() { + let field = line.split(':').collect::>(); + if field[0] == info.groupname { + is_group_exist = true; + info.gid = field[2].to_string(); + } + + if let Some(new_gid) = &info.new_gid { + if new_gid == field[2] { + ErrorHandler::error_handle( + format!("gid:[{}] is already used", new_gid), + ExitStatus::InvalidArg, + ); + } + } + + if let Some(new_groupname) = &info.new_groupname { + if new_groupname == field[0] { + ErrorHandler::error_handle( + format!("groupname:[{}] is already used", new_groupname), + ExitStatus::InvalidArg, + ); + } + } + } + } + Err(_) => ErrorHandler::error_handle( + "Can't read file: /etc/group".to_string(), + ExitStatus::GroupFile, + ), + } + + if !is_group_exist { + ErrorHandler::error_handle( + format!("groupname:[{}] doesn't exist", info.groupname), + ExitStatus::GroupNotExist, + ); + } + } +} + +/// passwd文件待校验字段 +pub struct PasswdField { + username: Option, + uid: Option, +} + +/// group文件待校验字段 +pub struct GroupField { + groups: Option>, + gid: Option, +} + +/// **校验uid** +/// +/// ## 参数 +/// - `passwd_field`: passwd文件字段 +/// - `should_exist`: 是否应该存在 +fn scan_passwd(passwd_field: PasswdField, should_exist: bool) { + let mut username_check = false; + let mut uid_check = false; + match fs::read_to_string("/etc/passwd") { + Ok(content) => { + for line in content.lines() { + let field = line.split(':').collect::>(); + if let Some(uid) = &passwd_field.uid { + // uid必须是有效的数字 + let r = uid.parse::(); + if r.is_err() { + ErrorHandler::error_handle( + format!("Uid {} is invalid", uid), + ExitStatus::InvalidArg, + ); + } + if field[2] == uid { + uid_check = true; + // username如果不用校验或者被校验过了,才可以return + if should_exist && (passwd_field.username.is_none() || username_check) { + return; + } else { + ErrorHandler::error_handle( + format!("UID {} already exists", uid), + ExitStatus::UidInUse, + ); + } + } + } + + if let Some(username) = &passwd_field.username { + if field[0] == username { + username_check = true; + // uid如果不用校验或者被校验过了,才可以return + if should_exist && (passwd_field.uid.is_none() || uid_check) { + return; + } else { + ErrorHandler::error_handle( + format!("Username {} already exists", username), + ExitStatus::UsernameInUse, + ); + } + } + } + } + + if should_exist { + if let Some(uid) = &passwd_field.uid { + if !uid_check { + ErrorHandler::error_handle( + format!("UID {} doesn't exist", uid), + ExitStatus::InvalidArg, + ); + } + } + if let Some(username) = &passwd_field.username { + if !username_check { + ErrorHandler::error_handle( + format!("User {} doesn't exist", username), + ExitStatus::InvalidArg, + ); + } + } + } + } + Err(_) => ErrorHandler::error_handle( + "Can't read file: /etc/passwd".to_string(), + ExitStatus::PasswdFile, + ), + } +} + +/// **校验gid** +/// +/// ## 参数 +/// - `group_field`: group文件字段 +/// - `should_exist`: 是否应该存在 +fn scan_group(group_field: GroupField, should_exist: bool) { + let mut gid_check = false; + let mut set1 = HashSet::new(); + let mut set2 = HashSet::new(); + if let Some(groups) = group_field.groups.clone() { + set2.extend(groups.into_iter()); + } + match fs::read_to_string("/etc/group") { + Ok(content) => { + for line in content.lines() { + let field = line.split(':').collect::>(); + if let Some(gid) = &group_field.gid { + // gid必须是有效的数字 + let r = gid.parse::(); + if r.is_err() { + ErrorHandler::error_handle( + format!("Gid {} is invalid", gid), + ExitStatus::InvalidArg, + ); + } + if field[2] == gid { + gid_check = true; + if should_exist && group_field.groups.is_none() { + return; + } else { + ErrorHandler::error_handle( + format!("GID {} already exists", gid), + ExitStatus::InvalidArg, + ); + } + } + } + + // 统计所有组 + set1.insert(field[0].to_string()); + } + + if should_exist { + if let Some(gid) = &group_field.gid { + if !gid_check { + ErrorHandler::error_handle( + format!("GID {} doesn't exist", gid), + ExitStatus::InvalidArg, + ); + } + } + if group_field.groups.is_some() { + let mut non_exist_group = Vec::new(); + for group in set2.iter() { + if !set1.contains(group) { + non_exist_group.push(group.clone()); + } + } + + if non_exist_group.len() > 0 { + ErrorHandler::error_handle( + format!("group: {} doesn't exist", non_exist_group.join(",")), + ExitStatus::GroupNotExist, + ); + } + } + } + } + + Err(_) => ErrorHandler::error_handle( + "Can't read file: /etc/group".to_string(), + ExitStatus::GroupFile, + ), + } +} + +/// **校验shell是否有效** +/// +/// ## 参数 +/// - `shell`: shell路径 +fn check_shell(shell: &String) { + if let Ok(file) = fs::File::open(shell.clone()) { + if !file.metadata().unwrap().is_file() { + ErrorHandler::error_handle(format!("{} is not a file", shell), ExitStatus::InvalidArg); + } + } else { + ErrorHandler::error_handle(format!("{} doesn't exist", shell), ExitStatus::InvalidArg); + } +} + +/// **校验组名,判断该用户组是否存在,以及成员是否为空** +/// +/// ## 参数 +/// - `groupname`: 组名 +/// +/// ## 返回 +/// Some(gid): 组id +/// None +fn check_groupname(groupname: String) -> Option { + let r = fs::read_to_string("/etc/group"); + match r { + Ok(content) => { + for line in content.lines() { + let field = line.split(":").collect::>(); + let users = field[3].split(",").collect::>(); + let filter_users = users + .iter() + .filter(|&x| !x.is_empty()) + .collect::>(); + if field[0] == groupname { + if filter_users.is_empty() { + return Some(field[2].to_string()); + } else { + ErrorHandler::error_handle( + format!("group:[{}] is not empty, unable to delete", groupname), + ExitStatus::InvalidArg, + ) + } + } + } + } + Err(_) => { + ErrorHandler::error_handle( + "Can't read file: /etc/group".to_string(), + ExitStatus::GroupFile, + ); + } + } + + None +} diff --git a/user/apps/user-manage/src/check/info.rs b/user/apps/user-manage/src/check/info.rs new file mode 100644 index 000000000..b56fb952e --- /dev/null +++ b/user/apps/user-manage/src/check/info.rs @@ -0,0 +1,95 @@ +#[derive(Debug, Default, Clone)] +/// useradd的信息 +pub struct UAddInfo { + /// 用户名 + pub username: String, + pub uid: String, + pub gid: String, + /// 所在组的组名 + pub group: String, + /// 用户描述信息 + pub comment: String, + /// 主目录 + pub home_dir: String, + /// 终端程序名 + pub shell: String, +} + +impl From for String { + fn from(info: UAddInfo) -> Self { + format!( + "{}::{}:{}:{}:{}:{}\n", + info.username, info.uid, info.gid, info.comment, info.home_dir, info.shell + ) + } +} + +#[derive(Debug, Default, Clone)] +/// userdel的信息 +pub struct UDelInfo { + pub username: String, + pub home: Option, +} + +#[derive(Debug, Default, Clone)] +/// usermod的信息 +pub struct UModInfo { + pub username: String, + pub groups: Option>, + pub new_comment: Option, + pub new_home: Option, + pub new_gid: Option, + pub new_group: Option, + pub new_name: Option, + pub new_shell: Option, + pub new_uid: Option, +} + +#[derive(Debug, Default, Clone)] +/// passwd的信息 +pub struct PasswdInfo { + pub username: String, + pub new_password: String, +} + +#[derive(Debug, Default, Clone)] +/// groupadd的信息 +pub struct GAddInfo { + pub groupname: String, + pub gid: String, + pub passwd: Option, +} + +impl GAddInfo { + pub fn to_string_group(&self) -> String { + let mut passwd = String::from(""); + if self.passwd.is_some() { + passwd = "x".to_string(); + } + format!("{}:{}:{}:\n", self.groupname, passwd, self.gid) + } + + pub fn to_string_gshadow(&self) -> String { + let mut passwd = String::from("!"); + if let Some(gpasswd) = &self.passwd { + passwd = gpasswd.clone(); + } + + format!("{}:{}::\n", self.groupname, passwd) + } +} + +#[derive(Debug, Default, Clone)] +/// groupdel的信息 +pub struct GDelInfo { + pub groupname: String, +} + +#[derive(Debug, Default, Clone)] +/// groupmod的信息 +pub struct GModInfo { + pub groupname: String, + pub gid: String, + pub new_groupname: Option, + pub new_gid: Option, +} diff --git a/user/apps/user-manage/src/check/mod.rs b/user/apps/user-manage/src/check/mod.rs new file mode 100644 index 000000000..8bea63796 --- /dev/null +++ b/user/apps/user-manage/src/check/mod.rs @@ -0,0 +1,3 @@ +#![allow(dead_code)] +pub mod check; +pub mod info; diff --git a/user/apps/user-manage/src/cmd/groupadd.rs b/user/apps/user-manage/src/cmd/groupadd.rs new file mode 100644 index 000000000..78e3ac7a5 --- /dev/null +++ b/user/apps/user-manage/src/cmd/groupadd.rs @@ -0,0 +1,45 @@ +use crate::{ + check::check::GAddCheck, + error::error::{ErrorHandler, ExitStatus}, + executor::executor::GAddExecutor, + parser::parser::GroupParser, +}; +use libc::geteuid; +use std::process::exit; + +#[path = "../check/mod.rs"] +mod check; +#[path = "../error/mod.rs"] +mod error; +#[path = "../executor/mod.rs"] +mod executor; +#[path = "../parser/mod.rs"] +mod parser; + +#[allow(dead_code)] +fn main() { + let args = std::env::args().collect::>(); + + if unsafe { geteuid() } != 0 { + ErrorHandler::error_handle( + "permission denied (are you root?)".to_string(), + ExitStatus::PermissionDenied, + ) + } + + if args.len() < 2 { + ErrorHandler::error_handle( + format!("usage: {} [options] groupname", args[0]), + ExitStatus::InvalidCmdSyntax, + ); + } + + let cmd = GroupParser::parse(args); + let info = GAddCheck::check(cmd); + let groupname = info.groupname.clone(); + GAddExecutor::execute(info); + + println!("Add group [{}] successfully!", groupname); + + exit(ExitStatus::Success as i32); +} diff --git a/user/apps/user-manage/src/cmd/groupdel.rs b/user/apps/user-manage/src/cmd/groupdel.rs new file mode 100644 index 000000000..ecb1a1739 --- /dev/null +++ b/user/apps/user-manage/src/cmd/groupdel.rs @@ -0,0 +1,45 @@ +use crate::{ + check::check::GDelCheck, + error::error::{ErrorHandler, ExitStatus}, + executor::executor::GDelExecutor, + parser::parser::GroupParser, +}; +use libc::geteuid; +use std::process::exit; + +#[path = "../check/mod.rs"] +mod check; +#[path = "../error/mod.rs"] +mod error; +#[path = "../executor/mod.rs"] +mod executor; +#[path = "../parser/mod.rs"] +mod parser; + +#[allow(dead_code)] +fn main() { + let args = std::env::args().collect::>(); + + if unsafe { geteuid() } != 0 { + ErrorHandler::error_handle( + "permission denied (are you root?)".to_string(), + ExitStatus::PermissionDenied, + ) + } + + if args.len() < 2 { + ErrorHandler::error_handle( + format!("usage: {} [options] groupname", args[0]), + ExitStatus::InvalidCmdSyntax, + ); + } + + let cmd = GroupParser::parse(args); + let info = GDelCheck::check(cmd); + let groupname = info.groupname.clone(); + GDelExecutor::execute(info); + + println!("Delete group [{}] successfully!", groupname); + + exit(ExitStatus::Success as i32); +} diff --git a/user/apps/user-manage/src/cmd/groupmod.rs b/user/apps/user-manage/src/cmd/groupmod.rs new file mode 100644 index 000000000..3d374132b --- /dev/null +++ b/user/apps/user-manage/src/cmd/groupmod.rs @@ -0,0 +1,46 @@ +use crate::{ + check::check::GModCheck, + error::error::{ErrorHandler, ExitStatus}, + executor::executor::GModExecutor, + parser::parser::GroupParser, +}; +use libc::geteuid; +use std::process::exit; + +#[path = "../check/mod.rs"] +mod check; +#[path = "../error/mod.rs"] +mod error; +#[path = "../executor/mod.rs"] +mod executor; +#[path = "../parser/mod.rs"] +mod parser; + +#[allow(dead_code)] +fn main() { + let args = std::env::args().collect::>(); + + if unsafe { geteuid() } != 0 { + ErrorHandler::error_handle( + "permission denied (are you root?)".to_string(), + ExitStatus::PermissionDenied, + ) + } + + if args.len() < 2 { + ErrorHandler::error_handle( + format!("usage: {} [options] groupname", args[0]), + ExitStatus::InvalidCmdSyntax, + ); + } + + let cmd = GroupParser::parse(args); + if !cmd.options.is_empty() { + let info = GModCheck::check(cmd); + let groupname = info.groupname.clone(); + GModExecutor::execute(info); + println!("Modify group [{}] successfully!", groupname); + } + + exit(ExitStatus::Success as i32); +} diff --git a/user/apps/user-manage/src/cmd/mod.rs b/user/apps/user-manage/src/cmd/mod.rs new file mode 100644 index 000000000..93886dea6 --- /dev/null +++ b/user/apps/user-manage/src/cmd/mod.rs @@ -0,0 +1,7 @@ +mod groupadd; +mod groupdel; +mod groupmod; +mod passwd; +mod useradd; +mod userdel; +mod usermod; diff --git a/user/apps/user-manage/src/cmd/passwd.rs b/user/apps/user-manage/src/cmd/passwd.rs new file mode 100644 index 000000000..204be906f --- /dev/null +++ b/user/apps/user-manage/src/cmd/passwd.rs @@ -0,0 +1,25 @@ +use crate::{ + check::check::PasswdCheck, error::error::ExitStatus, executor::executor::PasswdExecutor, + parser::parser::PasswdParser, +}; +use std::process::exit; + +#[path = "../check/mod.rs"] +mod check; +#[path = "../error/mod.rs"] +mod error; +#[path = "../executor/mod.rs"] +mod executor; +#[path = "../parser/mod.rs"] +mod parser; + +#[allow(dead_code)] +fn main() { + let args = std::env::args().collect::>(); + + let cmd = PasswdParser::parse(args); + let info = PasswdCheck::check(cmd); + PasswdExecutor::execute(info); + + exit(ExitStatus::Success as i32); +} diff --git a/user/apps/user-manage/src/cmd/useradd.rs b/user/apps/user-manage/src/cmd/useradd.rs new file mode 100644 index 000000000..4ad109a24 --- /dev/null +++ b/user/apps/user-manage/src/cmd/useradd.rs @@ -0,0 +1,44 @@ +use crate::{ + check::check::UAddCheck, + error::error::{ErrorHandler, ExitStatus}, + executor::executor::UAddExecutor, + parser::parser::UserParser, +}; +use libc::geteuid; +use std::process::exit; + +#[path = "../check/mod.rs"] +mod check; +#[path = "../error/mod.rs"] +mod error; +#[path = "../executor/mod.rs"] +mod executor; +#[path = "../parser/mod.rs"] +mod parser; + +#[allow(dead_code)] +fn main() { + let args = std::env::args().collect::>(); + + if unsafe { geteuid() } != 0 { + ErrorHandler::error_handle( + "permission denied (are you root?)".to_string(), + ExitStatus::PermissionDenied, + ) + } + + if args.len() < 2 { + ErrorHandler::error_handle( + format!("usage: {} [options] username", args[0]), + ExitStatus::InvalidCmdSyntax, + ); + } + + let cmd = UserParser::parse(args); + let info = UAddCheck::check(cmd); + let username = info.username.clone(); + UAddExecutor::execute(info); + println!("Add user[{}] successfully!", username); + + exit(ExitStatus::Success as i32); +} diff --git a/user/apps/user-manage/src/cmd/userdel.rs b/user/apps/user-manage/src/cmd/userdel.rs new file mode 100644 index 000000000..8d1a675fe --- /dev/null +++ b/user/apps/user-manage/src/cmd/userdel.rs @@ -0,0 +1,44 @@ +use crate::{ + check::check::UDelCheck, + error::error::{ErrorHandler, ExitStatus}, + executor::executor::UDelExecutor, + parser::parser::UserParser, +}; +use libc::geteuid; +use std::process::exit; + +#[path = "../check/mod.rs"] +mod check; +#[path = "../error/mod.rs"] +mod error; +#[path = "../executor/mod.rs"] +mod executor; +#[path = "../parser/mod.rs"] +mod parser; + +#[allow(dead_code)] +fn main() { + let args = std::env::args().collect::>(); + + if unsafe { geteuid() } != 0 { + ErrorHandler::error_handle( + "permission denied (are you root?)".to_string(), + ExitStatus::PermissionDenied, + ) + } + + if args.len() < 2 { + ErrorHandler::error_handle( + format!("usage: {} [options] username", args[0]), + ExitStatus::InvalidCmdSyntax, + ); + } + + let cmd = UserParser::parse(args); + let info = UDelCheck::check(cmd); + let username = info.username.clone(); + UDelExecutor::execute(info); + println!("Delete user[{}] successfully!", username); + + exit(ExitStatus::Success as i32); +} diff --git a/user/apps/user-manage/src/cmd/usermod.rs b/user/apps/user-manage/src/cmd/usermod.rs new file mode 100644 index 000000000..37d8ba585 --- /dev/null +++ b/user/apps/user-manage/src/cmd/usermod.rs @@ -0,0 +1,46 @@ +use crate::{ + check::check::UModCheck, + error::error::{ErrorHandler, ExitStatus}, + executor::executor::UModExecutor, + parser::parser::UserParser, +}; +use libc::geteuid; +use std::process::exit; + +#[path = "../check/mod.rs"] +mod check; +#[path = "../error/mod.rs"] +mod error; +#[path = "../executor/mod.rs"] +mod executor; +#[path = "../parser/mod.rs"] +mod parser; + +#[allow(dead_code)] +fn main() { + let args = std::env::args().collect::>(); + + if unsafe { geteuid() } != 0 { + ErrorHandler::error_handle( + "permission denied (are you root?)".to_string(), + ExitStatus::PermissionDenied, + ) + } + + if args.len() < 2 { + ErrorHandler::error_handle( + format!("usage: {} [options] username", args[0]), + ExitStatus::InvalidCmdSyntax, + ); + } + + let cmd = UserParser::parse(args); + if !cmd.options.is_empty() { + let info = UModCheck::check(cmd); + let username = info.username.clone(); + UModExecutor::execute(info); + println!("Modify user[{}] successfully!", username); + } + + exit(ExitStatus::Success as i32); +} diff --git a/user/apps/user-manage/src/error/error.rs b/user/apps/user-manage/src/error/error.rs new file mode 100644 index 000000000..cb299d5f6 --- /dev/null +++ b/user/apps/user-manage/src/error/error.rs @@ -0,0 +1,33 @@ +use std::process::exit; + +#[derive(Debug)] +pub enum ExitStatus { + Success = 0, + PasswdFile = 1, + InvalidCmdSyntax = 2, + InvalidArg = 3, + UidInUse = 4, + GroupNotExist = 6, + UsernameInUse = 9, + GroupFile = 10, + CreateHomeFail = 12, + PermissionDenied = -1, + ShadowFile = -2, + GshadowFile = -3, + GroupaddFail = -4, +} + +pub struct ErrorHandler; + +impl ErrorHandler { + /// **错误处理函数** + /// + /// ## 参数 + /// + /// - `error`错误信息 + /// - `exit_status` - 退出状态码 + pub fn error_handle(error: String, exit_status: ExitStatus) { + eprintln!("{error}"); + exit(exit_status as i32); + } +} diff --git a/user/apps/user-manage/src/error/mod.rs b/user/apps/user-manage/src/error/mod.rs new file mode 100644 index 000000000..7c82bf8fb --- /dev/null +++ b/user/apps/user-manage/src/error/mod.rs @@ -0,0 +1,2 @@ +#![allow(dead_code)] +pub mod error; diff --git a/user/apps/user-manage/src/executor/executor.rs b/user/apps/user-manage/src/executor/executor.rs new file mode 100644 index 000000000..fdfa50e0c --- /dev/null +++ b/user/apps/user-manage/src/executor/executor.rs @@ -0,0 +1,729 @@ +use crate::{ + check::info::{GAddInfo, GDelInfo, GModInfo, PasswdInfo, UAddInfo, UDelInfo, UModInfo}, + error::error::{ErrorHandler, ExitStatus}, +}; +use lazy_static::lazy_static; +use std::{ + fs::{self, File, OpenOptions}, + io::{Read, Seek, Write}, + sync::Mutex, +}; + +lazy_static! { + static ref GLOBAL_FILE: Mutex = Mutex::new(GlobalFile::new()); +} + +#[derive(Debug)] +pub struct GlobalFile { + passwd_file: File, + shadow_file: File, + group_file: File, + gshadow_file: File, +} + +impl GlobalFile { + pub fn new() -> Self { + let passwd = open_file("/etc/passwd"); + let shadow = open_file("/etc/shadow"); + let group = open_file("/etc/group"); + let gshadow = open_file("/etc/gshadow"); + Self { + passwd_file: passwd, + shadow_file: shadow, + group_file: group, + gshadow_file: gshadow, + } + } +} + +fn open_file(file_path: &str) -> File { + let r = OpenOptions::new() + .read(true) + .write(true) + .append(true) + .open(file_path); + + let exit_status = match file_path { + "/etc/group" => ExitStatus::GroupFile, + "/etc/gshadow" => ExitStatus::GshadowFile, + "/etc/passwd" => ExitStatus::PasswdFile, + "/etc/shadow" => ExitStatus::ShadowFile, + _ => ExitStatus::InvalidArg, + }; + + if r.is_err() { + ErrorHandler::error_handle(format!("Can't open file: {}", file_path), exit_status); + } + + r.unwrap() +} + +/// useradd执行器 +pub struct UAddExecutor; + +impl UAddExecutor { + /// **执行useradd** + /// + /// ## 参数 + /// - `info`: 用户信息 + pub fn execute(info: UAddInfo) { + // 创建用户home目录 + let home = info.home_dir.clone(); + let dir_builder = fs::DirBuilder::new(); + if dir_builder.create(home.clone()).is_err() { + ErrorHandler::error_handle( + format!("unable to create {}", home), + ExitStatus::CreateHomeFail, + ); + } + + Self::write_passwd_file(&info); + Self::write_shadow_file(&info); + Self::write_group_file(&info); + Self::write_gshadow_file(&info); + } + + /// 写入/etc/passwd文件:添加用户信息 + fn write_passwd_file(info: &UAddInfo) { + let userinfo: String = info.clone().into(); + GLOBAL_FILE + .lock() + .unwrap() + .passwd_file + .write_all(userinfo.as_bytes()) + .unwrap(); + } + + /// 写入/etc/group文件:将用户添加到对应用户组中 + fn write_group_file(info: &UAddInfo) { + if info.group == info.username { + return; + } + + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.group_file); + let mut new_content = String::new(); + for line in content.lines() { + let mut field = line.split(":").collect::>(); + let mut users = field.last().unwrap().split(",").collect::>(); + users = users + .into_iter() + .filter(|username| !username.is_empty()) + .collect::>(); + if field[0].eq(info.group.as_str()) && !users.contains(&info.username.as_str()) { + users.push(info.username.as_str()); + } + + let new_users = users.join(","); + field[3] = new_users.as_str(); + new_content.push_str(format!("{}\n", field.join(":")).as_str()); + } + + guard.group_file.set_len(0).unwrap(); + guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.group_file.write_all(new_content.as_bytes()).unwrap(); + guard.group_file.flush().unwrap(); + } + + /// 写入/etc/shadow文件:添加用户口令相关信息 + fn write_shadow_file(info: &UAddInfo) { + let data = format!("{}::::::::\n", info.username,); + GLOBAL_FILE + .lock() + .unwrap() + .shadow_file + .write_all(data.as_bytes()) + .unwrap(); + } + + /// 写入/etc/gshadow文件:将用户添加到对应用户组中 + fn write_gshadow_file(info: &UAddInfo) { + if info.group == info.username { + return; + } + + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.gshadow_file); + let mut new_content = String::new(); + for line in content.lines() { + let mut field = line.split(":").collect::>(); + let mut users = field.last().unwrap().split(",").collect::>(); + users = users + .into_iter() + .filter(|username| !username.is_empty()) + .collect::>(); + if field[0].eq(info.group.as_str()) && !users.contains(&info.username.as_str()) { + users.push(info.username.as_str()); + } + + let new_users = users.join(","); + field[3] = new_users.as_str(); + new_content.push_str(format!("{}\n", field.join(":")).as_str()); + } + guard.gshadow_file.set_len(0).unwrap(); + guard + .gshadow_file + .seek(std::io::SeekFrom::Start(0)) + .unwrap(); + guard + .gshadow_file + .write_all(new_content.as_bytes()) + .unwrap(); + guard.gshadow_file.flush().unwrap(); + } +} + +/// userdel执行器 +pub struct UDelExecutor; + +impl UDelExecutor { + /// **执行userdel** + /// + /// ## 参数 + /// - `info`: 用户信息 + pub fn execute(info: UDelInfo) { + // 移除home目录 + if let Some(home) = info.home.clone() { + std::fs::remove_dir_all(home).unwrap(); + } + + Self::update_passwd_file(&info); + Self::update_shadow_file(&info); + Self::update_group_file(&info); + Self::update_gshadow_file(&info); + } + + /// 更新/etc/passwd文件: 删除用户信息 + fn update_passwd_file(info: &UDelInfo) { + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.passwd_file); + let lines: Vec<&str> = content.lines().collect(); + let new_content = lines + .into_iter() + .filter(|&line| { + let field = line.split(':').collect::>(); + field[0] != info.username.as_str() + }) + .collect::>() + .join("\n"); + + guard.passwd_file.set_len(0).unwrap(); + guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.passwd_file.write_all(new_content.as_bytes()).unwrap(); + guard.passwd_file.flush().unwrap(); + } + + /// 更新/etc/group文件: 将用户从组中移除 + fn update_group_file(info: &UDelInfo) { + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.group_file); + let mut new_content = String::new(); + for line in content.lines() { + let mut field = line.split(':').collect::>(); + let mut users = field.last().unwrap().split(",").collect::>(); + if users.contains(&info.username.as_str()) { + field.remove(field.len() - 1); + users.remove( + users + .iter() + .position(|&x| x == info.username.as_str()) + .unwrap(), + ); + let users = users.join(","); + field.push(&users.as_str()); + new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str()); + } else { + new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str()); + } + + guard.group_file.set_len(0).unwrap(); + guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.group_file.write_all(new_content.as_bytes()).unwrap(); + guard.group_file.flush().unwrap(); + } + } + + /// 更新/etc/shadow文件: 将用户信息删去 + fn update_shadow_file(info: &UDelInfo) { + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.shadow_file); + let lines: Vec<&str> = content.lines().collect(); + let new_content = lines + .into_iter() + .filter(|&line| !line.contains(&info.username)) + .collect::>() + .join("\n"); + + guard.shadow_file.set_len(0).unwrap(); + guard.shadow_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.shadow_file.write_all(new_content.as_bytes()).unwrap(); + guard.shadow_file.flush().unwrap(); + } + + /// 更新/etc/gshadow文件: 将用户从组中移除 + fn update_gshadow_file(info: &UDelInfo) { + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.gshadow_file); + let mut new_content = String::new(); + for line in content.lines() { + let mut field = line.split(':').collect::>(); + let mut users = field.last().unwrap().split(",").collect::>(); + if users.contains(&info.username.as_str()) { + field.remove(field.len() - 1); + users.remove( + users + .iter() + .position(|&x| x == info.username.as_str()) + .unwrap(), + ); + let users = users.join(","); + field.push(&users.as_str()); + new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str()); + } else { + new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str()); + } + + guard.gshadow_file.set_len(0).unwrap(); + guard + .gshadow_file + .seek(std::io::SeekFrom::Start(0)) + .unwrap(); + guard + .gshadow_file + .write_all(new_content.as_bytes()) + .unwrap(); + guard.gshadow_file.flush().unwrap(); + } + } +} + +/// usermod执行器 +pub struct UModExecutor; + +impl UModExecutor { + /// **执行usermod** + /// + /// ## 参数 + /// - `info`: 用户信息 + pub fn execute(mut info: UModInfo) { + // 创建new_home + if let Some(new_home) = &info.new_home { + let dir_builder = fs::DirBuilder::new(); + if dir_builder.create(new_home.clone()).is_err() { + ErrorHandler::error_handle( + format!("unable to create {}", new_home), + ExitStatus::CreateHomeFail, + ); + } + } + + Self::update_passwd_file(&info); + Self::update_shadow_file(&info); + Self::update_group_file(&mut info); + Self::update_gshadow_file(&info); + } + + /// 更新/etc/passwd文件的username、uid、comment、home、shell + fn update_passwd_file(info: &UModInfo) { + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.passwd_file); + for line in content.lines() { + let mut fields = line.split(':').collect::>(); + if fields[0] == info.username { + if let Some(new_username) = &info.new_name { + fields[0] = new_username; + } + if let Some(new_uid) = &info.new_uid { + fields[2] = new_uid; + } + if let Some(new_gid) = &info.new_gid { + fields[3] = new_gid; + } + if let Some(new_comment) = &info.new_comment { + fields[4] = new_comment; + } + if let Some(new_home) = &info.new_home { + fields[5] = new_home; + } + if let Some(new_shell) = &info.new_shell { + fields[6] = new_shell; + } + new_content.push_str(format!("{}\n", fields.join(":")).as_str()); + } else { + new_content.push_str(format!("{}\n", line).as_str()); + } + + guard.passwd_file.set_len(0).unwrap(); + guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.passwd_file.write_all(new_content.as_bytes()).unwrap(); + guard.passwd_file.flush().unwrap(); + } + } + + /// 更新/etc/group文件中各用户组中的用户 + fn update_group_file(info: &mut UModInfo) { + let mut name = info.username.clone(); + if let Some(new_name) = &info.new_name { + name = new_name.clone(); + } + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.group_file); + for line in content.lines() { + let mut fields = line.split(':').collect::>(); + let mut users = fields[3].split(",").collect::>(); + users = users + .into_iter() + .filter(|username| !username.is_empty()) + .collect::>(); + if let Some(idx) = users.iter().position(|&r| r == info.username) { + if let Some(gid) = &info.new_gid { + // 换组,将用户从当前组删去 + if gid != fields[2] { + users.remove(idx); + } else { + info.new_group = Some(fields[0].to_string()) + } + } else { + // 不换组但是要更新名字 + users[idx] = &name; + } + } + + if let Some(groups) = &info.groups { + if groups.contains(&fields[0].to_string()) && !users.contains(&name.as_str()) { + users.push(&name); + } + } + + let new_users = users.join(","); + fields[3] = new_users.as_str(); + new_content.push_str(format!("{}\n", fields.join(":")).as_str()); + } + + guard.group_file.set_len(0).unwrap(); + guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.group_file.write_all(new_content.as_bytes()).unwrap(); + guard.group_file.flush().unwrap(); + } + + /// 更新/etc/shadow文件的username + fn update_shadow_file(info: &UModInfo) { + if let Some(new_name) = &info.new_name { + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.shadow_file); + for line in content.lines() { + let mut fields = line.split(':').collect::>(); + if fields[0] == info.username { + fields[0] = new_name; + new_content.push_str(format!("{}\n", fields.join(":")).as_str()); + } else { + new_content.push_str(format!("{}\n", line).as_str()); + } + } + + guard.shadow_file.set_len(0).unwrap(); + guard.shadow_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.shadow_file.write_all(new_content.as_bytes()).unwrap(); + guard.shadow_file.flush().unwrap(); + } + } + + /// 更新/etc/gshadow文件中各用户组中的用户 + fn update_gshadow_file(info: &UModInfo) { + let mut name = info.username.clone(); + if let Some(new_name) = &info.new_name { + name = new_name.clone(); + } + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.gshadow_file); + for line in content.lines() { + let mut fields = line.split(':').collect::>(); + let mut users = fields[3].split(",").collect::>(); + users = users + .into_iter() + .filter(|username| !username.is_empty()) + .collect::>(); + if let Some(idx) = users.iter().position(|&r| r == info.username) { + if let Some(group) = &info.new_group { + // 换组,将用户从当前组删去 + if group != fields[0] { + users.remove(idx); + } + } else { + // 不换组但是要更新名字 + users[idx] = &name; + } + } + + let tmp = format!(",{}", name); + if let Some(groups) = &info.groups { + if groups.contains(&fields[0].to_string()) && !users.contains(&name.as_str()) { + if users.is_empty() { + users.push(&name); + } else { + users.push(tmp.as_str()); + } + } + } + + let new_users = users.join(","); + fields[3] = new_users.as_str(); + new_content.push_str(format!("{}\n", fields.join(":")).as_str()); + } + + guard.gshadow_file.set_len(0).unwrap(); + guard + .gshadow_file + .seek(std::io::SeekFrom::Start(0)) + .unwrap(); + guard + .gshadow_file + .write_all(new_content.as_bytes()) + .unwrap(); + guard.gshadow_file.flush().unwrap(); + } +} + +/// passwd执行器 +pub struct PasswdExecutor; + +impl PasswdExecutor { + /// **执行passwd** + /// + /// ## 参数 + /// - `info`: 用户密码信息 + pub fn execute(info: PasswdInfo) { + Self::update_passwd_file(&info); + Self::update_shadow_file(&info); + } + + /// 更新/etc/passwd文件: 修改用户密码 + fn update_passwd_file(info: &PasswdInfo) { + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.passwd_file); + for line in content.lines() { + let mut field = line.split(':').collect::>(); + if field[0] == info.username { + if info.new_password.is_empty() { + field[1] = ""; + } else { + field[1] = "x"; + } + } + new_content.push_str(format!("{}\n", field.join(":")).as_str()); + } + + guard.passwd_file.set_len(0).unwrap(); + guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.passwd_file.write_all(new_content.as_bytes()).unwrap(); + guard.passwd_file.flush().unwrap(); + } + + /// 更新/etc/shadow文件: 修改用户密码 + fn update_shadow_file(info: &PasswdInfo) { + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.shadow_file); + for line in content.lines() { + let mut field = line.split(':').collect::>(); + if field[0] == info.username { + field[1] = info.new_password.as_str(); + } + new_content.push_str(format!("{}\n", field.join(":")).as_str()); + } + + guard.shadow_file.set_len(0).unwrap(); + guard.shadow_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.shadow_file.write_all(new_content.as_bytes()).unwrap(); + guard.shadow_file.flush().unwrap(); + } +} + +/// groupadd执行器 +pub struct GAddExecutor; + +impl GAddExecutor { + /// **执行groupadd** + /// + /// ## 参数 + /// - `info`: 组信息 + pub fn execute(info: GAddInfo) { + Self::write_group_file(&info); + Self::write_gshadow_file(&info); + } + + /// 写入/etc/group文件: 添加用户组信息 + fn write_group_file(info: &GAddInfo) { + GLOBAL_FILE + .lock() + .unwrap() + .group_file + .write_all(info.to_string_group().as_bytes()) + .unwrap() + } + + /// 写入/etc/gshadow文件: 添加用户组密码信息 + fn write_gshadow_file(info: &GAddInfo) { + GLOBAL_FILE + .lock() + .unwrap() + .gshadow_file + .write_all(info.to_string_gshadow().as_bytes()) + .unwrap(); + } +} + +/// groupdel执行器 +pub struct GDelExecutor; + +impl GDelExecutor { + /// **执行groupdel** + /// + /// ## 参数 + /// - `info`: 组信息 + pub fn execute(info: GDelInfo) { + Self::update_group_file(&info); + Self::update_gshadow_file(&info); + } + + /// 更新/etc/group文件:删除用户组 + pub fn update_group_file(info: &GDelInfo) { + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.group_file); + for line in content.lines() { + let field = line.split(':').collect::>(); + if field[0] != info.groupname { + new_content.push_str(format!("{}\n", line).as_str()); + } + } + + guard.group_file.set_len(0).unwrap(); + guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.group_file.write_all(new_content.as_bytes()).unwrap(); + guard.group_file.flush().unwrap(); + } + + /// 更新/etc/gshadow文件:移除用户组 + pub fn update_gshadow_file(info: &GDelInfo) { + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.gshadow_file); + for line in content.lines() { + let field = line.split(':').collect::>(); + if field[0] != info.groupname { + new_content.push_str(format!("{}\n", line).as_str()); + } + } + + guard.gshadow_file.set_len(0).unwrap(); + guard + .gshadow_file + .seek(std::io::SeekFrom::Start(0)) + .unwrap(); + guard + .gshadow_file + .write_all(new_content.as_bytes()) + .unwrap(); + guard.gshadow_file.flush().unwrap(); + } +} + +/// groupmod执行器 +pub struct GModExecutor; + +impl GModExecutor { + /// **执行groupmod** + /// + /// ## 参数 + /// - `info`: 组信息 + pub fn execute(info: GModInfo) { + Self::update_passwd_file(&info); + Self::update_group_file(&info); + Self::update_gshadow_file(&info); + } + + /// 更新/etc/group文件: 更新用户组信息 + fn update_group_file(info: &GModInfo) { + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.group_file); + for line in content.lines() { + let mut field = line.split(':').collect::>(); + if field[0] == info.groupname { + if let Some(new_groupname) = &info.new_groupname { + field[0] = new_groupname; + } + if let Some(new_gid) = &info.new_gid { + field[2] = new_gid; + } + } + new_content.push_str(format!("{}\n", field.join(":")).as_str()); + } + + guard.group_file.set_len(0).unwrap(); + guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.group_file.write_all(new_content.as_bytes()).unwrap(); + guard.group_file.flush().unwrap(); + } + + /// 更新/etc/gshadow文件: 更新用户组密码信息 + fn update_gshadow_file(info: &GModInfo) { + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.gshadow_file); + for line in content.lines() { + let mut field = line.split(':').collect::>(); + if field[0] == info.groupname { + if let Some(new_groupname) = &info.new_groupname { + field[0] = new_groupname; + } + } + new_content.push_str(format!("{}\n", field.join(":")).as_str()); + } + + guard.gshadow_file.set_len(0).unwrap(); + guard + .gshadow_file + .seek(std::io::SeekFrom::Start(0)) + .unwrap(); + guard + .gshadow_file + .write_all(new_content.as_bytes()) + .unwrap(); + guard.gshadow_file.flush().unwrap(); + } + + /// 更新/etc/passwd文件: 更新用户组ID信息,因为用户组ID可能会被修改 + fn update_passwd_file(info: &GModInfo) { + let mut new_content = String::new(); + let mut guard = GLOBAL_FILE.lock().unwrap(); + let content = read_to_string(&guard.passwd_file); + for line in content.lines() { + let mut field = line.split(':').collect::>(); + if field[3] == info.gid { + if let Some(new_gid) = &info.new_gid { + field[3] = new_gid; + } + } + new_content.push_str(format!("{}\n", field.join(":")).as_str()); + } + + guard.passwd_file.set_len(0).unwrap(); + guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap(); + guard.passwd_file.write_all(new_content.as_bytes()).unwrap(); + guard.passwd_file.flush().unwrap(); + } +} + +fn read_to_string(mut file: &File) -> String { + file.seek(std::io::SeekFrom::Start(0)).unwrap(); + let mut content = String::new(); + file.read_to_string(&mut content).unwrap(); + content +} diff --git a/user/apps/user-manage/src/executor/mod.rs b/user/apps/user-manage/src/executor/mod.rs new file mode 100644 index 000000000..f989249a2 --- /dev/null +++ b/user/apps/user-manage/src/executor/mod.rs @@ -0,0 +1,2 @@ +#![allow(dead_code)] +pub mod executor; diff --git a/user/apps/user-manage/src/lib.rs b/user/apps/user-manage/src/lib.rs new file mode 100644 index 000000000..b85c394e7 --- /dev/null +++ b/user/apps/user-manage/src/lib.rs @@ -0,0 +1,5 @@ +pub mod check; +pub mod cmd; +pub mod error; +pub mod executor; +pub mod parser; diff --git a/user/apps/user-manage/src/parser/cmd.rs b/user/apps/user-manage/src/parser/cmd.rs new file mode 100644 index 000000000..a8a8a3231 --- /dev/null +++ b/user/apps/user-manage/src/parser/cmd.rs @@ -0,0 +1,96 @@ +use std::collections::HashMap; + +/// 命令类型 +pub enum CmdType { + User, + Passwd, + Group, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub enum CmdOption { + /// 用户描述 + Comment, + /// 用户主目录 + Dir, + /// 组名 + Group, + /// 组id + Gid, + /// 终端程序 + Shell, + /// 用户id + Uid, + /// 删除用户的home目录 + Remove, + /// 添加到其它用户组中 + Append, + /// 修改用户名 + Login, + /// 设置组密码 + Passwd, + /// 修改组名 + NewGroupName, + /// 无效选项 + Invalid, +} + +impl From for CmdOption { + fn from(s: String) -> Self { + match s.as_str() { + "-c" => CmdOption::Comment, + "-d" => CmdOption::Dir, + "-G" => CmdOption::Group, + "-g" => CmdOption::Gid, + "-s" => CmdOption::Shell, + "-u" => CmdOption::Uid, + "-r" => CmdOption::Remove, + "-a" => CmdOption::Append, + "-l" => CmdOption::Login, + "-p" => CmdOption::Passwd, + "-n" => CmdOption::NewGroupName, + _ => CmdOption::Invalid, + } + } +} + +impl From for &str { + fn from(option: CmdOption) -> Self { + match option { + CmdOption::Comment => "-c", + CmdOption::Dir => "-d", + CmdOption::Group => "-G", + CmdOption::Shell => "-s", + CmdOption::Uid => "-u", + CmdOption::Login => "-l", + CmdOption::Append => "-a", + CmdOption::Gid => "-g", + CmdOption::NewGroupName => "-n", + CmdOption::Passwd => "-p", + CmdOption::Remove => "-r", + CmdOption::Invalid => "Invalid option", + } + } +} + +/// useradd/userdel/usermod命令 +#[derive(Debug)] +pub struct UserCommand { + /// 用户名 + pub username: String, + /// 选项 + pub options: HashMap, +} + +/// passwd命令 +#[derive(Debug)] +pub struct PasswdCommand { + pub username: Option, +} + +/// groupadd/groupdel/groupmod命令 +#[derive(Debug)] +pub struct GroupCommand { + pub groupname: String, + pub options: HashMap, +} diff --git a/user/apps/user-manage/src/parser/mod.rs b/user/apps/user-manage/src/parser/mod.rs new file mode 100644 index 000000000..2913a06f1 --- /dev/null +++ b/user/apps/user-manage/src/parser/mod.rs @@ -0,0 +1,3 @@ +#![allow(dead_code)] +pub mod cmd; +pub mod parser; diff --git a/user/apps/user-manage/src/parser/parser.rs b/user/apps/user-manage/src/parser/parser.rs new file mode 100644 index 000000000..5f13d4ae9 --- /dev/null +++ b/user/apps/user-manage/src/parser/parser.rs @@ -0,0 +1,137 @@ +use super::cmd::{CmdOption, GroupCommand, PasswdCommand, UserCommand}; +use crate::error::error::{ErrorHandler, ExitStatus}; +use std::collections::HashMap; + +/// 用户命令(useradd/userdel/usermod)解析器 +pub struct UserParser; + +impl UserParser { + /// **解析用户命令** + /// + /// ## 参数 + /// - `args`: 用户命令参数 + /// + /// ## 返回 + /// - `UserCommand`: 用户命令 + pub fn parse(args: Vec) -> UserCommand { + let username = args.last().unwrap().clone(); + let args = &args[1..args.len() - 1]; + let mut options = HashMap::new(); + + let mut idx = 0; + loop { + if idx >= args.len() { + break; + } + let option: CmdOption = args[idx].clone().into(); + match option { + CmdOption::Invalid => invalid_handle(), + CmdOption::Remove => { + if idx + 1 < args.len() { + let op: &str = option.clone().into(); + ErrorHandler::error_handle( + format!("Invalid arg {} of option: {}", args[idx + 1], op), + ExitStatus::InvalidCmdSyntax, + ) + } + options.insert(option, "".to_string()); + } + CmdOption::Append => { + if idx + 1 >= args.len() || idx + 2 >= args.len() || args[idx + 1] != "-G" { + ErrorHandler::error_handle( + "Invalid option: -a -G ".to_string(), + ExitStatus::InvalidCmdSyntax, + ); + } + idx += 2; + let groups = &args[idx]; + options.insert(option, groups.clone()); + } + _ => { + if idx + 1 >= args.len() { + let op: &str = option.clone().into(); + ErrorHandler::error_handle( + format!("Invalid arg of option: {}", op), + ExitStatus::InvalidCmdSyntax, + ); + } + idx += 1; + let value = args[idx].clone(); + options.insert(option, value); + } + } + idx += 1; + } + + UserCommand { username, options } + } +} + +/// passwd命令解析器 +pub struct PasswdParser; + +impl PasswdParser { + /// **解析passwd命令** + /// + /// ## 参数 + /// - `args`: passwd命令参数 + /// + /// ## 返回 + /// - `PasswdCommand`: passwd命令 + pub fn parse(args: Vec) -> PasswdCommand { + let mut username = None; + if args.len() > 1 { + username = Some(args.last().unwrap().clone()); + } + PasswdCommand { username } + } +} + +/// 组命令(groupadd/groupdel/groupmod)解析器 +pub struct GroupParser; + +impl GroupParser { + /// **解析组命令** + /// + /// ## 参数 + /// - `args`: 组命令参数 + /// + /// ## 返回 + /// - `GroupCommand`: 组命令 + pub fn parse(args: Vec) -> GroupCommand { + let groupname = args.last().unwrap().clone(); + let args = &args[1..args.len() - 1]; + let mut options = HashMap::new(); + + let mut idx = 0; + loop { + if idx >= args.len() { + break; + } + let option: CmdOption = args[idx].clone().into(); + match option { + CmdOption::Invalid => invalid_handle(), + _ => { + if idx + 1 >= args.len() { + let op: &str = option.clone().into(); + ErrorHandler::error_handle( + format!("Invalid arg of option: {}", op), + ExitStatus::InvalidCmdSyntax, + ); + } + idx += 1; + let value = args[idx].clone(); + options.insert(option, value); + } + } + idx += 1; + } + + GroupCommand { groupname, options } + } +} + +#[inline] +fn invalid_handle() { + ErrorHandler::error_handle("Invalid option".to_string(), ExitStatus::InvalidCmdSyntax); +} diff --git a/user/dadk/config/held-0.1.0.dadk b/user/dadk/config/held-0.1.0.dadk index eb9124a5e..60bb04010 100644 --- a/user/dadk/config/held-0.1.0.dadk +++ b/user/dadk/config/held-0.1.0.dadk @@ -6,7 +6,7 @@ "BuildFromSource": { "Git": { "url" : "https://git.mirrors.dragonos.org.cn/DragonOS-Community/Held.git", - "revision": "76304e995f" + "revision": "f192df4" } } }, diff --git a/user/dadk/config/libgcc.dadk b/user/dadk/config/libgcc.dadk new file mode 100644 index 000000000..a350285cd --- /dev/null +++ b/user/dadk/config/libgcc.dadk @@ -0,0 +1,24 @@ +{ + "name": "libgcc", + "version": "0.1.0", + "description": "gcc lib", + "rust_target": null, + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/libgcc" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/" + }, + "clean": { + "clean_command": "cd build & make clean" + }, + "envs": [] +} \ No newline at end of file diff --git a/user/dadk/config/test_cred-0.1.0.dadk b/user/dadk/config/test_cred-0.1.0.dadk new file mode 100644 index 000000000..8efce9aab --- /dev/null +++ b/user/dadk/config/test_cred-0.1.0.dadk @@ -0,0 +1,23 @@ +{ + "name": "test_cred", + "version": "0.1.0", + "description": "测试cred", + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test_cred" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "clean": { + "clean_command": "make clean" + }, + "install": { + "in_dragonos_path": "/bin" + }, + "target_arch": ["x86_64"] +} diff --git a/user/dadk/config/test_eventfd_0_1_0.dadk b/user/dadk/config/test_eventfd_0_1_0.dadk new file mode 100644 index 000000000..ddd8f1f5e --- /dev/null +++ b/user/dadk/config/test_eventfd_0_1_0.dadk @@ -0,0 +1,23 @@ +{ + "name": "test_eventfd", + "version": "0.1.0", + "description": "test_eventfd", + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test_eventfd" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/bin" + }, + "clean": { + "clean_command": "make clean" + }, + "target_arch": ["x86_64"] +} \ No newline at end of file diff --git a/user/dadk/config/test_glibc-0.1.0.dadk b/user/dadk/config/test_glibc-0.1.0.dadk index 58be8fff3..ee9f94dcf 100644 --- a/user/dadk/config/test_glibc-0.1.0.dadk +++ b/user/dadk/config/test_glibc-0.1.0.dadk @@ -14,7 +14,7 @@ "build_command": "make install" }, "install": { - "in_dragonos_path": "/bin" + "in_dragonos_path": "/" }, "clean": { "clean_command": "make clean" diff --git a/user/dadk/config/test_lo_0_1_0.dadk b/user/dadk/config/test_lo_0_1_0.dadk new file mode 100644 index 000000000..2eed4571c --- /dev/null +++ b/user/dadk/config/test_lo_0_1_0.dadk @@ -0,0 +1,27 @@ +{ + "name": "test_lo", + "version": "0.1.0", + "description": "test for lo interface", + "rust_target": null, + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test_lo" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/" + }, + "clean": { + "clean_command": "make clean" + }, + "envs": [], + "build_once": false, + "install_once": false, + "target_arch": ["x86_64"] +} \ No newline at end of file diff --git a/user/dadk/config/test_tokio-0.1.0.dadk b/user/dadk/config/test_tokio-0.1.0.dadk new file mode 100644 index 000000000..ea7c2e443 --- /dev/null +++ b/user/dadk/config/test_tokio-0.1.0.dadk @@ -0,0 +1,23 @@ +{ + "name": "test_tokio", + "version": "0.1.0", + "description": "测试tokio", + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test_tokio" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "clean": { + "clean_command": "make clean" + }, + "install": { + "in_dragonos_path": "/" + }, + "target_arch": ["x86_64"] +} diff --git a/user/dadk/config/test_utimensat_0_1_0.dadk b/user/dadk/config/test_utimensat_0_1_0.dadk new file mode 100644 index 000000000..098a36701 --- /dev/null +++ b/user/dadk/config/test_utimensat_0_1_0.dadk @@ -0,0 +1,23 @@ +{ + "name": "test_utimensat", + "version": "0.1.0", + "description": "test_utimensat", + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/test_utimensat" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/bin" + }, + "clean": { + "clean_command": "make clean" + }, + "target_arch": ["x86_64"] +} \ No newline at end of file diff --git a/user/dadk/config/user_manage-0.1.0.dadk b/user/dadk/config/user_manage-0.1.0.dadk new file mode 100644 index 000000000..03892a482 --- /dev/null +++ b/user/dadk/config/user_manage-0.1.0.dadk @@ -0,0 +1,24 @@ +{ + "name": "user_manage_tool", + "version": "0.1.0", + "description": "用户管理工具", + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/user-manage" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/" + }, + "clean": { + "clean_command": "make clean" + }, + "envs": [], + "target_arch": ["x86_64"] +} \ No newline at end of file diff --git a/user/sysconfig/etc/group b/user/sysconfig/etc/group new file mode 100644 index 000000000..ce813f1aa --- /dev/null +++ b/user/sysconfig/etc/group @@ -0,0 +1 @@ +root::0: \ No newline at end of file diff --git a/user/sysconfig/etc/gshadow b/user/sysconfig/etc/gshadow new file mode 100644 index 000000000..653b2e409 --- /dev/null +++ b/user/sysconfig/etc/gshadow @@ -0,0 +1 @@ +root::: \ No newline at end of file diff --git a/user/sysconfig/etc/passwd b/user/sysconfig/etc/passwd new file mode 100644 index 000000000..3891904b9 --- /dev/null +++ b/user/sysconfig/etc/passwd @@ -0,0 +1 @@ +root::0:0:root:/root:/bin/NovaShell \ No newline at end of file diff --git a/user/sysconfig/etc/shadow b/user/sysconfig/etc/shadow new file mode 100644 index 000000000..9c6022a5a --- /dev/null +++ b/user/sysconfig/etc/shadow @@ -0,0 +1 @@ +root:::0:99999:7::: \ No newline at end of file diff --git a/user/sysconfig/home/reach/system/shell.service b/user/sysconfig/home/reach/system/shell.service new file mode 100644 index 000000000..75bd9a405 --- /dev/null +++ b/user/sysconfig/home/reach/system/shell.service @@ -0,0 +1,8 @@ +[Unit] +Description=Shell + +[Service] +Type=simple +ExecStart=/bin/NovaShell +Restart=always +ExecStartPre=-/bin/about.elf From 9ee2f643235a88c747ff064c5f7e50d5085ea272 Mon Sep 17 00:00:00 2001 From: Chiichen Date: Thu, 10 Oct 2024 21:59:26 +0800 Subject: [PATCH 45/60] =?UTF-8?q?Revert=20"feat:=20=E5=88=9D=E6=AD=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8A=A8=E6=80=81=E9=93=BE=E6=8E=A5=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E8=BF=90=E8=A1=8C=20(#908)"=20(#966)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit c966d612d2af506ae5ae29cf90411050afa2a35e. --- .github/workflows/cache-toolchain.yml | 21 +- .github/workflows/makefile.yml | 4 +- Makefile | 4 +- README.md | 76 +- README_EN.md | 80 +- build-scripts/.gitignore | 2 +- build-scripts/Makefile | 2 - build-scripts/kernel_build/src/lib.rs | 2 + docs/community/ChangeLog/V0.1.x/V0.1.10.md | 1062 ----------------- docs/community/ChangeLog/index.rst | 1 - docs/conf.py | 15 +- docs/introduction/build_system.md | 1 - docs/kernel/locking/mutex.md | 4 +- docs/kernel/locking/spinlock.md | 4 +- docs/kernel/sched/cfs.md | 30 +- docs/kernel/sched/core.md | 67 +- kernel/.cargo/config.toml | 4 - kernel/Cargo.toml | 24 +- kernel/Makefile | 10 +- kernel/crates/bitmap/src/lib.rs | 1 - kernel/crates/bitmap/src/static_bitmap.rs | 9 - kernel/crates/bitmap/src/traits.rs | 5 + kernel/crates/klog_types/src/lib.rs | 6 - kernel/crates/rust-slabmalloc/src/pages.rs | 8 +- kernel/crates/unified-init/Cargo.toml | 2 +- kernel/crates/unified-init/macros/Cargo.toml | 2 +- kernel/crates/unified-init/src/lib.rs | 2 +- kernel/crates/wait_queue_macros/Cargo.toml | 7 - kernel/crates/wait_queue_macros/src/lib.rs | 60 - kernel/env.mk | 3 - kernel/rust-toolchain.toml | 2 +- kernel/src/Makefile | 4 +- kernel/src/arch/io.rs | 1 - kernel/src/arch/riscv64/driver/of.rs | 4 +- kernel/src/arch/riscv64/init/mod.rs | 11 +- kernel/src/arch/riscv64/interrupt/handle.rs | 40 +- kernel/src/arch/riscv64/ipc/signal.rs | 13 +- kernel/src/arch/riscv64/mm/init.rs | 24 +- kernel/src/arch/riscv64/mm/mod.rs | 74 +- kernel/src/arch/riscv64/pci/pci_host_ecam.rs | 9 +- kernel/src/arch/riscv64/process/idle.rs | 8 +- kernel/src/arch/riscv64/process/mod.rs | 10 +- kernel/src/arch/riscv64/process/syscall.rs | 28 +- kernel/src/arch/riscv64/smp/mod.rs | 14 +- kernel/src/arch/riscv64/syscall/mod.rs | 4 +- kernel/src/arch/riscv64/time.rs | 9 +- kernel/src/arch/x86_64/acpi.rs | 5 +- .../src/arch/x86_64/driver/apic/apic_timer.rs | 26 +- kernel/src/arch/x86_64/driver/apic/ioapic.rs | 14 +- .../arch/x86_64/driver/apic/lapic_vector.rs | 7 +- kernel/src/arch/x86_64/driver/apic/mod.rs | 14 +- kernel/src/arch/x86_64/driver/apic/x2apic.rs | 11 +- kernel/src/arch/x86_64/driver/apic/xapic.rs | 13 +- kernel/src/arch/x86_64/driver/hpet.rs | 22 +- kernel/src/arch/x86_64/driver/rtc.rs | 4 +- kernel/src/arch/x86_64/driver/tsc.rs | 38 +- kernel/src/arch/x86_64/init/mod.rs | 13 +- kernel/src/arch/x86_64/interrupt/entry.rs | 1 - kernel/src/arch/x86_64/interrupt/ipi.rs | 10 +- kernel/src/arch/x86_64/interrupt/mod.rs | 10 +- kernel/src/arch/x86_64/interrupt/msi.rs | 1 - kernel/src/arch/x86_64/interrupt/trap.rs | 46 +- kernel/src/arch/x86_64/ipc/signal.rs | 41 +- kernel/src/arch/x86_64/kvm/mod.rs | 15 +- kernel/src/arch/x86_64/kvm/vmx/ept.rs | 4 +- kernel/src/arch/x86_64/kvm/vmx/mmu.rs | 10 +- kernel/src/arch/x86_64/kvm/vmx/vcpu.rs | 68 +- kernel/src/arch/x86_64/kvm/vmx/vmexit.rs | 31 +- .../arch/x86_64/kvm/vmx/vmx_asm_wrapper.rs | 17 +- kernel/src/arch/x86_64/mm/fault.rs | 26 +- kernel/src/arch/x86_64/mm/mod.rs | 142 +-- kernel/src/arch/x86_64/mm/pkru.rs | 4 +- kernel/src/arch/x86_64/mod.rs | 1 - kernel/src/arch/x86_64/pci/pci.rs | 42 +- kernel/src/arch/x86_64/process/idle.rs | 5 +- kernel/src/arch/x86_64/process/kthread.rs | 3 +- kernel/src/arch/x86_64/process/mod.rs | 10 +- kernel/src/arch/x86_64/process/syscall.rs | 36 +- kernel/src/arch/x86_64/process/table.rs | 1 - kernel/src/arch/x86_64/smp/mod.rs | 7 +- kernel/src/arch/x86_64/syscall/mod.rs | 7 +- .../src/arch/x86_64/x86_64-unknown-none.json | 2 +- kernel/src/debug/klog/mm.rs | 27 +- kernel/src/driver/acpi/bus.rs | 2 - kernel/src/driver/acpi/mod.rs | 10 +- kernel/src/driver/acpi/pmtmr.rs | 2 +- kernel/src/driver/acpi/sysfs.rs | 12 +- kernel/src/driver/base/block/block_device.rs | 30 +- kernel/src/driver/base/char/mod.rs | 8 +- kernel/src/driver/base/device/bus.rs | 16 +- kernel/src/driver/base/device/dd.rs | 42 +- kernel/src/driver/base/device/driver.rs | 13 +- kernel/src/driver/base/device/init.rs | 23 +- kernel/src/driver/base/device/mod.rs | 34 +- kernel/src/driver/base/kobject.rs | 6 +- .../driver/base/platform/platform_device.rs | 1 - .../driver/base/platform/platform_driver.rs | 1 - kernel/src/driver/base/platform/subsys.rs | 5 +- .../driver/block/cache/cached_block_device.rs | 5 +- kernel/src/driver/block/virtio_blk.rs | 12 +- kernel/src/driver/clocksource/acpi_pm.rs | 69 +- kernel/src/driver/clocksource/timer_riscv.rs | 42 +- kernel/src/driver/disk/ahci/ahcidisk.rs | 74 +- kernel/src/driver/disk/ahci/hba.rs | 37 +- kernel/src/driver/disk/ahci/mod.rs | 51 +- kernel/src/driver/firmware/efi/fdt.rs | 5 +- kernel/src/driver/firmware/efi/init.rs | 34 +- kernel/src/driver/firmware/efi/memmap.rs | 5 +- kernel/src/driver/firmware/efi/mod.rs | 8 +- kernel/src/driver/firmware/efi/tables.rs | 21 +- kernel/src/driver/input/ps2_dev/ps2_device.rs | 1 - .../driver/input/ps2_mouse/ps_mouse_device.rs | 10 +- .../driver/input/ps2_mouse/ps_mouse_driver.rs | 5 +- kernel/src/driver/input/serio/i8042/mod.rs | 13 +- kernel/src/driver/input/serio/serio_device.rs | 1 - kernel/src/driver/input/serio/serio_driver.rs | 1 - kernel/src/driver/input/serio/subsys.rs | 5 +- kernel/src/driver/irqchip/riscv_intc.rs | 5 +- .../src/driver/irqchip/riscv_sifive_plic.rs | 35 +- kernel/src/driver/net/dma.rs | 4 +- kernel/src/driver/net/e1000e/e1000e.rs | 18 +- kernel/src/driver/net/e1000e/e1000e_driver.rs | 4 +- kernel/src/driver/net/loopback.rs | 483 -------- kernel/src/driver/net/mod.rs | 3 +- kernel/src/driver/net/virtio_net.rs | 16 +- kernel/src/driver/open_firmware/fdt.rs | 44 +- kernel/src/driver/pci/attr.rs | 160 --- kernel/src/driver/pci/dev_id.rs | 109 -- kernel/src/driver/pci/device.rs | 219 ---- kernel/src/driver/pci/driver.rs | 84 -- kernel/src/driver/pci/ecam.rs | 30 +- kernel/src/driver/pci/mod.rs | 7 - kernel/src/driver/pci/pci.rs | 90 +- kernel/src/driver/pci/pci_irq.rs | 5 +- kernel/src/driver/pci/raw_device.rs | 194 --- kernel/src/driver/pci/root.rs | 107 +- kernel/src/driver/pci/subsys.rs | 191 --- kernel/src/driver/pci/test/mod.rs | 30 - kernel/src/driver/pci/test/pt_device.rs | 236 ---- kernel/src/driver/pci/test/pt_driver.rs | 171 --- kernel/src/driver/rtc/class.rs | 3 +- kernel/src/driver/rtc/mod.rs | 1 - kernel/src/driver/serial/mod.rs | 2 - kernel/src/driver/serial/serial8250/mod.rs | 4 +- .../serial/serial8250/serial8250_pio.rs | 2 - kernel/src/driver/tty/console.rs | 1 - kernel/src/driver/tty/tty_core.rs | 12 + kernel/src/driver/tty/tty_device.rs | 45 +- kernel/src/driver/tty/tty_driver.rs | 3 +- kernel/src/driver/tty/tty_ldisc/ntty.rs | 8 +- .../tty/virtual_terminal/virtual_console.rs | 7 +- .../fbdev/base/fbcon/framebuffer_console.rs | 3 +- .../src/driver/video/fbdev/base/fbcon/mod.rs | 13 +- kernel/src/driver/video/fbdev/base/fbmem.rs | 3 +- kernel/src/driver/video/fbdev/base/fbsysfs.rs | 5 +- kernel/src/driver/video/fbdev/base/mod.rs | 5 +- kernel/src/driver/video/fbdev/vesafb.rs | 7 +- kernel/src/driver/video/mod.rs | 10 +- kernel/src/driver/virtio/irq.rs | 3 +- kernel/src/driver/virtio/mmio.rs | 3 +- kernel/src/driver/virtio/mod.rs | 1 - kernel/src/driver/virtio/sysfs.rs | 15 +- kernel/src/driver/virtio/transport_mmio.rs | 5 +- kernel/src/driver/virtio/transport_pci.rs | 4 +- kernel/src/driver/virtio/virtio.rs | 13 +- kernel/src/driver/virtio/virtio_impl.rs | 6 +- kernel/src/exception/handle.rs | 23 +- kernel/src/exception/irqchip.rs | 10 +- kernel/src/exception/irqdomain.rs | 19 +- kernel/src/exception/manage.rs | 47 +- kernel/src/exception/msi.rs | 1 - kernel/src/exception/softirq.rs | 18 +- kernel/src/exception/sysfs.rs | 3 +- kernel/src/filesystem/devfs/mod.rs | 12 +- kernel/src/filesystem/devpts/mod.rs | 3 +- kernel/src/filesystem/eventfd.rs | 268 ----- kernel/src/filesystem/fat/bpb.rs | 26 +- kernel/src/filesystem/fat/entry.rs | 29 +- kernel/src/filesystem/fat/fs.rs | 89 +- kernel/src/filesystem/kernfs/mod.rs | 5 +- kernel/src/filesystem/mbr.rs | 9 +- kernel/src/filesystem/mod.rs | 1 - kernel/src/filesystem/procfs/kmsg.rs | 5 +- kernel/src/filesystem/procfs/mod.rs | 26 +- kernel/src/filesystem/procfs/syscall.rs | 2 + kernel/src/filesystem/ramfs/mod.rs | 3 - kernel/src/filesystem/sysfs/file.rs | 8 +- kernel/src/filesystem/sysfs/group.rs | 13 +- kernel/src/filesystem/sysfs/mod.rs | 10 +- kernel/src/filesystem/sysfs/symlink.rs | 2 +- kernel/src/filesystem/vfs/core.rs | 24 +- kernel/src/filesystem/vfs/file.rs | 151 +-- kernel/src/filesystem/vfs/mod.rs | 45 +- kernel/src/filesystem/vfs/mount.rs | 45 +- kernel/src/filesystem/vfs/open.rs | 104 +- kernel/src/filesystem/vfs/syscall.rs | 158 +-- kernel/src/filesystem/vfs/utils.rs | 9 +- kernel/src/init/init.rs | 3 +- kernel/src/init/initial_kthread.rs | 24 +- kernel/src/init/mod.rs | 1 - kernel/src/ipc/pipe.rs | 57 +- kernel/src/ipc/shm.rs | 22 +- kernel/src/ipc/signal.rs | 47 +- kernel/src/ipc/signal_types.rs | 9 +- kernel/src/ipc/syscall.rs | 33 +- kernel/src/lib.rs | 23 +- kernel/src/libs/elf.rs | 97 +- kernel/src/libs/ffi_convert.rs | 15 + kernel/src/libs/font/mod.rs | 1 - kernel/src/libs/futex/futex.rs | 5 +- kernel/src/libs/ida/src/lib.rs | 1 - .../libs}/intertrait/.gitignore | 0 .../libs}/intertrait/Cargo.toml | 2 +- .../libs}/intertrait/LICENSE-MIT | 0 .../{crates => src/libs}/intertrait/README.md | 2 +- .../libs}/intertrait/macros/Cargo.toml | 2 +- .../libs}/intertrait/macros/LICENSE-APACHE | 0 .../libs}/intertrait/macros/LICENSE-MIT | 0 .../libs}/intertrait/macros/src/args.rs | 0 .../libs}/intertrait/macros/src/gen_caster.rs | 0 .../libs}/intertrait/macros/src/item_impl.rs | 0 .../libs}/intertrait/macros/src/item_type.rs | 0 .../libs}/intertrait/macros/src/lib.rs | 0 .../libs}/intertrait/src/cast.rs | 0 .../libs}/intertrait/src/cast/cast_arc.rs | 0 .../libs}/intertrait/src/cast/cast_box.rs | 0 .../libs}/intertrait/src/cast/cast_mut.rs | 0 .../libs}/intertrait/src/cast/cast_rc.rs | 0 .../libs}/intertrait/src/cast/cast_ref.rs | 0 .../libs}/intertrait/src/hasher.rs | 0 .../libs}/intertrait/src/lib.rs | 0 .../libs}/intertrait/tests/castable_to.rs | 0 .../libs}/intertrait/tests/on-enum.rs | 0 .../libs}/intertrait/tests/on-struct.rs | 0 .../tests/on-trait-impl-assoc-type1.rs | 0 .../tests/on-trait-impl-assoc-type2.rs | 0 .../tests/on-trait-impl-assoc-type3.rs | 0 .../libs}/intertrait/tests/on-trait-impl.rs | 0 .../intertrait/tests/on-type-multi-traits.rs | 0 .../libs}/intertrait/tests/run.rs | 0 .../intertrait/tests/ui/duplicate-flags.rs | 0 .../tests/ui/duplicate-flags.stderr | 0 .../intertrait/tests/ui/on-generic-type.rs | 0 .../tests/ui/on-generic-type.stderr | 0 .../libs}/intertrait/tests/ui/on-type-impl.rs | 0 .../intertrait/tests/ui/on-type-impl.stderr | 0 .../libs}/intertrait/tests/ui/unknown-flag.rs | 0 .../intertrait/tests/ui/unknown-flag.stderr | 0 kernel/src/libs/keyboard_parser.rs | 28 +- kernel/src/libs/lib_ui/screen_manager.rs | 1 - kernel/src/libs/lib_ui/textui.rs | 14 +- kernel/src/libs/lib_ui/textui_no_alloc.rs | 2 +- kernel/src/libs/mod.rs | 2 +- kernel/src/libs/name.rs | 13 - kernel/src/libs/notifier.rs | 8 +- kernel/src/libs/printk.rs | 135 +-- kernel/src/libs/rbtree.rs | 23 +- kernel/src/libs/rwlock.rs | 2 +- kernel/src/libs/semaphore.rs | 5 +- kernel/src/libs/wait_queue.rs | 39 +- kernel/src/misc/ksysfs.rs | 3 +- kernel/src/mm/allocator/buddy.rs | 40 +- kernel/src/mm/allocator/kernel_allocator.rs | 1 - kernel/src/mm/allocator/page_frame.rs | 5 +- kernel/src/mm/allocator/slab.rs | 5 +- kernel/src/mm/c_adapter.rs | 16 +- kernel/src/mm/early_ioremap.rs | 8 +- kernel/src/mm/fault.rs | 550 ++------- kernel/src/mm/init.rs | 12 +- kernel/src/mm/kernel_mapper.rs | 6 +- kernel/src/mm/madvise.rs | 2 +- kernel/src/mm/memblock.rs | 5 +- kernel/src/mm/mmio_buddy.rs | 57 +- kernel/src/mm/mod.rs | 83 +- kernel/src/mm/no_init.rs | 8 +- kernel/src/mm/page.rs | 430 +------ kernel/src/mm/syscall.rs | 173 +-- kernel/src/mm/ucontext.rs | 428 ++----- kernel/src/net/event_poll/mod.rs | 13 +- kernel/src/net/net_core.rs | 59 +- kernel/src/net/socket/inet.rs | 397 +++--- kernel/src/net/socket/mod.rs | 235 ++-- kernel/src/net/socket/unix.rs | 17 +- kernel/src/net/syscall.rs | 10 +- kernel/src/process/abi.rs | 4 +- kernel/src/process/cred.rs | 170 --- kernel/src/process/exec.rs | 53 +- kernel/src/process/exit.rs | 12 +- kernel/src/process/fork.rs | 4 +- kernel/src/process/kthread.rs | 16 +- kernel/src/process/mod.rs | 81 +- kernel/src/process/syscall.rs | 144 +-- kernel/src/process/timer.rs | 2 +- kernel/src/sched/completion.rs | 4 +- kernel/src/sched/fair.rs | 32 +- kernel/src/sched/mod.rs | 33 +- kernel/src/smp/cpu/mod.rs | 9 +- kernel/src/smp/init.rs | 4 +- kernel/src/syscall/misc.rs | 3 +- kernel/src/syscall/mod.rs | 104 +- kernel/src/syscall/user_access.rs | 19 +- kernel/src/time/clocksource.rs | 184 ++- kernel/src/time/jiffies.rs | 31 +- kernel/src/time/mod.rs | 13 +- kernel/src/time/syscall.rs | 3 +- kernel/src/time/tick_common.rs | 25 - kernel/src/time/timekeep.rs | 30 +- kernel/src/time/timekeeping.rs | 314 ++--- kernel/src/time/timer.rs | 34 +- kernel/src/virt/kvm/host_mem.rs | 20 +- kernel/src/virt/kvm/kvm_dev.rs | 9 +- kernel/src/virt/kvm/mod.rs | 17 +- kernel/src/virt/kvm/vcpu.rs | 1 - kernel/src/virt/kvm/vcpu_dev.rs | 18 +- kernel/src/virt/kvm/vm.rs | 9 +- kernel/src/virt/kvm/vm_dev.rs | 15 +- tools/Makefile | 2 +- tools/bootstrap.sh | 29 +- tools/change_rust_src.sh | 31 +- .../logmonitor/src/backend/monitor/mm.rs | 2 +- tools/grub_auto_install.sh | 21 +- triagebot.toml | 29 +- user/apps/about/about.c | 2 +- user/apps/clear/Makefile | 2 +- user/apps/http_server/main.c | 2 - user/apps/libgcc/Makefile | 2 - user/apps/libgcc/lib/libgcc_s.so.1 | Bin 732952 -> 0 bytes user/apps/test-backlog/Makefile | 2 +- user/apps/test-blockcache/Makefile | 2 +- user/apps/test-for-robustfutex/Makefile | 2 +- user/apps/test-mount/Makefile | 2 +- user/apps/test_alarm/Makefile | 2 +- user/apps/test_cred/.gitignore | 1 - user/apps/test_cred/Makefile | 20 - user/apps/test_cred/main.c | 42 - user/apps/test_eventfd/.gitignore | 1 - user/apps/test_eventfd/Makefile | 20 - user/apps/test_eventfd/main.c | 52 - user/apps/test_lo/.gitignore | 3 - user/apps/test_lo/Cargo.toml | 7 - user/apps/test_lo/Makefile | 56 - user/apps/test_lo/README.md | 7 - user/apps/test_lo/src/main.rs | 25 - user/apps/test_socket/Makefile | 2 +- user/apps/test_statx/Makefile | 2 +- user/apps/test_tokio/.gitignore | 3 - user/apps/test_tokio/Cargo.toml | 13 - user/apps/test_tokio/Makefile | 56 - user/apps/test_tokio/src/main.rs | 17 - user/apps/test_utimensat/.gitignore | 1 - user/apps/test_utimensat/Makefile | 20 - user/apps/test_utimensat/main.c | 12 - user/apps/user-manage/.gitignore | 3 - user/apps/user-manage/Cargo.toml | 36 - user/apps/user-manage/Makefile | 47 - user/apps/user-manage/README.md | 142 --- user/apps/user-manage/src/check/check.rs | 908 -------------- user/apps/user-manage/src/check/info.rs | 95 -- user/apps/user-manage/src/check/mod.rs | 3 - user/apps/user-manage/src/cmd/groupadd.rs | 45 - user/apps/user-manage/src/cmd/groupdel.rs | 45 - user/apps/user-manage/src/cmd/groupmod.rs | 46 - user/apps/user-manage/src/cmd/mod.rs | 7 - user/apps/user-manage/src/cmd/passwd.rs | 25 - user/apps/user-manage/src/cmd/useradd.rs | 44 - user/apps/user-manage/src/cmd/userdel.rs | 44 - user/apps/user-manage/src/cmd/usermod.rs | 46 - user/apps/user-manage/src/error/error.rs | 33 - user/apps/user-manage/src/error/mod.rs | 2 - .../apps/user-manage/src/executor/executor.rs | 729 ----------- user/apps/user-manage/src/executor/mod.rs | 2 - user/apps/user-manage/src/lib.rs | 5 - user/apps/user-manage/src/parser/cmd.rs | 96 -- user/apps/user-manage/src/parser/mod.rs | 3 - user/apps/user-manage/src/parser/parser.rs | 137 --- user/dadk/config/held-0.1.0.dadk | 2 +- user/dadk/config/libgcc.dadk | 24 - user/dadk/config/test_cred-0.1.0.dadk | 23 - user/dadk/config/test_eventfd_0_1_0.dadk | 23 - user/dadk/config/test_glibc-0.1.0.dadk | 2 +- user/dadk/config/test_lo_0_1_0.dadk | 27 - user/dadk/config/test_tokio-0.1.0.dadk | 23 - user/dadk/config/test_utimensat_0_1_0.dadk | 23 - user/dadk/config/user_manage-0.1.0.dadk | 24 - user/sysconfig/etc/group | 1 - user/sysconfig/etc/gshadow | 1 - user/sysconfig/etc/passwd | 1 - user/sysconfig/etc/shadow | 1 - .../sysconfig/home/reach/system/shell.service | 8 - 389 files changed, 2767 insertions(+), 11755 deletions(-) delete mode 100644 docs/community/ChangeLog/V0.1.x/V0.1.10.md delete mode 100644 kernel/crates/wait_queue_macros/Cargo.toml delete mode 100644 kernel/crates/wait_queue_macros/src/lib.rs delete mode 100644 kernel/src/driver/net/loopback.rs delete mode 100644 kernel/src/driver/pci/attr.rs delete mode 100644 kernel/src/driver/pci/dev_id.rs delete mode 100644 kernel/src/driver/pci/device.rs delete mode 100644 kernel/src/driver/pci/driver.rs delete mode 100644 kernel/src/driver/pci/raw_device.rs delete mode 100644 kernel/src/driver/pci/subsys.rs delete mode 100644 kernel/src/driver/pci/test/mod.rs delete mode 100644 kernel/src/driver/pci/test/pt_device.rs delete mode 100644 kernel/src/driver/pci/test/pt_driver.rs delete mode 100644 kernel/src/filesystem/eventfd.rs create mode 100644 kernel/src/libs/ffi_convert.rs rename kernel/{crates => src/libs}/intertrait/.gitignore (100%) rename kernel/{crates => src/libs}/intertrait/Cargo.toml (97%) rename kernel/{crates => src/libs}/intertrait/LICENSE-MIT (100%) rename kernel/{crates => src/libs}/intertrait/README.md (99%) rename kernel/{crates => src/libs}/intertrait/macros/Cargo.toml (97%) rename kernel/{crates => src/libs}/intertrait/macros/LICENSE-APACHE (100%) rename kernel/{crates => src/libs}/intertrait/macros/LICENSE-MIT (100%) rename kernel/{crates => src/libs}/intertrait/macros/src/args.rs (100%) rename kernel/{crates => src/libs}/intertrait/macros/src/gen_caster.rs (100%) rename kernel/{crates => src/libs}/intertrait/macros/src/item_impl.rs (100%) rename kernel/{crates => src/libs}/intertrait/macros/src/item_type.rs (100%) rename kernel/{crates => src/libs}/intertrait/macros/src/lib.rs (100%) rename kernel/{crates => src/libs}/intertrait/src/cast.rs (100%) rename kernel/{crates => src/libs}/intertrait/src/cast/cast_arc.rs (100%) rename kernel/{crates => src/libs}/intertrait/src/cast/cast_box.rs (100%) rename kernel/{crates => src/libs}/intertrait/src/cast/cast_mut.rs (100%) rename kernel/{crates => src/libs}/intertrait/src/cast/cast_rc.rs (100%) rename kernel/{crates => src/libs}/intertrait/src/cast/cast_ref.rs (100%) rename kernel/{crates => src/libs}/intertrait/src/hasher.rs (100%) rename kernel/{crates => src/libs}/intertrait/src/lib.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/castable_to.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/on-enum.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/on-struct.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/on-trait-impl-assoc-type1.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/on-trait-impl-assoc-type2.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/on-trait-impl-assoc-type3.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/on-trait-impl.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/on-type-multi-traits.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/run.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/ui/duplicate-flags.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/ui/duplicate-flags.stderr (100%) rename kernel/{crates => src/libs}/intertrait/tests/ui/on-generic-type.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/ui/on-generic-type.stderr (100%) rename kernel/{crates => src/libs}/intertrait/tests/ui/on-type-impl.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/ui/on-type-impl.stderr (100%) rename kernel/{crates => src/libs}/intertrait/tests/ui/unknown-flag.rs (100%) rename kernel/{crates => src/libs}/intertrait/tests/ui/unknown-flag.stderr (100%) delete mode 100644 kernel/src/libs/name.rs delete mode 100644 kernel/src/process/cred.rs delete mode 100644 kernel/src/time/tick_common.rs delete mode 100644 user/apps/libgcc/Makefile delete mode 100644 user/apps/libgcc/lib/libgcc_s.so.1 delete mode 100644 user/apps/test_cred/.gitignore delete mode 100644 user/apps/test_cred/Makefile delete mode 100644 user/apps/test_cred/main.c delete mode 100644 user/apps/test_eventfd/.gitignore delete mode 100644 user/apps/test_eventfd/Makefile delete mode 100644 user/apps/test_eventfd/main.c delete mode 100644 user/apps/test_lo/.gitignore delete mode 100644 user/apps/test_lo/Cargo.toml delete mode 100644 user/apps/test_lo/Makefile delete mode 100644 user/apps/test_lo/README.md delete mode 100644 user/apps/test_lo/src/main.rs delete mode 100644 user/apps/test_tokio/.gitignore delete mode 100644 user/apps/test_tokio/Cargo.toml delete mode 100644 user/apps/test_tokio/Makefile delete mode 100644 user/apps/test_tokio/src/main.rs delete mode 100644 user/apps/test_utimensat/.gitignore delete mode 100644 user/apps/test_utimensat/Makefile delete mode 100644 user/apps/test_utimensat/main.c delete mode 100644 user/apps/user-manage/.gitignore delete mode 100644 user/apps/user-manage/Cargo.toml delete mode 100644 user/apps/user-manage/Makefile delete mode 100644 user/apps/user-manage/README.md delete mode 100644 user/apps/user-manage/src/check/check.rs delete mode 100644 user/apps/user-manage/src/check/info.rs delete mode 100644 user/apps/user-manage/src/check/mod.rs delete mode 100644 user/apps/user-manage/src/cmd/groupadd.rs delete mode 100644 user/apps/user-manage/src/cmd/groupdel.rs delete mode 100644 user/apps/user-manage/src/cmd/groupmod.rs delete mode 100644 user/apps/user-manage/src/cmd/mod.rs delete mode 100644 user/apps/user-manage/src/cmd/passwd.rs delete mode 100644 user/apps/user-manage/src/cmd/useradd.rs delete mode 100644 user/apps/user-manage/src/cmd/userdel.rs delete mode 100644 user/apps/user-manage/src/cmd/usermod.rs delete mode 100644 user/apps/user-manage/src/error/error.rs delete mode 100644 user/apps/user-manage/src/error/mod.rs delete mode 100644 user/apps/user-manage/src/executor/executor.rs delete mode 100644 user/apps/user-manage/src/executor/mod.rs delete mode 100644 user/apps/user-manage/src/lib.rs delete mode 100644 user/apps/user-manage/src/parser/cmd.rs delete mode 100644 user/apps/user-manage/src/parser/mod.rs delete mode 100644 user/apps/user-manage/src/parser/parser.rs delete mode 100644 user/dadk/config/libgcc.dadk delete mode 100644 user/dadk/config/test_cred-0.1.0.dadk delete mode 100644 user/dadk/config/test_eventfd_0_1_0.dadk delete mode 100644 user/dadk/config/test_lo_0_1_0.dadk delete mode 100644 user/dadk/config/test_tokio-0.1.0.dadk delete mode 100644 user/dadk/config/test_utimensat_0_1_0.dadk delete mode 100644 user/dadk/config/user_manage-0.1.0.dadk delete mode 100644 user/sysconfig/etc/group delete mode 100644 user/sysconfig/etc/gshadow delete mode 100644 user/sysconfig/etc/passwd delete mode 100644 user/sysconfig/etc/shadow delete mode 100644 user/sysconfig/home/reach/system/shell.service diff --git a/.github/workflows/cache-toolchain.yml b/.github/workflows/cache-toolchain.yml index 94519c834..a58b700f6 100644 --- a/.github/workflows/cache-toolchain.yml +++ b/.github/workflows/cache-toolchain.yml @@ -51,17 +51,17 @@ jobs: cargo install cargo-binutils rustup toolchain install nightly-x86_64-unknown-linux-gnu - rustup toolchain install nightly-2024-07-23-x86_64-unknown-linux-gnu + rustup toolchain install nightly-2023-01-21-x86_64-unknown-linux-gnu rustup toolchain install nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup component add rust-src --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu + rustup component add rust-src --toolchain nightly-2023-01-21-x86_64-unknown-linux-gnu rustup component add rust-src --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup target add x86_64-unknown-none --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu + rustup target add x86_64-unknown-none --toolchain nightly-2023-01-21-x86_64-unknown-linux-gnu rustup target add x86_64-unknown-none --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup toolchain install nightly-2024-07-23-riscv64gc-unknown-linux-gnu --force-non-host + rustup toolchain install nightly-2023-01-21-riscv64gc-unknown-linux-gnu --force-non-host rustup toolchain install nightly-2023-08-15-riscv64gc-unknown-linux-gnu --force-non-host - rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2024-07-23-riscv64gc-unknown-linux-gnu - rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2024-07-23-riscv64gc-unknown-linux-gnu + rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2023-01-21-riscv64gc-unknown-linux-gnu + rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2023-01-21-riscv64gc-unknown-linux-gnu rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2023-08-15-riscv64gc-unknown-linux-gnu rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2023-08-15-riscv64gc-unknown-linux-gnu @@ -71,12 +71,12 @@ jobs: rustup component add rustfmt rustup component add rustfmt --toolchain nightly-x86_64-unknown-linux-gnu - rustup component add rustfmt --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu + rustup component add rustfmt --toolchain nightly-2023-01-21-x86_64-unknown-linux-gnu rustup component add rustfmt --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup component add rustfmt --toolchain nightly-2024-07-23-riscv64gc-unknown-linux-gnu + rustup component add rustfmt --toolchain nightly-2023-01-21-riscv64gc-unknown-linux-gnu rustup component add rustfmt --toolchain nightly-2023-08-15-riscv64gc-unknown-linux-gnu - rustup default nightly-2024-07-23 + rustup default nightly cargo install dadk --version 0.1.11 @@ -86,9 +86,6 @@ jobs: rustup toolchain install ${userapp_musl_toolchain} rustup component add --toolchain ${userapp_musl_toolchain} rust-src rustup target add --toolchain ${userapp_musl_toolchain} x86_64-unknown-linux-musl - - rustup target add x86_64-unknown-linux-musl --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu - rustup component add rust-src --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index c785dee80..efcad5302 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -2,9 +2,9 @@ name: Build Check on: push: - branches: [ "master", "feat-*", "fix-*"] + branches: [ "master" ] pull_request: - branches: [ "master", "feat-*", "fix-*"] + branches: [ "master" ] jobs: # ensure the toolchain is cached diff --git a/Makefile b/Makefile index 333f7c1b6..015f17e4b 100644 --- a/Makefile +++ b/Makefile @@ -156,14 +156,14 @@ log-monitor: .PHONY: update-submodules update-submodules: @echo "更新子模块" - @git submodule update --recursive --init + @git submodule update --recursive @git submodule foreach git pull origin master .PHONY: update-submodules-by-mirror update-submodules-by-mirror: @echo "从镜像更新子模块" @git config --global url."https://git.mirrors.dragonos.org.cn/DragonOS-Community/".insteadOf https://github.com/DragonOS-Community/ - @$(MAKE) update-submodules --init + @$(MAKE) update-submodules @git config --global --unset url."https://git.mirrors.dragonos.org.cn/DragonOS-Community/".insteadOf help: diff --git a/README.md b/README.md index 1378864c4..e4379736d 100644 --- a/README.md +++ b/README.md @@ -26,25 +26,25 @@   DragonOS目前在社区驱动下正在快速发展中,目前DragonOS已经实现了约1/4的Linux接口,在未来我们将提供对Linux的100%兼容性,并且提供新特性。 -## 参与开发? - -仔细阅读 [DragonOS社区介绍文档] ,能够帮助你了解社区的运作方式,以及如何参与贡献! - -- **了解开发动态、开发任务,请访问DragonOS社区论坛**: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) -- 您也可以从项目的issue里面了解相关的开发内容。 +[关于DragonOS,你想了解的都在这儿 - DragonOS](https://dragonos.org/?p=46) +## 网站 -  如果你愿意加入我们,你可以查看issue,并在issue下发表讨论、想法,或者访问DragonOS的论坛,了解开发动态、开发任务: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) +- 项目官网 **[DragonOS.org](https://dragonos.org)** -  你也可以带着你的创意与想法,和社区的小伙伴一起讨论,为DragonOS创造一些新的功能。 +- 项目文档 **[docs.DragonOS.org](https://docs.dragonos.org)** -## 网站 +- **了解开发动态、开发任务,请访问DragonOS社区论坛**: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) +- 软件镜像站 **[mirrors.DragonOS.org](https://mirrors.DragonOS.org)** +- Git镜像站 **[git.mirrors.DragonOS.org](https://git.mirrors.DragonOS.org)** +- 国内镜像站 **[mirrors.DragonOS.org.cn](https://mirrors.DragonOS.org.cn)** -- 项目官网 **[DragonOS.org](https://dragonos.org)** -- 文档:**[docs.dragonos.org](https://docs.dragonos.org)** -- 社区介绍文档: **[community.dragonos.org](https://community.dragonos.org)** +- 开发交流QQ群 **115763565** +- 代码搜索引擎 [code.DragonOS.org](http://code.dragonos.org) + +   ## 如何运行? @@ -52,20 +52,34 @@ - [构建DragonOS — DragonOS dev 文档](https://docs.dragonos.org/zh_CN/latest/introduction/build_system.html) +## 系统特性 + +  请参见文档:[系统特性](https://docs.dragonos.org/zh_CN/latest/introduction/features.html) + +## 如何加入? +  如果你愿意加入我们,你可以查看issue,并在issue下发表讨论、想法,或者访问DragonOS的论坛,了解开发动态、开发任务: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) + +  你也可以带着你的创意与想法,和社区的小伙伴一起讨论,为DragonOS创造一些新的功能。 ## 如何与社区建立联系? -请阅读[贡献者指南](https://community.dragonos.org/contributors/#%E7%A4%BE%E5%8C%BA)~ +  你可以发邮件给Maintainer: longjin,邮件地址是 [longjin@DragonOS.org](mailto:longjin@DragonOS.org) 。 + +  或者是加入我们的开发交流QQ群:**115763565** + +  对于正式问题的讨论,请在 **[https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn)** 上的对应板块,使用正式的语言发帖讨论。亦或者是在本仓库的issue下提出问题。 -- 您可以通过[社区管理团队]信息,与各委员会的成员们建立联系~ -- 同时,您可以通过[SIGs]和[WGs]页面,找到对应的社区团体负责人的联系方式~ ## 贡献者名单 [Contributors to DragonOS-Community/DragonOS · GitHub](https://github.com/DragonOS-Community/DragonOS/graphs/contributors) +## 联系我们 +社区对外联系邮箱:contact@DragonOS.org + +社区负责人邮箱:longjin@DragonOS.org ## 赞助 @@ -120,10 +134,32 @@ **我们谴责**:任何不遵守开源协议的行为。包括但不限于:剽窃该项目的代码作为你的毕业设计等学术不端行为以及商业闭源使用而不付费。 -若您发现了任何违背开源协议的使用行为,我们欢迎您发邮件到 pmc@dragonos.org 反馈!让我们共同建设诚信的开源社区。 +若您发现了任何违背开源协议的使用行为,我们欢迎您发邮件反馈!让我们共同建设诚信的开源社区。 + +## 参考资料 + +  本项目参考了以下资料,我对这些项目、书籍、文档的作者表示感谢! + +- 《一个64位操作系统的实现》田宇(人民邮电出版社) + +- 《现代操作系统 原理与实现》陈海波、夏虞斌(机械工业出版社) + +- [SimpleKernel](https://github.com/Simple-XX/SimpleKernel) + +- [osdev.org](https://wiki.osdev.org/Main_Page) + +- ACPI_6_3_final_Jan30 + +- the GNU GRUB manual + +- Intel® 64 and IA-32 Architectures Software Developer’s Manual + +- IA-PC HPET (High Precision Event Timers) Specification + +- [skiftOS]([GitHub - skiftOS/skift: 🥑 A hobby operating system built from scratch in modern C++. Featuring a reactive UI library and a strong emphasis on user experience.](https://github.com/skiftOS/skift)) + +- [GuideOS](https://github.com/Codetector1374/GuideOS) +- [redox-os](https://gitlab.redox-os.org/redox-os/redox) -[DragonOS社区介绍文档]: https://community.dragonos.org/ -[社区管理团队]: https://community.dragonos.org/governance/staff-info.html -[SIGs]: https://community.dragonos.org/sigs/ -[WGs]: https://community.dragonos.org/wgs/ +- [rcore](https://github.com/rcore-os/rCore) diff --git a/README_EN.md b/README_EN.md index b83b5bdba..8e787f861 100644 --- a/README_EN.md +++ b/README_EN.md @@ -25,37 +25,52 @@   Driven by the community, DragonOS is currently evolving rapidly. DragonOS has already implemented about 1/4 of Linux interfaces, and in the future, we will strive to provide 100% compatibility with Linux, along with new features. +[All you want to know about DragonOS is here - DragonOS](https://dragonos.org/?p=46) -## Get Involved in Development? +## Websites -Carefully read the [DragonOS Community Introduction Document] to understand how the community operates and how you can contribute! +- Home Page **[DragonOS.org](https://dragonos.org)** +- Documentation **[docs.DragonOS.org](https://docs.dragonos.org)** +- **To learn about development dynamics and development tasks, please visit DragonOS's BBS:** [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) +- Software mirror website **[mirrors.DragonOS.org](https://mirrors.DragonOS.org)** +- Git mirror website **[git.mirrors.DragonOS.org](https://git.mirrors.DragonOS.org)** +- QQ group **115763565** +- Code search engine [code.DragonOS.org](http://code.dragonos.org)  -- **To stay updated on development news and tasks, visit the DragonOS Community Forum**: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) -- You can also learn about the development progress by checking the project's issues. +## How to run? -  If you're interested in joining us, you can check out the issues and post your discussions or ideas under them, or visit the DragonOS forum to learn about development updates and tasks: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) +  The steps to run DragonOS are very simple. You can refer to the following information to run DragonOS within 15 minutes at the fastest! -  You're also welcome to bring your creative ideas and discuss them with the community members, working together to create new features for DragonOS. +- [Building DragonOS - DragonOS dev document](https://docs.dragonos.org/zh_CN/latest/introduction/build_system.html) +## DragonOS' Features -## Website +  See documentation:[Features](https://docs.dragonos.org/zh_CN/latest/introduction/features.html) -- **Project's Website**: [DragonOS.org](https://dragonos.org) -- Documentation: [docs.dragonos.org](https://docs.dragonos.org) -- Community Introduction Document: [community.dragonos.org](https://community.dragonos.org) +## How to join DragonOS ? -## How to Run? +  If you are willing to join us, you can visit DragonOS's BBS , learn about development dynamics and development tasks: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) -  Running DragonOS is quite straightforward. You can refer to the following resources and get DragonOS up and running in as little as 15 minutes! +  Or, you can also bring your ideas, discuss with community members, and create some new functions for DragonOS. -- [Building DragonOS — DragonOS Development Documentation](https://docs.dragonos.org/zh_CN/latest/introduction/build_system.html) +## How to contact the community? -## How to Connect with the Community? +  You can send an email to the project's maintainer: longjin. His email address is [longjin@DragonOS.org](mailto: longjin@DragonOS.org) . -Please read the [Contributor Guide](https://community.dragonos.org/contributors/#%E7%A4%BE%E5%8C%BA)~ +  Or join our development exchange QQ group: **115763565** -- You can establish contact with the members of various committees through the [Community Management Team] information. -- Additionally, you can find the contact information of the respective community group leaders via the [SIGs] and [WGs] pages. +  For the discussion of formal issues, we recommend that you use the official language to post on the corresponding section of **[https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn)**. Or you can post questions under the issue of this repository. + + +## List of contributors + +[Contributors to DragonOS-Community/DragonOS · GitHub](https://github.com/DragonOS-Community/DragonOS/graphs/contributors) + +## Get contact with us + +Community Contact Email: contact@DragonOS.org + +Maintainer longjin's Email:longjin@DragonOS.org ## Reward @@ -113,7 +128,30 @@ We guarantee that all sponsorship funds and items will be used for: If you find any violation of the open source license, we welcome you to send email feedback! Let's build an honest open source community together! -[DragonOS Community Introduction Document]: https://community.dragonos.org/ -[Community Management Team]: https://community.dragonos.org/governance/staff-info.html -[SIGs]: https://community.dragonos.org/sigs/ -[WGs]: https://community.dragonos.org/wgs/ +## References + +  This project refers to the following materials. I sincerely give my thanks to the authors of these projects, books and documents! + +- Implementation of a 64 bit operating system, Tian Yu (POSTS&TELECOM PRESS) + +- Principle and implementation of modern operating system, Chen Haibo, Xia Yubin (China Machine Press) + +- [SimpleKernel](https://github.com/Simple-XX/SimpleKernel) + +- [osdev.org](https://wiki.osdev.org/Main_Page) + +- ACPI_6_3_final_Jan30 + +- the GNU GRUB manual + +- Intel® 64 and IA-32 Architectures Software Developer’s Manual + +- IA-PC HPET (High Precision Event Timers) Specification + +- [skiftOS]([GitHub - skiftOS/skift: 🥑 A hobby operating system built from scratch in modern C++. Featuring a reactive UI library and a strong emphasis on user experience.](https://github.com/skiftOS/skift)) + +- [GuideOS](https://github.com/Codetector1374/GuideOS) + +- [redox-os](https://gitlab.redox-os.org/redox-os/redox) + +- [rcore](https://github.com/rcore-os/rCore) diff --git a/build-scripts/.gitignore b/build-scripts/.gitignore index a6f89c2da..1de565933 100644 --- a/build-scripts/.gitignore +++ b/build-scripts/.gitignore @@ -1 +1 @@ -/target/ \ No newline at end of file +target \ No newline at end of file diff --git a/build-scripts/Makefile b/build-scripts/Makefile index 14c1bd930..ba60f4b53 100644 --- a/build-scripts/Makefile +++ b/build-scripts/Makefile @@ -4,5 +4,3 @@ fmt: clean: @cargo clean -check: - @cargo +nightly-2024-07-23 check --workspace $(CARGO_ZBUILD) --message-format=json diff --git a/build-scripts/kernel_build/src/lib.rs b/build-scripts/kernel_build/src/lib.rs index 21844c53b..d0de77f84 100644 --- a/build-scripts/kernel_build/src/lib.rs +++ b/build-scripts/kernel_build/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(cfg_target_abi)] + #[macro_use] extern crate lazy_static; extern crate cc; diff --git a/docs/community/ChangeLog/V0.1.x/V0.1.10.md b/docs/community/ChangeLog/V0.1.x/V0.1.10.md deleted file mode 100644 index b09ef3a07..000000000 --- a/docs/community/ChangeLog/V0.1.x/V0.1.10.md +++ /dev/null @@ -1,1062 +0,0 @@ -# V0.1.10 - -:::{note} -本文作者:龙进 - -DragonOS官方论坛:[bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) - -2024年5月13日 -::: - -## 简介 - -  本次版本更新,引入了42个feature类型的PR,24个bug修复,5个文档更新,以及一些软件移植、ci相关的内容。 - -  当前版本核心看点: - -- 对调度子系统进行了重构 -- 能在riscv64下运行到hello world应用程序 -- 内存管理子系统引入了匿名页反向映射、写时拷贝以及延迟分配的特性 -- 文件系统引入了大量的新的系统接口 -- 实现了pty,并能运行简单的ssh服务端 - - -## 赞助商列表 - -- **[中国雅云](https://yacloud.net)** 雅安大数据产业园为DragonOS提供了云服务器支持。 - -## 更新内容-内核 - -- feat(fs): 实现了sys_rename (#578) -- feat(fs): 实现get_pathname (#615) -- feat(kernel): 实现uname系统调用 (#614) -- feat(fs): 添加mount系统调用 (#561) -- feat(smp): 重写SMP模块 (#633) -- feat(fs): 添加Statx系统调用 (#632) -- feat(riscv64): 添加flush tlb的ipi (#636) -- feat(fs): 实现SYS_LINK和SYS_LINKAT (#611) -- fix(fs): mkdir输出错误信息; -- fix(clippy): 修复内核的clippy检查报错 (#637) -- feat(net): 实现socketpair (#576) -- feat(process/riscv): 进程管理初始化 (#654) -- fix(time): 修复clock_gettime返回类型错误,修复小时间间隔duration返回0问题 (#664) -- fix(driver/base): 把Device trait的set_class改为设置Weak指针,以避免循环引用问题。 (#666) -- feat(textui): 支持绘制24位深和16位深显示缓冲区 (#640) -- fix(driver/tty): 修复tty设备显示在/sys目录下的bug (#668) -- feat(fs): 新加结构体POSIXSTATFS与SuperBlock用于处理statfs系统调用 (#667) -- feat(driver/rtc):实现了rtc的抽象,并且把x86的cmos rtc接入到设备驱动模型 (#674) -- fix(net): 修复udp bind的时候,对port0处理不正确的问题(#676) -- fix(fs/ramfs): 修复了ramfs中move_to未更新parent字段的bug (#673) -- feat(mm): 实现页面反向映射 (#670) -- fix(misc): 修复get_ramdom的长度错误问题() (#677) -- feat(process/riscv): riscv64: switch process (#678) -- fix(misc): 使nproc可以正确获取到cpu核心数 (#689) -- fix(time): 修复jiffy时钟过快问题,启用gettimeofday测试,修改mount测试 (#680) -- feat(driver/pty): 实现pty,附带测试程序 (#685) -- feat(process/riscv): 实现copy-thread (#696) -- feat(sched): 重写调度模块 (#679) -- fix(riscv): 把内核编译target改为riscv64gc & 获取time csr的频率 & 修正浮点保存与恢复的汇编的问题 (#699) -- feat(lock): 实现robust futex (#682) -- feat(fs): BlockCache-read cache支持 (#521) -- feat(mm): 实现SystemV共享内存 (#690) -- chore(tools): add bootstrap support for Centos/RHEL8/fedora (#713) -- feat(driver/pty): 完善pty,目前pty能够支持ssh (#708) -- fix(smp): 修复smp启动的时候,损坏0号核心的idle进程的内核栈的问题 (#711) -- feat(driver/riscv): 初始化riscv-sbi-timer (#716) -- doc: Update DragonOS description and introduction (#717) -- feat(riscv): 让riscv64能正常切换进程,并运行完所有的initcall (#721) -- feat(net): 实现tcp backlog功能 (#714) -- feat(mm): 添加slab内存分配器 (#683) -- feat(fs): 引入Umount系统调用 (#719) -- doc: Update build instructions for riscv64 architecture (#725) -- fix(fs): socket统一改用`GlobalSocketHandle`,并且修复fcntl SETFD的错误 (#730) -- feat: alarm系统调用实现 (#710) -- feat(tty): add dummy console (#735) -- fix(driver/pci): pci: 统一使用ecam root (#744) -- feat(driver/pci): pci: 添加pci root manager来管理pci root,并使得riscv能够正常扫描pci设备. (#745) -- build: 将smoltcp升级到0.11.0版本 (#740) -- fix(unified-init): 修复unified-init导致cargo check失败的问题 (#747) -- chore: Update virtio-drivers to commit 61ece509c4 and modify max_queue_size implementation (#748) -- feat(net): 实现raw socket的poll (#739) -- feat(mm): 实现缺页中断处理,支持页面延迟分配和写时拷贝,以及用户栈自动拓展 (#715) -- feat(driver): 把virtio添加到sysfs (#752) -- fix(dog): 添加CC环境变量,解决编译时找不到musl-gcc的问题 (#753) -- doc(community): add description of conventional commit standard (#754) -- feat(driver/virtio): riscv: 添加virtio-blk driver,并在riscv下能够正确挂载FAT32 (#761) -- feat(fs): add sys_dup3 (#755) -- feat(riscv): riscv下能够运行hello world用户程序 (#770) -- feat(sched): add sched_yield (#766) -- refactor(process): 调整arch_switch_to_user函数,把riscv和x86_64的共用逻辑抽取出来。 (#773) -- feat(driver/acpi_pm): Implement ACPI PM Timer (#772) -- chore: 适配dadk 0.1.11 (#777) -- fix(libs/lib_ui): fix the display errors when system initialize (#779) -- fix(riscv/process): 把riscv的调度时钟节拍率与HZ同步,并且修复切换到用户态的时候忘了在内核态关中断的bug (#780) -- fix: (riscv/timer): 修复riscv下没有更新墙上时钟以及没有处理软中断的bug (#783) -- feat(mm): add slab usage calculation (#768) -- feat(bitmap): Add bit and for AllocBitMap (#793) -- fix(mm): 修复vma映射标志错误 (#801) -- feat:(riscv/intr) 实现riscv plic驱动,能处理外部中断 (#799) -- doc(sched):调度子系统文档即cfs文档 (#807) -- fix(net): Fix TCP Unresponsiveness and Inability to Close Connections (#791) -- fix: disable mm debug log to prevent system lockup due to thingbuf issue (#808) -- feat(driver/pci): add pci bus into sysfs (#792) -- doc: Add Gentoo Linux In build_system.md (#810) - -## 更新内容-用户环境 - -### 新特性/新应用移植 - -- 添加core utils到系统 (#624) -- 移植dns查询工具dog的--tcp功能 (#652) - - -## 更新内容-CI - -- 引入triagebot对issue和PR进行分类 -- 添加clippy检测的自动化工作流 (#649) -- ci: import issue checker (#750) -- ci: update the match regex of issue checker (#784) -- ci: 添加支持gentoo系统的一键安装脚本 (#809) - -## 源码、发布版镜像下载 - -  您可以通过以下方式获得源代码: - -### 通过Git获取 - -- 您可以访问DragonOS的仓库获取源代码:[https://github.com/DragonOS-Community/DragonOS](https://github.com/DragonOS-Community/DragonOS) -- 您可以访问[https://github.com/DragonOS-Community/DragonOS/releases](https://github.com/DragonOS-Community/DragonOS/releases)下载发布版的代码。 - -### 通过DragonOS软件镜像站获取 - -  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站: - -  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。 - -- [https://mirrors.DragonOS.org.cn](https://mirrors.DragonOS.org.cn) -- [https://git.mirrors.DragonOS.org.cn](https://git.mirrors.DragonOS.org.cn) - -## 开放源代码声明 - -:::{note} -为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。 - -这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。 - -对于大部分的善意的人们而言,您不会违反我们的开源协议。 - -我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。 - -请特别注意,对于违反开源协议的,尤其是**商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责**。(这是最容易违反我们的开源协议的场景)。 - -并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。 - -您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。 - -关于协议详细内容,还敬请您请阅读项目根目录下的**LICENSE**文件。请注意,按照GPLv2协议的要求,**只有英文原版才具有法律效力**。任何翻译版本都仅供参考。 -::: - -### 开源软件使用情况 - -  DragonOS在开发的过程中,参考了Linux社区的一些设计,或者引入了他们的部分思想,亦或是受到了他们的启发。我们在这里对Linux社区以及Linux社区的贡献者们致以最衷心的感谢! - -## 当前版本的所有提交记录 - -```text -commit 9a0802fd2ddda39e96342997abbfc30bf65f1f0e -Author: donjuanplatinum <113148619+donjuanplatinum@users.noreply.github.com> -Date: Mon May 13 15:36:23 2024 +0800 - - doc: Add Gentoo Linux In build_system.md (#810) - - * 增加安装文档中的Gentoo Linux提示 - -commit 1f4877a4c512eb5ad232436128a0c52287b39aaa -Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> -Date: Mon May 13 15:27:08 2024 +0800 - - feat(driver/pci): add pci bus into sysfs (#792) - - 把pci设备加入sysfs - -commit 1df85daf8f1b4426fe09d489d815997cdf989a87 -Author: donjuanplatinum <113148619+donjuanplatinum@users.noreply.github.com> -Date: Sun May 12 22:58:59 2024 +0800 - - 添加支持gentoo系统的一键安装脚本 (#809) - -commit 352ee04918f4585ad4f8a896ca6e18b1ef7d7934 -Author: LoGin -Date: Sat May 11 18:02:13 2024 +0800 - - fix: disable mm debug log to prevent system lockup due to thingbuf issue (#808) - -commit 37cef00bb404c9cc01509c12df57548029967dc2 -Author: Samuel Dai -Date: Sat May 11 17:17:43 2024 +0800 - - fix(net): Fix TCP Unresponsiveness and Inability to Close Connections (#791) - - * fix(net): Improve stability. 为RawSocket与UdpSocket实现close时调用close方法,符合smoltcp的行为。为SocketInode实现drop,保证程序任何情况下退出时都能正确close对应socket, 释放被占用的端口。 - - * fix(net): Correct socket close behavior. - -commit b941261d943fac38d3154495e19ec99c90ebea8d -Author: GnoCiYeH -Date: Tue May 7 22:01:01 2024 +0800 - - docs(sched):调度子系统文档即cfs文档 (#807) - - * 调度子系统文档以及cfs文档 - -commit 0102d69fdd231e472d7bb3d609a41ae56a3799ee -Author: LoGin -Date: Wed May 1 21:11:32 2024 +0800 - - feat:(riscv/intr) 实现riscv plic驱动,能处理外部中断 (#799) - - * feat:(riscv/intr) 实现riscv plic驱动,能处理外部中断 - - - 实现riscv plic驱动,能处理外部中断 - - 能收到virtio-blk的中断 - - 实现fasteoi interrupt handler - -commit 17dc558977663433bd0181aa73ad131a1a265c1f -Author: MemoryShore <105195940+MemoryShore@users.noreply.github.com> -Date: Wed May 1 21:09:51 2024 +0800 - - 修复vma映射标志错误 (#801) - -commit 7db6e06354328ea7c6164723f504e8ba58d0c4a4 -Author: LoGin -Date: Tue Apr 30 18:45:01 2024 +0800 - - feat(bitmap): Add bit and for AllocBitMap (#793) - -commit 7401bec5e3c42015399a46e29c370abe7c7388b5 -Author: laokengwt <143977175+laokengwt@users.noreply.github.com> -Date: Mon Apr 29 23:03:33 2024 +0800 - - feat(mm): add slab usage calculation (#768) - - * Add slab free space calculation and add it to freeram of sysinfo - -commit bde4a334c1ff2ae27989de4f6f8b45f5154b684d -Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> -Date: Mon Apr 29 18:55:17 2024 +0800 - - 修复了未初始化时ui显示模块内存越界的问题,优化了代码结构 (#789) - -commit 0722a06a09ed52cb980a6147123453f86d0ea267 -Author: LoGin -Date: Sun Apr 28 19:40:09 2024 +0800 - - fix: (riscv/timer): 修复riscv下没有更新墙上时钟以及没有处理软中断的bug (#783) - -commit ab53b2eb75fe79167aa100e655b3589ee306f793 -Author: Chiichen -Date: Sun Apr 28 19:37:58 2024 +0800 - - ci: update the match regex of issue checker (#784) - - The previous regex can not successfully match the pattern like `feat(driver/pci)`, which has a slash in the scope - -commit 942cf26b48c8b024a6fa7867bb0c8ae39bb1ae09 -Author: LoGin -Date: Sun Apr 28 16:49:40 2024 +0800 - - fix(riscv/process): 把riscv的调度时钟节拍率与HZ同步,并且修复切换到用户态的时候忘了在内核态关中断的bug (#780) - -commit 13b057cc0fda0cf9630c98d246937b85fa01a7c9 -Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> -Date: Sun Apr 28 16:49:19 2024 +0800 - - fix(libs/lib_ui): fix the display errors when system initialize (#779) - - * 修复了系统初启动时会花屏的bug - -commit 182b778a3ca8c633b605ae7dd90a5e9f1131cc6d -Author: LoGin -Date: Sun Apr 28 13:39:51 2024 +0800 - - chore: 适配dadk 0.1.11 (#777) - - * chore: 适配dadk 0.1.11 - -commit dd8e74ef0d7f91a141bd217736bef4fe7dc6df3d -Author: Mingtao Huang <114841534+1037827920@users.noreply.github.com> -Date: Sun Apr 28 13:25:12 2024 +0800 - - feat(driver/acpi_pm): Implement ACPI PM Timer (#772) - - * feat: Implement ACPI PM Timer - -commit f75cb0f8ed754d94c3b2924519b785db3321c1d9 -Author: LoGin -Date: Sat Apr 27 15:35:24 2024 +0800 - - refactor(process): 调整arch_switch_to_user函数,把riscv和x86_64的共用逻辑抽取出来。 (#773) - - * refactor(process): Extract common logic for riscv and x86_64 in arch_switch_to_user to run_init_process - - 调整arch_switch_to_user函数,把riscv和x86_64的共用逻辑抽取出来。写成run_init_process函数,并且能够尝试运行多个不同的init程序,直到某个运行成功 - -commit 173c4567cf4fb2276ef3f4614b69da7913fc8381 -Author: zwb0x00 <163394849+zwb0x00@users.noreply.github.com> -Date: Fri Apr 26 15:33:29 2024 +0800 - - feat(sched): add sched_yield (#766) - - * 实现sched_yield系统调用 - -commit 471d65cf158c9bf741c21f5d0ab92efe7bf1c3d4 -Author: LoGin -Date: Fri Apr 26 11:59:47 2024 +0800 - - feat(riscv): riscv下能够运行hello world用户程序 (#770) - - * feat(riscv): riscv下能够运行hello world用户程序 - -commit 40348dd8d5a008ecc9eb3aab931933e4eba0e6da -Author: zwb0x00 <163394849+zwb0x00@users.noreply.github.com> -Date: Tue Apr 23 19:35:02 2024 +0800 - - feat(fs): add sys_dup3 (#755) - - * feat(fs): add sys_dup3 - -commit 3b799d13beeb80900d728937308e47f8011835e1 -Author: LoGin -Date: Tue Apr 23 19:14:41 2024 +0800 - - Create FUNDING.yml (#763) - -commit 731bc2b32d7b37298883d7a15b6dca659b436ee4 -Author: LoGin -Date: Tue Apr 23 17:19:54 2024 +0800 - - feat(virtio): riscv: 添加virtio-blk driver,并在riscv下能够正确挂载FAT32 (#761) - -commit 0c1ef30087d10035c256fed08097f5897041979d -Author: Chiichen -Date: Tue Apr 23 00:27:05 2024 +0800 - - docs(community): add description of conventional commit standard (#754) - - * docs(community): add description of conventional commit standard - - * docs: add index - -commit 70c991af204167db26ec1d9494efcff010893482 -Author: laokengwt <143977175+laokengwt@users.noreply.github.com> -Date: Mon Apr 22 17:40:03 2024 +0800 - - fix(dog): 添加CC环境变量,解决编译时找不到musl-gcc的问题 (#753) - -commit e32effb1507773d32c216d9e77b963786e275c06 -Author: LoGin -Date: Mon Apr 22 15:11:47 2024 +0800 - - feat(driver): 把virtio添加到sysfs (#752) - -commit a17651b14b86dd70655090381db4a2f710853aa1 -Author: MemoryShore <105195940+MemoryShore@users.noreply.github.com> -Date: Mon Apr 22 15:10:47 2024 +0800 - - feat(mm): 实现缺页中断处理,支持页面延迟分配和写时拷贝,以及用户栈自动拓展 (#715) - - * 实现缺页中断处理 - - * 完善页表拷贝逻辑 - - * 优化代码结构 - - * 完善缺页异常信息 - - * 修改大页映射逻辑 - - * 修正大页映射错误 - - * 添加缺页中断支持标志 - - * 实现用户栈自动拓展功能 - -commit cb02d0bbc213867ac845b7e8a0fb337f723d396a -Author: Chiichen -Date: Sun Apr 21 23:23:21 2024 +0800 - - ci: import issue checker (#750) - - * ci: supprot auto tag on pull request - - * ci: update issue checker config - - * ci: update issue checker & block merge while - -commit 93c379703e3be210799953bc0686d02f97119b39 -Author: sun5etop <146408999+sun5etop@users.noreply.github.com> -Date: Sun Apr 21 13:36:44 2024 +0800 - - feat(net): 实现raw socket的poll (#739) - - feat(net): 实现raw socket的poll - -commit b502fbf0b9c575a4c04e103d0fb708c4e383ab06 -Author: LoGin -Date: Sun Apr 21 13:30:29 2024 +0800 - - chore: Update virtio-drivers to commit 61ece509c4 and modify max_queue_size implementation (#748) - -commit d770de5d53ce9b598fb0024800a347b081f92a73 -Author: LoGin -Date: Sun Apr 21 13:12:31 2024 +0800 - - fix: 修复unified-init导致cargo check失败的问题 (#747) - -commit 881ff6f95e4addc373d815d66cb912bf721c20e6 -Author: yuyi2439 <68320855+yuyi2439@users.noreply.github.com> -Date: Sun Apr 21 11:39:00 2024 +0800 - - 将smoltcp升级到0.11.0版本 (#740) - -commit 370472f7288b568c7b80815f5b150daf4496446c -Author: LoGin -Date: Sun Apr 21 11:27:36 2024 +0800 - - pci: 添加pci root manager来管理pci root,并使得riscv能够正常扫描pci设备. (#745) - - * pci: 添加pci root manager来管理pci root. - pci: 使得riscv能够正常扫描pci设备. - - * doc: 添加注释 - -commit 2709e017d0d216d61b2caed3c7286459de7794c7 -Author: LoGin -Date: Sat Apr 20 18:31:56 2024 +0800 - - pci: 统一使用ecam root (#744) - -commit 418ad41fd84c15ed7e132e56970150ac38fc24a9 -Author: LoGin -Date: Wed Apr 17 10:03:22 2024 +0800 - - Feat(tty): add dummy console (#735) - - 使得riscv能暂时完成stdio_init(将来需要实现riscv的串口console) - -commit 1012552dea71bf04cf1d329d570c4c9ca9b2a2f8 -Author: Saga1718 <161323888+Saga1718@users.noreply.github.com> -Date: Tue Apr 16 21:37:42 2024 +0800 - - 删除无用的hid代码 (#734) - -commit fbd63a301c5648f906eeb802f10ac03518ba1264 -Author: SMALLC <121806694+SMALLC04@users.noreply.github.com> -Date: Tue Apr 16 21:34:36 2024 +0800 - - feat: alarm系统调用实现 (#710) - - * alarm系统调用实现 - -commit d623e90231ef6a31d091c3f611c0af3a83d3343b -Author: GnoCiYeH -Date: Mon Apr 15 22:01:32 2024 +0800 - - socket统一改用`GlobalSocketHandle`,并且修复fcntl SETFD的错误 (#730) - - * socket统一改用`GlobalSocketHandle`,并且修复fcntl SETFD的错误 - - --------- - - Co-authored-by: longjin - -commit 7162a8358d94c7799dd2b5300192b6a794b23d79 -Author: LoGin -Date: Mon Apr 15 13:20:46 2024 +0800 - - doc: Update build instructions for riscv64 architecture (#725) - -commit 1074eb34e784aa2adfc5b9e0d89fa4b7e6ea03ef -Author: Samuel Dai -Date: Mon Apr 15 13:02:04 2024 +0800 - - feat(filesystem): 引入Umount系统调用 (#719) - - * feat(filesystem): 引入Umount系统调用 - - * 将所有ENOSYS误用更正 - - * 修复了一个使同一个挂载点可以挂载2个文件系统的bug - - * 统一注释,增强程序稳定性,统一接口。注意:Umount时在fatfs的路径要使用大写,此受限于当前文件系统设计。 - -commit ceeb2e943ca7645609920ec7ad8bfceea2b13de6 -Author: laokengwt <143977175+laokengwt@users.noreply.github.com> -Date: Mon Apr 15 12:51:14 2024 +0800 - - feat(mm): 添加slab内存分配器 (#683) - - feat(mm): 添加slab内存分配器 - --------- - - Co-authored-by: longjin - -commit c719ddc6312acd7976e0f6fd449a94ff9abad5a6 -Author: Saga1718 <161323888+Saga1718@users.noreply.github.com> -Date: Sun Apr 14 23:51:47 2024 +0800 - - feat(net): 实现tcp backlog功能 (#714) - - * feat:实现tcp的backlog功能 - -commit 9621ab16ef27bc94f223e6254fafb9bb07d46d57 -Author: LoGin -Date: Sun Apr 14 20:39:20 2024 +0800 - - 让riscv64能正常切换进程,并运行完所有的initcall (#721) - -commit 9fab312ea9921618629924ab15c28c2d255b21c6 -Author: LoGin -Date: Fri Apr 12 15:27:44 2024 +0800 - - Update DragonOS description and introduction (#717) - -commit f049d1af01da7b92f312245ed411b22475b76065 -Author: LoGin -Date: Fri Apr 12 14:46:47 2024 +0800 - - 初始化riscv-sbi-timer (#716) - -commit 3959e94df38073fdb80b199777015f95611ba05f -Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> -Date: Wed Apr 10 19:00:32 2024 +0800 - - bugfix: 修复smp启动的时候,损坏0号核心的idle进程的内核栈的问题 (#711) - - --------- - - Co-authored-by: longjin - Co-authored-by: heyicong - -commit 9365e8017b39582eca620ba93c64f1b3c87c73d4 -Author: GnoCiYeH -Date: Wed Apr 10 19:00:12 2024 +0800 - - 完善pty,目前pty能够支持ssh (#708) - -commit 4b0170bd6bb374d0e9699a0076cc23b976ad6db7 -Author: Chiichen -Date: Wed Apr 10 18:58:54 2024 +0800 - - chore(tools): add bootstrap support for Centos/RHEL8/fedora (#713) - - Co-authored-by: kejianchi - -commit 15b94df01adc7e8931961b9b9a89db4e7c014b64 -Author: Jomo -Date: Wed Apr 10 10:58:07 2024 +0800 - - add xuzihao (#712) - -commit 6fc066ac11d2f9a3ac629d57487a6144fda1ac63 -Author: Jomo <2512364506@qq.com> -Date: Sun Apr 7 14:04:19 2024 +0800 - - 实现SystemV共享内存 (#690) - - * 实现SystemV共享内存 - - * 测试shm - - * 添加测试程序 - - * 完善细节 - - * 修正shm的时间数据错误的问题 - - * fix: devfs的metadata权限为0x777的错误 - - --------- - - Co-authored-by: longjin - -commit eb49bb993a39964f92494ec3effafed3fb9adfd8 -Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> -Date: Sun Apr 7 14:03:51 2024 +0800 - - BlockCache-read cache支持 (#521) - - 支持block cache的读缓存 - -commit 06560afa2aa4db352526f4be8b6262719b8b3eac -Author: hmt <114841534+1037827920@users.noreply.github.com> -Date: Sat Apr 6 22:26:34 2024 +0800 - - Patch feat robust futex (#682) - - * feat: 实现robust lock机制 - - * 前面更改vscode,修改回来 - - * 修改dadk的路径 - - * 提交.gitnore和.cargo,删除LICENSE,修改README - - * 修改一个warn - - * 删除.rustc_info.json - - * 删除target文件夹 - - * 恢复DragonOS的LICENSE,删除Cargo.lock - - * 将校验用户空间地址的代码写入函数内;将部分match分支用ok_or代替 - - * 修改wakeup函数获取running queue时unwrap一个None值发生panic - - * 测试程序使用syscalls库进行系统调用 - -commit 23ef2b33d1e3cfd2506eb7449a33df4ec42f11d3 -Author: LoGin -Date: Sat Apr 6 22:13:26 2024 +0800 - - riscv: 把内核编译target改为riscv64gc & 获取time csr的频率 & 修正浮点保存与恢复的汇编的问题 (#699) - - * 1. 把内核编译target改为riscv64gc - 2. fix: 修正浮点保存与恢复的汇编的问题 - - * riscv: 获取time csr的频率 - -commit f0c87a897fe813b7f06bf5a9e93c43ad9519dafd -Author: GnoCiYeH -Date: Fri Apr 5 17:54:48 2024 +0800 - - 重写调度模块 (#679) - - ## PR:重写调度模块 - --- - ### 完成的部分 - - 实现cfs调度策略 - - 搭建框架,后续功能可以迭代开发 - - 目前能跑,未测试性能 - - ### 需要后续接力的部分 - - 实现组内调度(task_group) - - 实现跨核负载均衡(pelt算法) - - 接入sysfs,实现参数动态调节(sched_stat等) - - nice值以及priority等参数的设置及调优 - -commit e8eab1ac824e1b1e638e50debb8326dfed4f05e5 -Author: LoGin -Date: Fri Apr 5 16:37:08 2024 +0800 - - riscv: copy-thread (#696) - -commit dfe53cf087ef4c7b6db63d992906b062dc63e93f -Author: GnoCiYeH -Date: Fri Apr 5 00:21:55 2024 +0800 - - 实现pty,附带测试程序 (#685) - - * 实现pty,附带测试程序 - - * fmt ** clippy - - * 将file层的锁粒度缩小,从而不使用no_preempt。更改pipe在sleep部分的bug - - * 修复拼写错误 - -commit b8ed38251dc255b0c525801b5dbf37d3b0d0d61e -Author: Donkey Kane <109840258+xiaolin2004@users.noreply.github.com> -Date: Fri Apr 5 00:06:26 2024 +0800 - - 修复jiffy时钟过快问题,启用gettimeofday测试,修改mount测试 (#680) - - 1. 把clock tick rate与hpet频率关联起来 - 2. 修复墙上时间同步错误的问题 - 3. 启用时间watch dog. - 4. 修复时间流逝速度异常 - - --------- - - Co-authored-by: longjin - -commit 9430523b465b19db4dd476e9fd3038bdc2aa0c8d -Author: yuyi2439 <68320855+yuyi2439@users.noreply.github.com> -Date: Thu Apr 4 12:41:19 2024 +0800 - - 使nproc可以正确获取到cpu核心数 (#689) - -commit 9b96c5b547c337502db7ec820312f119f95eece1 -Author: LoGin -Date: Sun Mar 31 22:53:01 2024 +0800 - - riscv64: switch process (#678) - - * riscv64: switch process - - * fixname - -commit 7d580ef99d2a52250b384afd49c7f87ab66a8c84 -Author: Val213 <112376067+val213@users.noreply.github.com> -Date: Sun Mar 31 18:01:32 2024 +0800 - - 修复get_ramdom的长度错误问题() (#677) - -commit 56cc4dbe27e132aac5c61b8bd4f4ec9a223b49ee -Author: Jomo <2512364506@qq.com> -Date: Sun Mar 31 16:33:49 2024 +0800 - - 实现页面反向映射 (#670) - - * 实现页面反向映射 - - * 完善PAGE_MANAGER初始化时机 && 封装lock函数 && 删掉过时注释 - -commit 924d64de8def99488f57dc618de763f7aca4a68b -Author: BrahmaMantra <140599389+BrahmaMantra@users.noreply.github.com> -Date: Sun Mar 31 15:19:12 2024 +0800 - - 修复了ramfs中move_to未更新parent字段的bug (#673) - - 修复了ramfs中move_to未更新parent字段的bug - - --------- - - Co-authored-by: Samuel Dai - -commit 9d9a09841ce2d650a41fed776916c0a11d52f92e -Author: sun5etop <146408999+sun5etop@users.noreply.github.com> -Date: Sun Mar 31 15:11:10 2024 +0800 - - 修复udp bind的时候,对port0处理不正确的问题(#676) - -commit da152319797436368304cbc3f85a3b9ec049134b -Author: LoGin -Date: Thu Mar 28 00:28:13 2024 +0800 - - 实现了rtc的抽象,并且把x86的cmos rtc接入到设备驱动模型 (#674) - - * 实现了rtc的抽象,并且把x86的cmos rtc接入到设备驱动模型。 - -commit 597ecc08c2444dcc8f527eb021932718b69c9cc5 -Author: TTaq <103996388+TTaq@users.noreply.github.com> -Date: Tue Mar 26 18:28:26 2024 +0800 - - 新加结构体POSIXSTATFS与SuperBlock用于处理statfs系统调用 (#667) - - * 新加结构体POSIXSTATFS与SuperBlock用于处理statfs系统调用 - -commit 0cb807346cb3c47924538585087d9fc846cf5e6f -Author: LoGin -Date: Tue Mar 26 18:26:02 2024 +0800 - - 修复tty设备显示在/sys目录下的bug (#668) - -commit 2755467c790d6510fa97cbf052ce8e91ad1372c6 -Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> -Date: Mon Mar 25 16:39:36 2024 +0800 - - 支持绘制24位深和16位深显示缓冲区 (#640) - - * 修复了初始化时显示,边界条件的一个bug - - * 解决了内存未初始前字体显示的兼容性问题 - * 支持绘制24位深和16位深显示缓冲区 - -commit 4256da7fb6ad25a3caab6f656607aaf047cb6446 -Author: LoGin -Date: Mon Mar 25 15:47:05 2024 +0800 - - 把Device trait的set_class改为设置Weak指针,以避免循环引用问题。 (#666) - -commit 5c20e05a2eb82da6dd73104fcf51d538500c2856 -Author: LoGin -Date: Mon Mar 25 13:59:00 2024 +0800 - - 修改bug report模版label (#665) - -commit 7c958c9ef0cd25eb15abb21d0d3420aac1c67c88 -Author: Val213 <112376067+val213@users.noreply.github.com> -Date: Mon Mar 25 13:04:53 2024 +0800 - - 移植dns查询工具dog的--tcp功能 (#652) - - * add dog, modify user/Makefile and user.sysconfig - - * add dog, modify user/Makefile and user.sysconfig - - * fix tty unicode - - * 修正无法正确编译dog的问题 - - --------- - - Co-authored-by: val213 - Co-authored-by: GnoCiYeH - Co-authored-by: longjin - -commit 911132c4b8ea0e9c49a4e84b9fa1db114102acbb -Author: Donkey Kane <109840258+xiaolin2004@users.noreply.github.com> -Date: Mon Mar 25 13:04:32 2024 +0800 - - 修复clock_gettime返回类型错误,修复小时间间隔duration返回0问题 (#664) - - * 修复clock_gettime返回类型错误,修正wtm初始化逻辑 - - * 修复duration在小时间间隔下为0的问题 - - * 临时修复时间流逝速度异常,在test-mount中加入运行时间检测 - -commit 401699735b5ec29768c3c0c47df6c529991f108f -Author: LoGin -Date: Sat Mar 23 16:25:56 2024 +0800 - - riscv: 进程管理初始化 (#654) - -commit 6046f77591cf23dc9cc53b68b25c0d74f94fa493 -Author: 裕依 <68320855+yuyi2439@users.noreply.github.com> -Date: Sat Mar 23 15:56:49 2024 +0800 - - Patch socketpair (#576) - - * 将sockets分成inet和unix域 - - 添加File端点 - - 添加SocketPair trait并将Socket trait中的pair相关方法移动 - - 添加对SockAddrUn的处理 - - * 精简SocketHandleItem - - * 重构socketpair相关逻辑 - - 将File端点换成Inode端点 - - 尝试使用SocketInode进行socketpair(未成功) - - - * 将SocketPair trait合并到Socket trait中,去除downcast - -commit 3660256a9ee94abc30b5b22508cbd48c44c86089 -Author: LoGin -Date: Sat Mar 23 11:51:30 2024 +0800 - - 只对x86_64进行clippy check (#651) - -commit 4e4c8c41e90989c1f732995511e0f9a77a33f650 -Author: LoGin -Date: Fri Mar 22 23:56:30 2024 +0800 - - 添加clippy检测的自动化工作流 (#649) - - * 添加clippy检测的自动化工作流 - - * fmt - - * 1 - -commit b5b571e02693d91eb6918d3b7561e088c3e7ee81 -Author: LoGin -Date: Fri Mar 22 23:26:39 2024 +0800 - - 修复内核的clippy检查报错 (#637) - - 修复内核的clippy检查报错 - --------- - - Co-authored-by: Samuel Dai <947309196@qq.com> - Co-authored-by: Donkey Kane <109840258+xiaolin2004@users.noreply.github.com> - Co-authored-by: themildwind <107623059+themildwind@users.noreply.github.com> - Co-authored-by: GnoCiYeH - Co-authored-by: MemoryShore <105195940+MemoryShore@users.noreply.github.com> - Co-authored-by: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> - Co-authored-by: sun5etop <146408999+sun5etop@users.noreply.github.com> - Co-authored-by: hmt <114841534+1037827920@users.noreply.github.com> - Co-authored-by: laokengwt <143977175+laokengwt@users.noreply.github.com> - Co-authored-by: TTaq <103996388+TTaq@users.noreply.github.com> - Co-authored-by: Jomo <2512364506@qq.com> - Co-authored-by: Samuel Dai - Co-authored-by: sspphh <112558065+sspphh@users.noreply.github.com> - -commit 4695947e1b601c83641676485571d42c692a2bbd -Author: Chenzx <109664121+schulice@users.noreply.github.com> -Date: Fri Mar 22 18:27:07 2024 +0800 - - 实现SYS_LINK和SYS_LINKAT (#611) - - * 实现do_linkat及SYS_LINK和SYS_LINKAT - - * 未在riscv上测试,添加target_arch - - * 将c字符串检查移动到vfs/syscall.rs,修改do_linkat()逻辑 - - * 修改部分注释 - -commit 70f159a3988eab656ea1d2b204fde87948526ecf -Author: LoGin -Date: Thu Mar 21 21:35:39 2024 +0800 - - riscv64: 添加flush tlb的ipi (#636) - - * riscv64: 添加flush tlb的ipi - - * update triagebot - -commit b4eb05a17f0f65668f69e7979660874ef8e01a2e -Author: TTaq <103996388+TTaq@users.noreply.github.com> -Date: Thu Mar 21 19:59:10 2024 +0800 - - Statx (#632) - - - * 实现statx及测试的应用程序 - -commit 8cb2e9b344230227fe5f3ab3ebeb2522f1c5e289 -Author: LoGin -Date: Thu Mar 21 19:19:32 2024 +0800 - - 重写SMP模块 (#633) - - * 修复cpumask的迭代器的错误。 - - * 能进系统(AP核心还没有初始化自身) - - * 初始化ap core - - * 修改percpu - - * 删除无用的cpu.c - - * riscv64编译通过 - -commit 1d37ca6d172e01a98fa6785d2b3e07fb8202a4a9 -Author: Donkey Kane <109840258+xiaolin2004@users.noreply.github.com> -Date: Wed Mar 20 15:31:20 2024 +0800 - - 添加mount系统调用 (#561) - - * Modify dadk config to switch NovaShell revision - - * finish primary build of mount(2), usable now - - * 使用read_from_cstr函数优化代码可读性 , 针对文件系统新增错误EUNSUPFS - - * small changes - - * 添加系统调用文档 - - * cargo fmt - - * Revert "small changes" - - This reverts commit e1991314ce687faa2d652479e8ef64f5bea25fa1. - - * 修复用户程序参数传入错误 - - * Revert "small changes" - - This reverts commit e1991314ce687faa2d652479e8ef64f5bea25fa1. - - * 解决合并冲突,最终提交 - - * 将dadk_config切换为相对路径以修复依赖问题 - - * Update settings.json - - * Delete user/apps/test-mount/LICENSE - - * 换用更好的c字符串读取函数,优化系统调用函数注释,修复错误处理bug,删除无用文件,修改测试程序readme - - * 修改用户程序readme - - * 代码格式化,初级版本 - - * 初级版本,未实现文件系统管理器,未支持设备挂载 - - * 为文件系统添加name方法,返回文件系统名字字符串,为挂载查询服务 - - * mount系统调用:添加统一文件系统初始化管理器 - - * null - - * 解除冲突 - - * 删除无用kdebug - -commit 1cd9bb43f0256aecf19a090dd71e4ac2b86a5e29 -Author: LoGin -Date: Tue Mar 19 21:31:02 2024 +0800 - - 添加core utils到系统 (#624) - -commit 8c6f21840f820a161d4386000aea1d79e3bc8d13 -Author: sspphh <112558065+sspphh@users.noreply.github.com> -Date: Tue Mar 19 17:01:20 2024 +0800 - - 实现uname系统调用 (#614) - - * 实现uname系统调用 - - Co-authored-by: longjin - -commit 82df0a13109e400602ddaec049d04ae230eb485b -Author: hmt <114841534+1037827920@users.noreply.github.com> -Date: Tue Mar 19 16:45:44 2024 +0800 - - fix: mkdir输出错误信息; feat: 实现get_pathname (#615) - - * fix: mkdir输出错误信息; feat: 实现get_pathname - - * fix: 将处理路径的操作放入vfs而不是在syscall/mod.rs中 - - * 调整入参类型 - - --------- - - Co-authored-by: longjin - -commit 9e481b3bfe303e0b104694da9750ae978dfeecae -Author: TTaq <103996388+TTaq@users.noreply.github.com> -Date: Mon Mar 18 14:47:59 2024 +0800 - - 实现了sys_rename (#578) - - * 基本实现了rename的系统调用 - - * 实现相对路径的mv - - * confilct resolve - - * make fmt - - * 更改校验位置, - 增加了SYS_RENAMEAT与SYS_RENAMEAT2两个系统调用,其实现与SYS_RENAME基本一致 - - * 删除了fat中的link - - * fix - - * 修改注释格式,删除管道文件判断 - - * 1 - -commit c3c73444516b7b47b6327cd66f5453133f47998d -Author: LoGin -Date: Sat Mar 16 22:28:59 2024 +0800 - - 更新triagebot配置 (#616) - - * 更新triagebot配置 - -commit 4fd916113e576a1c5d8ca9faae7a9d6b25afb9ae -Author: LoGin -Date: Sat Mar 16 18:09:32 2024 +0800 - - triagebot-add-shortcut (#612) - -commit fbc174499f5200924c732263e461c79b4a936c5b -Author: LoGin -Date: Fri Mar 15 20:06:24 2024 +0800 - - 添加triagebot文件 (#608) - - * 添加triagebot文件 - -``` \ No newline at end of file diff --git a/docs/community/ChangeLog/index.rst b/docs/community/ChangeLog/index.rst index 4c123b595..44336db67 100644 --- a/docs/community/ChangeLog/index.rst +++ b/docs/community/ChangeLog/index.rst @@ -6,7 +6,6 @@ .. toctree:: :maxdepth: 1 - V0.1.x/V0.1.10 V0.1.x/V0.1.9 V0.1.x/V0.1.8 V0.1.x/V0.1.7 diff --git a/docs/conf.py b/docs/conf.py index 6410fc765..a772f3d60 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,14 +10,15 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -import os +# import os # import sys # sys.path.insert(0, os.path.abspath('.')) + # -- Project information ----------------------------------------------------- project = 'DragonOS' -copyright = '2022-2024, DragonOS Community' +copyright = '2022-2023, DragonOS Community' author = 'longjin' # The full version, including alpha/beta/rc tags @@ -72,12 +73,4 @@ "strikethrough", "substitution", "tasklist", -] - - -# Define the canonical URL if you are using a custom domain on Read the Docs -html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") - -# Tell Jinja2 templates the build is running on Read the Docs -if os.environ.get("READTHEDOCS", "") == "True": - html_context["READTHEDOCS"] = True \ No newline at end of file +] \ No newline at end of file diff --git a/docs/introduction/build_system.md b/docs/introduction/build_system.md index f6098e912..c746e66ae 100644 --- a/docs/introduction/build_system.md +++ b/docs/introduction/build_system.md @@ -48,7 +48,6 @@ bash bootstrap.sh # 这里请不要加上sudo, 因为需要安装的开发依 一键配置脚本目前只支持以下系统: - Ubuntu/Debian/Deepin/UOS 等基于Debian的衍生版本 -- Gentoo 由于Gentoo系统的特性 当gentoo出现USE或循环依赖问题时 请根据emerge提示信息进行对应的处理 官方的依赖处理实例[GentooWiki](https://wiki.gentoo.org/wiki/Handbook:AMD64/Full/Working/zh-cn#.E5.BD.93_Portage_.E6.8A.A5.E9.94.99.E7.9A.84.E6.97.B6.E5.80.99) 欢迎您为其他的系统完善构建脚本! ::: diff --git a/docs/kernel/locking/mutex.md b/docs/kernel/locking/mutex.md index a3eb84848..9071de7b6 100644 --- a/docs/kernel/locking/mutex.md +++ b/docs/kernel/locking/mutex.md @@ -59,10 +59,10 @@ let x :Mutex>= Mutex::new(Vec::new()); g.push(2); assert!(g.as_slice() == [1, 2, 2] || g.as_slice() == [2, 2, 1]); // 在此处,Mutex是加锁的状态 - debug!("x={:?}", x); + kdebug!("x={:?}", x); } // 由于上方的变量`g`,也就是Mutex守卫的生命周期结束,自动释放了Mutex。因此,在此处,Mutex是放锁的状态 - debug!("x={:?}", x); + kdebug!("x={:?}", x); ```   对于结构体内部的变量,我们可以使用Mutex进行细粒度的加锁,也就是使用Mutex包裹需要细致加锁的成员变量,比如这样: diff --git a/docs/kernel/locking/spinlock.md b/docs/kernel/locking/spinlock.md index a9bcf7bdd..c4b90f01c 100644 --- a/docs/kernel/locking/spinlock.md +++ b/docs/kernel/locking/spinlock.md @@ -65,10 +65,10 @@ let x :SpinLock>= SpinLock::new(Vec::new()); g.push(2); assert!(g.as_slice() == [1, 2, 2] || g.as_slice() == [2, 2, 1]); // 在此处,SpinLock是加锁的状态 - debug!("x={:?}", x); + kdebug!("x={:?}", x); } // 由于上方的变量`g`,也就是SpinLock守卫的生命周期结束,自动释放了SpinLock。因此,在此处,SpinLock是放锁的状态 - debug!("x={:?}", x); + kdebug!("x={:?}", x); ```   对于结构体内部的变量,我们可以使用SpinLock进行细粒度的加锁,也就是使用SpinLock包裹需要细致加锁的成员变量,比如这样: diff --git a/docs/kernel/sched/cfs.md b/docs/kernel/sched/cfs.md index 284f11ec2..caaeb538e 100644 --- a/docs/kernel/sched/cfs.md +++ b/docs/kernel/sched/cfs.md @@ -2,27 +2,23 @@    CFS(Completely Fair Scheduler),顾名思义,完全公平调度器。CFS作为主线调度器之一,也是最典型的O(1)调度器之一 -## 结构体介绍 +## 1. CFSQueue 介绍 -- ``CompletelyFairScheduler`` -   ``CompletelyFairScheduler``实现了``Scheduler``trait,他是完全调度算法逻辑的主要实施者。 +   CFSQueue是用来存放普通进程的调度队列,每个CPU维护一个CFSQueue,主要使用Vec作为主要存储结构来实现。 -- ``FairSchedEntity`` - - **重要字段** - - ``cfs_rq``: 它指向了自己所在的完全公平调度队列。 - - ``my_cfs_rq``: 为一个``Option``变量,当该实体作为一个单独进程时,这个值为``None``,但是若这个实体为一个组,那这个变量必需为这个组内的私有调度队列。这个``cfs_rq``还可以继续往下深入,就构成了上述的树型结构。 - - ``pcb``: 它指向了当前实体对应的``PCB``,同样,若当前实体为一个组,则这个``Weak``指针不指向任何值。 +### 1.1 主要函数 +1. enqueue(): 将pcb入队列 +2. dequeue(): 将pcb从调度队列中弹出,若队列为空,则返回IDLE进程的pcb +3. sort(): 将进程按照虚拟运行时间的升序进行排列 -  ``FairSchedEntity``是完全公平调度器中最重要的结构体,他代表一个实体单位,它不止表示一个进程,它还可以是一个组或者一个用户,但是它在cfs队列中所表示的就单单是一个调度实体。这样的设计可以为上层提供更多的思路,比如上层可以把不同的进程归纳到一个调度实体从而实现组调度等功能而不需要改变调度算法。 +## 2. SchedulerCFS 介绍 -  在cfs中,整体的结构是**一棵树**,每一个调度实体作为``cfs_rq``中的一个节点,若该调度实体不是单个进程(它可能是一个进程组),则在该调度实体中还需要维护一个自己的``cfs_rq``,这样的嵌套展开后,每一个叶子节点就是一个单独的进程。需要理解这样一棵树,**在后续文档中会以这棵树为核心讲解**。 -  该结构体具体的字段意义请查阅源代码。这里提及几个重要的字段: +   CFS调度器类,主要实现了CFS调度器类的初始化以及调度功能函数。 +### 2.1 主要函数 -- ``CfsRunQueue`` -  ``CfsRunQueue``完全公平调度算法中管理``FairSchedEntity``的队列,它可以挂在总的``CpuRunQueue``下,也可以作为子节点挂在``FairSchedEntity``上,详见上文``FairSchedEntity``。 - - - **重要字段** - - ``entities``: 存储调度实体的红黑树 - - ``current``: 当前正在运行的实体 +1. sched(): 是对于Scheduler trait的sched()实现,是普通进程进行调度时的逻辑处理,该函数会返回接下来要执行的pcb,若没有符合要求的pcb,返回None +2. enqueue(): 同样是对于Scheduler trait的sched()实现,将一个pcb加入调度器的调度队列 +3. update_cpu_exec_proc_jiffies(): 更新这个cpu上,这个进程的可执行时间。 +4. timer_update_jiffies(): 时钟中断到来时,由sched的core模块中的函数,调用本函数,更新CFS进程的可执行时间 diff --git a/docs/kernel/sched/core.md b/docs/kernel/sched/core.md index 4f94dff31..760f6aae6 100644 --- a/docs/kernel/sched/core.md +++ b/docs/kernel/sched/core.md @@ -1,65 +1,14 @@ # 进程调度器相关的api -   定义了DragonOS的进程调度相关的api,是系统进行进程调度的接口。同时也抽象出了Scheduler的trait,以供具体的调度器实现。 +   定义了DragonOS的进程调度相关的api,是系统进行进程调度的接口。同时也抽象出了Scheduler的trait,以供具体的调度器实现 -## 调度器介绍 +## 1. 调度器介绍    一般来说,一个系统会同时处理多个请求,但是其资源是优先的,调度就是用来协调每个请求对资源的使用的方法。 -## 整体架构 -  整个调度子系统以**树形结构**来组织,每个CPU都会管理这样一棵树,每个CPU的``CpuRunQueue``即可以理解为树的根节点。每个``CpuRunQueue``下会管理着不同调度策略的子树,根据不同的调度策略深入到对应子树中实施调度。大体结构如下: - -- CpuRunQueue - - Cfs - - CfsRunQueue - - FairSchedEntity - - CfsRunQueue - - ...(嵌套) - - Rt - - ... - - Idle - - ... - - RR - - ... - - ... - -  基于这个结构,调度子系统能够更轻松地解耦以及添加其他调度策略。 -   - -## 重要结构 -- ``Scheduler:`` -  ``Scheduler``是各个调度算法提供给上层的接口,实现不同的调度算法,只需要向外提供这样一组接口即可。 - -- ``CpuRunQueue:`` -  ``CpuRunQueue``为总的CPU运行队列,他会根据不同的调度策略来进行调度。他作为调度子系统的根节点来组织调度。 - - **重要字段** - - ``lock``: 过程锁,因为在深入到具体调度策略后的调度过程中还会需要访问``CpuRunQueue``中的信息,在cfs中保存了``CpuRunQueue``对象,我们需要确保在整体过程上锁后,子对象中不需要二次加锁即可访问,所以过程锁比较适合这个场景,若使用对象锁,则在对应调度策略中想要访问``CpuRunQueue``中的信息时需要加锁,但是最外层已经将``CpuRunQueue``对象上锁,会导致内层永远拿不到锁。对于该字段,详见[CpuRunQueue的self_lock方法及其注释](https://code.dragonos.org.cn/xref/DragonOS/kernel/src/sched/mod.rs?r=dd8e74ef0d7f91a141bd217736bef4fe7dc6df3d#360)。 - - ``cfs``: Cfs调度器的根节点,往下伸展为一棵子树,详见完全公平调度文档。 - - ``current``: 当前在CPU上运行的进程。 - - ``idle``: 当前CPU的Idle进程。 - - -## 调度流程 -  一次有效的调度分两种情况,第一是主动调用``__schedule``或者``schedule``函数进行调度,第二是通过时钟中断,判断当前运行的任务时间是否到期。 - -- **主动调度** - - ``__schedule``和``schedule``函数: - - ``__schedule``:真正执行调度。会按照当前调度策略来选择下一个任务执行。 - - ``schedule``: ``__schedule``的上层封装,它需要该任务在内核中的所有资源释放干净才能进行调度,即判断当前进程的``preempt_count``是否为0,若不为0则会**panic**。 - - 参数:这两个函数都需要提供一个参数:``SchedMode``。用于控制此次调度的行为,可选参数主要有以下两个: - - ``SchedMode::SM_NONE``: 标志当前进程没有被抢占而是主动让出,他**不会**被再次加入队列,直到有其他进程主动唤醒它,这个标志位主要用于信号量、等待队列以及一些主动唤醒场景的实现。 - - ``SchedMode::SM_PREEMPT``:标志当前是被**抢占**运行的,他**会**再次被加入调度队列等待下次调度,通俗来说:它是被别的进程抢占了运行时间,有机会运行时他会继续执行。 - -- **时钟调度** -  时钟中断到来的时候,调度系统会进行更新,包括判断是否需要下一次调度。以下为主要的函数调用栈: - - ``LocalApicTimer::handle_irq``: 中断处理函数 - - ``ProcessManager::update_process_times``: 更新当前进程的时钟信息(统计运行时等) - - ``scheduler_tick``: 调度子系统tick入口 - - ``CompletelyFairScheduler::tick``: 以cfs为例,此为cfs调度算法的tick入口 - - ``CfsRunQueue::entity_tick``: 对所有调度实体进行tick - - ``CfsRunQueue::update_current``: 更新当前运行任务的运行时间及判断是否到期 - - ``CfsRunQueue::account_cfs_rq_runtime``: 计算当前队列的运行时间 - - ``CpuRunQueue::resched_current``: 若上一步计算的时间超时则到这一步,这里会设置进程标志为``NEED_SCHEDULE``. - - - 退出中断:退出中断时检查当前进程是否存在标志位``NEED_SCHEDULE``,若存在则调用``__schedule``进行调度。 - +### 1.1 主要函数 +1. cpu_executing(): 获取指定的cpu上正在执行的进程的pcb +2. sched_enqueue(): 将进程加入调度队列 +3. sched_init(): 初始化进程调度器模块 +4. sched_update_jiffies(): 当时钟中断到达时,更新时间片。*请注意,该函数只能被时钟中断处理程序调用* +5. sys_sched(): 让系统立即运行调度器的系统调用。*请注意,该系统调用不能由ring3的程序发起* diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml index c12d67fb7..146246b45 100644 --- a/kernel/.cargo/config.toml +++ b/kernel/.cargo/config.toml @@ -5,8 +5,4 @@ [target.'cfg(target_os = "none")'] runner = "bootimage runner" -[build] -rustflags = ["-Clink-args=-znostart-stop-gc"] -rustdocflags = ["-Clink-args=-znostart-stop-gc"] - [env] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 2e4938625..376709b3c 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dragonos_kernel" -version = "0.1.10" +version = "0.1.9" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -12,18 +12,16 @@ crate-type = ["staticlib"] [workspace] members = [ "crates/*", + "src/libs/intertrait" ] [features] -default = ["backtrace", "kvm", "fatfs", "fatfs-secure"] +default = ["backtrace", "kvm"] # 内核栈回溯 backtrace = [] # kvm kvm = [] -fatfs = [] -fatfs-secure = ["fatfs"] - # 运行时依赖项 [dependencies] @@ -37,28 +35,26 @@ bitmap = { path = "crates/bitmap" } driver_base_macros = { "path" = "crates/driver_base_macros" } # 一个no_std的hashmap、hashset elf = { version = "=0.7.2", default-features = false } -fdt = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/fdt", rev = "9862813020" } hashbrown = "=0.13.2" ida = { path = "src/libs/ida" } -intertrait = { path = "crates/intertrait" } +intertrait = { path = "src/libs/intertrait" } kdepends = { path = "crates/kdepends" } klog_types = { path = "crates/klog_types" } -linkme = "=0.3.27" +linkme = "=0.2" num = { version = "=0.4.0", default-features = false } num-derive = "=0.3" num-traits = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/num-traits.git", rev="1597c1c", default-features = false } smoltcp = { version = "=0.11.0", default-features = false, features = ["log", "alloc", "socket-raw", "socket-udp", "socket-tcp", "socket-icmp", "socket-dhcpv4", "socket-dns", "proto-ipv4", "proto-ipv6"]} system_error = { path = "crates/system_error" } -uefi = { version = "=0.26.0", features = ["alloc"] } -uefi-raw = "=0.5.0" unified-init = { path = "crates/unified-init" } virtio-drivers = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/virtio-drivers", rev = "f91c807965" } -wait_queue_macros = { path = "crates/wait_queue_macros" } +fdt = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/fdt", rev = "9862813020" } +uefi = { version = "=0.26.0", features = ["alloc"] } +uefi-raw = "=0.5.0" paste = "=1.0.14" slabmalloc = { path = "crates/rust-slabmalloc" } log = "0.4.21" -xarray = "0.1.0" -lru = "0.12.3" + # target为x86_64时,使用下面的依赖 [target.'cfg(target_arch = "x86_64")'.dependencies] @@ -89,4 +85,4 @@ debug = true # Controls whether the compiler passes `-g` # The release profile, used for `cargo build --release` [profile.release] -debug = true +debug = false diff --git a/kernel/Makefile b/kernel/Makefile index be85d3e7b..c50007c6d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -25,9 +25,9 @@ clean: .PHONY: fmt fmt: - RUSTFLAGS="$(RUSTFLAGS)" cargo fmt --all $(FMT_CHECK) + @cargo fmt --all $(FMT_CHECK) ifeq ($(ARCH), x86_64) - RUSTFLAGS="$(RUSTFLAGS)" cargo clippy --all-features + @cargo clippy --all-features endif @@ -36,12 +36,12 @@ check: ECHO # @echo "Checking kernel... ARCH=$(ARCH)" # @exit 1 ifeq ($(ARCH), x86_64) - RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-07-23 check --workspace $(CARGO_ZBUILD) --message-format=json --target ./src/$(TARGET_JSON) + @cargo +nightly-2023-08-15 check --workspace $(CARGO_ZBUILD) --message-format=json --target ./src/$(TARGET_JSON) else ifeq ($(ARCH), riscv64) - RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-07-23 check --workspace $(CARGO_ZBUILD) --message-format=json --target $(TARGET_JSON) + @cargo +nightly-2023-08-15 check --workspace $(CARGO_ZBUILD) --message-format=json --target $(TARGET_JSON) endif test: # 测试内核库 - RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-07-23 test --workspace --exclude dragonos_kernel + @cargo +nightly-2023-08-15 test --workspace --exclude dragonos_kernel diff --git a/kernel/crates/bitmap/src/lib.rs b/kernel/crates/bitmap/src/lib.rs index 4d7991316..7af67331e 100644 --- a/kernel/crates/bitmap/src/lib.rs +++ b/kernel/crates/bitmap/src/lib.rs @@ -2,7 +2,6 @@ #![feature(core_intrinsics)] #![allow(incomplete_features)] // for const generics #![feature(generic_const_exprs)] -#![allow(internal_features)] #![allow(clippy::needless_return)] #[macro_use] diff --git a/kernel/crates/bitmap/src/static_bitmap.rs b/kernel/crates/bitmap/src/static_bitmap.rs index c391a7da0..96281aafc 100644 --- a/kernel/crates/bitmap/src/static_bitmap.rs +++ b/kernel/crates/bitmap/src/static_bitmap.rs @@ -14,15 +14,6 @@ where core: BitMapCore, } -impl Default for StaticBitmap -where - [(); (N + usize::BITS as usize - 1) / (usize::BITS as usize)]:, -{ - fn default() -> Self { - Self::new() - } -} - impl StaticBitmap where [(); (N + usize::BITS as usize - 1) / (usize::BITS as usize)]:, diff --git a/kernel/crates/bitmap/src/traits.rs b/kernel/crates/bitmap/src/traits.rs index ba7cfd888..8fc3a4ca8 100644 --- a/kernel/crates/bitmap/src/traits.rs +++ b/kernel/crates/bitmap/src/traits.rs @@ -182,6 +182,11 @@ macro_rules! bitops_for { } } + #[cfg(feature = "std")] + fn to_hex(bits: &Self) -> String { + format!("{:x}", bits) + } + #[inline] fn bit_size() -> usize { <$target>::BITS as usize diff --git a/kernel/crates/klog_types/src/lib.rs b/kernel/crates/klog_types/src/lib.rs index 22db10681..a9b180d79 100644 --- a/kernel/crates/klog_types/src/lib.rs +++ b/kernel/crates/klog_types/src/lib.rs @@ -175,12 +175,6 @@ impl MMLogCycle { } } -impl Default for MMLogCycle { - fn default() -> Self { - Self::new() - } -} - impl kdepends::thingbuf::Recycle for MMLogCycle { fn new_element(&self) -> AllocatorLog { AllocatorLog::zeroed() diff --git a/kernel/crates/rust-slabmalloc/src/pages.rs b/kernel/crates/rust-slabmalloc/src/pages.rs index 6d2f65162..ba667148f 100644 --- a/kernel/crates/rust-slabmalloc/src/pages.rs +++ b/kernel/crates/rust-slabmalloc/src/pages.rs @@ -38,7 +38,7 @@ impl Bitfield for [AtomicU64] { fn initialize(&mut self, for_size: usize, capacity: usize) { // Set everything to allocated for bitmap in self.iter_mut() { - *bitmap = AtomicU64::new(u64::MAX); + *bitmap = AtomicU64::new(u64::max_value()); } // Mark actual slots as free @@ -64,7 +64,7 @@ impl Bitfield for [AtomicU64] { for (base_idx, b) in self.iter().enumerate() { let bitval = b.load(Ordering::Relaxed); - if bitval == u64::MAX { + if bitval == u64::max_value() { continue; } else { let negated = !bitval; @@ -125,7 +125,7 @@ impl Bitfield for [AtomicU64] { #[inline(always)] fn is_full(&self) -> bool { self.iter() - .filter(|&x| x.load(Ordering::Relaxed) != u64::MAX) + .filter(|&x| x.load(Ordering::Relaxed) != u64::max_value()) .count() == 0 } @@ -410,7 +410,6 @@ impl<'a, T: AllocablePage> PageList<'a, T> { } /// Removes `slab_page` from the list. - #[allow(clippy::manual_inspect)] pub(crate) fn pop<'b>(&'b mut self) -> Option<&'a mut T> { match self.head { None => None, @@ -454,7 +453,6 @@ impl<'a, P: AllocablePage + 'a> Iterator for ObjectPageIterMut<'a, P> { type Item = &'a mut P; #[inline] - #[allow(clippy::manual_inspect)] fn next(&mut self) -> Option<&'a mut P> { unsafe { self.head.resolve_mut().map(|next| { diff --git a/kernel/crates/unified-init/Cargo.toml b/kernel/crates/unified-init/Cargo.toml index d52fce584..893819976 100644 --- a/kernel/crates/unified-init/Cargo.toml +++ b/kernel/crates/unified-init/Cargo.toml @@ -10,5 +10,5 @@ path = "src/main.rs" [dependencies] unified-init-macros = { path = "macros" } -linkme = "=0.3.27" +linkme = "0.2" system_error = { path = "../system_error" } \ No newline at end of file diff --git a/kernel/crates/unified-init/macros/Cargo.toml b/kernel/crates/unified-init/macros/Cargo.toml index a6a746f9c..0fe25a1f8 100644 --- a/kernel/crates/unified-init/macros/Cargo.toml +++ b/kernel/crates/unified-init/macros/Cargo.toml @@ -16,5 +16,5 @@ uuid = { version = "0.8", features = ["v4"] } [dev-dependencies] unified-init = { path = ".." } -linkme = "=0.3.27" +linkme = "0.2" system_error = { path = "../../system_error" } diff --git a/kernel/crates/unified-init/src/lib.rs b/kernel/crates/unified-init/src/lib.rs index b4e75078d..b1dac7ef0 100644 --- a/kernel/crates/unified-init/src/lib.rs +++ b/kernel/crates/unified-init/src/lib.rs @@ -66,7 +66,7 @@ macro_rules! unified_init { ($initializer_slice:ident) => { for initializer in $initializer_slice.iter() { initializer.call().unwrap_or_else(|e| { - log::error!("Failed to call initializer {}: {:?}", initializer.name(), e); + kerror!("Failed to call initializer {}: {:?}", initializer.name(), e); }); } }; diff --git a/kernel/crates/wait_queue_macros/Cargo.toml b/kernel/crates/wait_queue_macros/Cargo.toml deleted file mode 100644 index 7a47a8a48..000000000 --- a/kernel/crates/wait_queue_macros/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "wait_queue_macros" -version = "0.1.0" -edition = "2021" -authors = ["longjin "] - -[dependencies] diff --git a/kernel/crates/wait_queue_macros/src/lib.rs b/kernel/crates/wait_queue_macros/src/lib.rs deleted file mode 100644 index 122fd27a6..000000000 --- a/kernel/crates/wait_queue_macros/src/lib.rs +++ /dev/null @@ -1,60 +0,0 @@ -#![no_std] - -/// Wait for a condition to become true. -/// -/// This macro will wait for a condition to become true. -/// -/// ## Parameters -/// -/// - `$wq`: The wait queue to wait on. -/// - `$condition`: The condition to wait for. (you can pass a function or a boolean expression) -/// - `$cmd`: The command to execute while waiting. -#[macro_export] -macro_rules! wq_wait_event_interruptible { - ($wq:expr, $condition: expr, $cmd: expr) => {{ - let mut retval = Ok(()); - if !$condition { - retval = wait_queue_macros::_wq_wait_event_interruptible!($wq, $condition, $cmd); - } - - retval - }}; -} - -#[macro_export] -#[allow(clippy::crate_in_macro_def)] -macro_rules! _wq_wait_event_interruptible { - ($wq:expr, $condition: expr, $cmd: expr) => {{ - wait_queue_macros::__wq_wait_event!($wq, $condition, true, Ok(()), { - $cmd; - crate::sched::schedule(SchedMode::SM_NONE) - }) - }}; -} - -#[macro_export] -macro_rules! __wq_wait_event( - ($wq:expr, $condition: expr, $interruptible: expr, $ret: expr, $cmd:expr) => {{ - let mut retval = $ret; - let mut exec_finish_wait = true; - loop { - let x = $wq.prepare_to_wait_event($interruptible); - if $condition { - break; - } - - if $interruptible && !x.is_ok() { - retval = x; - exec_finish_wait = false; - break; - } - - $cmd; - } - if exec_finish_wait { - $wq.finish_wait(); - } - - retval - }}; -); diff --git a/kernel/env.mk b/kernel/env.mk index 7f3f3f605..666dcc5f5 100644 --- a/kernel/env.mk +++ b/kernel/env.mk @@ -42,6 +42,3 @@ endif ifeq ($(DEBUG), DEBUG) GLOBAL_CFLAGS += -g endif - -export RUSTFLAGS := -C link-args=-znostart-stop-gc -export RUSTDOCFLAGS := -C link-args=-znostart-stop-gc \ No newline at end of file diff --git a/kernel/rust-toolchain.toml b/kernel/rust-toolchain.toml index 325731828..ddfcd41a7 100644 --- a/kernel/rust-toolchain.toml +++ b/kernel/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2024-07-23" +channel = "nightly-2023-08-15" components = ["rust-src", "clippy"] \ No newline at end of file diff --git a/kernel/src/Makefile b/kernel/src/Makefile index e9da4b024..40299bc9a 100644 --- a/kernel/src/Makefile +++ b/kernel/src/Makefile @@ -21,7 +21,7 @@ ifeq ($(ARCH), x86_64) endif endif -RUSTFLAGS += $(RUSTFLAGS_UNWIND) +RUSTFLAGS = $(RUSTFLAGS_UNWIND) CFLAGS = $(GLOBAL_CFLAGS) -fno-pie $(CFLAGS_UNWIND) -I $(shell pwd) -I $(shell pwd)/include @@ -40,7 +40,7 @@ kernel_subdirs := common driver debug syscall libs kernel_rust: - RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-07-23 $(CARGO_ZBUILD) build --release --target $(TARGET_JSON) + RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2023-08-15 $(CARGO_ZBUILD) build --release --target $(TARGET_JSON) all: kernel diff --git a/kernel/src/arch/io.rs b/kernel/src/arch/io.rs index 4b6384b6f..498f5793b 100644 --- a/kernel/src/arch/io.rs +++ b/kernel/src/arch/io.rs @@ -1,5 +1,4 @@ /// 每个架构都需要实现的IO接口 -#[allow(unused)] pub trait PortIOArch { unsafe fn in8(port: u16) -> u8; unsafe fn in16(port: u16) -> u16; diff --git a/kernel/src/arch/riscv64/driver/of.rs b/kernel/src/arch/riscv64/driver/of.rs index 4c93b91ad..2aa6adbee 100644 --- a/kernel/src/arch/riscv64/driver/of.rs +++ b/kernel/src/arch/riscv64/driver/of.rs @@ -17,7 +17,7 @@ impl OpenFirmwareFdtDriver { let offset = fdt_paddr.data() & crate::arch::MMArch::PAGE_OFFSET_MASK; let map_size = page_align_up(fdt_size + offset); let map_paddr = PhysAddr::new(fdt_paddr.data() & crate::arch::MMArch::PAGE_MASK); - // debug!( + // kdebug!( // "map_fdt paddr: {:?}, map_pa: {:?},fdt_size: {}, size: {:?}", // fdt_paddr, // map_paddr, @@ -28,7 +28,7 @@ impl OpenFirmwareFdtDriver { // drop the boot params guard in order to avoid deadlock drop(bp_guard); - // debug!("map_fdt: map fdt to {:?}, size: {}", map_paddr, map_size); + // kdebug!("map_fdt: map fdt to {:?}, size: {}", map_paddr, map_size); mmio_guard.map_phys(map_paddr, map_size)?; let mut bp_guard = boot_params().write(); let vaddr = mmio_guard.vaddr() + offset; diff --git a/kernel/src/arch/riscv64/init/mod.rs b/kernel/src/arch/riscv64/init/mod.rs index b71c077d2..166965c7e 100644 --- a/kernel/src/arch/riscv64/init/mod.rs +++ b/kernel/src/arch/riscv64/init/mod.rs @@ -1,11 +1,11 @@ use fdt::node::FdtNode; -use log::{debug, info}; use system_error::SystemError; use crate::{ arch::{driver::sbi::SbiDriver, mm::init::mm_early_init}, driver::{firmware::efi::init::efi_init, open_firmware::fdt::open_firmware_fdt_driver}, init::{boot_params, init::start_kernel}, + kdebug, kinfo, mm::{memblock::mem_block_manager, PhysAddr, VirtAddr}, print, println, smp::cpu::ProcessorId, @@ -112,12 +112,13 @@ pub fn early_setup_arch() -> Result<(), SystemError> { arch_boot_params_guard.arch.fdt_paddr = fdt_paddr; arch_boot_params_guard.arch.fdt_size = fdt.total_size(); arch_boot_params_guard.arch.boot_hartid = ProcessorId::new(hartid); - // debug!("fdt_paddr: {:?}, fdt_size: {}", fdt_paddr, fdt.total_size()); + // kdebug!("fdt_paddr: {:?}, fdt_size: {}", fdt_paddr, fdt.total_size()); drop(arch_boot_params_guard); - info!( + kinfo!( "DragonOS kernel is running on hart {}, fdt address:{:?}", - hartid, fdt_paddr + hartid, + fdt_paddr ); mm_early_init(); @@ -126,7 +127,7 @@ pub fn early_setup_arch() -> Result<(), SystemError> { unsafe { parse_dtb() }; for x in mem_block_manager().to_iter() { - debug!("before efi: {x:?}"); + kdebug!("before efi: {x:?}"); } efi_init(); diff --git a/kernel/src/arch/riscv64/interrupt/handle.rs b/kernel/src/arch/riscv64/interrupt/handle.rs index faff690f2..97f94d29f 100644 --- a/kernel/src/arch/riscv64/interrupt/handle.rs +++ b/kernel/src/arch/riscv64/interrupt/handle.rs @@ -3,10 +3,9 @@ //! 架构相关的处理逻辑参考: https://code.dragonos.org.cn/xref/linux-6.6.21/arch/riscv/kernel/traps.c use core::hint::spin_loop; -use log::error; use system_error::SystemError; -use crate::{arch::syscall::syscall_handler, driver::irqchip::riscv_intc::riscv_intc_irq}; +use crate::{arch::syscall::syscall_handler, driver::irqchip::riscv_intc::riscv_intc_irq, kerror}; use super::TrapFrame; @@ -53,7 +52,7 @@ fn riscv64_do_exception(trap_frame: &mut TrapFrame) { let handler = EXCEPTION_HANDLERS[code]; handler(trap_frame).ok(); } else { - error!("riscv64_do_irq: exception code out of range"); + kerror!("riscv64_do_irq: exception code out of range"); loop { // kernel die spin_loop(); @@ -62,7 +61,7 @@ fn riscv64_do_exception(trap_frame: &mut TrapFrame) { } fn default_handler(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - error!("riscv64_do_irq: handler not found"); + kerror!("riscv64_do_irq: handler not found"); loop { spin_loop(); } @@ -70,7 +69,7 @@ fn default_handler(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { /// 处理指令地址不对齐异常 #0 fn do_trap_insn_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - error!("riscv64_do_irq: do_trap_insn_misaligned"); + kerror!("riscv64_do_irq: do_trap_insn_misaligned"); loop { spin_loop(); } @@ -78,7 +77,7 @@ fn do_trap_insn_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemErro /// 处理指令访问异常 #1 fn do_trap_insn_access_fault(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - error!("riscv64_do_irq: do_trap_insn_access_fault"); + kerror!("riscv64_do_irq: do_trap_insn_access_fault"); loop { spin_loop(); } @@ -86,7 +85,7 @@ fn do_trap_insn_access_fault(_trap_frame: &mut TrapFrame) -> Result<(), SystemEr /// 处理非法指令异常 #2 fn do_trap_insn_illegal(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - error!("riscv64_do_irq: do_trap_insn_illegal"); + kerror!("riscv64_do_irq: do_trap_insn_illegal"); loop { spin_loop(); } @@ -94,7 +93,7 @@ fn do_trap_insn_illegal(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> /// 处理断点异常 #3 fn do_trap_break(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - error!("riscv64_do_irq: do_trap_break"); + kerror!("riscv64_do_irq: do_trap_break"); loop { spin_loop(); } @@ -102,7 +101,7 @@ fn do_trap_break(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { /// 处理加载地址不对齐异常 #4 fn do_trap_load_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - error!("riscv64_do_irq: do_trap_load_misaligned"); + kerror!("riscv64_do_irq: do_trap_load_misaligned"); loop { spin_loop(); } @@ -110,7 +109,7 @@ fn do_trap_load_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemErro /// 处理加载访问异常 #5 fn do_trap_load_access_fault(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - error!("riscv64_do_irq: do_trap_load_access_fault"); + kerror!("riscv64_do_irq: do_trap_load_access_fault"); loop { spin_loop(); } @@ -118,7 +117,7 @@ fn do_trap_load_access_fault(_trap_frame: &mut TrapFrame) -> Result<(), SystemEr /// 处理存储地址不对齐异常 #6 fn do_trap_store_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - error!("riscv64_do_irq: do_trap_store_misaligned"); + kerror!("riscv64_do_irq: do_trap_store_misaligned"); loop { spin_loop(); } @@ -126,7 +125,7 @@ fn do_trap_store_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemErr /// 处理存储访问异常 #7 fn do_trap_store_access_fault(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - error!("riscv64_do_irq: do_trap_store_access_fault"); + kerror!("riscv64_do_irq: do_trap_store_access_fault"); loop { spin_loop(); } @@ -152,9 +151,11 @@ fn do_trap_insn_page_fault(trap_frame: &mut TrapFrame) -> Result<(), SystemError let vaddr = trap_frame.badaddr; let cause = trap_frame.cause; let epc = trap_frame.epc; - error!( + kerror!( "riscv64_do_irq: do_insn_page_fault vaddr: {:#x}, cause: {:?} epc: {:#x}", - vaddr, cause, epc + vaddr, + cause, + epc ); loop { spin_loop(); @@ -166,9 +167,10 @@ fn do_trap_load_page_fault(trap_frame: &mut TrapFrame) -> Result<(), SystemError let vaddr = trap_frame.badaddr; let cause = trap_frame.cause; let epc = trap_frame.epc; - error!( + kerror!( "riscv64_do_irq: do_trap_load_page_fault: epc: {epc:#x}, vaddr={:#x}, cause={:?}", - vaddr, cause + vaddr, + cause ); loop { @@ -180,9 +182,11 @@ fn do_trap_load_page_fault(trap_frame: &mut TrapFrame) -> Result<(), SystemError /// 处理页存储错误异常 #15 fn do_trap_store_page_fault(trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - error!( + kerror!( "riscv64_do_irq: do_trap_store_page_fault: epc: {:#x}, vaddr={:#x}, cause={:?}", - trap_frame.epc, trap_frame.badaddr, trap_frame.cause + trap_frame.epc, + trap_frame.badaddr, + trap_frame.cause ); loop { spin_loop(); diff --git a/kernel/src/arch/riscv64/ipc/signal.rs b/kernel/src/arch/riscv64/ipc/signal.rs index f902e6965..3ebb66c17 100644 --- a/kernel/src/arch/riscv64/ipc/signal.rs +++ b/kernel/src/arch/riscv64/ipc/signal.rs @@ -1,8 +1,7 @@ -use log::error; - use crate::{ arch::{sched::sched, CurrentIrqArch}, exception::InterruptArch, + kerror, process::ProcessManager, }; @@ -69,7 +68,7 @@ impl From for Signal { let ret: Signal = unsafe { core::mem::transmute(value) }; return ret; } else { - error!("Try to convert an invalid number to Signal"); + kerror!("Try to convert an invalid number to Signal"); return Signal::INVALID; } } @@ -84,7 +83,7 @@ impl Into for Signal { impl From for Signal { fn from(value: i32) -> Self { if value < 0 { - error!("Try to convert an invalid number to Signal"); + kerror!("Try to convert an invalid number to Signal"); return Signal::INVALID; } else { return Self::from(value as usize); @@ -128,7 +127,7 @@ impl Signal { pub fn handle_default(&self) { match self { Signal::INVALID => { - error!("attempting to handler an Invalid"); + kerror!("attempting to handler an Invalid"); } Signal::SIGHUP => sig_terminate(self.clone()), Signal::SIGINT => sig_terminate(self.clone()), @@ -313,7 +312,7 @@ fn sig_terminate_dump(sig: Signal) { fn sig_stop(sig: Signal) { let guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_stop().unwrap_or_else(|e| { - error!( + kerror!( "sleep error :{:?},failed to sleep process :{:?}, with signal :{:?}", e, ProcessManager::current_pcb(), @@ -328,7 +327,7 @@ fn sig_stop(sig: Signal) { /// 信号默认处理函数——继续进程 fn sig_continue(sig: Signal) { ProcessManager::wakeup_stop(&ProcessManager::current_pcb()).unwrap_or_else(|_| { - error!( + kerror!( "Failed to wake up process pid = {:?} with signal :{:?}", ProcessManager::current_pcb().pid(), sig diff --git a/kernel/src/arch/riscv64/mm/init.rs b/kernel/src/arch/riscv64/mm/init.rs index f545f1098..79a5fade2 100644 --- a/kernel/src/arch/riscv64/mm/init.rs +++ b/kernel/src/arch/riscv64/mm/init.rs @@ -1,6 +1,5 @@ use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; -use log::{debug, info}; use system_error::SystemError; use crate::{ @@ -12,6 +11,7 @@ use crate::{ MMArch, }, driver::firmware::efi::efi_manager, + kdebug, kinfo, libs::lib_ui::screen_manager::scm_disable_put_to_window, mm::{ allocator::{buddy::BuddyAllocator, bump::BumpAllocator, page_frame::FrameAllocator}, @@ -56,7 +56,7 @@ unsafe fn init_kernel_addr() { KERNEL_BEGIN_VA = VirtAddr::new(boot_text_start_pa as usize); KERNEL_END_VA = VirtAddr::new(_end as usize); - debug!( + kdebug!( "init_kernel_addr: \n\tKERNEL_BEGIN_PA: {KERNEL_BEGIN_PA:?} \tKERNEL_END_PA: {KERNEL_END_PA:?} \tKERNEL_BEGIN_VA: {KERNEL_BEGIN_VA:?} @@ -78,7 +78,7 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { // 使用bump分配器,把所有的内存页都映射到页表 { - // debug!("to create new page table"); + // kdebug!("to create new page table"); // 用bump allocator创建新的页表 let mut mapper: crate::mm::page::PageMapper> = crate::mm::page::PageMapper::::create( @@ -87,7 +87,7 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { ) .expect("Failed to create page mapper"); new_page_table = mapper.table().phys(); - // debug!("PageMapper created"); + // kdebug!("PageMapper created"); // 取消最开始时候,在head.S中指定的映射(暂时不刷新TLB) { @@ -99,12 +99,12 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { .expect("Failed to empty page table entry"); } } - debug!("Successfully emptied page table"); + kdebug!("Successfully emptied page table"); let total_num = mem_block_manager().total_initial_memory_regions(); for i in 0..total_num { let area = mem_block_manager().get_initial_memory_region(i).unwrap(); - // debug!("area: base={:?}, size={:#x}, end={:?}", area.base, area.size, area.base + area.size); + // kdebug!("area: base={:?}, size={:#x}, end={:?}", area.base, area.size, area.base + area.size); for i in 0..((area.size + MMArch::PAGE_SIZE - 1) / MMArch::PAGE_SIZE) { let paddr = area.base.add(i * MMArch::PAGE_SIZE); let vaddr = unsafe { MMArch::phys_2_virt(paddr) }.unwrap(); @@ -125,7 +125,7 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { unsafe { INITIAL_PGTABLE_VALUE = new_page_table; } - debug!( + kdebug!( "After mapping all physical memory, DragonOS used: {} KB", bump_allocator.usage().used().bytes() / 1024 ); @@ -134,7 +134,7 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { let buddy_allocator = unsafe { BuddyAllocator::::new(bump_allocator).unwrap() }; // 设置全局的页帧分配器 unsafe { set_inner_allocator(buddy_allocator) }; - info!("Successfully initialized buddy allocator"); + kinfo!("Successfully initialized buddy allocator"); // 关闭显示输出 scm_disable_put_to_window(); @@ -142,7 +142,7 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { { let mut binding = INNER_ALLOCATOR.lock(); let mut allocator_guard = binding.as_mut().unwrap(); - debug!("To enable new page table."); + kdebug!("To enable new page table."); compiler_fence(Ordering::SeqCst); let mapper = crate::mm::page::PageMapper::::new( PageTableKind::Kernel, @@ -152,10 +152,10 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { compiler_fence(Ordering::SeqCst); mapper.make_current(); compiler_fence(Ordering::SeqCst); - // debug!("New page table enabled"); + // kdebug!("New page table enabled"); } - debug!("Successfully enabled new page table"); - info!("riscv mm init done"); + kdebug!("Successfully enabled new page table"); + kinfo!("riscv mm init done"); return Ok(()); } diff --git a/kernel/src/arch/riscv64/mm/mod.rs b/kernel/src/arch/riscv64/mm/mod.rs index 1970d3d6f..76bfbed04 100644 --- a/kernel/src/arch/riscv64/mm/mod.rs +++ b/kernel/src/arch/riscv64/mm/mod.rs @@ -12,9 +12,9 @@ use crate::{ page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage, PhysPageFrame}, }, kernel_mapper::KernelMapper, - page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}, + page::{PageEntry, PageFlags, PAGE_1G_SHIFT}, ucontext::UserMapper, - MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, VmFlags, + MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, }, smp::cpu::ProcessorId, }; @@ -256,74 +256,8 @@ impl MemoryManagementArch for RiscV64MMArch { ) -> bool { true } - - const PAGE_NONE: usize = Self::ENTRY_FLAG_GLOBAL | Self::ENTRY_FLAG_READONLY; - - const PAGE_READ: usize = PAGE_ENTRY_BASE | Self::ENTRY_FLAG_READONLY; - - const PAGE_WRITE: usize = - PAGE_ENTRY_BASE | Self::ENTRY_FLAG_READONLY | Self::ENTRY_FLAG_WRITEABLE; - - const PAGE_EXEC: usize = PAGE_ENTRY_BASE | Self::ENTRY_FLAG_EXEC; - - const PAGE_READ_EXEC: usize = - PAGE_ENTRY_BASE | Self::ENTRY_FLAG_READONLY | Self::ENTRY_FLAG_EXEC; - - const PAGE_WRITE_EXEC: usize = PAGE_ENTRY_BASE - | Self::ENTRY_FLAG_READONLY - | Self::ENTRY_FLAG_EXEC - | Self::ENTRY_FLAG_WRITEABLE; - - const PAGE_COPY: usize = Self::PAGE_READ; - const PAGE_COPY_EXEC: usize = Self::PAGE_READ_EXEC; - const PAGE_SHARED: usize = Self::PAGE_WRITE; - const PAGE_SHARED_EXEC: usize = Self::PAGE_WRITE_EXEC; - - const PAGE_COPY_NOEXEC: usize = 0; - const PAGE_READONLY: usize = 0; - const PAGE_READONLY_EXEC: usize = 0; - - const PROTECTION_MAP: [EntryFlags; 16] = protection_map(); -} - -const fn protection_map() -> [EntryFlags; 16] { - let mut map = [0; 16]; - map[VmFlags::VM_NONE.bits()] = MMArch::PAGE_NONE; - map[VmFlags::VM_READ.bits()] = MMArch::PAGE_READONLY; - map[VmFlags::VM_WRITE.bits()] = MMArch::PAGE_COPY; - map[VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = MMArch::PAGE_COPY; - map[VmFlags::VM_EXEC.bits()] = MMArch::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = MMArch::PAGE_READONLY_EXEC; - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = MMArch::PAGE_COPY_EXEC; - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = - MMArch::PAGE_COPY_EXEC; - map[VmFlags::VM_SHARED.bits()] = MMArch::PAGE_NONE; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_READ.bits()] = MMArch::PAGE_READONLY; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits()] = MMArch::PAGE_SHARED; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = - MMArch::PAGE_SHARED; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits()] = MMArch::PAGE_READONLY_EXEC; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = - MMArch::PAGE_READONLY_EXEC; - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = - MMArch::PAGE_SHARED_EXEC; - map[VmFlags::VM_SHARED.bits() - | VmFlags::VM_EXEC.bits() - | VmFlags::VM_WRITE.bits() - | VmFlags::VM_READ.bits()] = MMArch::PAGE_SHARED_EXEC; - let mut ret = [unsafe { EntryFlags::from_data(0) }; 16]; - let mut index = 0; - while index < 16 { - ret[index] = unsafe { EntryFlags::from_data(map[index]) }; - index += 1; - } - ret } -const PAGE_ENTRY_BASE: usize = RiscV64MMArch::ENTRY_FLAG_PRESENT - | RiscV64MMArch::ENTRY_FLAG_ACCESSED - | RiscV64MMArch::ENTRY_FLAG_USER; - impl VirtAddr { /// 判断虚拟地址是否合法 #[inline(always)] @@ -336,8 +270,8 @@ impl VirtAddr { } /// 获取内核地址默认的页面标志 -pub unsafe fn kernel_page_flags(_virt: VirtAddr) -> EntryFlags { - EntryFlags::from_data(RiscV64MMArch::ENTRY_FLAG_DEFAULT_PAGE) +pub unsafe fn kernel_page_flags(_virt: VirtAddr) -> PageFlags { + PageFlags::from_data(RiscV64MMArch::ENTRY_FLAG_DEFAULT_PAGE) .set_user(false) .set_execute(true) } diff --git a/kernel/src/arch/riscv64/pci/pci_host_ecam.rs b/kernel/src/arch/riscv64/pci/pci_host_ecam.rs index 4e1b4ed08..59d658c26 100644 --- a/kernel/src/arch/riscv64/pci/pci_host_ecam.rs +++ b/kernel/src/arch/riscv64/pci/pci_host_ecam.rs @@ -1,5 +1,4 @@ use fdt::{node::FdtNode, Fdt}; -use log::debug; use system_error::SystemError; use crate::{ @@ -7,6 +6,7 @@ use crate::{ open_firmware::fdt::open_firmware_fdt_driver, pci::ecam::{pci_ecam_root_info_manager, EcamRootInfo}, }, + kdebug, mm::PhysAddr, }; @@ -39,7 +39,7 @@ pub(super) fn pci_host_ecam_driver_init(fdt: &Fdt<'_>) -> Result<(), SystemError _ => panic!("Unexpected linux,pci-domain length"), }; - debug!( + kdebug!( "pci_host_ecam_driver_init(): {} paddr: {:#x} size: {:#x} bus-range: {}-{} segement_group_number: {}", node.name, paddr, @@ -61,9 +61,10 @@ pub(super) fn pci_host_ecam_driver_init(fdt: &Fdt<'_>) -> Result<(), SystemError for node in open_firmware_fdt_driver().find_node_by_compatible(&fdt, "pci-host-ecam-generic") { if let Err(err) = do_check(node) { - debug!( + kdebug!( "pci_host_ecam_driver_init(): check {} error: {:?}", - node.name, err + node.name, + err ); } } diff --git a/kernel/src/arch/riscv64/process/idle.rs b/kernel/src/arch/riscv64/process/idle.rs index ed21f51ee..196a709ec 100644 --- a/kernel/src/arch/riscv64/process/idle.rs +++ b/kernel/src/arch/riscv64/process/idle.rs @@ -1,8 +1,6 @@ use core::hint::spin_loop; -use log::error; - -use crate::{arch::CurrentIrqArch, exception::InterruptArch, process::ProcessManager}; +use crate::{arch::CurrentIrqArch, exception::InterruptArch, kBUG, process::ProcessManager}; impl ProcessManager { /// 每个核的idle进程 @@ -11,11 +9,11 @@ impl ProcessManager { if CurrentIrqArch::is_irq_enabled() { riscv::asm::wfi(); } else { - error!("Idle process should not be scheduled with IRQs disabled."); + kBUG!("Idle process should not be scheduled with IRQs disabled."); spin_loop(); } - // debug!("idle loop"); + // kdebug!("idle loop"); } } } diff --git a/kernel/src/arch/riscv64/process/mod.rs b/kernel/src/arch/riscv64/process/mod.rs index 89ec982d3..32e699294 100644 --- a/kernel/src/arch/riscv64/process/mod.rs +++ b/kernel/src/arch/riscv64/process/mod.rs @@ -6,7 +6,6 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, }; use kdepends::memoffset::offset_of; -use log::error; use riscv::register::sstatus::Sstatus; use system_error::SystemError; @@ -16,6 +15,7 @@ use crate::{ CurrentIrqArch, }, exception::InterruptArch, + kerror, libs::spinlock::SpinLockGuard, mm::VirtAddr, process::{ @@ -166,7 +166,7 @@ impl ProcessManager { /// 参考: https://code.dragonos.org.cn/xref/linux-6.6.21/arch/riscv/include/asm/switch_to.h#76 pub unsafe fn switch_process(prev: Arc, next: Arc) { assert!(!CurrentIrqArch::is_irq_enabled()); - // debug!( + // kdebug!( // "riscv switch process: prev: {:?}, next: {:?}", // prev.pid(), // next.pid() @@ -182,7 +182,7 @@ impl ProcessManager { drop(next_addr_space); compiler_fence(Ordering::SeqCst); - // debug!("current sum={}, prev sum={}, next_sum={}", riscv::register::sstatus::read().sum(), prev.arch_info_irqsave().sstatus.sum(), next.arch_info_irqsave().sstatus.sum()); + // kdebug!("current sum={}, prev sum={}, next_sum={}", riscv::register::sstatus::read().sum(), prev.arch_info_irqsave().sstatus.sum(), next.arch_info_irqsave().sstatus.sum()); // 获取arch info的锁,并强制泄露其守卫(切换上下文后,在switch_finish_hook中会释放锁) let next_arch = SpinLockGuard::leak(next.arch_info_irqsave()) as *mut ArchPCBInfo; @@ -193,7 +193,7 @@ impl ProcessManager { ProcessManager::current_pcb().preempt_enable(); PROCESS_SWITCH_RESULT.as_mut().unwrap().get_mut().prev_pcb = Some(prev); PROCESS_SWITCH_RESULT.as_mut().unwrap().get_mut().next_pcb = Some(next); - // debug!("riscv switch process: before to inner"); + // kdebug!("riscv switch process: before to inner"); compiler_fence(Ordering::SeqCst); // 正式切换上下文 switch_to_inner(prev_arch, next_arch); @@ -326,7 +326,7 @@ impl ProcessControlBlock { // 从内核栈的最低地址处取出pcb的地址 let p = stack_base.data() as *const *const ProcessControlBlock; if core::intrinsics::unlikely((unsafe { *p }).is_null()) { - error!("p={:p}", p); + kerror!("p={:p}", p); panic!("current_pcb is null"); } unsafe { diff --git a/kernel/src/arch/riscv64/process/syscall.rs b/kernel/src/arch/riscv64/process/syscall.rs index f914a3402..0b446f41c 100644 --- a/kernel/src/arch/riscv64/process/syscall.rs +++ b/kernel/src/arch/riscv64/process/syscall.rs @@ -1,4 +1,4 @@ -use alloc::{ffi::CString, string::String, vec::Vec}; +use alloc::{string::String, vec::Vec}; use riscv::register::sstatus::{FS, SPP}; use system_error::SystemError; @@ -16,14 +16,14 @@ use crate::{ impl Syscall { pub fn do_execve( path: String, - argv: Vec, - envp: Vec, + argv: Vec, + envp: Vec, regs: &mut TrapFrame, ) -> Result<(), SystemError> { // 关中断,防止在设置地址空间的时候,发生中断,然后进调度器,出现错误。 let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; let pcb = ProcessManager::current_pcb(); - // crate::debug!( + // crate::kdebug!( // "pid: {:?} do_execve: path: {:?}, argv: {:?}, envp: {:?}\n", // pcb.pid(), // path, @@ -52,20 +52,20 @@ impl Syscall { AddressSpace::is_current(&address_space), "Failed to set address space" ); - // debug!("Switch to new address space"); + // kdebug!("Switch to new address space"); // 切换到新的用户地址空间 unsafe { address_space.read().user_mapper.utable.make_current() }; drop(old_address_space); drop(irq_guard); - // debug!("to load binary file"); + // kdebug!("to load binary file"); let mut param = ExecParam::new(path.as_str(), address_space.clone(), ExecParamFlags::EXEC)?; // 加载可执行文件 let load_result = load_binary_file(&mut param)?; - // debug!("load binary file done"); - // debug!("argv: {:?}, envp: {:?}", argv, envp); + // kdebug!("load binary file done"); + // kdebug!("argv: {:?}, envp: {:?}", argv, envp); param.init_info_mut().args = argv; param.init_info_mut().envs = envp; @@ -79,13 +79,19 @@ impl Syscall { }; let (user_sp, argv_ptr) = unsafe { param - .init_info_mut() - .push_at(&mut ustack_message) + .init_info() + .push_at( + // address_space + // .write() + // .user_stack_mut() + // .expect("No user stack found"), + &mut ustack_message, + ) .expect("Failed to push proc_init_info to user stack") }; address_space.write().user_stack = Some(ustack_message); - // debug!("write proc_init_info to user stack done"); + // kdebug!("write proc_init_info to user stack done"); regs.a0 = param.init_info().args.len(); regs.a1 = argv_ptr.data(); diff --git a/kernel/src/arch/riscv64/smp/mod.rs b/kernel/src/arch/riscv64/smp/mod.rs index 32969d8cc..a283b4502 100644 --- a/kernel/src/arch/riscv64/smp/mod.rs +++ b/kernel/src/arch/riscv64/smp/mod.rs @@ -1,9 +1,11 @@ -use log::warn; use system_error::SystemError; -use crate::smp::{ - cpu::{CpuHpCpuState, ProcessorId}, - SMPArch, +use crate::{ + kwarn, + smp::{ + cpu::{CpuHpCpuState, ProcessorId}, + SMPArch, + }, }; pub struct RiscV64SMPArch; @@ -11,12 +13,12 @@ pub struct RiscV64SMPArch; impl SMPArch for RiscV64SMPArch { #[inline(never)] fn prepare_cpus() -> Result<(), SystemError> { - warn!("RiscV64SMPArch::prepare_cpus() is not implemented"); + kwarn!("RiscV64SMPArch::prepare_cpus() is not implemented"); Ok(()) } fn start_cpu(_cpu_id: ProcessorId, _hp_state: &CpuHpCpuState) -> Result<(), SystemError> { - warn!("RiscV64SMPArch::start_cpu() is not implemented"); + kwarn!("RiscV64SMPArch::start_cpu() is not implemented"); Ok(()) } } diff --git a/kernel/src/arch/riscv64/syscall/mod.rs b/kernel/src/arch/riscv64/syscall/mod.rs index 5609084a8..fe1a1908d 100644 --- a/kernel/src/arch/riscv64/syscall/mod.rs +++ b/kernel/src/arch/riscv64/syscall/mod.rs @@ -18,7 +18,7 @@ macro_rules! syscall_return { if $show { let pid = ProcessManager::current_pcb().pid(); - log::debug!("syscall return:pid={:?},ret= {:?}\n", pid, ret as isize); + crate::kdebug!("syscall return:pid={:?},ret= {:?}\n", pid, ret as isize); } unsafe { @@ -29,7 +29,7 @@ macro_rules! syscall_return { } pub(super) fn syscall_handler(syscall_num: usize, frame: &mut TrapFrame) -> () { - // debug!("syscall_handler: syscall_num: {}", syscall_num); + // kdebug!("syscall_handler: syscall_num: {}", syscall_num); unsafe { CurrentIrqArch::interrupt_enable(); } diff --git a/kernel/src/arch/riscv64/time.rs b/kernel/src/arch/riscv64/time.rs index 0b2f11288..d6be314a8 100644 --- a/kernel/src/arch/riscv64/time.rs +++ b/kernel/src/arch/riscv64/time.rs @@ -1,7 +1,6 @@ -use log::{debug, info}; - use crate::{ driver::open_firmware::fdt::open_firmware_fdt_driver, + kdebug, kinfo, time::{clocksource::HZ, TimeArch}, }; pub struct RiscV64TimeArch; @@ -15,12 +14,12 @@ static mut TIME_FREQ: usize = 0; /// /// todo: 支持从acpi中获取 fn init_time_freq() { - debug!("init_time_freq: init"); + kdebug!("init_time_freq: init"); let fdt = open_firmware_fdt_driver().fdt_ref(); if fdt.is_err() { panic!("init_time_freq: failed to get fdt"); } - debug!("init_time_freq: get fdt"); + kdebug!("init_time_freq: get fdt"); let fdt = fdt.unwrap(); let cpu_node = fdt.find_node("/cpus"); if cpu_node.is_none() { @@ -37,7 +36,7 @@ fn init_time_freq() { } let time_freq: usize = time_freq.unwrap(); - info!("init_time_freq: timebase-frequency: {}", time_freq); + kinfo!("init_time_freq: timebase-frequency: {}", time_freq); unsafe { TIME_FREQ = time_freq; } diff --git a/kernel/src/arch/x86_64/acpi.rs b/kernel/src/arch/x86_64/acpi.rs index 0b834ee68..d29b3bcd1 100644 --- a/kernel/src/arch/x86_64/acpi.rs +++ b/kernel/src/arch/x86_64/acpi.rs @@ -1,6 +1,5 @@ use super::smp::SMP_BOOT_DATA; -use crate::{driver::acpi::acpi_manager, mm::percpu::PerCpu, smp::cpu::ProcessorId}; -use log::info; +use crate::{driver::acpi::acpi_manager, kinfo, mm::percpu::PerCpu, smp::cpu::ProcessorId}; use system_error::SystemError; pub(super) fn early_acpi_boot_init() -> Result<(), SystemError> { @@ -25,7 +24,7 @@ pub(super) fn early_acpi_boot_init() -> Result<(), SystemError> { SMP_BOOT_DATA.set_cpu_count(cnt.data()); SMP_BOOT_DATA.mark_initialized(); } - info!( + kinfo!( "early_acpi_boot_init: cpu_count: {}\n", SMP_BOOT_DATA.cpu_count() ); diff --git a/kernel/src/arch/x86_64/driver/apic/apic_timer.rs b/kernel/src/arch/x86_64/driver/apic/apic_timer.rs index 1a18e5436..81928da47 100644 --- a/kernel/src/arch/x86_64/driver/apic/apic_timer.rs +++ b/kernel/src/arch/x86_64/driver/apic/apic_timer.rs @@ -11,15 +11,15 @@ use crate::exception::irqdesc::{ use crate::exception::manage::irq_manager; use crate::exception::IrqNumber; +use crate::kdebug; use crate::mm::percpu::PerCpu; +use crate::process::ProcessManager; use crate::smp::core::smp_get_processor_id; use crate::smp::cpu::ProcessorId; use crate::time::clocksource::HZ; -use crate::time::tick_common::tick_handle_periodic; use alloc::string::ToString; use alloc::sync::Arc; pub use drop; -use log::debug; use system_error::SystemError; use x86::cpuid::cpuid; use x86::msr::{wrmsr, IA32_X2APIC_DIV_CONF, IA32_X2APIC_INIT_COUNT}; @@ -105,7 +105,7 @@ pub(super) fn local_apic_timer_irq_desc_init() { /// 初始化BSP的APIC定时器 /// fn init_bsp_apic_timer() { - debug!("init_bsp_apic_timer"); + kdebug!("init_bsp_apic_timer"); assert!(smp_get_processor_id().data() == 0); let mut local_apic_timer = local_apic_timer_instance_mut(ProcessorId::new(0)); local_apic_timer.init( @@ -113,11 +113,11 @@ fn init_bsp_apic_timer() { LocalApicTimer::periodic_default_initial_count(), LocalApicTimer::DIVISOR as u32, ); - debug!("init_bsp_apic_timer done"); + kdebug!("init_bsp_apic_timer done"); } fn init_ap_apic_timer() { - debug!("init_ap_apic_timer"); + kdebug!("init_ap_apic_timer"); let cpu_id = smp_get_processor_id(); assert!(cpu_id.data() != 0); @@ -127,14 +127,14 @@ fn init_ap_apic_timer() { LocalApicTimer::periodic_default_initial_count(), LocalApicTimer::DIVISOR as u32, ); - debug!("init_ap_apic_timer done"); + kdebug!("init_ap_apic_timer done"); } pub(super) struct LocalApicTimerIntrController; impl LocalApicTimerIntrController { pub(super) fn install(&self) { - debug!("LocalApicTimerIntrController::install"); + kdebug!("LocalApicTimerIntrController::install"); if smp_get_processor_id().data() == 0 { init_bsp_apic_timer(); } else { @@ -150,13 +150,12 @@ impl LocalApicTimerIntrController { } pub(super) fn enable(&self) { - debug!("LocalApicTimerIntrController::enable"); + kdebug!("LocalApicTimerIntrController::enable"); let cpu_id = smp_get_processor_id(); let mut local_apic_timer = local_apic_timer_instance_mut(cpu_id); local_apic_timer.start_current(); } - #[allow(dead_code)] pub(super) fn disable(&self) { let cpu_id = smp_get_processor_id(); let local_apic_timer = local_apic_timer_instance_mut(cpu_id); @@ -222,18 +221,19 @@ impl LocalApicTimer { } fn install_periodic_mode(&mut self, initial_count: u64, divisor: u32) { - debug!( + kdebug!( "install_periodic_mode: initial_count = {}, divisor = {}", - initial_count, divisor + initial_count, + divisor ); self.mode = LocalApicTimerMode::Periodic; self.set_divisor(divisor); + self.set_initial_cnt(initial_count); self.setup_lvt( APIC_TIMER_IRQ_NUM.data() as u8, true, LocalApicTimerMode::Periodic, ); - self.set_initial_cnt(initial_count); } fn setup_lvt(&mut self, vector: u8, mask: bool, mode: LocalApicTimerMode) { @@ -278,7 +278,7 @@ impl LocalApicTimer { pub(super) fn handle_irq(trap_frame: &TrapFrame) -> Result { // sched_update_jiffies(); - tick_handle_periodic(trap_frame); + ProcessManager::update_process_times(trap_frame.is_from_user()); return Ok(IrqReturn::Handled); } } diff --git a/kernel/src/arch/x86_64/driver/apic/ioapic.rs b/kernel/src/arch/x86_64/driver/apic/ioapic.rs index b2b46b46e..fcfbbb94f 100644 --- a/kernel/src/arch/x86_64/driver/apic/ioapic.rs +++ b/kernel/src/arch/x86_64/driver/apic/ioapic.rs @@ -4,7 +4,6 @@ use acpi::madt::Madt; use alloc::sync::Arc; use bit_field::BitField; use bitflags::bitflags; -use log::{debug, info}; use system_error::SystemError; use crate::{ @@ -17,6 +16,7 @@ use crate::{ manage::irq_manager, IrqNumber, }, + kdebug, kinfo, libs::{ cpumask::CpuMask, once::Once, @@ -68,7 +68,7 @@ impl IoApic { let mut result: Option = None; INIT_STATE.call_once(|| { - info!("Initializing ioapic..."); + kinfo!("Initializing ioapic..."); // get ioapic base from acpi @@ -104,7 +104,7 @@ impl IoApic { mmio_guard.map_phys(phys_base, 0x1000).is_ok(), "IoApic::new(): failed to map phys" ); - debug!("Ioapic map ok"); + kdebug!("Ioapic map ok"); let reg = mmio_guard.vaddr(); result = Some(IoApic { @@ -114,13 +114,13 @@ impl IoApic { phys_base, mmio_guard, }); - debug!("IOAPIC: to mask all RTE"); + kdebug!("IOAPIC: to mask all RTE"); // 屏蔽所有的RTE let res_mut = result.as_mut().unwrap(); for i in 0..res_mut.supported_interrupts() { res_mut.write_rte(i, 0x20 + i, RedirectionEntry::DISABLED, 0); } - debug!("Ioapic init done"); + kdebug!("Ioapic init done"); }); assert!( @@ -393,7 +393,7 @@ impl InnerIoApicChipData { #[inline(never)] pub fn ioapic_init(ignore: &'static [IrqNumber]) { - info!("Initializing ioapic..."); + kinfo!("Initializing ioapic..."); let ioapic = unsafe { IoApic::new() }; unsafe { __IOAPIC = Some(SpinLock::new(ioapic)); @@ -424,7 +424,7 @@ pub fn ioapic_init(ignore: &'static [IrqNumber]) { register_handler(&desc, level); } - info!("IO Apic initialized."); + kinfo!("IO Apic initialized."); } fn register_handler(desc: &Arc, level_triggered: bool) { diff --git a/kernel/src/arch/x86_64/driver/apic/lapic_vector.rs b/kernel/src/arch/x86_64/driver/apic/lapic_vector.rs index 8b46f0409..74a1ee79d 100644 --- a/kernel/src/arch/x86_64/driver/apic/lapic_vector.rs +++ b/kernel/src/arch/x86_64/driver/apic/lapic_vector.rs @@ -2,7 +2,6 @@ use core::intrinsics::unlikely; use alloc::{string::ToString, sync::Arc}; use intertrait::CastFrom; -use log::warn; use system_error::SystemError; use crate::{ @@ -26,6 +25,7 @@ use crate::{ msi::MsiMsg, HardwareIrqNumber, IrqNumber, }, + kwarn, libs::spinlock::{SpinLock, SpinLockGuard}, smp::{core::smp_get_processor_id, cpu::ProcessorId}, }; @@ -179,7 +179,6 @@ bitflags! { } } -#[allow(dead_code)] pub(super) fn irq_msi_compose_msg(cfg: &HardwareIrqConfig, msg: &mut MsiMsg, dmar: bool) { *msg = MsiMsg::new_zeroed(); @@ -207,7 +206,7 @@ pub(super) fn irq_msi_compose_msg(cfg: &HardwareIrqConfig, msg: &mut MsiMsg, dma // 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/arch/x86/kernel/apic/apic.c?fi=__irq_msi_compose_msg#2580 address_lo.set_virt_destid_8_14(cfg.apic_id.data() >> 8); } else if unlikely(cfg.apic_id.data() > 0xff) { - warn!( + kwarn!( "irq_msi_compose_msg: Invalid APIC ID: {}", cfg.apic_id.data() ); @@ -253,7 +252,7 @@ pub fn arch_early_irq_init() -> Result<(), SystemError> { // todo: add vector matrix // 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/arch/x86/kernel/apic/vector.c#803 - warn!("arch_early_irq_init: todo: add vector matrix"); + kwarn!("arch_early_irq_init: todo: add vector matrix"); local_apic_timer_irq_desc_init(); arch_ipi_handler_init(); diff --git a/kernel/src/arch/x86_64/driver/apic/mod.rs b/kernel/src/arch/x86_64/driver/apic/mod.rs index af89dedfb..ae78415b4 100644 --- a/kernel/src/arch/x86_64/driver/apic/mod.rs +++ b/kernel/src/arch/x86_64/driver/apic/mod.rs @@ -1,7 +1,6 @@ use core::sync::atomic::Ordering; use atomic_enum::atomic_enum; -use log::{debug, info}; use system_error::SystemError; use x86::{apic::Icr, msr::IA32_APIC_BASE}; @@ -11,6 +10,7 @@ use crate::{ io::PortIOArch, CurrentPortIOArch, }, + kdebug, kinfo, mm::PhysAddr, smp::core::smp_get_processor_id, }; @@ -468,7 +468,7 @@ impl CurrentApic { CurrentPortIOArch::out8(0x20, 0x20); CurrentPortIOArch::out8(0xa0, 0x20); - debug!("8259A Masked."); + kdebug!("8259A Masked."); // enable IMCR CurrentPortIOArch::out8(0x22, 0x70); @@ -488,14 +488,14 @@ impl LocalAPIC for CurrentApic { self.mask8259a(); } } - info!("Initializing apic for cpu {:?}", cpu_id); + kinfo!("Initializing apic for cpu {:?}", cpu_id); if X2Apic::support() && X2Apic.init_current_cpu() { if cpu_id.data() == 0 { LOCAL_APIC_ENABLE_TYPE.store(LocalApicEnableType::X2Apic, Ordering::SeqCst); } - info!("x2APIC initialized for cpu {:?}", cpu_id); + kinfo!("x2APIC initialized for cpu {:?}", cpu_id); } else { - info!("x2APIC not supported or failed to initialize, fallback to xAPIC."); + kinfo!("x2APIC not supported or failed to initialize, fallback to xAPIC."); if cpu_id.data() == 0 { LOCAL_APIC_ENABLE_TYPE.store(LocalApicEnableType::XApic, Ordering::SeqCst); } @@ -514,10 +514,10 @@ impl LocalAPIC for CurrentApic { xapic.init_current_cpu(); } - info!("xAPIC initialized for cpu {:?}", cpu_id); + kinfo!("xAPIC initialized for cpu {:?}", cpu_id); } - info!("Apic initialized."); + kinfo!("Apic initialized."); return true; } diff --git a/kernel/src/arch/x86_64/driver/apic/x2apic.rs b/kernel/src/arch/x86_64/driver/apic/x2apic.rs index e34715eae..7718cb45d 100644 --- a/kernel/src/arch/x86_64/driver/apic/x2apic.rs +++ b/kernel/src/arch/x86_64/driver/apic/x2apic.rs @@ -1,11 +1,12 @@ use core::sync::atomic::{fence, Ordering}; -use log::info; use x86::msr::{ rdmsr, wrmsr, IA32_APIC_BASE, IA32_X2APIC_APICID, IA32_X2APIC_EOI, IA32_X2APIC_SIVR, IA32_X2APIC_VERSION, }; +use crate::kinfo; + use super::{hw_irq::ApicId, LVTRegister, LocalAPIC, LVT}; #[derive(Debug)] @@ -44,19 +45,19 @@ impl LocalAPIC for X2Apic { (rdmsr(IA32_X2APIC_SIVR) & 0x100) == 0x100, "x2APIC software enable failed." ); - info!("x2APIC software enabled."); + kinfo!("x2APIC software enabled."); if self.support_eoi_broadcast_suppression() { assert!( (rdmsr(IA32_X2APIC_SIVR) & 0x1000) == 0x1000, "x2APIC EOI broadcast suppression enable failed." ); - info!("x2APIC EOI broadcast suppression enabled."); + kinfo!("x2APIC EOI broadcast suppression enabled."); } } - // debug!("x2apic: to mask all lvt"); + // kdebug!("x2apic: to mask all lvt"); self.mask_all_lvt(); - // debug!("x2apic: all lvt masked"); + // kdebug!("x2apic: all lvt masked"); } true } diff --git a/kernel/src/arch/x86_64/driver/apic/xapic.rs b/kernel/src/arch/x86_64/driver/apic/xapic.rs index f764d1df8..49211e0c2 100644 --- a/kernel/src/arch/x86_64/driver/apic/xapic.rs +++ b/kernel/src/arch/x86_64/driver/apic/xapic.rs @@ -4,9 +4,8 @@ use core::{ ptr::{read_volatile, write_volatile}, }; -use log::{debug, error, info}; - use crate::{ + kdebug, kerror, kinfo, mm::{ mmio_buddy::{mmio_pool, MMIOSpaceGuard}, percpu::PerCpu, @@ -158,7 +157,7 @@ impl XApic { g.map_phys(paddr, 4096).expect("Fail to map MMIO for XAPIC"); let addr = g.vaddr() + offset; - debug!( + kdebug!( "XAPIC: {:#x} -> {:#x}, offset={offset}", xapic_base.data(), addr.data() @@ -220,7 +219,7 @@ impl LocalAPIC for XApic { x86::msr::wrmsr(x86::msr::APIC_BASE, (self.xapic_base.data() | 0x800) as u64); let val = x86::msr::rdmsr(x86::msr::APIC_BASE); if val & 0x800 != 0x800 { - error!("xAPIC enable failed: APIC_BASE & 0x800 != 0x800"); + kerror!("xAPIC enable failed: APIC_BASE & 0x800 != 0x800"); return false; } // 设置 Spurious Interrupt Vector Register @@ -230,15 +229,15 @@ impl LocalAPIC for XApic { let val = self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_SVR); if val & ENABLE == 0 { - error!("xAPIC software enable failed."); + kerror!("xAPIC software enable failed."); return false; } else { - info!("xAPIC software enabled."); + kinfo!("xAPIC software enabled."); } if val & 0x1000 != 0 { - info!("xAPIC EOI broadcast suppression enabled."); + kinfo!("xAPIC EOI broadcast suppression enabled."); } self.mask_all_lvt(); diff --git a/kernel/src/arch/x86_64/driver/hpet.rs b/kernel/src/arch/x86_64/driver/hpet.rs index 89a7d5da1..36202dbf1 100644 --- a/kernel/src/arch/x86_64/driver/hpet.rs +++ b/kernel/src/arch/x86_64/driver/hpet.rs @@ -7,7 +7,6 @@ use core::{ use acpi::HpetInfo; use alloc::{string::ToString, sync::Arc}; -use log::{debug, error, info}; use system_error::SystemError; use crate::{ @@ -22,6 +21,7 @@ use crate::{ manage::irq_manager, InterruptArch, IrqNumber, }, + kdebug, kerror, kinfo, libs::{ rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, volatile::volwrite, @@ -30,7 +30,10 @@ use crate::{ mmio_buddy::{mmio_pool, MMIOSpaceGuard}, PhysAddr, }, - time::jiffies::NSEC_PER_JIFFY, + time::{ + jiffies::NSEC_PER_JIFFY, + timer::{try_raise_timer_softirq, update_timer_jiffies}, + }, }; static mut HPET_INSTANCE: Option = None; @@ -77,8 +80,8 @@ impl Hpet { .unwrap() }; let tm_num = hpet.timers_num(); - debug!("HPET0_INTERVAL_USEC: {}", Self::HPET0_INTERVAL_USEC); - info!("HPET has {} timers", tm_num); + kdebug!("HPET0_INTERVAL_USEC: {}", Self::HPET0_INTERVAL_USEC); + kinfo!("HPET has {} timers", tm_num); hpet_info.hpet_number = tm_num as u8; drop(mmio); @@ -121,10 +124,10 @@ impl Hpet { // !!!这里是临时糊代码的,需要在apic重构的时候修改!!! let (inner_guard, regs) = unsafe { self.hpet_regs_mut() }; let freq = regs.frequency(); - debug!("HPET frequency: {} Hz", freq); + kdebug!("HPET frequency: {} Hz", freq); let ticks = Self::HPET0_INTERVAL_USEC * freq / 1000000; if ticks == 0 || ticks > freq * 8 { - error!("HPET enable: ticks '{ticks}' is invalid"); + kerror!("HPET enable: ticks '{ticks}' is invalid"); return Err(SystemError::EINVAL); } if unlikely(regs.timers_num() == 0) { @@ -163,7 +166,7 @@ impl Hpet { drop(inner_guard); - info!("HPET enabled"); + kinfo!("HPET enabled"); drop(irq_guard); return Ok(()); @@ -236,7 +239,7 @@ impl Hpet { pub fn period(&self) -> u64 { let (inner_guard, regs) = unsafe { self.hpet_regs() }; let period = regs.counter_clock_period(); - debug!("HPET period: {}", period); + kdebug!("HPET period: {}", period); drop(inner_guard); return period; @@ -246,6 +249,9 @@ impl Hpet { pub(super) fn handle_irq(&self, timer_num: u32) { if timer_num == 0 { assert!(!CurrentIrqArch::is_irq_enabled()); + update_timer_jiffies(1, Self::HPET0_INTERVAL_USEC as i64); + + try_raise_timer_softirq(); } } } diff --git a/kernel/src/arch/x86_64/driver/rtc.rs b/kernel/src/arch/x86_64/driver/rtc.rs index bb6486a11..0612038e1 100644 --- a/kernel/src/arch/x86_64/driver/rtc.rs +++ b/kernel/src/arch/x86_64/driver/rtc.rs @@ -4,7 +4,6 @@ use alloc::{ string::{String, ToString}, sync::{Arc, Weak}, }; -use log::error; use system_error::SystemError; use unified_init::macros::unified_init; @@ -26,6 +25,7 @@ use crate::{ exception::InterruptArch, filesystem::kernfs::KernFSInode, init::initcall::INITCALL_DEVICE, + kerror, libs::{ mutex::Mutex, rwlock::{RwLockReadGuard, RwLockWriteGuard}, @@ -286,7 +286,7 @@ impl RtcClassOps for CmosRtcClassOps { } fn set_time(&self, _dev: &Arc, _time: &RtcTime) -> Result<(), SystemError> { - error!("set_time is not implemented for CmosRtcClassOps"); + kerror!("set_time is not implemented for CmosRtcClassOps"); Err(SystemError::ENOSYS) } } diff --git a/kernel/src/arch/x86_64/driver/tsc.rs b/kernel/src/arch/x86_64/driver/tsc.rs index ef97ec6a9..f1aab65dc 100644 --- a/kernel/src/arch/x86_64/driver/tsc.rs +++ b/kernel/src/arch/x86_64/driver/tsc.rs @@ -2,13 +2,13 @@ use crate::{ arch::{io::PortIOArch, CurrentIrqArch, CurrentPortIOArch, CurrentTimeArch}, driver::acpi::pmtmr::{acpi_pm_read_early, ACPI_PM_OVERRUN, PMTMR_TICKS_PER_SEC}, exception::InterruptArch, + kdebug, kerror, kinfo, kwarn, time::{TimeArch, PIT_TICK_RATE}, }; use core::{ cmp::{max, min}, intrinsics::unlikely, }; -use log::{debug, error, info, warn}; use system_error::SystemError; use super::hpet::{hpet_instance, is_hpet_enabled}; @@ -31,13 +31,13 @@ impl TSCManager { let cpuid = x86::cpuid::CpuId::new(); let feat = cpuid.get_feature_info().ok_or(SystemError::ENODEV)?; if !feat.has_tsc() { - error!("TSC is not available"); + kerror!("TSC is not available"); return Err(SystemError::ENODEV); } if unsafe { TSC_KHZ == 0 } { if let Err(e) = Self::determine_cpu_tsc_frequency(false) { - error!("Failed to determine CPU TSC frequency: {:?}", e); + kerror!("Failed to determine CPU TSC frequency: {:?}", e); // todo: mark TSC as unstable clock source return Err(e); } @@ -57,7 +57,7 @@ impl TSCManager { /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#1438 fn determine_cpu_tsc_frequency(early: bool) -> Result<(), SystemError> { if unlikely(Self::cpu_khz() != 0 || Self::tsc_khz() != 0) { - warn!("TSC and CPU frequency already determined"); + kwarn!("TSC and CPU frequency already determined"); } if early { @@ -79,16 +79,16 @@ impl TSCManager { } if Self::cpu_khz() == 0 { - error!("Failed to determine CPU frequency"); + kerror!("Failed to determine CPU frequency"); return Err(SystemError::ENODEV); } - info!( + kinfo!( "Detected {}.{} MHz processor", Self::cpu_khz() / 1000, Self::cpu_khz() % 1000 ); - info!( + kinfo!( "Detected {}.{} MHz TSC", Self::tsc_khz() / 1000, Self::tsc_khz() % 1000 @@ -102,7 +102,7 @@ impl TSCManager { /// 使用pit、hpet、ptimer来测量CPU总线的频率 fn calibrate_cpu_by_pit_hpet_ptimer() -> Result { let hpet = is_hpet_enabled(); - debug!( + kdebug!( "Calibrating TSC with {}", if hpet { "HPET" } else { "PMTIMER" } ); @@ -143,7 +143,7 @@ impl TSCManager { // HPET或者PTIMER可能是不可用的 if ref1 == ref2 { - debug!("HPET/PMTIMER not available"); + kdebug!("HPET/PMTIMER not available"); continue; } @@ -169,7 +169,7 @@ impl TSCManager { // 如果误差在10%以内,那么认为测量成功 // 返回参考值,因为它是更精确的 if (90..=110).contains(&delta) { - info!( + kinfo!( "PIT calibration matches {}. {} loops", if hpet { "HPET" } else { "PMTIMER" }, i + 1 @@ -185,20 +185,20 @@ impl TSCManager { } if tsc_pit_min == u64::MAX { - warn!("Unable to calibrate against PIT"); + kwarn!("Unable to calibrate against PIT"); // 如果没有参考值,那么禁用tsc if (!hpet) && (global_ref1 == 0) && (global_ref2 == 0) { - warn!("No reference (HPET/PMTIMER) available"); + kwarn!("No reference (HPET/PMTIMER) available"); return Err(SystemError::ENODEV); } if tsc_ref_min == u64::MAX { - warn!("Unable to calibrate against HPET/PMTIMER"); + kwarn!("Unable to calibrate against HPET/PMTIMER"); return Err(SystemError::ENODEV); } - info!( + kinfo!( "Using {} reference calibration", if hpet { "HPET" } else { "PMTIMER" } ); @@ -207,27 +207,27 @@ impl TSCManager { // We don't have an alternative source, use the PIT calibration value if (!hpet) && (global_ref1 == 0) && (global_ref2 == 0) { - info!("Using PIT calibration value"); + kinfo!("Using PIT calibration value"); return Ok(tsc_pit_min); } // The alternative source failed, use the PIT calibration value if tsc_ref_min == u64::MAX { - warn!("Unable to calibrate against HPET/PMTIMER, using PIT calibration value"); + kwarn!("Unable to calibrate against HPET/PMTIMER, using PIT calibration value"); return Ok(tsc_pit_min); } // The calibration values differ too much. In doubt, we use // the PIT value as we know that there are PMTIMERs around // running at double speed. At least we let the user know: - warn!( + kwarn!( "PIT calibration deviates from {}: tsc_pit_min={}, tsc_ref_min={}", if hpet { "HPET" } else { "PMTIMER" }, tsc_pit_min, tsc_ref_min ); - info!("Using PIT calibration value"); + kinfo!("Using PIT calibration value"); return Ok(tsc_pit_min); } @@ -326,7 +326,7 @@ impl TSCManager { } } - warn!("TSCManager: Failed to read reference value, tsc delta too high"); + kwarn!("TSCManager: Failed to read reference value, tsc delta too high"); return (u64::MAX, ref_ret); } diff --git a/kernel/src/arch/x86_64/init/mod.rs b/kernel/src/arch/x86_64/init/mod.rs index c1ae2d8d7..6f392d3a1 100644 --- a/kernel/src/arch/x86_64/init/mod.rs +++ b/kernel/src/arch/x86_64/init/mod.rs @@ -1,6 +1,5 @@ use core::sync::atomic::{compiler_fence, Ordering}; -use log::debug; use system_error::SystemError; use x86::dtables::DescriptorTablePointer; @@ -8,6 +7,7 @@ use crate::{ arch::{interrupt::trap::arch_trap_init, process::table::TSSManager}, driver::clocksource::acpi_pm::init_acpi_pm_clocksource, init::init::start_kernel, + kdebug, mm::{MemoryManagementArch, PhysAddr}, }; @@ -35,7 +35,6 @@ extern "C" { } #[no_mangle] -#[allow(static_mut_refs)] unsafe extern "C" fn kernel_main( mb2_info: u64, mb2_magic: u64, @@ -67,17 +66,16 @@ unsafe extern "C" fn kernel_main( /// 在内存管理初始化之前的架构相关的早期初始化 #[inline(never)] -#[allow(static_mut_refs)] pub fn early_setup_arch() -> Result<(), SystemError> { let stack_start = unsafe { *(head_stack_start as *const u64) } as usize; - debug!("head_stack_start={:#x}\n", stack_start); + kdebug!("head_stack_start={:#x}\n", stack_start); unsafe { let gdt_vaddr = MMArch::phys_2_virt(PhysAddr::new(&GDT_Table as *const usize as usize)).unwrap(); let idt_vaddr = MMArch::phys_2_virt(PhysAddr::new(&IDT_Table as *const usize as usize)).unwrap(); - debug!("GDT_Table={:?}, IDT_Table={:?}\n", gdt_vaddr, idt_vaddr); + kdebug!("GDT_Table={:?}, IDT_Table={:?}\n", gdt_vaddr, idt_vaddr); } set_current_core_tss(stack_start, 0); @@ -109,9 +107,10 @@ pub fn setup_arch_post() -> Result<(), SystemError> { fn set_current_core_tss(stack_start: usize, ist0: usize) { let current_tss = unsafe { TSSManager::current_tss() }; - debug!( + kdebug!( "set_current_core_tss: stack_start={:#x}, ist0={:#x}\n", - stack_start, ist0 + stack_start, + ist0 ); current_tss.set_rsp(x86::Ring::Ring0, stack_start as u64); current_tss.set_ist(0, ist0 as u64); diff --git a/kernel/src/arch/x86_64/interrupt/entry.rs b/kernel/src/arch/x86_64/interrupt/entry.rs index 3d8637a31..da0921927 100644 --- a/kernel/src/arch/x86_64/interrupt/entry.rs +++ b/kernel/src/arch/x86_64/interrupt/entry.rs @@ -564,7 +564,6 @@ pub unsafe fn set_system_trap_gate(irq: u32, ist: u8, vaddr: VirtAddr) { set_gate(idt_entry, 0xEF, ist, vaddr); } -#[allow(static_mut_refs)] unsafe fn get_idt_entry(irq: u32) -> &'static mut [u64] { assert!(irq < 256); let mut idt_vaddr = diff --git a/kernel/src/arch/x86_64/interrupt/ipi.rs b/kernel/src/arch/x86_64/interrupt/ipi.rs index 78c6ae780..87cc785d9 100644 --- a/kernel/src/arch/x86_64/interrupt/ipi.rs +++ b/kernel/src/arch/x86_64/interrupt/ipi.rs @@ -1,5 +1,4 @@ use alloc::sync::Arc; -use log::error; use system_error::SystemError; use x86::apic::ApicId; @@ -14,6 +13,7 @@ use crate::{ irqdesc::{irq_desc_manager, IrqDesc, IrqFlowHandler, IrqHandler}, HardwareIrqNumber, IrqNumber, }, + kerror, smp::cpu::ProcessorId, }; @@ -122,14 +122,14 @@ impl From for x86::apic::DestinationShorthand { #[inline(always)] pub fn send_ipi(kind: IpiKind, target: IpiTarget) { - // debug!("send_ipi: {:?} {:?}", kind, target); + // kdebug!("send_ipi: {:?} {:?}", kind, target); let ipi_vec = ArchIpiKind::from(kind).into(); let target = ArchIpiTarget::from(target); let shorthand: x86::apic::DestinationShorthand = target.into(); let destination: x86::apic::ApicId = target.into(); let icr = if CurrentApic.x2apic_enabled() { - // debug!("send_ipi: x2apic"); + // kdebug!("send_ipi: x2apic"); x86::apic::Icr::for_x2apic( ipi_vec, destination, @@ -141,7 +141,7 @@ pub fn send_ipi(kind: IpiKind, target: IpiTarget) { x86::apic::TriggerMode::Edge, ) } else { - // debug!("send_ipi: xapic"); + // kdebug!("send_ipi: xapic"); x86::apic::Icr::for_xapic( ipi_vec, destination, @@ -257,7 +257,7 @@ impl IrqFlowHandler for X86_64IpiIrqFlowHandler { CurrentApic.send_eoi(); } _ => { - error!("Unknown IPI: {}", irq.data()); + kerror!("Unknown IPI: {}", irq.data()); CurrentApic.send_eoi(); } } diff --git a/kernel/src/arch/x86_64/interrupt/mod.rs b/kernel/src/arch/x86_64/interrupt/mod.rs index 0eb4a88a8..ddd7db57d 100644 --- a/kernel/src/arch/x86_64/interrupt/mod.rs +++ b/kernel/src/arch/x86_64/interrupt/mod.rs @@ -9,12 +9,12 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, }; -use log::error; use system_error::SystemError; use crate::{ arch::CurrentIrqArch, exception::{InterruptArch, IrqFlags, IrqFlagsGuard, IrqNumber}, + kerror, }; use super::{ @@ -85,7 +85,7 @@ impl InterruptArch for X86_64InterruptArch { } fn ack_bad_irq(irq: IrqNumber) { - error!("Unexpected IRQ trap at vector {}", irq.data()); + kerror!("Unexpected IRQ trap at vector {}", irq.data()); CurrentApic.send_eoi(); } @@ -132,12 +132,6 @@ pub struct TrapFrame { pub ss: ::core::ffi::c_ulong, } -impl Default for TrapFrame { - fn default() -> Self { - Self::new() - } -} - impl TrapFrame { pub fn new() -> Self { Self { diff --git a/kernel/src/arch/x86_64/interrupt/msi.rs b/kernel/src/arch/x86_64/interrupt/msi.rs index 84c73918b..34d6c4207 100644 --- a/kernel/src/arch/x86_64/interrupt/msi.rs +++ b/kernel/src/arch/x86_64/interrupt/msi.rs @@ -26,7 +26,6 @@ pub struct X86MsiDataNormal { } #[derive(Debug)] -#[allow(dead_code)] pub struct X86MsiDataDmar { pub dmar_subhandle: u32, } diff --git a/kernel/src/arch/x86_64/interrupt/trap.rs b/kernel/src/arch/x86_64/interrupt/trap.rs index 9cd31fa9b..a75c73717 100644 --- a/kernel/src/arch/x86_64/interrupt/trap.rs +++ b/kernel/src/arch/x86_64/interrupt/trap.rs @@ -1,9 +1,9 @@ -use log::{error, warn}; use system_error::SystemError; use crate::{ arch::{CurrentIrqArch, MMArch}, exception::InterruptArch, + kerror, kwarn, mm::VirtAddr, process::ProcessManager, smp::core::smp_get_processor_id, @@ -112,7 +112,7 @@ pub fn arch_trap_init() -> Result<(), SystemError> { /// 处理除法错误 0 #DE #[no_mangle] unsafe extern "C" fn do_divide_error(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_divide_error(0), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -126,7 +126,7 @@ unsafe extern "C" fn do_divide_error(regs: &'static TrapFrame, error_code: u64) /// 处理调试异常 1 #DB #[no_mangle] unsafe extern "C" fn do_debug(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_debug(1), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -140,7 +140,7 @@ unsafe extern "C" fn do_debug(regs: &'static TrapFrame, error_code: u64) { /// 处理NMI中断 2 NMI #[no_mangle] unsafe extern "C" fn do_nmi(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_nmi(2), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -154,7 +154,7 @@ unsafe extern "C" fn do_nmi(regs: &'static TrapFrame, error_code: u64) { /// 处理断点异常 3 #BP #[no_mangle] unsafe extern "C" fn do_int3(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_int3(3), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -168,7 +168,7 @@ unsafe extern "C" fn do_int3(regs: &'static TrapFrame, error_code: u64) { /// 处理溢出异常 4 #OF #[no_mangle] unsafe extern "C" fn do_overflow(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_overflow(4), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -182,7 +182,7 @@ unsafe extern "C" fn do_overflow(regs: &'static TrapFrame, error_code: u64) { /// 处理BOUND指令检查异常 5 #BR #[no_mangle] unsafe extern "C" fn do_bounds(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_bounds(5), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -196,7 +196,7 @@ unsafe extern "C" fn do_bounds(regs: &'static TrapFrame, error_code: u64) { /// 处理未定义操作码异常 6 #UD #[no_mangle] unsafe extern "C" fn do_undefined_opcode(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_undefined_opcode(6), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -210,7 +210,7 @@ unsafe extern "C" fn do_undefined_opcode(regs: &'static TrapFrame, error_code: u /// 处理设备不可用异常(FPU不存在) 7 #NM #[no_mangle] unsafe extern "C" fn do_dev_not_avaliable(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_dev_not_avaliable(7), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -224,7 +224,7 @@ unsafe extern "C" fn do_dev_not_avaliable(regs: &'static TrapFrame, error_code: /// 处理双重错误 8 #DF #[no_mangle] unsafe extern "C" fn do_double_fault(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_double_fault(8), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -238,7 +238,7 @@ unsafe extern "C" fn do_double_fault(regs: &'static TrapFrame, error_code: u64) /// 处理协处理器段越界 9 #MF #[no_mangle] unsafe extern "C" fn do_coprocessor_segment_overrun(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_coprocessor_segment_overrun(9), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -272,7 +272,7 @@ unsafe extern "C" fn do_invalid_TSS(regs: &'static TrapFrame, error_code: u64) { ERR_MSG_4 }; - error!( + kerror!( "do_invalid_TSS(10), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}\n{}{}", error_code, regs.rsp, @@ -288,7 +288,7 @@ unsafe extern "C" fn do_invalid_TSS(regs: &'static TrapFrame, error_code: u64) { /// 处理段不存在 11 #NP #[no_mangle] unsafe extern "C" fn do_segment_not_exists(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_segment_not_exists(11), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -302,7 +302,7 @@ unsafe extern "C" fn do_segment_not_exists(regs: &'static TrapFrame, error_code: /// 处理栈段错误 12 #SS #[no_mangle] unsafe extern "C" fn do_stack_segment_fault(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_stack_segment_fault(12), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -343,7 +343,7 @@ unsafe extern "C" fn do_general_protection(regs: &'static TrapFrame, error_code: } else { "" }; - error!( + kerror!( "do_general_protection(13), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t rflags: {:#x}\t CPU: {}, \tpid: {:?} {}{}{} Segment Selector Index: {:#x}\n @@ -363,7 +363,7 @@ Segment Selector Index: {:#x}\n /// 处理页错误 14 #PF #[no_mangle] unsafe extern "C" fn do_page_fault(regs: &'static TrapFrame, error_code: u64) { - // error!( + // kerror!( // "do_page_fault(14), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}, \nFault Address: {:#x}", // error_code, // regs.rsp, @@ -401,7 +401,7 @@ unsafe extern "C" fn do_page_fault(regs: &'static TrapFrame, error_code: u64) { // panic!("Page Fault"); CurrentIrqArch::interrupt_disable(); let address = x86::controlregs::cr2(); - // log::info!( + // crate::kinfo!( // "fault address: {:#x}, error_code: {:#b}, pid: {}\n", // address, // error_code, @@ -421,7 +421,7 @@ unsafe extern "C" fn do_page_fault(regs: &'static TrapFrame, error_code: u64) { /// 处理x87 FPU错误 16 #MF #[no_mangle] unsafe extern "C" fn do_x87_FPU_error(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_x87_FPU_error(16), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -435,7 +435,7 @@ unsafe extern "C" fn do_x87_FPU_error(regs: &'static TrapFrame, error_code: u64) /// 处理对齐检查 17 #AC #[no_mangle] unsafe extern "C" fn do_alignment_check(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_alignment_check(17), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -449,7 +449,7 @@ unsafe extern "C" fn do_alignment_check(regs: &'static TrapFrame, error_code: u6 /// 处理机器检查 18 #MC #[no_mangle] unsafe extern "C" fn do_machine_check(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_machine_check(18), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -463,7 +463,7 @@ unsafe extern "C" fn do_machine_check(regs: &'static TrapFrame, error_code: u64) /// 处理SIMD异常 19 #XM #[no_mangle] unsafe extern "C" fn do_SIMD_exception(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_SIMD_exception(19), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -477,7 +477,7 @@ unsafe extern "C" fn do_SIMD_exception(regs: &'static TrapFrame, error_code: u64 /// 处理虚拟化异常 20 #VE #[no_mangle] unsafe extern "C" fn do_virtualization_exception(regs: &'static TrapFrame, error_code: u64) { - error!( + kerror!( "do_virtualization_exception(20), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -490,5 +490,5 @@ unsafe extern "C" fn do_virtualization_exception(regs: &'static TrapFrame, error #[no_mangle] unsafe extern "C" fn ignore_int_handler(_regs: &'static TrapFrame, _error_code: u64) { - warn!("Unknown interrupt."); + kwarn!("Unknown interrupt."); } diff --git a/kernel/src/arch/x86_64/ipc/signal.rs b/kernel/src/arch/x86_64/ipc/signal.rs index c9d0af99e..af4c60269 100644 --- a/kernel/src/arch/x86_64/ipc/signal.rs +++ b/kernel/src/arch/x86_64/ipc/signal.rs @@ -1,6 +1,5 @@ use core::{ffi::c_void, intrinsics::unlikely, mem::size_of}; -use log::error; use system_error::SystemError; use crate::{ @@ -15,6 +14,7 @@ use crate::{ signal::set_current_sig_blocked, signal_types::{SaHandlerType, SigInfo, Sigaction, SigactionType, SignalArch}, }, + kerror, mm::MemoryManagementArch, process::ProcessManager, sched::{schedule, SchedMode}, @@ -86,7 +86,7 @@ impl From for Signal { let ret: Signal = unsafe { core::mem::transmute(value) }; return ret; } else { - error!("Try to convert an invalid number to Signal"); + kerror!("Try to convert an invalid number to Signal"); return Signal::INVALID; } } @@ -101,7 +101,7 @@ impl From for usize { impl From for Signal { fn from(value: i32) -> Self { if value < 0 { - error!("Try to convert an invalid number to Signal"); + kerror!("Try to convert an invalid number to Signal"); return Signal::INVALID; } else { return Self::from(value as usize); @@ -145,7 +145,7 @@ impl Signal { pub fn handle_default(&self) { match self { Signal::INVALID => { - error!("attempting to handler an Invalid"); + kerror!("attempting to handler an Invalid"); } Signal::SIGHUP => sig_terminate(*self), Signal::SIGINT => sig_terminate(*self), @@ -396,7 +396,6 @@ impl SigContext { } } /// @brief 信号处理备用栈的信息 -#[allow(dead_code)] #[derive(Debug, Clone, Copy)] pub struct SigStack { pub sp: *mut c_void, @@ -462,7 +461,7 @@ impl SignalArch for X86_64SignalArch { match sigaction.action() { SigactionType::SaHandler(action_type) => match action_type { SaHandlerType::Error => { - error!("Trying to handle a Sigerror on Process:{:?}", pcb.pid()); + kerror!("Trying to handle a Sigerror on Process:{:?}", pcb.pid()); return; } SaHandlerType::Default => { @@ -489,7 +488,7 @@ impl SignalArch for X86_64SignalArch { let res: Result = handle_signal(sig_number, &mut sigaction, &info.unwrap(), &oldset, frame); if res.is_err() { - error!( + kerror!( "Error occurred when handling signal: {}, pid={:?}, errcode={:?}", sig_number as i32, ProcessManager::current_pcb().pid(), @@ -503,7 +502,7 @@ impl SignalArch for X86_64SignalArch { // 如果当前的rsp不来自用户态,则认为产生了错误(或被SROP攻击) if UserBufferWriter::new(frame, size_of::(), true).is_err() { - error!("rsp doesn't from user level"); + kerror!("rsp doesn't from user level"); let _r = Syscall::kill(ProcessManager::current_pcb().pid(), Signal::SIGSEGV as i32) .map_err(|e| e.to_posix_errno()); return trap_frame.rax; @@ -512,7 +511,7 @@ impl SignalArch for X86_64SignalArch { set_current_sig_blocked(&mut sigmask); // 从用户栈恢复sigcontext if !unsafe { &mut (*frame).context }.restore_sigcontext(trap_frame) { - error!("unable to restore sigcontext"); + kerror!("unable to restore sigcontext"); let _r = Syscall::kill(ProcessManager::current_pcb().pid(), Signal::SIGSEGV as i32) .map_err(|e| e.to_posix_errno()); // 如果这里返回 err 值的话会丢失上一个系统调用的返回值 @@ -570,7 +569,7 @@ fn setup_frame( sig.handle_default(); return Ok(0); } else { - error!("attempting to execute a signal handler from kernel"); + kerror!("attempting to execute a signal handler from kernel"); sig.handle_default(); return Err(SystemError::EINVAL); } @@ -579,7 +578,7 @@ fn setup_frame( if sigaction.flags().contains(SigFlags::SA_RESTORER) { ret_code_ptr = sigaction.restorer().unwrap().data() as *mut c_void; } else { - error!( + kerror!( "pid-{:?} forgot to set SA_FLAG_RESTORER for signal {:?}", ProcessManager::current_pcb().pid(), sig as i32 @@ -589,12 +588,12 @@ fn setup_frame( Signal::SIGSEGV as i32, ); if r.is_err() { - error!("In setup_sigcontext: generate SIGSEGV signal failed"); + kerror!("In setup_sigcontext: generate SIGSEGV signal failed"); } return Err(SystemError::EINVAL); } if sigaction.restorer().is_none() { - error!( + kerror!( "restorer in process:{:?} is not defined", ProcessManager::current_pcb().pid() ); @@ -612,12 +611,12 @@ fn setup_frame( }, SigactionType::SaSigaction(_) => { //TODO 这里应该是可以恢复栈的,等后续来做 - error!("trying to recover from sigaction type instead of handler"); + kerror!("trying to recover from sigaction type instead of handler"); return Err(SystemError::EINVAL); } } let frame: *mut SigFrame = get_stack(trap_frame, size_of::()); - // debug!("frame=0x{:016x}", frame as usize); + // kdebug!("frame=0x{:016x}", frame as usize); // 要求这个frame的地址位于用户空间,因此进行校验 let r: Result, SystemError> = UserBufferWriter::new(frame, size_of::(), true); @@ -626,9 +625,9 @@ fn setup_frame( // todo: 生成一个sigsegv let r = Syscall::kill(ProcessManager::current_pcb().pid(), Signal::SIGSEGV as i32); if r.is_err() { - error!("In setup frame: generate SIGSEGV signal failed"); + kerror!("In setup frame: generate SIGSEGV signal failed"); } - error!("In setup frame: access check failed"); + kerror!("In setup frame: access check failed"); return Err(SystemError::EFAULT); } @@ -637,7 +636,7 @@ fn setup_frame( .map_err(|e| -> SystemError { let r = Syscall::kill(ProcessManager::current_pcb().pid(), Signal::SIGSEGV as i32); if r.is_err() { - error!("In copy_siginfo_to_user: generate SIGSEGV signal failed"); + kerror!("In copy_siginfo_to_user: generate SIGSEGV signal failed"); } return e; })?; @@ -651,7 +650,7 @@ fn setup_frame( .map_err(|e: SystemError| -> SystemError { let r = Syscall::kill(ProcessManager::current_pcb().pid(), Signal::SIGSEGV as i32); if r.is_err() { - error!("In setup_sigcontext: generate SIGSEGV signal failed"); + kerror!("In setup_sigcontext: generate SIGSEGV signal failed"); } return e; })? @@ -708,7 +707,7 @@ fn sig_terminate_dump(sig: Signal) { fn sig_stop(sig: Signal) { let guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_stop().unwrap_or_else(|e| { - error!( + kerror!( "sleep error :{:?},failed to sleep process :{:?}, with signal :{:?}", e, ProcessManager::current_pcb(), @@ -722,7 +721,7 @@ fn sig_stop(sig: Signal) { /// 信号默认处理函数——继续进程 fn sig_continue(sig: Signal) { ProcessManager::wakeup_stop(&ProcessManager::current_pcb()).unwrap_or_else(|_| { - error!( + kerror!( "Failed to wake up process pid = {:?} with signal :{:?}", ProcessManager::current_pcb().pid(), sig diff --git a/kernel/src/arch/x86_64/kvm/mod.rs b/kernel/src/arch/x86_64/kvm/mod.rs index d5f6c07aa..c9b8b19aa 100644 --- a/kernel/src/arch/x86_64/kvm/mod.rs +++ b/kernel/src/arch/x86_64/kvm/mod.rs @@ -2,10 +2,13 @@ use crate::arch::kvm::vmx::vmcs::VmcsFields; use crate::arch::kvm::vmx::vmx_asm_wrapper::{vmx_vmlaunch, vmx_vmread}; use crate::libs::mutex::Mutex; use crate::virt::kvm::vm; - +use crate::{ + kdebug, + kerror, + // libs::spinlock::{SpinLock, SpinLockGuard}, +}; use alloc::sync::Arc; use core::arch::asm; -use log::{debug, error}; use raw_cpuid::CpuId; use system_error::SystemError; // use crate::virt::kvm::guest_code; @@ -51,7 +54,7 @@ impl X86_64KVMArch { #[deny(clippy::match_single_binding)] pub fn kvm_arch_dev_ioctl(cmd: u32, _arg: usize) -> Result { - error!("unknown kvm ioctl cmd: {}", cmd); + kerror!("unknown kvm ioctl cmd: {}", cmd); return Err(SystemError::EINVAL); } @@ -71,7 +74,7 @@ impl X86_64KVMArch { Ok(_) => {} Err(e) => { let vmx_err = vmx_vmread(VmcsFields::VMEXIT_INSTR_ERR as u32).unwrap(); - debug!("vmlaunch failed: {:?}", vmx_err); + kdebug!("vmlaunch failed: {:?}", vmx_err); return Err(e); } } @@ -100,12 +103,12 @@ impl X86_64KVMArch { #[no_mangle] pub extern "C" fn guest_code() { - debug!("guest_code"); + kdebug!("guest_code"); loop { unsafe { asm!("mov rax, 0", "mov rcx, 0", "cpuid"); } unsafe { asm!("nop") }; - debug!("guest_code"); + kdebug!("guest_code"); } } diff --git a/kernel/src/arch/x86_64/kvm/vmx/ept.rs b/kernel/src/arch/x86_64/kvm/vmx/ept.rs index 838c1a159..032231902 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/ept.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/ept.rs @@ -1,7 +1,7 @@ use crate::arch::mm::LockedFrameAllocator; use crate::arch::mm::PageMapper; use crate::arch::MMArch; -use crate::mm::page::EntryFlags; +use crate::mm::page::PageFlags; use crate::mm::{PageTableKind, PhysAddr, VirtAddr}; use crate::smp::core::smp_get_processor_id; use crate::smp::cpu::AtomicProcessorId; @@ -92,7 +92,7 @@ impl EptMapper { &mut self, gpa: u64, hpa: u64, - flags: EntryFlags, + flags: PageFlags, ) -> Result<(), SystemError> { if self.readonly { return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); diff --git a/kernel/src/arch/x86_64/kvm/vmx/mmu.rs b/kernel/src/arch/x86_64/kvm/vmx/mmu.rs index c05ef9bb5..2c03c2383 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/mmu.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/mmu.rs @@ -1,11 +1,11 @@ use crate::{ arch::kvm::vmx::ept::EptMapper, + kdebug, libs::mutex::Mutex, - mm::{page::EntryFlags, syscall::ProtFlags}, + mm::{page::PageFlags, syscall::ProtFlags}, virt::kvm::host_mem::{__gfn_to_pfn, kvm_vcpu_gfn_to_memslot, PAGE_MASK, PAGE_SHIFT}, }; use bitfield_struct::bitfield; -use log::debug; use system_error::SystemError; use super::{ @@ -105,7 +105,7 @@ fn tdp_page_fault( error_code: u32, prefault: bool, ) -> Result<(), SystemError> { - debug!("tdp_page_fault"); + kdebug!("tdp_page_fault"); let gfn = gpa >> PAGE_SHIFT; // 物理地址右移12位得到物理页框号(相对于虚拟机而言) // 分配缓存池,为了避免在运行时分配空间失败,这里提前分配/填充足额的空间 mmu_topup_memory_caches(vcpu)?; @@ -211,14 +211,14 @@ pub fn __direct_map( pfn: u64, _prefault: bool, ) -> Result { - debug!("gpa={}, pfn={}, root_hpa={:x}", gpa, pfn, vcpu.mmu.root_hpa); + kdebug!("gpa={}, pfn={}, root_hpa={:x}", gpa, pfn, vcpu.mmu.root_hpa); // 判断vcpu.mmu.root_hpa是否有效 if vcpu.mmu.root_hpa == 0 { return Err(SystemError::KVM_HVA_ERR_BAD); } // 把gpa映射到hpa let mut ept_mapper = EptMapper::lock(); - let page_flags = EntryFlags::from_prot_flags(ProtFlags::from_bits_truncate(0x7_u64), false); + let page_flags = PageFlags::from_prot_flags(ProtFlags::from_bits_truncate(0x7_u64), false); unsafe { assert!(ept_mapper.walk(gpa, pfn << PAGE_SHIFT, page_flags).is_ok()); } diff --git a/kernel/src/arch/x86_64/kvm/vmx/vcpu.rs b/kernel/src/arch/x86_64/kvm/vmx/vcpu.rs index ae6f99baf..b718f5f6f 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/vcpu.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/vcpu.rs @@ -9,15 +9,14 @@ use crate::arch::kvm::vmx::{VcpuRegIndex, X86_CR0}; use crate::arch::mm::{LockedFrameAllocator, PageMapper}; use crate::arch::x86_64::mm::X86_64MMArch; use crate::arch::MMArch; - +use crate::kdebug; +use crate::mm::{phys_2_virt, VirtAddr}; use crate::mm::{MemoryManagementArch, PageTableKind}; -use crate::mm::{PhysAddr, VirtAddr}; use crate::virt::kvm::vcpu::Vcpu; use crate::virt::kvm::vm::Vm; use alloc::alloc::Global; use alloc::boxed::Box; use core::slice; -use log::debug; use raw_cpuid::CpuId; use system_error::SystemError; use x86; @@ -42,7 +41,6 @@ pub struct MSRBitmap { pub data: [u8; PAGE_SIZE], } -#[allow(dead_code)] #[derive(Debug)] pub struct VcpuData { /// The virtual and physical address of the Vmxon naturally aligned 4-KByte region of memory @@ -74,7 +72,6 @@ pub enum VcpuState { Act = 2, } -#[allow(dead_code)] #[derive(Debug)] pub struct VmxVcpu { pub vcpu_id: u32, @@ -135,13 +132,13 @@ impl VcpuData { // Get the Virtual Machine Control Structure revision identifier (VMCS revision ID) // (Intel Manual: 25.11.5 VMXON Region) let revision_id = unsafe { (msr::rdmsr(msr::IA32_VMX_BASIC) as u32) & 0x7FFF_FFFF }; - debug!("[+] VMXON Region Virtual Address: {:p}", self.vmxon_region); - debug!( + kdebug!("[+] VMXON Region Virtual Address: {:p}", self.vmxon_region); + kdebug!( "[+] VMXON Region Physical Addresss: 0x{:x}", self.vmxon_region_physical_address ); - debug!("[+] VMCS Region Virtual Address: {:p}", self.vmcs_region); - debug!( + kdebug!("[+] VMCS Region Virtual Address: {:p}", self.vmcs_region); + kdebug!( "[+] VMCS Region Physical Address1: 0x{:x}", self.vmcs_region_physical_address ); @@ -153,7 +150,7 @@ impl VcpuData { impl VmxVcpu { pub fn new(vcpu_id: u32, parent_vm: Vm) -> Result { - debug!("Creating processor {}", vcpu_id); + kdebug!("Creating processor {}", vcpu_id); let instance = Self { vcpu_id, vcpu_ctx: VcpuContextFrame { @@ -254,8 +251,8 @@ impl VmxVcpu { self.vcpu_ctx.regs[VcpuRegIndex::Rsp as usize] as u64, )?; vmx_vmwrite(VmcsFields::GUEST_RIP as u32, self.vcpu_ctx.rip as u64)?; - debug!("vmcs init guest rip: {:#x}", self.vcpu_ctx.rip as u64); - debug!( + kdebug!("vmcs init guest rip: {:#x}", self.vcpu_ctx.rip as u64); + kdebug!( "vmcs init guest rsp: {:#x}", self.vcpu_ctx.regs[VcpuRegIndex::Rsp as usize] as u64 ); @@ -320,13 +317,13 @@ impl VmxVcpu { )?; vmx_vmwrite( VmcsFields::HOST_GDTR_BASE as u32, - pseudo_descriptpr.base as usize as u64, + pseudo_descriptpr.base.to_bits() as u64, )?; vmx_vmwrite(VmcsFields::HOST_IDTR_BASE as u32, unsafe { let mut pseudo_descriptpr: x86::dtables::DescriptorTablePointer = Default::default(); x86::dtables::sidt(&mut pseudo_descriptpr); - pseudo_descriptpr.base as usize as u64 + pseudo_descriptpr.base.to_bits() as u64 })?; // fast entry into the kernel @@ -341,7 +338,7 @@ impl VmxVcpu { })?; // vmx_vmwrite(VmcsFields::HOST_RIP as u32, vmx_return as *const () as u64)?; - // debug!("vmcs init host rip: {:#x}", vmx_return as *const () as u64); + // kdebug!("vmcs init host rip: {:#x}", vmx_return as *const () as u64); Ok(()) } @@ -391,7 +388,7 @@ impl VmxVcpu { } fn kvm_mmu_load(&mut self) -> Result<(), SystemError> { - debug!("kvm_mmu_load!"); + kdebug!("kvm_mmu_load!"); // 申请并创建新的页表 let mapper: crate::mm::page::PageMapper = unsafe { PageMapper::create(PageTableKind::EPT, LockedFrameAllocator) @@ -402,7 +399,7 @@ impl VmxVcpu { let set_eptp_fn = self.mmu.set_eptp.unwrap(); set_eptp_fn(ept_root_hpa.data() as u64)?; self.mmu.root_hpa = ept_root_hpa.data() as u64; - debug!("ept_root_hpa:{:x}!", ept_root_hpa.data() as u64); + kdebug!("ept_root_hpa:{:x}!", ept_root_hpa.data() as u64); return Ok(()); } @@ -418,33 +415,33 @@ impl Vcpu for VmxVcpu { fn virtualize_cpu(&mut self) -> Result<(), SystemError> { match has_intel_vmx_support() { Ok(_) => { - debug!("[+] CPU supports Intel VMX"); + kdebug!("[+] CPU supports Intel VMX"); } Err(e) => { - debug!("[-] CPU does not support Intel VMX: {:?}", e); + kdebug!("[-] CPU does not support Intel VMX: {:?}", e); return Err(SystemError::ENOSYS); } }; match enable_vmx_operation() { Ok(_) => { - debug!("[+] Enabling Virtual Machine Extensions (VMX)"); + kdebug!("[+] Enabling Virtual Machine Extensions (VMX)"); } Err(_) => { - debug!("[-] VMX operation is not supported on this processor."); + kdebug!("[-] VMX operation is not supported on this processor."); return Err(SystemError::ENOSYS); } } vmxon(self.data.vmxon_region_physical_address)?; - debug!("[+] VMXON successful!"); + kdebug!("[+] VMXON successful!"); vmx_vmclear(self.data.vmcs_region_physical_address)?; vmx_vmptrld(self.data.vmcs_region_physical_address)?; - debug!("[+] VMPTRLD successful!"); + kdebug!("[+] VMPTRLD successful!"); self.vmcs_init().expect("vncs_init fail"); - debug!("[+] VMCS init!"); - // debug!("vmcs init host rip: {:#x}", vmx_return as *const () as u64); - // debug!("vmcs init host rsp: {:#x}", x86::bits64::registers::rsp()); + kdebug!("[+] VMCS init!"); + // kdebug!("vmcs init host rip: {:#x}", vmx_return as *const () as u64); + // kdebug!("vmcs init host rsp: {:#x}", x86::bits64::registers::rsp()); // vmx_vmwrite(VmcsFields::HOST_RSP as u32, x86::bits64::registers::rsp())?; // vmx_vmwrite(VmcsFields::HOST_RIP as u32, vmx_return as *const () as u64)?; // vmx_vmwrite(VmcsFields::HOST_RSP as u32, x86::bits64::registers::rsp())?; @@ -476,9 +473,14 @@ pub fn get_segment_base(gdt_base: *const u64, gdt_size: u16, segment_selector: u let base_mid = (descriptor & 0x0000_00FF_0000_0000) >> 16; let base_low = (descriptor & 0x0000_0000_FFFF_0000) >> 16; let segment_base = (base_high | base_mid | base_low) & 0xFFFFFFFF; - let virtaddr = unsafe { MMArch::phys_2_virt(PhysAddr::new(segment_base as usize)).unwrap() }; - - return virtaddr.data() as u64; + let virtaddr = phys_2_virt(segment_base.try_into().unwrap()) + .try_into() + .unwrap(); + kdebug!( + "segment_base={:x}", + phys_2_virt(segment_base.try_into().unwrap()) + ); + return virtaddr; } // FIXME: may have bug @@ -534,7 +536,7 @@ pub fn adjust_vmx_exit_controls() -> u32 { pub fn adjust_vmx_pinbased_controls() -> u32 { let mut controls: u32 = 16; adjust_vmx_controls(0, 0, msr::IA32_VMX_TRUE_PINBASED_CTLS, &mut controls); - // debug!("adjust_vmx_pinbased_controls: {:x}", controls); + // kdebug!("adjust_vmx_pinbased_controls: {:x}", controls); return controls; } @@ -591,11 +593,11 @@ pub fn enable_vmx_operation() -> Result<(), SystemError> { unsafe { controlregs::cr4_write(cr4) }; set_lock_bit()?; - debug!("[+] Lock bit set via IA32_FEATURE_CONTROL"); + kdebug!("[+] Lock bit set via IA32_FEATURE_CONTROL"); set_cr0_bits(); - debug!("[+] Mandatory bits in CR0 set/cleared"); + kdebug!("[+] Mandatory bits in CR0 set/cleared"); set_cr4_bits(); - debug!("[+] Mandatory bits in CR4 set/cleared"); + kdebug!("[+] Mandatory bits in CR4 set/cleared"); Ok(()) } diff --git a/kernel/src/arch/x86_64/kvm/vmx/vmexit.rs b/kernel/src/arch/x86_64/kvm/vmx/vmexit.rs index b95d51dfb..32fb33727 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/vmexit.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/vmexit.rs @@ -1,9 +1,8 @@ use super::vmcs::{VmcsFields, VmxExitReason}; use super::vmx_asm_wrapper::{vmx_vmread, vmx_vmwrite}; - +use crate::kdebug; use crate::virt::kvm::vm; use core::arch::asm; -use log::debug; use system_error::SystemError; use x86::vmx::vmcs::ro::GUEST_PHYSICAL_ADDR_FULL; @@ -148,7 +147,7 @@ pub struct GuestCpuContext { #[no_mangle] pub extern "C" fn vmx_return() { - debug!("vmx_return!"); + kdebug!("vmx_return!"); unsafe { save_rpg() }; vmexit_handler(); // XMM registers are vector registers. They're renamed onto the FP/SIMD register file @@ -182,14 +181,14 @@ pub extern "C" fn vmx_return() { #[no_mangle] extern "C" fn vmexit_handler() { // let guest_cpu_context = unsafe { guest_cpu_context_ptr.as_mut().unwrap() }; - // debug!("guest_cpu_context_ptr={:p}",guest_cpu_context_ptr); - debug!("vmexit handler!"); + // kdebug!("guest_cpu_context_ptr={:p}",guest_cpu_context_ptr); + kdebug!("vmexit handler!"); let exit_reason = vmx_vmread(VmcsFields::VMEXIT_EXIT_REASON as u32).unwrap() as u32; let exit_basic_reason = exit_reason & 0x0000_ffff; let guest_rip = vmx_vmread(VmcsFields::GUEST_RIP as u32).unwrap(); // let guest_rsp = vmx_vmread(VmcsFields::GUEST_RSP as u32).unwrap(); - debug!("guest_rip={:x}", guest_rip); + kdebug!("guest_rip={:x}", guest_rip); let _guest_rflags = vmx_vmread(VmcsFields::GUEST_RFLAGS as u32).unwrap(); match VmxExitReason::from(exit_basic_reason as i32) { @@ -206,28 +205,28 @@ extern "C" fn vmexit_handler() { | VmxExitReason::VMFUNC | VmxExitReason::INVEPT | VmxExitReason::INVVPID => { - debug!("vmexit handler: vmx instruction!"); + kdebug!("vmexit handler: vmx instruction!"); vmexit_vmx_instruction_executed().expect("previledge instruction handle error"); } VmxExitReason::CPUID => { - debug!("vmexit handler: cpuid instruction!"); + kdebug!("vmexit handler: cpuid instruction!"); // vmexit_cpuid_handler(guest_cpu_context); adjust_rip(guest_rip).unwrap(); } VmxExitReason::RDMSR => { - debug!("vmexit handler: rdmsr instruction!"); + kdebug!("vmexit handler: rdmsr instruction!"); adjust_rip(guest_rip).unwrap(); } VmxExitReason::WRMSR => { - debug!("vmexit handler: wrmsr instruction!"); + kdebug!("vmexit handler: wrmsr instruction!"); adjust_rip(guest_rip).unwrap(); } VmxExitReason::TRIPLE_FAULT => { - debug!("vmexit handler: triple fault!"); + kdebug!("vmexit handler: triple fault!"); adjust_rip(guest_rip).unwrap(); } VmxExitReason::EPT_VIOLATION => { - debug!("vmexit handler: ept violation!"); + kdebug!("vmexit handler: ept violation!"); let gpa = vmx_vmread(GUEST_PHYSICAL_ADDR_FULL).unwrap(); let exit_qualification = vmx_vmread(VmcsFields::VMEXIT_QUALIFICATION as u32).unwrap(); /* It is a write fault? */ @@ -245,17 +244,17 @@ extern "C" fn vmexit_handler() { .expect("ept page fault error"); } _ => { - debug!( + kdebug!( "vmexit handler: unhandled vmexit reason: {}!", exit_basic_reason ); let info = vmx_vmread(VmcsFields::VMEXIT_INSTR_LEN as u32).unwrap() as u32; - debug!("vmexit handler: VMEXIT_INSTR_LEN: {}!", info); + kdebug!("vmexit handler: VMEXIT_INSTR_LEN: {}!", info); let info = vmx_vmread(VmcsFields::VMEXIT_INSTR_INFO as u32).unwrap() as u32; - debug!("vmexit handler: VMEXIT_INSTR_INFO: {}!", info); + kdebug!("vmexit handler: VMEXIT_INSTR_INFO: {}!", info); let info = vmx_vmread(VmcsFields::CTRL_EXPECTION_BITMAP as u32).unwrap() as u32; - debug!("vmexit handler: CTRL_EXPECTION_BITMAP: {}!", info); + kdebug!("vmexit handler: CTRL_EXPECTION_BITMAP: {}!", info); adjust_rip(guest_rip).unwrap(); // panic!(); diff --git a/kernel/src/arch/x86_64/kvm/vmx/vmx_asm_wrapper.rs b/kernel/src/arch/x86_64/kvm/vmx/vmx_asm_wrapper.rs index 0540846a3..449127eae 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/vmx_asm_wrapper.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/vmx_asm_wrapper.rs @@ -1,7 +1,6 @@ use super::vmcs::VmcsFields; - +use crate::kdebug; use core::arch::asm; -use log::debug; use system_error::SystemError; use x86; /// Enable VMX operation. @@ -9,7 +8,7 @@ pub fn vmxon(vmxon_pa: u64) -> Result<(), SystemError> { match unsafe { x86::bits64::vmx::vmxon(vmxon_pa) } { Ok(_) => Ok(()), Err(e) => { - debug!("vmxon fail: {:?}", e); + kdebug!("vmxon fail: {:?}", e); Err(SystemError::EVMXONFailed) } } @@ -28,8 +27,8 @@ pub fn vmx_vmwrite(vmcs_field: u32, value: u64) -> Result<(), SystemError> { match unsafe { x86::bits64::vmx::vmwrite(vmcs_field, value) } { Ok(_) => Ok(()), Err(e) => { - debug!("vmx_write fail: {:?}", e); - debug!("vmcs_field: {:x}", vmcs_field); + kdebug!("vmx_write fail: {:?}", e); + kdebug!("vmcs_field: {:x}", vmcs_field); Err(SystemError::EVMWRITEFailed) } } @@ -40,7 +39,7 @@ pub fn vmx_vmread(vmcs_field: u32) -> Result { match unsafe { x86::bits64::vmx::vmread(vmcs_field) } { Ok(value) => Ok(value), Err(e) => { - debug!("vmx_read fail: {:?}", e); + kdebug!("vmx_read fail: {:?}", e); Err(SystemError::EVMREADFailed) } } @@ -64,10 +63,10 @@ pub fn vmx_vmlaunch() -> Result<(), SystemError> { "push rsi", "push rdi", "vmwrite {0:r}, rsp", - "lea rax, 2f[rip]", + "lea rax, 1f[rip]", "vmwrite {1:r}, rax", "vmlaunch", - "2:", + "1:", "pop rdi", "pop rsi", "pop rdx", @@ -83,7 +82,7 @@ pub fn vmx_vmlaunch() -> Result<(), SystemError> { // match unsafe { x86::bits64::vmx::vmlaunch() } { // Ok(_) => Ok(()), // Err(e) => { - // debug!("vmx_launch fail: {:?}", e); + // kdebug!("vmx_launch fail: {:?}", e); // Err(SystemError::EVMLAUNCHFailed) // }, // } diff --git a/kernel/src/arch/x86_64/mm/fault.rs b/kernel/src/arch/x86_64/mm/fault.rs index 8d7c3346d..02f00cbd6 100644 --- a/kernel/src/arch/x86_64/mm/fault.rs +++ b/kernel/src/arch/x86_64/mm/fault.rs @@ -4,7 +4,6 @@ use core::{ }; use alloc::sync::Arc; -use log::error; use x86::{bits64::rflags::RFlags, controlregs::Cr4}; use crate::{ @@ -14,6 +13,7 @@ use crate::{ CurrentIrqArch, MMArch, }, exception::InterruptArch, + kerror, mm::{ fault::{FaultFlags, PageFaultHandler, PageFaultMessage}, ucontext::{AddressSpace, LockedVMA}, @@ -28,7 +28,7 @@ pub type PageMapper = impl X86_64MMArch { pub fn vma_access_error(vma: Arc, error_code: X86PfErrorCode) -> bool { - let vm_flags = *vma.lock_irqsave().vm_flags(); + let vm_flags = *vma.lock().vm_flags(); let foreign = false; if error_code.contains(X86PfErrorCode::X86_PF_PK) { return true; @@ -74,27 +74,27 @@ impl X86_64MMArch { if let Some(entry) = mapper.get_entry(address, 0) { if entry.present() { if !entry.flags().has_execute() { - error!("kernel tried to execute NX-protected page - exploit attempt?"); + kerror!("kernel tried to execute NX-protected page - exploit attempt?"); } else if mapper.table().phys().data() & MMArch::ENTRY_FLAG_USER != 0 && unsafe { x86::controlregs::cr4().contains(Cr4::CR4_ENABLE_SMEP) } { - error!("unable to execute userspace code (SMEP?)"); + kerror!("unable to execute userspace code (SMEP?)"); } } } if address.data() < X86_64MMArch::PAGE_SIZE && !regs.is_from_user() { - error!( + kerror!( "BUG: kernel NULL pointer dereference, address: {:#x}", address.data() ); } else { - error!( + kerror!( "BUG: unable to handle page fault for address: {:#x}", address.data() ); } - error!( + kerror!( "#PF: {} {} in {} mode\n", if error_code.contains(X86PfErrorCode::X86_PF_USER) { "user" @@ -114,7 +114,7 @@ impl X86_64MMArch { "kernel" } ); - error!( + kerror!( "#PF: error_code({:#04x}) - {}\n", error_code, if !error_code.contains(X86PfErrorCode::X86_PF_PROT) { @@ -223,7 +223,7 @@ impl X86_64MMArch { } let current_address_space: Arc = AddressSpace::current().unwrap(); - let mut space_guard = current_address_space.write_irqsave(); + let mut space_guard = current_address_space.write(); let mut fault; loop { let vma = space_guard.mappings.find_nearest(address); @@ -236,7 +236,7 @@ impl X86_64MMArch { address.data(), ) }); - let guard = vma.lock_irqsave(); + let guard = vma.lock(); let region = *guard.region(); let vm_flags = *guard.vm_flags(); drop(guard); @@ -269,9 +269,11 @@ impl X86_64MMArch { ); } let mapper = &mut space_guard.user_mapper.utable; - let message = PageFaultMessage::new(vma.clone(), address, flags, mapper); - fault = PageFaultHandler::handle_mm_fault(message); + fault = PageFaultHandler::handle_mm_fault( + PageFaultMessage::new(vma.clone(), address, flags), + mapper, + ); if fault.contains(VmFaultReason::VM_FAULT_COMPLETED) { return; diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index f5c5badc6..bfc491eff 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -6,7 +6,6 @@ pub mod pkru; use alloc::sync::Arc; use alloc::vec::Vec; use hashbrown::HashSet; -use log::{debug, info, warn}; use x86::time::rdtsc; use x86_64::registers::model_specific::EferFlags; @@ -28,9 +27,9 @@ use crate::{ }; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}; -use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, VmFlags}; - +use crate::mm::page::{PageEntry, PageFlags, PAGE_1G_SHIFT}; +use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr}; +use crate::{kdebug, kinfo, kwarn}; use system_error::SystemError; use core::arch::asm; @@ -160,8 +159,8 @@ impl MemoryManagementArch for X86_64MMArch { // 初始化物理内存区域(从multiboot2中获取) Self::init_memory_area_from_multiboot2().expect("init memory area failed"); - debug!("bootstrap info: {:?}", unsafe { BOOTSTRAP_MM_INFO }); - debug!("phys[0]=virt[0x{:x}]", unsafe { + kdebug!("bootstrap info: {:?}", unsafe { BOOTSTRAP_MM_INFO }); + kdebug!("phys[0]=virt[0x{:x}]", unsafe { MMArch::phys_2_virt(PhysAddr::new(0)).unwrap().data() }); @@ -326,93 +325,6 @@ impl MemoryManagementArch for X86_64MMArch { } pkru::pkru_allows_pkey(pkru::vma_pkey(vma), write) } - - const PROTECTION_MAP: [EntryFlags; 16] = protection_map(); - - const PAGE_NONE: usize = - Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_ACCESSED | Self::ENTRY_FLAG_GLOBAL; - - const PAGE_SHARED: usize = Self::ENTRY_FLAG_PRESENT - | Self::ENTRY_FLAG_READWRITE - | Self::ENTRY_FLAG_USER - | Self::ENTRY_FLAG_ACCESSED - | Self::ENTRY_FLAG_NO_EXEC; - - const PAGE_SHARED_EXEC: usize = Self::ENTRY_FLAG_PRESENT - | Self::ENTRY_FLAG_READWRITE - | Self::ENTRY_FLAG_USER - | Self::ENTRY_FLAG_ACCESSED; - - const PAGE_COPY_NOEXEC: usize = Self::ENTRY_FLAG_PRESENT - | Self::ENTRY_FLAG_USER - | Self::ENTRY_FLAG_ACCESSED - | Self::ENTRY_FLAG_NO_EXEC; - - const PAGE_COPY_EXEC: usize = - Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_USER | Self::ENTRY_FLAG_ACCESSED; - - const PAGE_COPY: usize = Self::ENTRY_FLAG_PRESENT - | Self::ENTRY_FLAG_USER - | Self::ENTRY_FLAG_ACCESSED - | Self::ENTRY_FLAG_NO_EXEC; - - const PAGE_READONLY: usize = Self::ENTRY_FLAG_PRESENT - | Self::ENTRY_FLAG_USER - | Self::ENTRY_FLAG_ACCESSED - | Self::ENTRY_FLAG_NO_EXEC; - - const PAGE_READONLY_EXEC: usize = - Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_USER | Self::ENTRY_FLAG_ACCESSED; - - const PAGE_READ: usize = 0; - const PAGE_READ_EXEC: usize = 0; - const PAGE_WRITE: usize = 0; - const PAGE_WRITE_EXEC: usize = 0; - const PAGE_EXEC: usize = 0; -} - -/// 获取保护标志的映射表 -/// -/// -/// ## 返回值 -/// - `[usize; 16]`: 长度为16的映射表 -const fn protection_map() -> [EntryFlags; 16] { - let mut map = [unsafe { EntryFlags::from_data(0) }; 16]; - unsafe { - map[VmFlags::VM_NONE.bits()] = EntryFlags::from_data(MMArch::PAGE_NONE); - map[VmFlags::VM_READ.bits()] = EntryFlags::from_data(MMArch::PAGE_READONLY); - map[VmFlags::VM_WRITE.bits()] = EntryFlags::from_data(MMArch::PAGE_COPY); - map[VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = - EntryFlags::from_data(MMArch::PAGE_COPY); - map[VmFlags::VM_EXEC.bits()] = EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = - EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = - EntryFlags::from_data(MMArch::PAGE_COPY_EXEC); - map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = - EntryFlags::from_data(MMArch::PAGE_COPY_EXEC); - map[VmFlags::VM_SHARED.bits()] = EntryFlags::from_data(MMArch::PAGE_NONE); - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_READ.bits()] = - EntryFlags::from_data(MMArch::PAGE_READONLY); - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits()] = - EntryFlags::from_data(MMArch::PAGE_SHARED); - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = - EntryFlags::from_data(MMArch::PAGE_SHARED); - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits()] = - EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = - EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); - map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = - EntryFlags::from_data(MMArch::PAGE_SHARED_EXEC); - map[VmFlags::VM_SHARED.bits() - | VmFlags::VM_EXEC.bits() - | VmFlags::VM_WRITE.bits() - | VmFlags::VM_READ.bits()] = EntryFlags::from_data(MMArch::PAGE_SHARED_EXEC); - } - // if X86_64MMArch::is_xd_reserved() { - // map.iter_mut().for_each(|x| *x &= !Self::ENTRY_FLAG_NO_EXEC) - // } - map } impl X86_64MMArch { @@ -470,16 +382,18 @@ impl X86_64MMArch { info_entry.len as usize, ) .unwrap_or_else(|e| { - warn!( + kwarn!( "Failed to add memory block: base={:#x}, size={:#x}, error={:?}", - info_entry.addr, info_entry.len, e + info_entry.addr, + info_entry.len, + e ); }); areas_count += 1; } } send_to_default_serial8250_port("init_memory_area_from_multiboot2 end\n\0".as_bytes()); - info!("Total memory size: {} MB, total areas from multiboot2: {mb2_count}, valid areas: {areas_count}", total_mem_size / 1024 / 1024); + kinfo!("Total memory size: {} MB, total areas from multiboot2: {mb2_count}, valid areas: {areas_count}", total_mem_size / 1024 / 1024); return Ok(areas_count); } @@ -488,7 +402,7 @@ impl X86_64MMArch { let efer: EferFlags = x86_64::registers::model_specific::Efer::read(); if !efer.contains(EferFlags::NO_EXECUTE_ENABLE) { // NO_EXECUTE_ENABLE是false,那么就设置xd_reserved为true - debug!("NO_EXECUTE_ENABLE is false, set XD_RESERVED to true"); + kdebug!("NO_EXECUTE_ENABLE is false, set XD_RESERVED to true"); XD_RESERVED.store(true, Ordering::Relaxed); } compiler_fence(Ordering::SeqCst); @@ -524,7 +438,7 @@ unsafe fn allocator_init() { .reserve_block(PhysAddr::new(0), phy_offset.data()) .expect("Failed to reserve block"); let mut bump_allocator = BumpAllocator::::new(phy_offset.data()); - debug!( + kdebug!( "BumpAllocator created, offset={:?}", bump_allocator.offset() ); @@ -545,7 +459,7 @@ unsafe fn allocator_init() { ) .expect("Failed to create page mapper"); new_page_table = mapper.table().phys(); - debug!("PageMapper created"); + kdebug!("PageMapper created"); // 取消最开始时候,在head.S中指定的映射(暂时不刷新TLB) { @@ -557,12 +471,12 @@ unsafe fn allocator_init() { .expect("Failed to empty page table entry"); } } - debug!("Successfully emptied page table"); + kdebug!("Successfully emptied page table"); let total_num = mem_block_manager().total_initial_memory_regions(); for i in 0..total_num { let area = mem_block_manager().get_initial_memory_region(i).unwrap(); - // debug!("area: base={:?}, size={:#x}, end={:?}", area.base, area.size, area.base + area.size); + // kdebug!("area: base={:?}, size={:#x}, end={:?}", area.base, area.size, area.base + area.size); for i in 0..((area.size + MMArch::PAGE_SIZE - 1) / MMArch::PAGE_SIZE) { let paddr = area.base.add(i * MMArch::PAGE_SIZE); let vaddr = unsafe { MMArch::phys_2_virt(paddr) }.unwrap(); @@ -580,7 +494,7 @@ unsafe fn allocator_init() { unsafe { INITIAL_CR3_VALUE = new_page_table; } - debug!( + kdebug!( "After mapping all physical memory, DragonOS used: {} KB", bump_allocator.offset() / 1024 ); @@ -589,7 +503,7 @@ unsafe fn allocator_init() { let buddy_allocator = unsafe { BuddyAllocator::::new(bump_allocator).unwrap() }; // 设置全局的页帧分配器 unsafe { set_inner_allocator(buddy_allocator) }; - info!("Successfully initialized buddy allocator"); + kinfo!("Successfully initialized buddy allocator"); // 关闭显示输出 scm_disable_put_to_window(); @@ -597,7 +511,7 @@ unsafe fn allocator_init() { { let mut binding = INNER_ALLOCATOR.lock(); let mut allocator_guard = binding.as_mut().unwrap(); - debug!("To enable new page table."); + kdebug!("To enable new page table."); compiler_fence(Ordering::SeqCst); let mapper = crate::mm::page::PageMapper::::new( PageTableKind::Kernel, @@ -607,9 +521,9 @@ unsafe fn allocator_init() { compiler_fence(Ordering::SeqCst); mapper.make_current(); compiler_fence(Ordering::SeqCst); - debug!("New page table enabled"); + kdebug!("New page table enabled"); } - debug!("Successfully enabled new page table"); + kdebug!("Successfully enabled new page table"); } #[no_mangle] @@ -622,7 +536,7 @@ pub fn test_buddy() { const TOTAL_SIZE: usize = 200 * 1024 * 1024; for i in 0..10 { - debug!("Test buddy, round: {i}"); + kdebug!("Test buddy, round: {i}"); // 存放申请的内存块 let mut v: Vec<(PhysAddr, PageFrameCount)> = Vec::with_capacity(60 * 1024); // 存放已经申请的内存块的地址(用于检查重复) @@ -687,14 +601,14 @@ pub fn test_buddy() { } } - debug!( + kdebug!( "Allocated {} MB memory, release: {} MB, no release: {} bytes", allocated / 1024 / 1024, free_count / 1024 / 1024, (allocated - free_count) ); - debug!("Now, to release buddy memory"); + kdebug!("Now, to release buddy memory"); // 释放所有的内存 for (paddr, allocated_frame_count) in v { unsafe { LockedFrameAllocator.free(paddr, allocated_frame_count) }; @@ -702,7 +616,7 @@ pub fn test_buddy() { free_count += allocated_frame_count.data() * MMArch::PAGE_SIZE; } - debug!("release done!, allocated: {allocated}, free_count: {free_count}"); + kdebug!("release done!, allocated: {allocated}, free_count: {free_count}"); } } @@ -737,17 +651,17 @@ impl FrameAllocator for LockedFrameAllocator { } /// 获取内核地址默认的页面标志 -pub unsafe fn kernel_page_flags(virt: VirtAddr) -> EntryFlags { +pub unsafe fn kernel_page_flags(virt: VirtAddr) -> PageFlags { let info: X86_64MMBootstrapInfo = BOOTSTRAP_MM_INFO.unwrap(); if virt.data() >= info.kernel_code_start && virt.data() < info.kernel_code_end { // Remap kernel code execute - return EntryFlags::new().set_execute(true).set_write(true); + return PageFlags::new().set_execute(true).set_write(true); } else if virt.data() >= info.kernel_data_end && virt.data() < info.kernel_rodata_end { // Remap kernel rodata read only - return EntryFlags::new().set_execute(true); + return PageFlags::new().set_execute(true); } else { - return EntryFlags::new().set_write(true).set_execute(true); + return PageFlags::new().set_write(true).set_execute(true); } } diff --git a/kernel/src/arch/x86_64/mm/pkru.rs b/kernel/src/arch/x86_64/mm/pkru.rs index c40f5f0fa..f467f8d19 100644 --- a/kernel/src/arch/x86_64/mm/pkru.rs +++ b/kernel/src/arch/x86_64/mm/pkru.rs @@ -16,8 +16,8 @@ const PKEY_MASK: usize = 1 << 32 | 1 << 33 | 1 << 34 | 1 << 35; /// ## 返回值 /// - `u16`: vma的protection_key pub fn vma_pkey(vma: Arc) -> u16 { - let guard = vma.lock_irqsave(); - ((guard.vm_flags().bits() & PKEY_MASK) >> VM_PKEY_SHIFT) as u16 + let guard = vma.lock(); + ((guard.vm_flags().bits() & PKEY_MASK as u64) >> VM_PKEY_SHIFT) as u16 } // TODO pkru实现参考:https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/include/asm/pkru.h diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index ff95ab1d1..b2069f087 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -30,7 +30,6 @@ pub use interrupt::X86_64InterruptArch as CurrentIrqArch; pub use crate::arch::asm::pio::X86_64PortIOArch as CurrentPortIOArch; pub use kvm::X86_64KVMArch as KVMArch; -#[allow(unused_imports)] pub use crate::arch::ipc::signal::X86_64SignalArch as CurrentSignalArch; pub use crate::arch::time::X86_64TimeArch as CurrentTimeArch; diff --git a/kernel/src/arch/x86_64/pci/pci.rs b/kernel/src/arch/x86_64/pci/pci.rs index 427d6c88b..ce47e1ffb 100644 --- a/kernel/src/arch/x86_64/pci/pci.rs +++ b/kernel/src/arch/x86_64/pci/pci.rs @@ -2,40 +2,18 @@ use crate::arch::TraitPciArch; use crate::driver::acpi::acpi_manager; use crate::driver::pci::ecam::{pci_ecam_root_info_manager, EcamRootInfo}; use crate::driver::pci::pci::{ - pci_init, BusDeviceFunction, PciAddr, PciCam, PciError, PORT_PCI_CONFIG_ADDRESS, - PORT_PCI_CONFIG_DATA, + pci_init, BusDeviceFunction, PciAddr, PciError, PORT_PCI_CONFIG_ADDRESS, PORT_PCI_CONFIG_DATA, }; -use crate::driver::pci::root::{pci_root_manager, PciRoot}; -use crate::include::bindings::bindings::{io_in32, io_in8, io_out32}; +use crate::include::bindings::bindings::{io_in32, io_out32}; use crate::init::initcall::INITCALL_SUBSYS; +use crate::kerror; use crate::mm::PhysAddr; use acpi::mcfg::Mcfg; -use log::warn; use system_error::SystemError; use unified_init::macros::unified_init; pub struct X86_64PciArch; - -impl X86_64PciArch { - /// # 在早期引导阶段直接访问PCI配置空间的函数 - /// 参考:https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/pci/early.c?fi=read_pci_config_byte#19 - fn read_config_early(bus: u8, slot: u8, func: u8, offset: u8) -> u8 { - unsafe { - io_out32( - PORT_PCI_CONFIG_ADDRESS, - 0x80000000 - | ((bus as u32) << 16) - | ((slot as u32) << 11) - | ((func as u32) << 8) - | offset as u32, - ); - } - let value = unsafe { io_in8(PORT_PCI_CONFIG_DATA + (offset & 3) as u16) }; - return value; - } -} - impl TraitPciArch for X86_64PciArch { fn read_config(bus_device_function: &BusDeviceFunction, offset: u8) -> u32 { // 构造pci配置空间地址 @@ -72,18 +50,8 @@ impl TraitPciArch for X86_64PciArch { #[unified_init(INITCALL_SUBSYS)] fn x86_64_pci_init() -> Result<(), SystemError> { - if discover_ecam_root().is_err() { - // ecam初始化失败,使用portio访问pci配置空间 - // 参考:https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/pci/broadcom_bus.c#27 - let bus_begin = X86_64PciArch::read_config_early(0, 0, 0, 0x44); - let bus_end = X86_64PciArch::read_config_early(0, 0, 0, 0x45); - - if !pci_root_manager().has_root(bus_begin as u16) { - let root = PciRoot::new(None, PciCam::Portiocam, bus_begin, bus_end); - pci_root_manager().add_pci_root(root.unwrap()); - } else { - warn!("x86_64_pci_init(): pci_root_manager {}", bus_begin); - } + if let Err(e) = discover_ecam_root() { + kerror!("x86_64_pci_init(): discover_ecam_root error: {:?}", e); } pci_init(); diff --git a/kernel/src/arch/x86_64/process/idle.rs b/kernel/src/arch/x86_64/process/idle.rs index 79dfb8a59..2ce5f5091 100644 --- a/kernel/src/arch/x86_64/process/idle.rs +++ b/kernel/src/arch/x86_64/process/idle.rs @@ -1,10 +1,9 @@ use core::hint::spin_loop; -use log::error; - use crate::{ arch::CurrentIrqArch, exception::InterruptArch, + kBUG, process::{ProcessFlags, ProcessManager}, sched::{SchedMode, __schedule}, }; @@ -22,7 +21,7 @@ impl ProcessManager { x86::halt(); } } else { - error!("Idle process should not be scheduled with IRQs disabled."); + kBUG!("Idle process should not be scheduled with IRQs disabled."); spin_loop(); } } diff --git a/kernel/src/arch/x86_64/process/kthread.rs b/kernel/src/arch/x86_64/process/kthread.rs index 58f6df1f8..1c0517d2a 100644 --- a/kernel/src/arch/x86_64/process/kthread.rs +++ b/kernel/src/arch/x86_64/process/kthread.rs @@ -42,8 +42,9 @@ impl KernelThreadMechanism { frame.rip = kernel_thread_bootstrap_stage1 as usize as u64; // fork失败的话,子线程不会执行。否则将导致内存安全问题。 - let pid = ProcessManager::fork(&frame, clone_flags).inspect_err(|_e| { + let pid = ProcessManager::fork(&frame, clone_flags).map_err(|e| { unsafe { KernelThreadCreateInfo::parse_unsafe_arc_ptr(create_info) }; + e })?; ProcessManager::find(pid) diff --git a/kernel/src/arch/x86_64/process/mod.rs b/kernel/src/arch/x86_64/process/mod.rs index c4382cdc7..49685c223 100644 --- a/kernel/src/arch/x86_64/process/mod.rs +++ b/kernel/src/arch/x86_64/process/mod.rs @@ -8,13 +8,13 @@ use core::{ use alloc::sync::{Arc, Weak}; use kdepends::memoffset::offset_of; -use log::{error, warn}; use system_error::SystemError; use x86::{controlregs::Cr4, segmentation::SegmentSelector}; use crate::{ arch::process::table::TSSManager, exception::InterruptArch, + kerror, kwarn, libs::spinlock::SpinLockGuard, mm::VirtAddr, process::{ @@ -167,7 +167,7 @@ impl ArchPCBInfo { // 清空浮点寄存器 pub fn clear_fp_state(&mut self) { if unlikely(self.fp_state.is_none()) { - warn!("fp_state is none"); + kwarn!("fp_state is none"); return; } @@ -275,7 +275,7 @@ impl ProcessControlBlock { // 从内核栈的最低地址处取出pcb的地址 let p = stack_base.data() as *const *const ProcessControlBlock; if unlikely((unsafe { *p }).is_null()) { - error!("p={:p}", p); + kerror!("p={:p}", p); panic!("current_pcb is null"); } unsafe { @@ -406,7 +406,7 @@ impl ProcessManager { ); PROCESS_SWITCH_RESULT.as_mut().unwrap().get_mut().prev_pcb = Some(prev); PROCESS_SWITCH_RESULT.as_mut().unwrap().get_mut().next_pcb = Some(next); - // debug!("switch tss ok"); + // kdebug!("switch tss ok"); compiler_fence(Ordering::SeqCst); // 正式切换上下文 switch_to_inner(prev_arch, next_arch); @@ -515,7 +515,7 @@ pub unsafe fn arch_switch_to_user(trap_frame: TrapFrame) -> ! { let trap_frame_vaddr = VirtAddr::new( current_pcb.kernel_stack().stack_max_address().data() - core::mem::size_of::(), ); - // debug!("trap_frame_vaddr: {:?}", trap_frame_vaddr); + // kdebug!("trap_frame_vaddr: {:?}", trap_frame_vaddr); assert!( (x86::current::registers::rsp() as usize) < trap_frame_vaddr.data(), diff --git a/kernel/src/arch/x86_64/process/syscall.rs b/kernel/src/arch/x86_64/process/syscall.rs index 7fe4944cf..84448893f 100644 --- a/kernel/src/arch/x86_64/process/syscall.rs +++ b/kernel/src/arch/x86_64/process/syscall.rs @@ -1,4 +1,4 @@ -use alloc::{ffi::CString, string::String, sync::Arc, vec::Vec}; +use alloc::{string::String, sync::Arc, vec::Vec}; use system_error::SystemError; use crate::{ @@ -19,14 +19,14 @@ use crate::{ impl Syscall { pub fn do_execve( path: String, - argv: Vec, - envp: Vec, + argv: Vec, + envp: Vec, regs: &mut TrapFrame, ) -> Result<(), SystemError> { // 关中断,防止在设置地址空间的时候,发生中断,然后进调度器,出现错误。 let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; let pcb = ProcessManager::current_pcb(); - // log::debug!( + // crate::kdebug!( // "pid: {:?} do_execve: path: {:?}, argv: {:?}, envp: {:?}\n", // pcb.pid(), // path, @@ -55,27 +55,23 @@ impl Syscall { AddressSpace::is_current(&address_space), "Failed to set address space" ); - // debug!("Switch to new address space"); + // kdebug!("Switch to new address space"); // 切换到新的用户地址空间 unsafe { address_space.read().user_mapper.utable.make_current() }; drop(old_address_space); drop(irq_guard); - // debug!("to load binary file"); + // kdebug!("to load binary file"); let mut param = ExecParam::new(path.as_str(), address_space.clone(), ExecParamFlags::EXEC)?; // 加载可执行文件 let load_result = load_binary_file(&mut param)?; - // debug!("load binary file done"); - // debug!("argv: {:?}, envp: {:?}", argv, envp); + // kdebug!("load binary file done"); + // kdebug!("argv: {:?}, envp: {:?}", argv, envp); param.init_info_mut().args = argv; param.init_info_mut().envs = envp; - // 生成16字节随机数 - // TODO 暂时设为0 - param.init_info_mut().rand_num = [0u8; 16]; - // 把proc_init_info写到用户栈上 let mut ustack_message = unsafe { address_space @@ -86,13 +82,19 @@ impl Syscall { }; let (user_sp, argv_ptr) = unsafe { param - .init_info_mut() - .push_at(&mut ustack_message) + .init_info() + .push_at( + // address_space + // .write() + // .user_stack_mut() + // .expect("No user stack found"), + &mut ustack_message, + ) .expect("Failed to push proc_init_info to user stack") }; address_space.write().user_stack = Some(ustack_message); - // debug!("write proc_init_info to user stack done"); + // kdebug!("write proc_init_info to user stack done"); // (兼容旧版libc)把argv的指针写到寄存器内 // TODO: 改写旧版libc,不再需要这个兼容 @@ -114,9 +116,9 @@ impl Syscall { drop(param); - // debug!("regs: {:?}\n", regs); + // kdebug!("regs: {:?}\n", regs); - // crate::debug!( + // crate::kdebug!( // "tmp_rs_execve: done, load_result.entry_point()={:?}", // load_result.entry_point() // ); diff --git a/kernel/src/arch/x86_64/process/table.rs b/kernel/src/arch/x86_64/process/table.rs index cbe5d7878..e28c922c7 100644 --- a/kernel/src/arch/x86_64/process/table.rs +++ b/kernel/src/arch/x86_64/process/table.rs @@ -59,7 +59,6 @@ impl TSSManager { x86::task::load_tr(selector); } - #[allow(static_mut_refs)] unsafe fn set_tss_descriptor(index: u16, vaddr: VirtAddr) { const LIMIT: u64 = 103; let gdt_vaddr = VirtAddr::new(&GDT_Table as *const _ as usize); diff --git a/kernel/src/arch/x86_64/smp/mod.rs b/kernel/src/arch/x86_64/smp/mod.rs index 2eaa5fc74..450464864 100644 --- a/kernel/src/arch/x86_64/smp/mod.rs +++ b/kernel/src/arch/x86_64/smp/mod.rs @@ -5,12 +5,12 @@ use core::{ }; use kdepends::memoffset::offset_of; -use log::debug; use system_error::SystemError; use crate::{ arch::{mm::LowAddressRemapping, process::table::TSSManager, MMArch}, exception::InterruptArch, + kdebug, libs::{cpumask::CpuMask, rwlock::RwLock}, mm::{percpu::PerCpu, MemoryManagementArch, PhysAddr, VirtAddr, IDLE_PROCESS_ADDRESS_SPACE}, process::ProcessManager, @@ -77,7 +77,7 @@ unsafe extern "sysv64" fn smp_init_switch_stack(st: &ApStartStackInfo) -> ! { unsafe extern "C" fn smp_ap_start_stage1() -> ! { let id = smp_get_processor_id(); - debug!("smp_ap_start_stage1: id: {}\n", id.data()); + kdebug!("smp_ap_start_stage1: id: {}\n", id.data()); let current_idle = ProcessManager::idle_pcb()[smp_get_processor_id().data() as usize].clone(); let tss = TSSManager::current_tss(); @@ -187,7 +187,7 @@ fn print_cpus(s: &str, mask: &CpuMask) { v.push(cpu.data()); } - debug!("{s}: cpus: {v:?}\n"); + kdebug!("{s}: cpus: {v:?}\n"); } pub struct X86_64SMPArch; @@ -259,7 +259,6 @@ impl X86_64SMPArch { } impl SmpCpuManager { - #[allow(static_mut_refs)] pub fn arch_init(_boot_cpu: ProcessorId) { assert!(smp_get_processor_id().data() == 0); // 写入APU_START_CR3,这个值会在AP处理器启动时设置到CR3寄存器 diff --git a/kernel/src/arch/x86_64/syscall/mod.rs b/kernel/src/arch/x86_64/syscall/mod.rs index 4ed274c23..f5b8eb156 100644 --- a/kernel/src/arch/x86_64/syscall/mod.rs +++ b/kernel/src/arch/x86_64/syscall/mod.rs @@ -11,7 +11,6 @@ use crate::{ process::ProcessManager, syscall::{Syscall, SYS_SCHED}, }; -use log::debug; use system_error::SystemError; use super::{ @@ -53,7 +52,7 @@ macro_rules! syscall_return { if $show { let pid = ProcessManager::current_pcb().pid(); - debug!("syscall return:pid={:?},ret= {:?}\n", pid, ret as isize); + crate::kdebug!("syscall return:pid={:?},ret= {:?}\n", pid, ret as isize); } unsafe { @@ -95,7 +94,7 @@ pub extern "sysv64" fn syscall_handler(frame: &mut TrapFrame) { // }; if show { - debug!("syscall: pid: {:?}, num={:?}\n", pid, syscall_num); + crate::kdebug!("syscall: pid: {:?}, num={:?}\n", pid, syscall_num); } // Arch specific syscall @@ -127,7 +126,7 @@ pub extern "sysv64" fn syscall_handler(frame: &mut TrapFrame) { /// 系统调用初始化 pub fn arch_syscall_init() -> Result<(), SystemError> { - // info!("arch_syscall_init\n"); + // kinfo!("arch_syscall_init\n"); unsafe { set_system_trap_gate(0x80, 0, VirtAddr::new(syscall_int as usize)) }; // 系统调用门 unsafe { init_syscall_64() }; return Ok(()); diff --git a/kernel/src/arch/x86_64/x86_64-unknown-none.json b/kernel/src/arch/x86_64/x86_64-unknown-none.json index fbc13a4a2..a24b98788 100644 --- a/kernel/src/arch/x86_64/x86_64-unknown-none.json +++ b/kernel/src/arch/x86_64/x86_64-unknown-none.json @@ -1,6 +1,6 @@ { "llvm-target": "x86_64-unknown-none", - "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", + "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", "arch": "x86_64", "target-endian": "little", "target-pointer-width": "64", diff --git a/kernel/src/debug/klog/mm.rs b/kernel/src/debug/klog/mm.rs index ec3100261..619c5fa8f 100644 --- a/kernel/src/debug/klog/mm.rs +++ b/kernel/src/debug/klog/mm.rs @@ -1,10 +1,17 @@ extern crate klog_types; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::{ + intrinsics::unlikely, + sync::atomic::{compiler_fence, Ordering}, +}; use klog_types::{AllocatorLog, AllocatorLogType, LogSource, MMLogChannel}; -use crate::{arch::CurrentTimeArch, process::Pid, time::TimeArch}; +use crate::{ + arch::CurrentTimeArch, + process::{Pid, ProcessManager}, + time::TimeArch, +}; /// 全局的内存分配器日志通道 /// @@ -24,14 +31,13 @@ static __MM_DEBUG_LOG_IDA: ida::IdAllocator = ida::IdAllocator::new(1, usize::MA /// /// - `log_type`:日志类型 /// - `source`:日志来源 -pub fn mm_debug_log(_log_type: AllocatorLogType, _source: LogSource) { - // todo: 由于目前底层的thingbuf存在卡死的问题,因此这里暂时注释掉。 - // let pid = if unlikely(!ProcessManager::initialized()) { - // Some(Pid::new(0)) - // } else { - // Some(ProcessManager::current_pcb().pid()) - // }; - // MMDebugLogManager::log(log_type, source, pid); +pub fn mm_debug_log(log_type: AllocatorLogType, source: LogSource) { + let pid = if unlikely(!ProcessManager::initialized()) { + Some(Pid::new(0)) + } else { + Some(ProcessManager::current_pcb().pid()) + }; + MMDebugLogManager::log(log_type, source, pid); } #[derive(Debug)] @@ -48,7 +54,6 @@ impl MMDebugLogManager { /// - `log_type`:日志类型 /// - `source`:日志来源 /// - `pid`:日志来源的pid - #[allow(dead_code)] pub fn log(log_type: AllocatorLogType, source: LogSource, pid: Option) { let id = __MM_DEBUG_LOG_IDA.alloc().unwrap(); let log = AllocatorLog::new( diff --git a/kernel/src/driver/acpi/bus.rs b/kernel/src/driver/acpi/bus.rs index f91c27847..46869d11e 100644 --- a/kernel/src/driver/acpi/bus.rs +++ b/kernel/src/driver/acpi/bus.rs @@ -111,7 +111,6 @@ impl Bus for AcpiBus { /// /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/acpi/acpi_bus.h#364 -#[allow(unused)] pub trait AcpiDevice: Device {} /// Acpi驱动应当实现的trait @@ -121,5 +120,4 @@ pub trait AcpiDevice: Device {} /// todo: 仿照linux的acpi_driver去设计这个trait /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/acpi/acpi_bus.h#163 -#[allow(unused)] pub trait AcpiDriver: Driver {} diff --git a/kernel/src/driver/acpi/mod.rs b/kernel/src/driver/acpi/mod.rs index 8525f5f17..2d2f644cc 100644 --- a/kernel/src/driver/acpi/mod.rs +++ b/kernel/src/driver/acpi/mod.rs @@ -2,11 +2,11 @@ use core::{fmt::Debug, hint::spin_loop, ptr::NonNull}; use acpi::{AcpiHandler, AcpiTables, PlatformInfo}; use alloc::{string::ToString, sync::Arc}; -use log::{error, info}; use crate::{ arch::MMArch, driver::base::firmware::sys_firmware_kset, + kinfo, libs::align::{page_align_down, page_align_up, AlignedBox}, mm::{ mmio_buddy::{mmio_pool, MMIOSpaceGuard}, @@ -57,7 +57,7 @@ impl AcpiManager { /// /// https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/acpi/bus.c#1390 pub fn init(&self, rsdp_vaddr1: u64, rsdp_vaddr2: u64) -> Result<(), SystemError> { - info!("Initializing Acpi Manager..."); + kinfo!("Initializing Acpi Manager..."); // 初始化`/sys/firmware/acpi`的kset let kset = KSet::new("acpi".to_string()); @@ -67,7 +67,7 @@ impl AcpiManager { } self.map_tables(rsdp_vaddr1, rsdp_vaddr2)?; self.bus_init()?; - info!("Acpi Manager initialized."); + kinfo!("Acpi Manager initialized."); return Ok(()); } @@ -95,7 +95,7 @@ impl AcpiManager { } // 如果rsdpv1和rsdpv2都无法获取到acpi_table,说明有问题,打印报错信息后进入死循环 Err(e2) => { - error!("acpi_init(): failed to parse acpi tables, error: (rsdpv1: {:?}) or (rsdpv2: {:?})", e1, e2); + kerror!("acpi_init(): failed to parse acpi tables, error: (rsdpv1: {:?}) or (rsdpv2: {:?})", e1, e2); Self::drop_rsdp_tmp_box(); loop { spin_loop(); @@ -161,7 +161,7 @@ impl AcpiManager { pub fn platform_info(&self) -> Option> { let r = self.tables()?.platform_info(); if let Err(ref e) = r { - error!( + kerror!( "AcpiManager::platform_info(): failed to get platform info, error: {:?}", e ); diff --git a/kernel/src/driver/acpi/pmtmr.rs b/kernel/src/driver/acpi/pmtmr.rs index 37d292839..aec175376 100644 --- a/kernel/src/driver/acpi/pmtmr.rs +++ b/kernel/src/driver/acpi/pmtmr.rs @@ -12,7 +12,7 @@ pub const ACPI_PM_MASK: u64 = 0xffffff; pub fn acpi_pm_read_early() -> u32 { use crate::driver::clocksource::acpi_pm::{acpi_pm_read_verified, PMTMR_IO_PORT}; use core::sync::atomic::Ordering; - let port = PMTMR_IO_PORT.load(Ordering::SeqCst); + let port = unsafe { PMTMR_IO_PORT.load(Ordering::SeqCst) }; // 如果端口为零直接返回 if port == 0 { diff --git a/kernel/src/driver/acpi/sysfs.rs b/kernel/src/driver/acpi/sysfs.rs index 34bc04b25..73e8278f0 100644 --- a/kernel/src/driver/acpi/sysfs.rs +++ b/kernel/src/driver/acpi/sysfs.rs @@ -18,7 +18,6 @@ use alloc::{ sync::Arc, vec::Vec, }; -use log::{debug, error, warn}; use system_error::SystemError; use super::{acpi_kset, AcpiManager}; @@ -110,7 +109,7 @@ impl AcpiManager { let tables = self.tables().unwrap(); let headers = tables.headers(); for header in headers { - debug!("ACPI header: {:?}", header); + kdebug!("ACPI header: {:?}", header); let attr = AttrAcpiTable::new(&header)?; acpi_table_attr_list().write().push(attr); self.acpi_table_data_init(&header)?; @@ -173,7 +172,7 @@ impl AttrAcpiTable { // 将当前实例的序号加1 r.instance += 1; if r.instance > ACPI_MAX_TABLE_INSTANCES as isize { - warn!("too many table instances. name: {}", r.name); + kwarn!("too many table instances. name: {}", r.name); return Err(SystemError::ERANGE); } @@ -290,9 +289,10 @@ impl BinAttribute for AttrAcpiTable { ($name: ident, $tables: expr) => { define_struct!($name); let table = $tables.find_entire_table::<$name>().map_err(|e| { - warn!( + kwarn!( "AttrAcpiTable::read(): failed to find table. name: {}, error: {:?}", - self.name, e + self.name, + e ); SystemError::ENODEV })?; @@ -500,7 +500,7 @@ impl BinAttribute for AttrAcpiTable { } _ => { - error!("AttrAcpiTable::read(): unknown table. name: {}", self.name); + kerror!("AttrAcpiTable::read(): unknown table. name: {}", self.name); return Err(SystemError::ENODEV); } }; diff --git a/kernel/src/driver/base/block/block_device.rs b/kernel/src/driver/base/block/block_device.rs index 729157a62..37fd975a9 100644 --- a/kernel/src/driver/base/block/block_device.rs +++ b/kernel/src/driver/base/block/block_device.rs @@ -1,21 +1,23 @@ /// 引入Module -use crate::driver::{ - base::{ - device::{ - device_number::{DeviceNumber, Major}, - Device, DeviceError, IdTable, BLOCKDEVS, - }, - map::{ - DeviceStruct, DEV_MAJOR_DYN_END, DEV_MAJOR_DYN_EXT_END, DEV_MAJOR_DYN_EXT_START, - DEV_MAJOR_HASH_SIZE, DEV_MAJOR_MAX, +use crate::{ + driver::{ + base::{ + device::{ + device_number::{DeviceNumber, Major}, + Device, DeviceError, IdTable, BLOCKDEVS, + }, + map::{ + DeviceStruct, DEV_MAJOR_DYN_END, DEV_MAJOR_DYN_EXT_END, DEV_MAJOR_DYN_EXT_START, + DEV_MAJOR_HASH_SIZE, DEV_MAJOR_MAX, + }, }, + block::cache::{cached_block_device::BlockCache, BlockCacheError, BLOCK_SIZE}, }, - block::cache::{cached_block_device::BlockCache, BlockCacheError, BLOCK_SIZE}, + kerror, }; use alloc::{sync::Arc, vec::Vec}; use core::any::Any; -use log::error; use system_error::SystemError; use super::disk_info::Partition; @@ -473,7 +475,7 @@ impl BlockDeviceOps { let mut major = device_number.major(); let baseminor = device_number.minor(); if major >= DEV_MAJOR_MAX { - error!( + kerror!( "DEV {} major requested {:?} is greater than the maximum {}\n", name, major, @@ -481,7 +483,7 @@ impl BlockDeviceOps { ); } if minorct > DeviceNumber::MINOR_MASK + 1 - baseminor { - error!("DEV {} minor range requested ({}-{}) is out of range of maximum range ({}-{}) for a single major\n", + kerror!("DEV {} minor range requested ({}-{}) is out of range of maximum range ({}-{}) for a single major\n", name, baseminor, baseminor + minorct - 1, 0, DeviceNumber::MINOR_MASK); } let blockdev = DeviceStruct::new(DeviceNumber::new(major, baseminor), minorct, name); @@ -547,7 +549,7 @@ impl BlockDeviceOps { #[allow(dead_code)] pub fn bdev_add(_bdev: Arc, id_table: IdTable) -> Result<(), DeviceError> { if id_table.device_number().data() == 0 { - error!("Device number can't be 0!\n"); + kerror!("Device number can't be 0!\n"); } todo!("bdev_add") // return device_manager().add_device(bdev.id_table(), bdev.device()); diff --git a/kernel/src/driver/base/char/mod.rs b/kernel/src/driver/base/char/mod.rs index 090abef18..cf9483c34 100644 --- a/kernel/src/driver/base/char/mod.rs +++ b/kernel/src/driver/base/char/mod.rs @@ -1,6 +1,6 @@ use alloc::sync::Arc; -use log::error; +use crate::kerror; use system_error::SystemError; use super::{ @@ -129,7 +129,7 @@ impl CharDevOps { let mut major = device_number.major(); let baseminor = device_number.minor(); if major >= DEV_MAJOR_MAX { - error!( + kerror!( "DEV {} major requested {:?} is greater than the maximum {}\n", name, major, @@ -137,7 +137,7 @@ impl CharDevOps { ); } if minorct > DeviceNumber::MINOR_MASK + 1 - baseminor { - error!("DEV {} minor range requested ({}-{}) is out of range of maximum range ({}-{}) for a single major\n", + kerror!("DEV {} minor range requested ({}-{}) is out of range of maximum range ({}-{}) for a single major\n", name, baseminor, baseminor + minorct - 1, 0, DeviceNumber::MINOR_MASK); } let chardev = DeviceStruct::new(DeviceNumber::new(major, baseminor), minorct, name); @@ -207,7 +207,7 @@ impl CharDevOps { range: usize, ) -> Result<(), SystemError> { if id_table.device_number().data() == 0 { - error!("Device number can't be 0!\n"); + kerror!("Device number can't be 0!\n"); } device_manager().add_device(cdev.clone())?; kobj_map( diff --git a/kernel/src/driver/base/device/bus.rs b/kernel/src/driver/base/device/bus.rs index b7e70f893..a94b1fae3 100644 --- a/kernel/src/driver/base/device/bus.rs +++ b/kernel/src/driver/base/device/bus.rs @@ -25,7 +25,6 @@ use alloc::{ use core::{ffi::CStr, fmt::Debug, intrinsics::unlikely}; use hashbrown::HashMap; use intertrait::cast::CastArc; -use log::{debug, error, info}; use system_error::SystemError; /// `/sys/bus`的kset @@ -297,7 +296,7 @@ impl BusManager { .bus() .and_then(|bus| bus.upgrade()) .ok_or(SystemError::EINVAL)?; - debug!("bus '{}' add driver '{}'", bus.name(), driver.name()); + kdebug!("bus '{}' add driver '{}'", bus.name(), driver.name()); driver.set_kobj_type(Some(&BusDriverKType)); let kobj = driver.clone() as Arc; @@ -315,7 +314,7 @@ impl BusManager { driver_manager() .add_groups(driver, bus.drv_groups()) .map_err(|e| { - error!( + kerror!( "BusManager::add_driver: driver '{:?}' add_groups failed, err: '{:?}", driver.name(), e @@ -327,7 +326,7 @@ impl BusManager { if !driver.suppress_bind_attrs() { self.add_bind_files(driver) .map_err(|e| { - error!( + kerror!( "BusManager::add_driver: driver '{:?}' add_bind_files failed, err: '{:?}", driver.name(), e @@ -478,8 +477,9 @@ impl BusManager { driver_manager() .create_attr_file(driver, &DriverAttrBind) - .inspect_err(|_e| { + .map_err(|e| { driver_manager().remove_attr_file(driver, &DriverAttrUnbind); + e })?; return Ok(()); @@ -580,7 +580,7 @@ pub fn bus_add_device(dev: &Arc) -> Result<(), SystemError> { /// /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/base/bus.c?fi=bus_probe_device#478 pub fn bus_probe_device(dev: &Arc) { - info!("bus_probe_device: dev: {:?}", dev.name()); + kinfo!("bus_probe_device: dev: {:?}", dev.name()); bus_manager().probe_device(dev); } @@ -746,7 +746,7 @@ impl Attribute for DriverAttrUnbind { fn store(&self, kobj: Arc, buf: &[u8]) -> Result { let driver = kobj.cast::().map_err(|kobj| { - error!( + kerror!( "Intertrait casting not implemented for kobj: {}", kobj.name() ); @@ -795,7 +795,7 @@ impl Attribute for DriverAttrBind { */ fn store(&self, kobj: Arc, buf: &[u8]) -> Result { let driver = kobj.cast::().map_err(|kobj| { - error!( + kerror!( "Intertrait casting not implemented for kobj: {}", kobj.name() ); diff --git a/kernel/src/driver/base/device/dd.rs b/kernel/src/driver/base/device/dd.rs index 358c51950..7adb840bc 100644 --- a/kernel/src/driver/base/device/dd.rs +++ b/kernel/src/driver/base/device/dd.rs @@ -2,7 +2,6 @@ use core::intrinsics::unlikely; use alloc::{string::ToString, sync::Arc}; use intertrait::cast::CastArc; -use log::{debug, error, warn}; use crate::{ driver::base::kobject::KObject, @@ -60,20 +59,20 @@ impl DeviceManager { ) -> Result { if unlikely(allow_async) { // todo!("do_device_attach: allow_async") - warn!("do_device_attach: allow_async is true, but currently not supported"); + kwarn!("do_device_attach: allow_async is true, but currently not supported"); } if dev.is_dead() { return Ok(false); } - warn!("do_device_attach: dev: '{}'", dev.name()); + kwarn!("do_device_attach: dev: '{}'", dev.name()); let mut do_async = false; let mut r = Ok(false); if dev.driver().is_some() { if self.device_is_bound(dev) { - debug!( + kdebug!( "do_device_attach: device '{}' is already bound.", dev.name() ); @@ -87,7 +86,7 @@ impl DeviceManager { return Ok(false); } } else { - debug!("do_device_attach: device '{}' is not bound.", dev.name()); + kdebug!("do_device_attach: device '{}' is not bound.", dev.name()); let bus = dev .bus() .and_then(|bus| bus.upgrade()) @@ -117,7 +116,7 @@ impl DeviceManager { // try them. do_async = true; - debug!( + kdebug!( "do_device_attach: try scheduling asynchronous probe for device: {}", dev.name() ); @@ -142,7 +141,6 @@ impl DeviceManager { /// - Ok(true): 匹配成功 /// - Ok(false): 没有匹配成功 /// - Err(SystemError): 匹配过程中出现意外错误,没有匹配成功 - /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/base/dd.c#899 fn do_device_attach_driver( &self, @@ -155,7 +153,7 @@ impl DeviceManager { if let Err(e) = r { // 如果不是ENOSYS,则总线出错 if e != SystemError::ENOSYS { - debug!( + kdebug!( "do_device_attach_driver: bus.match_device() failed, dev: '{}', err: {:?}", data.dev.name(), e @@ -217,7 +215,7 @@ impl DeviceManager { } if let Err(e) = r.as_ref() { - error!( + kerror!( "device_bind_driver: driver_sysfs_add failed, dev: '{}', err: {:?}", dev.name(), e @@ -403,7 +401,7 @@ impl DriverManager { device.set_driver(Some(Arc::downgrade(driver))); self.add_to_sysfs(device).map_err(|e| { - error!( + kerror!( "really_probe: add_to_sysfs failed, dev: '{}', err: {:?}", device.name(), e @@ -414,7 +412,7 @@ impl DriverManager { })?; self.call_driver_probe(device, driver).map_err(|e| { - error!( + kerror!( "really_probe: call_driver_probe failed, dev: '{}', err: {:?}", device.name(), e @@ -429,7 +427,7 @@ impl DriverManager { device_manager() .add_groups(device, driver.dev_groups()) .map_err(|e| { - error!( + kerror!( "really_probe: add_groups failed, dev: '{}', err: {:?}", device.name(), e @@ -445,7 +443,7 @@ impl DriverManager { device_manager() .create_file(device, &DeviceAttrStateSynced) .map_err(|e| { - error!( + kerror!( "really_probe: create_file failed, dev: '{}', err: {:?}", device.name(), e @@ -485,15 +483,17 @@ impl DriverManager { sysfs_instance() .create_link(Some(&device_kobj), &driver_kobj, "driver".to_string()) - .inspect_err(|_e| { + .map_err(|e| { fail_rm_dev_link(); + e })?; device_manager() .create_file(device, &DeviceAttrCoredump) - .inspect_err(|_e| { + .map_err(|e| { sysfs_instance().remove_link(&device_kobj, "driver".to_string()); fail_rm_dev_link(); + e })?; return Ok(()); @@ -515,7 +515,7 @@ impl DriverManager { .ok_or(SystemError::EINVAL)?; let r = bus.probe(device); if r == Err(SystemError::ENOSYS) { - error!( + kerror!( "call_driver_probe: bus.probe() failed, dev: '{}', err: {:?}", device.name(), r @@ -530,7 +530,7 @@ impl DriverManager { let err = r.unwrap_err(); match err { SystemError::ENODEV | SystemError::ENXIO => { - debug!( + kdebug!( "driver'{}': probe of {} rejects match {:?}", driver.name(), device.name(), @@ -539,7 +539,7 @@ impl DriverManager { } _ => { - warn!( + kwarn!( "driver'{}': probe of {} failed with error {:?}", driver.name(), device.name(), @@ -555,7 +555,7 @@ impl DriverManager { /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/base/dd.c#393 fn driver_bound(&self, device: &Arc) { if self.driver_is_bound(device) { - warn!("driver_bound: device '{}' is already bound.", device.name()); + kwarn!("driver_bound: device '{}' is already bound.", device.name()); return; } @@ -600,7 +600,7 @@ impl Attribute for DeviceAttrStateSynced { fn show(&self, kobj: Arc, buf: &mut [u8]) -> Result { let dev = kobj.cast::().map_err(|kobj| { - error!( + kerror!( "Intertrait casting not implemented for kobj: {}", kobj.name() ); @@ -635,7 +635,7 @@ impl Attribute for DeviceAttrCoredump { fn store(&self, kobj: Arc, buf: &[u8]) -> Result { let dev = kobj.cast::().map_err(|kobj| { - error!( + kerror!( "Intertrait casting not implemented for kobj: {}", kobj.name() ); diff --git a/kernel/src/driver/base/device/driver.rs b/kernel/src/driver/base/device/driver.rs index c2f77a598..0df615e12 100644 --- a/kernel/src/driver/base/device/driver.rs +++ b/kernel/src/driver/base/device/driver.rs @@ -15,7 +15,6 @@ use alloc::{ vec::Vec, }; use core::fmt::Debug; -use log::error; use system_error::SystemError; /// @brief: Driver error @@ -194,7 +193,7 @@ impl DriverManager { /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/base/driver.c#222 pub fn register(&self, driver: Arc) -> Result<(), SystemError> { let bus = driver.bus().and_then(|bus| bus.upgrade()).ok_or_else(|| { - error!( + kerror!( "DriverManager::register() failed: driver.bus() is None. Driver: '{:?}'", driver.name() ); @@ -204,7 +203,7 @@ impl DriverManager { let drv_name = driver.name(); let other = bus.find_driver_by_name(&drv_name); if other.is_some() { - error!( + kerror!( "DriverManager::register() failed: driver '{}' already registered", drv_name ); @@ -213,10 +212,10 @@ impl DriverManager { bus_manager().add_driver(&driver)?; - self.add_groups(&driver, driver.groups()) - .inspect_err(|_e| { - bus_manager().remove_driver(&driver); - })?; + self.add_groups(&driver, driver.groups()).map_err(|e| { + bus_manager().remove_driver(&driver); + e + })?; // todo: 发送uevent diff --git a/kernel/src/driver/base/device/init.rs b/kernel/src/driver/base/device/init.rs index 62f5e8004..7138d6448 100644 --- a/kernel/src/driver/base/device/init.rs +++ b/kernel/src/driver/base/device/init.rs @@ -1,13 +1,16 @@ use alloc::{string::ToString, sync::Arc}; -use log::info; -use crate::driver::base::{ - device::{ - set_sys_dev_block_kset, set_sys_dev_char_kset, set_sys_devices_virtual_kset, sys_dev_kset, - sys_devices_kset, DeviceManager, DEVICES_KSET_INSTANCE, DEVICE_MANAGER, DEV_KSET_INSTANCE, +use crate::{ + driver::base::{ + device::{ + set_sys_dev_block_kset, set_sys_dev_char_kset, set_sys_devices_virtual_kset, + sys_dev_kset, sys_devices_kset, DeviceManager, DEVICES_KSET_INSTANCE, DEVICE_MANAGER, + DEV_KSET_INSTANCE, + }, + kobject::KObject, + kset::KSet, }, - kobject::KObject, - kset::KSet, + kinfo, }; use system_error::SystemError; @@ -51,7 +54,7 @@ pub fn devices_init() -> Result<(), SystemError> { // 创建 `/sys/dev/block` 目录 { - // debug!("create /sys/dev/block"); + // kdebug!("create /sys/dev/block"); let dev_kset = sys_dev_kset(); let dev_block_kset = KSet::new("block".to_string()); let parent = dev_kset.clone() as Arc; @@ -66,7 +69,7 @@ pub fn devices_init() -> Result<(), SystemError> { // 创建 `/sys/dev/char` 目录 { - // debug!("create /sys/dev/char"); + // kdebug!("create /sys/dev/char"); let dev_kset = sys_dev_kset(); let dev_char_kset = KSet::new("char".to_string()); let parent = dev_kset.clone() as Arc; @@ -79,7 +82,7 @@ pub fn devices_init() -> Result<(), SystemError> { unsafe { set_sys_dev_char_kset(dev_char_kset) }; } - info!("devices init success"); + kinfo!("devices init success"); return Ok(()); } diff --git a/kernel/src/driver/base/device/mod.rs b/kernel/src/driver/base/device/mod.rs index 8803b02b7..2281d124c 100644 --- a/kernel/src/driver/base/device/mod.rs +++ b/kernel/src/driver/base/device/mod.rs @@ -3,7 +3,6 @@ use alloc::{ sync::{Arc, Weak}, }; use intertrait::cast::CastArc; -use log::{error, warn}; use crate::{ driver::{ @@ -76,7 +75,7 @@ static mut DEVICES_VIRTUAL_KSET_INSTANCE: Option> = None; /// 获取`/sys/devices`的kset实例 #[inline(always)] -pub fn sys_devices_kset() -> Arc { +pub(super) fn sys_devices_kset() -> Arc { unsafe { DEVICES_KSET_INSTANCE.as_ref().unwrap().clone() } } @@ -140,7 +139,7 @@ pub trait Device: KObject { /// 设备释放时的回调函数 fn release(&self) { let name = self.name(); - warn!( + kwarn!( "device {} does not have a release() function, it is broken and must be fixed.", name ); @@ -288,7 +287,6 @@ pub enum DeviceType { Intc, PlatformDev, Char, - Pci, } /// @brief: 设备标识符类型 @@ -483,7 +481,7 @@ impl DeviceManager { let actual_parent = self.get_device_parent(&device, current_parent)?; if let Some(actual_parent) = actual_parent { - // debug!( + // kdebug!( // "device '{}' parent is '{}', strong_count: {}", // device.name().to_string(), // actual_parent.name(), @@ -493,7 +491,7 @@ impl DeviceManager { } KObjectManager::add_kobj(device.clone() as Arc, None).map_err(|e| { - error!("add device '{:?}' failed: {:?}", device.name(), e); + kerror!("add device '{:?}' failed: {:?}", device.name(), e); e })?; @@ -553,10 +551,10 @@ impl DeviceManager { device: &Arc, current_parent: Option>, ) -> Result>, SystemError> { - // debug!("get_device_parent() device:{:?}", device.name()); + // kdebug!("get_device_parent() device:{:?}", device.name()); if device.class().is_some() { let parent_kobj: Arc; - // debug!("current_parent:{:?}", current_parent); + // kdebug!("current_parent:{:?}", current_parent); if let Some(cp) = current_parent { if cp.class().is_some() { return Ok(Some(cp.clone() as Arc)); @@ -646,16 +644,18 @@ impl DeviceManager { let parent_kobj = parent.clone() as Arc; sysfs_instance() .create_link(Some(&dev_kobj), &parent_kobj, "device".to_string()) - .inspect_err(|_e| { + .map_err(|e| { err_remove_subsystem(&dev_kobj); + e })?; } sysfs_instance() .create_link(Some(&subsys_kobj), &dev_kobj, dev.name()) - .inspect_err(|_e| { + .map_err(|e| { err_remove_device(&dev_kobj); err_remove_subsystem(&dev_kobj); + e })?; return Ok(()); @@ -693,16 +693,18 @@ impl DeviceManager { // 添加kobj_type的属性文件 if let Some(kobj_type) = dev.kobj_type() { self.add_groups(dev, kobj_type.attribute_groups().unwrap_or(&[])) - .inspect_err(|_e| { + .map_err(|e| { err_remove_class_groups(dev); + e })?; } // 添加设备本身的属性文件 self.add_groups(dev, dev.attribute_groups().unwrap_or(&[])) - .inspect_err(|_e| { + .map_err(|e| { err_remove_kobj_type_groups(dev); err_remove_class_groups(dev); + e })?; return Ok(()); @@ -753,7 +755,7 @@ impl DeviceManager { attr.mode().contains(ModeType::S_IRUGO) && (!attr.support().contains(SysFSOpsSupport::ATTR_SHOW)), ) { - warn!( + kwarn!( "Attribute '{}': read permission without 'show'", attr.name() ); @@ -762,7 +764,7 @@ impl DeviceManager { attr.mode().contains(ModeType::S_IWUGO) && (!attr.support().contains(SysFSOpsSupport::ATTR_STORE)), ) { - warn!( + kwarn!( "Attribute '{}': write permission without 'store'", attr.name() ); @@ -804,7 +806,7 @@ impl DeviceManager { /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/base/core.c?fi=device_links_force_bind#1226 pub fn device_links_force_bind(&self, _dev: &Arc) { - warn!("device_links_force_bind not implemented"); + kwarn!("device_links_force_bind not implemented"); } /// 把device对象的一些结构进行默认初始化 @@ -869,7 +871,7 @@ impl Attribute for DeviceAttrDev { fn show(&self, kobj: Arc, buf: &mut [u8]) -> Result { let dev = kobj.cast::().map_err(|kobj| { - error!( + kerror!( "Intertrait casting not implemented for kobj: {}", kobj.name() ); diff --git a/kernel/src/driver/base/kobject.rs b/kernel/src/driver/base/kobject.rs index 73e3c7eeb..a8659c44d 100644 --- a/kernel/src/driver/base/kobject.rs +++ b/kernel/src/driver/base/kobject.rs @@ -6,13 +6,13 @@ use alloc::{ }; use driver_base_macros::get_weak_or_clear; use intertrait::CastFromSync; -use log::{debug, error}; use crate::{ filesystem::{ kernfs::KernFSInode, sysfs::{sysfs_instance, Attribute, AttributeGroup, SysFSOps, SysFSOpsSupport}, }, + kerror, libs::{ casting::DowncastArc, rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, @@ -213,7 +213,7 @@ impl KObjectManager { } kobj.set_parent(None); if e == SystemError::EEXIST { - error!("KObjectManager::add_kobj() failed with error: {e:?}, kobj:{kobj:?}"); + kerror!("KObjectManager::add_kobj() failed with error: {e:?}, kobj:{kobj:?}"); } return Err(e); @@ -269,7 +269,7 @@ pub struct DynamicKObjKType; impl KObjType for DynamicKObjKType { fn release(&self, kobj: Arc) { - debug!("DynamicKObjKType::release() kobj:{:?}", kobj.name()); + kdebug!("DynamicKObjKType::release() kobj:{:?}", kobj.name()); } fn sysfs_ops(&self) -> Option<&dyn SysFSOps> { diff --git a/kernel/src/driver/base/platform/platform_device.rs b/kernel/src/driver/base/platform/platform_device.rs index 32163e009..50701f5c9 100644 --- a/kernel/src/driver/base/platform/platform_device.rs +++ b/kernel/src/driver/base/platform/platform_device.rs @@ -64,7 +64,6 @@ pub trait PlatformDevice: Device { /// @brief: 判断设备是否初始化 /// @parameter: None /// @return: 如果已经初始化,返回true,否则,返回false - #[allow(dead_code)] fn is_initialized(&self) -> bool; /// @brief: 设置设备状态 diff --git a/kernel/src/driver/base/platform/platform_driver.rs b/kernel/src/driver/base/platform/platform_driver.rs index d0d786863..b9cda0f75 100644 --- a/kernel/src/driver/base/platform/platform_driver.rs +++ b/kernel/src/driver/base/platform/platform_driver.rs @@ -16,7 +16,6 @@ use super::{platform_bus, platform_device::PlatformDevice}; /// /// 应当在所有实现这个trait的结构体上方,添加 `#[cast_to([sync] PlatformDriver)]`, /// 否则运行时将报错“该对象不是PlatformDriver” -#[allow(dead_code)] pub trait PlatformDriver: Driver { /// 检测设备是否能绑定到这个驱动 /// diff --git a/kernel/src/driver/base/platform/subsys.rs b/kernel/src/driver/base/platform/subsys.rs index 57e5c2583..26eb3d888 100644 --- a/kernel/src/driver/base/platform/subsys.rs +++ b/kernel/src/driver/base/platform/subsys.rs @@ -3,7 +3,6 @@ use alloc::{ sync::{Arc, Weak}, }; use intertrait::cast::CastArc; -use log::error; use super::{platform_device::PlatformDevice, platform_driver::PlatformDriver}; use crate::{ @@ -59,12 +58,12 @@ impl Bus for PlatformBus { fn probe(&self, device: &Arc) -> Result<(), SystemError> { let drv = device.driver().ok_or(SystemError::EINVAL)?; let pdrv = drv.cast::().map_err(|_|{ - error!("PlatformBus::probe() failed: device.driver() is not a PlatformDriver. Device: '{:?}'", device.name()); + kerror!("PlatformBus::probe() failed: device.driver() is not a PlatformDriver. Device: '{:?}'", device.name()); SystemError::EINVAL })?; let pdev = device.clone().cast::().map_err(|_| { - error!( + kerror!( "PlatformBus::probe() failed: device is not a PlatformDevice. Device: '{:?}'", device.name() ); diff --git a/kernel/src/driver/block/cache/cached_block_device.rs b/kernel/src/driver/block/cache/cached_block_device.rs index 9d7f2020f..29443042f 100644 --- a/kernel/src/driver/block/cache/cached_block_device.rs +++ b/kernel/src/driver/block/cache/cached_block_device.rs @@ -1,6 +1,5 @@ use alloc::{boxed::Box, vec::Vec}; use hashbrown::HashMap; -use log::debug; use crate::{driver::base::block::block_device::BlockId, libs::rwlock::RwLock}; @@ -16,7 +15,6 @@ static mut CMAPPER: Option = None; /// 该结构体向外提供BlockCache服务 pub struct BlockCache; -#[allow(static_mut_refs)] unsafe fn mapper() -> Result<&'static mut LockedCacheMapper, BlockCacheError> { unsafe { match &mut CMAPPER { @@ -26,7 +24,6 @@ unsafe fn mapper() -> Result<&'static mut LockedCacheMapper, BlockCacheError> { }; } -#[allow(static_mut_refs)] unsafe fn space() -> Result<&'static mut LockedCacheSpace, BlockCacheError> { unsafe { match &mut CSPACE { @@ -44,7 +41,7 @@ impl BlockCache { CSPACE = Some(LockedCacheSpace::new(CacheSpace::new())); CMAPPER = Some(LockedCacheMapper::new(CacheMapper::new())); } - debug!("BlockCache Initialized!"); + kdebug!("BlockCache Initialized!"); } /// # 函数的功能 /// 使用blockcache进行对块设备进行连续块的读操作 diff --git a/kernel/src/driver/block/virtio_blk.rs b/kernel/src/driver/block/virtio_blk.rs index 2475e9f93..75fe897eb 100644 --- a/kernel/src/driver/block/virtio_blk.rs +++ b/kernel/src/driver/block/virtio_blk.rs @@ -5,7 +5,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::{debug, error}; use system_error::SystemError; use unified_init::macros::unified_init; use virtio_drivers::device::blk::VirtIOBlk; @@ -64,7 +63,7 @@ pub fn virtio_blk_0() -> Option> { pub fn virtio_blk(transport: VirtIOTransport, dev_id: Arc) { let device = VirtIOBlkDevice::new(transport, dev_id); if let Some(device) = device { - debug!("VirtIOBlkDevice '{:?}' created", device.dev_id); + kdebug!("VirtIOBlkDevice '{:?}' created", device.dev_id); virtio_device_manager() .device_add(device.clone() as Arc) .expect("Add virtio blk failed"); @@ -90,7 +89,7 @@ impl VirtIOBlkDevice { let irq = transport.irq().map(|irq| IrqNumber::new(irq.data())); let device_inner = VirtIOBlk::::new(transport); if let Err(e) = device_inner { - error!("VirtIOBlkDevice '{dev_id:?}' create failed: {:?}", e); + kerror!("VirtIOBlkDevice '{dev_id:?}' create failed: {:?}", e); return None; } @@ -135,9 +134,10 @@ impl BlockDevice for VirtIOBlkDevice { .device_inner .read_blocks(lba_id_start, &mut buf[..count * LBA_SIZE]) .map_err(|e| { - error!( + kerror!( "VirtIOBlkDevice '{:?}' read_at_sync failed: {:?}", - self.dev_id, e + self.dev_id, + e ); SystemError::EIO })?; @@ -416,7 +416,7 @@ impl VirtIODriver for VirtIOBlkDriver { .arc_any() .downcast::() .map_err(|_| { - error!( + kerror!( "VirtIOBlkDriver::probe() failed: device is not a VirtIO block device. Device: '{:?}'", device.name() ); diff --git a/kernel/src/driver/clocksource/acpi_pm.rs b/kernel/src/driver/clocksource/acpi_pm.rs index e5b7d7a58..00265698e 100644 --- a/kernel/src/driver/clocksource/acpi_pm.rs +++ b/kernel/src/driver/clocksource/acpi_pm.rs @@ -15,13 +15,12 @@ use acpi::fadt::Fadt; use alloc::sync::{Arc, Weak}; use core::intrinsics::unlikely; use core::sync::atomic::{AtomicU32, Ordering}; -use log::info; use system_error::SystemError; // 参考:https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/clocksource/acpi_pm.c /// acpi_pmtmr所在的I/O端口 -pub static PMTMR_IO_PORT: AtomicU32 = AtomicU32::new(0); +pub static mut PMTMR_IO_PORT: AtomicU32 = AtomicU32::new(0); /// # 读取acpi_pmtmr当前值,并对齐进行掩码操作 #[inline(always)] @@ -88,10 +87,8 @@ impl Acpipm { max_idle_ns: Default::default(), flags: ClocksourceFlags::CLOCK_SOURCE_IS_CONTINUOUS, watchdog_last: CycleNum::new(0), - cs_last: CycleNum::new(0), uncertainty_margin: 0, maxadj: 0, - cycle_last: CycleNum::new(0), }; let acpi_pm = Arc::new(Acpipm(SpinLock::new(InnerAcpipm { data, @@ -119,18 +116,14 @@ impl Clocksource for Acpipm { fn update_clocksource_data(&self, data: ClocksourceData) -> Result<(), SystemError> { let d = &mut self.0.lock_irqsave().data; - d.set_name(data.name); - d.set_rating(data.rating); + d.set_flags(data.flags); d.set_mask(data.mask); + d.set_max_idle_ns(data.max_idle_ns); d.set_mult(data.mult); + d.set_name(data.name); + d.set_rating(data.rating); d.set_shift(data.shift); - d.set_max_idle_ns(data.max_idle_ns); - d.set_flags(data.flags); d.watchdog_last = data.watchdog_last; - d.cs_last = data.cs_last; - d.set_uncertainty_margin(data.uncertainty_margin); - d.set_maxadj(data.maxadj); - d.cycle_last = data.cycle_last; return Ok(()); } } @@ -177,8 +170,6 @@ const PMTMR_EXPECTED_RATE: u64 = #[cfg(not(target_arch = "x86_64"))] #[allow(dead_code)] fn verify_pmtmr_rate() -> bool { - use log::info; - let mut count: u32 = 0; mach_prepare_counter(); @@ -188,7 +179,7 @@ fn verify_pmtmr_rate() -> bool { let delta = (value2 - value1) & ACPI_PM_MASK; if (delta < (PMTMR_EXPECTED_RATE * 19) / 20) || (delta > (PMTMR_EXPECTED_RATE * 21) / 20) { - info!( + kinfo!( "PM Timer running at invalid rate: {}", 100 * delta / PMTMR_EXPECTED_RATE ); @@ -215,13 +206,12 @@ fn find_acpi_pm_clock() -> Result<(), SystemError> { let pm_timer_block = fadt.pm_timer_block().map_err(|_| SystemError::ENODEV)?; let pm_timer_block = pm_timer_block.ok_or(SystemError::ENODEV)?; let pmtmr_addr = pm_timer_block.address; - - PMTMR_IO_PORT.store(pmtmr_addr as u32, Ordering::SeqCst); - - info!( - "apic_pmtmr I/O port: {}", + unsafe { + PMTMR_IO_PORT.store(pmtmr_addr as u32, Ordering::SeqCst); + } + kinfo!("apic_pmtmr I/O port: {}", unsafe { PMTMR_IO_PORT.load(Ordering::SeqCst) - ); + }); return Ok(()); } @@ -232,19 +222,18 @@ fn find_acpi_pm_clock() -> Result<(), SystemError> { #[allow(dead_code)] pub fn init_acpi_pm_clocksource() -> Result<(), SystemError> { let acpi_pm = Acpipm::new(); + unsafe { + CLOCKSOURCE_ACPI_PM = Some(acpi_pm); + } // 解析fadt find_acpi_pm_clock()?; // 检查pmtmr_io_port是否被设置 - if PMTMR_IO_PORT.load(Ordering::SeqCst) == 0 { + if unsafe { PMTMR_IO_PORT.load(Ordering::SeqCst) } == 0 { return Err(SystemError::ENODEV); } - unsafe { - CLOCKSOURCE_ACPI_PM = Some(acpi_pm); - } - // 验证ACPI PM Timer作为时钟源的稳定性和一致性 for j in 0..ACPI_PM_MONOTONIC_CHECKS { let mut cnt = 100 * j; @@ -266,28 +255,30 @@ pub fn init_acpi_pm_clocksource() -> Result<(), SystemError> { if (value2 < value1) && (value2 < 0xfff) { break; } - info!("PM Timer had inconsistens results: {} {}", value1, value2); - - PMTMR_IO_PORT.store(0, Ordering::SeqCst); - + kinfo!("PM Timer had inconsistens results: {} {}", value1, value2); + unsafe { + PMTMR_IO_PORT.store(0, Ordering::SeqCst); + } return Err(SystemError::EINVAL); } if i == ACPI_PM_READ_CHECKS { - info!("PM Timer failed consistency check: {}", value1); - - PMTMR_IO_PORT.store(0, Ordering::SeqCst); - + kinfo!("PM Timer failed consistency check: {}", value1); + unsafe { + PMTMR_IO_PORT.store(0, Ordering::SeqCst); + } return Err(SystemError::EINVAL); } } // 检查ACPI PM Timer的频率是否正确 if !verify_pmtmr_rate() { - PMTMR_IO_PORT.store(0, Ordering::SeqCst); + unsafe { + PMTMR_IO_PORT.store(0, Ordering::SeqCst); + } } // 检查TSC时钟源的监视器是否被禁用,如果被禁用则将时钟源的标志设置为CLOCK_SOURCE_MUST_VERIFY - // 是因为jiffies精度小于acpi pm,所以不需要被jiffies监视 + // 没有实现clocksource_selecet_watchdog函数,所以这里设置为false let tsc_clocksource_watchdog_disabled = false; if tsc_clocksource_watchdog_disabled { clocksource_acpi_pm().0.lock_irqsave().data.flags |= @@ -296,13 +287,13 @@ pub fn init_acpi_pm_clocksource() -> Result<(), SystemError> { // 注册ACPI PM Timer let acpi_pmtmr = clocksource_acpi_pm() as Arc; - match acpi_pmtmr.register(1, PMTMR_TICKS_PER_SEC as u32) { + match acpi_pmtmr.register(100, PMTMR_TICKS_PER_SEC as u32) { Ok(_) => { - info!("ACPI PM Timer registered as clocksource sccessfully"); + kinfo!("ACPI PM Timer registered as clocksource sccessfully"); return Ok(()); } Err(_) => { - info!("ACPI PM Timer init registered failed"); + kinfo!("ACPI PM Timer init registered failed"); return Err(SystemError::ENOSYS); } }; diff --git a/kernel/src/driver/clocksource/timer_riscv.rs b/kernel/src/driver/clocksource/timer_riscv.rs index fad652ef4..e5481f4ec 100644 --- a/kernel/src/driver/clocksource/timer_riscv.rs +++ b/kernel/src/driver/clocksource/timer_riscv.rs @@ -20,9 +20,12 @@ use crate::{ }, libs::spinlock::SpinLock, mm::percpu::PerCpu, + process::ProcessManager, smp::core::smp_get_processor_id, time::{ - clocksource::HZ, tick_common::tick_handle_periodic, timer::try_raise_timer_softirq, + clocksource::HZ, + jiffies::NSEC_PER_JIFFY, + timer::{try_raise_timer_softirq, update_timer_jiffies}, TimeArch, }, }; @@ -34,18 +37,25 @@ static SBI_TIMER_INIT_BMP: SpinLock Result<(), SystemError> { // 更新下一次中断时间 - // debug!( + // kdebug!( // "riscv_sbi_timer: handle_irq: cpu_id: {}, time: {}", // smp_get_processor_id().data(), // CurrentTimeArch::get_cycles() as u64 // ); - tick_handle_periodic(trap_frame); - compiler_fence(Ordering::SeqCst); + ProcessManager::update_process_times(trap_frame.is_from_user()); + Self::update_nsec_passed_and_walltime(); sbi_rt::set_timer(CurrentTimeArch::get_cycles() as u64 + unsafe { INTERVAL_CNT } as u64); Ok(()) } @@ -58,6 +68,30 @@ impl RiscVSbiTimer { fn disable() { unsafe { riscv::register::sie::clear_stimer() }; } + + fn update_nsec_passed_and_walltime() { + if smp_get_processor_id().data() != 0 { + return; + } + + let cycles = CurrentTimeArch::get_cycles() as u64; + let nsec_passed = + CurrentTimeArch::cycles2ns((cycles - unsafe { HART0_LAST_UPDATED }) as usize); + unsafe { + HART0_LAST_UPDATED = cycles; + HART0_NSEC_PASSED += nsec_passed; + } + + let jiffies = unsafe { HART0_NSEC_PASSED } / NSEC_PER_JIFFY as usize; + unsafe { HART0_NSEC_PASSED %= NSEC_PER_JIFFY as usize }; + + update_timer_jiffies( + jiffies as u64, + (jiffies * NSEC_PER_JIFFY as usize / 1000) as i64, + ); + try_raise_timer_softirq(); + compiler_fence(Ordering::SeqCst); + } } /// riscv 初始化本地调度时钟源 diff --git a/kernel/src/driver/disk/ahci/ahcidisk.rs b/kernel/src/driver/disk/ahci/ahcidisk.rs index 2bbb15141..4aaaa7993 100644 --- a/kernel/src/driver/disk/ahci/ahcidisk.rs +++ b/kernel/src/driver/disk/ahci/ahcidisk.rs @@ -1,5 +1,4 @@ -use super::{_port, hba::HbaCmdTable}; -use crate::arch::MMArch; +use super::{_port, hba::HbaCmdTable, virt_2_phys}; use crate::driver::base::block::block_device::{BlockDevice, BlockId}; use crate::driver::base::block::disk_info::Partition; use crate::driver::base::class::Class; @@ -14,14 +13,16 @@ use crate::driver::disk::ahci::HBA_PxIS_TFES; use crate::filesystem::kernfs::KernFSInode; use crate::filesystem::mbr::MbrDiskPartionTable; -use crate::driver::disk::ahci::hba::{ - FisRegH2D, FisType, HbaCmdHeader, ATA_CMD_READ_DMA_EXT, ATA_CMD_WRITE_DMA_EXT, ATA_DEV_BUSY, - ATA_DEV_DRQ, -}; use crate::libs::rwlock::{RwLockReadGuard, RwLockWriteGuard}; use crate::libs::spinlock::SpinLock; -use crate::mm::{verify_area, MemoryManagementArch, PhysAddr, VirtAddr}; -use log::error; +use crate::mm::{phys_2_virt, verify_area, VirtAddr}; +use crate::{ + driver::disk::ahci::hba::{ + FisRegH2D, FisType, HbaCmdHeader, ATA_CMD_READ_DMA_EXT, ATA_CMD_WRITE_DMA_EXT, + ATA_DEV_BUSY, ATA_DEV_DRQ, + }, + kerror, +}; use system_error::SystemError; use alloc::sync::Weak; @@ -69,7 +70,7 @@ impl AhciDisk { compiler_fence(Ordering::SeqCst); let check_length = ((count - 1) >> 4) + 1; // prdt length if count * 512 > buf.len() || check_length > 8_usize { - error!("ahci read: e2big"); + kerror!("ahci read: e2big"); // 不可能的操作 return Err(SystemError::E2BIG); } else if count == 0 { @@ -87,11 +88,9 @@ impl AhciDisk { #[allow(unused_unsafe)] let cmdheader: &mut HbaCmdHeader = unsafe { - (MMArch::phys_2_virt(PhysAddr::new( + (phys_2_virt( volatile_read!(port.clb) as usize + slot as usize * size_of::(), - )) - .unwrap() - .data() as *mut HbaCmdHeader) + ) as *mut HbaCmdHeader) .as_mut() .unwrap() }; @@ -121,9 +120,7 @@ impl AhciDisk { #[allow(unused_unsafe)] let cmdtbl = unsafe { - (MMArch::phys_2_virt(PhysAddr::new(volatile_read!(cmdheader.ctba) as usize)) - .unwrap() - .data() as *mut HbaCmdTable) + (phys_2_virt(volatile_read!(cmdheader.ctba) as usize) as *mut HbaCmdTable) .as_mut() .unwrap() // 必须使用 as_mut ,得到的才是原来的变量 }; @@ -133,14 +130,11 @@ impl AhciDisk { // 清空整个table的旧数据 write_bytes(cmdtbl, 0, 1); } - // debug!("cmdheader.prdtl={}", volatile_read!(cmdheader.prdtl)); + // kdebug!("cmdheader.prdtl={}", volatile_read!(cmdheader.prdtl)); // 8K bytes (16 sectors) per PRDT for i in 0..((volatile_read!(cmdheader.prdtl) - 1) as usize) { - volatile_write!( - cmdtbl.prdt_entry[i].dba, - MMArch::virt_2_phys(VirtAddr::new(buf_ptr)).unwrap().data() as u64 - ); + volatile_write!(cmdtbl.prdt_entry[i].dba, virt_2_phys(buf_ptr) as u64); cmdtbl.prdt_entry[i].dbc = 8 * 1024 - 1; volatile_set_bit!(cmdtbl.prdt_entry[i].dbc, 1 << 31, true); // 允许中断 prdt_entry.i buf_ptr += 8 * 1024; @@ -149,10 +143,7 @@ impl AhciDisk { // Last entry let las = (volatile_read!(cmdheader.prdtl) - 1) as usize; - volatile_write!( - cmdtbl.prdt_entry[las].dba, - MMArch::virt_2_phys(VirtAddr::new(buf_ptr)).unwrap().data() as u64 - ); + volatile_write!(cmdtbl.prdt_entry[las].dba, virt_2_phys(buf_ptr) as u64); cmdtbl.prdt_entry[las].dbc = ((tmp_count << 9) - 1) as u32; // 数据长度 volatile_set_bit!(cmdtbl.prdt_entry[las].dbc, 1 << 31, true); // 允许中断 @@ -190,24 +181,25 @@ impl AhciDisk { } if spin_count == SPIN_LIMIT { - error!("Port is hung"); + kerror!("Port is hung"); return Err(SystemError::EIO); } volatile_set_bit!(port.ci, 1 << slot, true); // Issue command - // debug!("To wait ahci read complete."); + // kdebug!("To wait ahci read complete."); // 等待操作完成 loop { if (volatile_read!(port.ci) & (1 << slot)) == 0 { break; } if (volatile_read!(port.is) & HBA_PxIS_TFES) > 0 { - error!("Read disk error"); + kerror!("Read disk error"); return Err(SystemError::EIO); } } - if let Some(kbuf) = &kbuf { - buf.copy_from_slice(kbuf); + + if kbuf.is_some() { + buf.copy_from_slice(kbuf.as_ref().unwrap()); } compiler_fence(Ordering::SeqCst); @@ -244,11 +236,9 @@ impl AhciDisk { compiler_fence(Ordering::SeqCst); #[allow(unused_unsafe)] let cmdheader: &mut HbaCmdHeader = unsafe { - (MMArch::phys_2_virt(PhysAddr::new( + (phys_2_virt( volatile_read!(port.clb) as usize + slot as usize * size_of::(), - )) - .unwrap() - .data() as *mut HbaCmdHeader) + ) as *mut HbaCmdHeader) .as_mut() .unwrap() }; @@ -285,9 +275,7 @@ impl AhciDisk { #[allow(unused_unsafe)] let cmdtbl = unsafe { - (MMArch::phys_2_virt(PhysAddr::new(volatile_read!(cmdheader.ctba) as usize)) - .unwrap() - .data() as *mut HbaCmdTable) + (phys_2_virt(volatile_read!(cmdheader.ctba) as usize) as *mut HbaCmdTable) .as_mut() .unwrap() }; @@ -301,10 +289,7 @@ impl AhciDisk { // 8K bytes (16 sectors) per PRDT for i in 0..((volatile_read!(cmdheader.prdtl) - 1) as usize) { - volatile_write!( - cmdtbl.prdt_entry[i].dba, - MMArch::virt_2_phys(VirtAddr::new(buf_ptr)).unwrap().data() as u64 - ); + volatile_write!(cmdtbl.prdt_entry[i].dba, virt_2_phys(buf_ptr) as u64); volatile_write_bit!(cmdtbl.prdt_entry[i].dbc, (1 << 22) - 1, 8 * 1024 - 1); // 数据长度 volatile_set_bit!(cmdtbl.prdt_entry[i].dbc, 1 << 31, true); // 允许中断 buf_ptr += 8 * 1024; @@ -313,10 +298,7 @@ impl AhciDisk { // Last entry let las = (volatile_read!(cmdheader.prdtl) - 1) as usize; - volatile_write!( - cmdtbl.prdt_entry[las].dba, - MMArch::virt_2_phys(VirtAddr::new(buf_ptr)).unwrap().data() as u64 - ); + volatile_write!(cmdtbl.prdt_entry[las].dba, virt_2_phys(buf_ptr) as u64); volatile_set_bit!(cmdtbl.prdt_entry[las].dbc, 1 << 31, true); // 允许中断 volatile_write_bit!( cmdtbl.prdt_entry[las].dbc, @@ -354,7 +336,7 @@ impl AhciDisk { break; } if (volatile_read!(port.is) & HBA_PxIS_TFES) > 0 { - error!("Write disk error"); + kerror!("Write disk error"); return Err(SystemError::EIO); } } diff --git a/kernel/src/driver/disk/ahci/hba.rs b/kernel/src/driver/disk/ahci/hba.rs index aa324e654..2aa504405 100644 --- a/kernel/src/driver/disk/ahci/hba.rs +++ b/kernel/src/driver/disk/ahci/hba.rs @@ -2,8 +2,7 @@ use core::{intrinsics::size_of, ptr}; use core::sync::atomic::compiler_fence; -use crate::arch::MMArch; -use crate::mm::{MemoryManagementArch, PhysAddr}; +use crate::mm::phys_2_virt; /// 文件说明: 实现了 AHCI 中的控制器 HBA 的相关行为 @@ -44,7 +43,6 @@ pub enum HbaPortType { /// 声明了 HBA 的所有属性 #[repr(packed)] -#[allow(dead_code)] pub struct HbaPort { pub clb: u64, // 0x00, command list base address, 1K-byte aligned pub fb: u64, // 0x08, FIS base address, 256-byte aligned @@ -67,7 +65,6 @@ pub struct HbaPort { /// 全称 HBA Memory Register,是HBA的寄存器在内存中的映射 #[repr(packed)] -#[allow(dead_code)] pub struct HbaMem { pub cap: u32, // 0x00, Host capability pub ghc: u32, // 0x04, Global host control @@ -97,7 +94,6 @@ pub struct HbaPrdtEntry { /// HAB Command Table /// 每个 Port 一个 Table,主机和设备的交互都靠这个数据结构 #[repr(packed)] -#[allow(dead_code)] pub struct HbaCmdTable { // 0x00 pub cfis: [u8; 64], // Command FIS @@ -199,13 +195,7 @@ impl HbaPort { unsafe { compiler_fence(core::sync::atomic::Ordering::SeqCst); - ptr::write_bytes( - MMArch::phys_2_virt(PhysAddr::new(clb as usize)) - .unwrap() - .data() as *mut u64, - 0, - 1024, - ); + ptr::write_bytes(phys_2_virt(clb as usize) as *mut u64, 0, 1024); } // 赋值 fis base address @@ -214,36 +204,20 @@ impl HbaPort { volatile_write!(self.fb, fb); unsafe { compiler_fence(core::sync::atomic::Ordering::SeqCst); - ptr::write_bytes( - MMArch::phys_2_virt(PhysAddr::new(fb as usize)) - .unwrap() - .data() as *mut u64, - 0, - 256, - ); + ptr::write_bytes(phys_2_virt(fb as usize) as *mut u64, 0, 256); } // 赋值 command table base address // Command table offset: 40K + 8K*portno // Command table size = 256*32 = 8K per port - let mut cmdheaders = unsafe { - MMArch::phys_2_virt(PhysAddr::new(clb as usize)) - .unwrap() - .data() - } as *mut u64 as *mut HbaCmdHeader; + let mut cmdheaders = phys_2_virt(clb as usize) as *mut u64 as *mut HbaCmdHeader; for ctbas_value in ctbas.iter().take(32) { volatile_write!((*cmdheaders).prdtl, 0); // 一开始没有询问,prdtl = 0(预留了8个PRDT项的空间) volatile_write!((*cmdheaders).ctba, *ctbas_value); // 这里限制了 prdtl <= 8, 所以一共用了256bytes,如果需要修改,可以修改这里 compiler_fence(core::sync::atomic::Ordering::SeqCst); unsafe { - ptr::write_bytes( - MMArch::phys_2_virt(PhysAddr::new(*ctbas_value as usize)) - .unwrap() - .data() as *mut u64, - 0, - 256, - ); + ptr::write_bytes(phys_2_virt(*ctbas_value as usize) as *mut u64, 0, 256); } cmdheaders = (cmdheaders as usize + size_of::()) as *mut HbaCmdHeader; } @@ -288,7 +262,6 @@ pub enum FisType { } #[repr(packed)] -#[allow(dead_code)] pub struct FisRegH2D { // DWORD 0 pub fis_type: u8, // FIS_TYPE_REG_H2D diff --git a/kernel/src/driver/disk/ahci/mod.rs b/kernel/src/driver/disk/ahci/mod.rs index c72256efc..778fc9a49 100644 --- a/kernel/src/driver/disk/ahci/mod.rs +++ b/kernel/src/driver/disk/ahci/mod.rs @@ -3,7 +3,6 @@ pub mod ahci_inode; pub mod ahcidisk; pub mod hba; -use crate::arch::MMArch; use crate::driver::base::block::disk_info::BLK_GF_AHCI; use crate::driver::block::cache::cached_block_device::BlockCache; // 依赖的rust工具包 @@ -11,19 +10,21 @@ use crate::driver::pci::pci::{ get_pci_device_structure_mut, PciDeviceStructure, PCI_DEVICE_LINKEDLIST, }; use crate::filesystem::devfs::devfs_register; - -use crate::driver::disk::ahci::{ - ahcidisk::LockedAhciDisk, - hba::HbaMem, - hba::{HbaPort, HbaPortType}, -}; +use crate::kerror; use crate::libs::rwlock::RwLockWriteGuard; use crate::libs::spinlock::{SpinLock, SpinLockGuard}; -use crate::mm::{MemoryManagementArch, VirtAddr}; +use crate::mm::virt_2_phys; +use crate::{ + driver::disk::ahci::{ + ahcidisk::LockedAhciDisk, + hba::HbaMem, + hba::{HbaPort, HbaPortType}, + }, + kdebug, +}; use ahci_inode::LockedAhciInode; use alloc::{boxed::Box, collections::LinkedList, format, string::String, sync::Arc, vec::Vec}; use core::sync::atomic::compiler_fence; -use log::{debug, error}; use system_error::SystemError; // 仅module内可见 全局数据区 hbr_port, disks @@ -89,34 +90,22 @@ pub fn ahci_init() -> Result<(), SystemError> { let tp = hba_mem_port.check_type(); match tp { HbaPortType::None => { - debug!(" Find a None type Disk."); + kdebug!(" Find a None type Disk."); } HbaPortType::Unknown(err) => { - debug!(" Find a Unknown({:?}) type Disk.", err); + kdebug!(" Find a Unknown({:?}) type Disk.", err); } _ => { - debug!(" Find a {:?} type Disk.", tp); + kdebug!(" Find a {:?} type Disk.", tp); // 计算地址 - let fb = unsafe { - MMArch::virt_2_phys(VirtAddr::new( - ahci_port_base_vaddr + (32 << 10) + (j << 8), - )) - } - .unwrap() - .data(); - let clb = unsafe { - MMArch::virt_2_phys(VirtAddr::new(ahci_port_base_vaddr + (j << 10))) - .unwrap() - .data() - }; + let fb = virt_2_phys(ahci_port_base_vaddr + (32 << 10) + (j << 8)); + let clb = virt_2_phys(ahci_port_base_vaddr + (j << 10)); let ctbas = (0..32) - .map(|x| unsafe { - MMArch::virt_2_phys(VirtAddr::new( + .map(|x| { + virt_2_phys( ahci_port_base_vaddr + (40 << 10) + (j << 13) + (x << 8), - )) - .unwrap() - .data() as u64 + ) as u64 }) .collect::>(); @@ -133,7 +122,7 @@ pub fn ahci_init() -> Result<(), SystemError> { )?); id += 1; // ID 从0开始 - debug!("start register ahci device"); + kdebug!("start register ahci device"); // 挂载到devfs上面去 let ret = devfs_register( @@ -141,7 +130,7 @@ pub fn ahci_init() -> Result<(), SystemError> { LockedAhciInode::new(disks_list.last().unwrap().clone()), ); if let Err(err) = ret { - error!( + kerror!( "Ahci_{} ctrl = {}, port = {} failed to register, error code = {:?}", id, hba_mem_index as u8, diff --git a/kernel/src/driver/firmware/efi/fdt.rs b/kernel/src/driver/firmware/efi/fdt.rs index 4276dfce5..52ad8bb75 100644 --- a/kernel/src/driver/firmware/efi/fdt.rs +++ b/kernel/src/driver/firmware/efi/fdt.rs @@ -3,7 +3,6 @@ use core::fmt::Debug; use fdt::Fdt; -use log::error; use system_error::SystemError; use crate::init::boot_params; @@ -118,7 +117,7 @@ impl EFIManager { ) } .map_err(|e| { - error!("failed to parse fdt, err={:?}", e); + kerror!("failed to parse fdt, err={:?}", e); SystemError::EINVAL })?; @@ -146,7 +145,7 @@ impl EFIManager { self.do_get_fdt_prop(prop_type, &prop, &mut ret) .unwrap_or_else(|e| { - error!("Failed to get fdt prop: {prop_type:?}, error: {e:?}"); + kerror!("Failed to get fdt prop: {prop_type:?}, error: {e:?}"); }) } } diff --git a/kernel/src/driver/firmware/efi/init.rs b/kernel/src/driver/firmware/efi/init.rs index 33a89213b..c425a8872 100644 --- a/kernel/src/driver/firmware/efi/init.rs +++ b/kernel/src/driver/firmware/efi/init.rs @@ -1,6 +1,5 @@ use core::{hint::spin_loop, intrinsics::unlikely, mem::size_of}; -use log::{error, info, warn}; use system_error::SystemError; use uefi_raw::table::boot::{MemoryAttribute, MemoryType}; @@ -22,17 +21,17 @@ use super::efi_manager; #[allow(dead_code)] #[inline(never)] pub fn efi_init() { - info!("Initializing efi..."); + kinfo!("Initializing efi..."); let data_from_fdt = efi_manager() .get_fdt_params() .expect("Failed to get fdt params"); if data_from_fdt.systable.is_none() { - error!("Failed to get systable from fdt"); + kerror!("Failed to get systable from fdt"); return; } - // debug!("to map memory table"); + // kdebug!("to map memory table"); // 映射mmap table if efi_manager().memmap_init_early(&data_from_fdt).is_err() { @@ -40,23 +39,23 @@ pub fn efi_init() { // 那么 UEFI memory map 就是我们拥有的关于内存的唯一描述, // 所以如果我们无法访问它,那么继续进行下去就没有什么意义了 - error!("Failed to initialize early memory map"); + kerror!("Failed to initialize early memory map"); loop { spin_loop(); } } - // debug!("NNNN"); - // warn!("BBBB, e:{:?}", SystemError::EINVAL); + // kdebug!("NNNN"); + // kwarn!("BBBB, e:{:?}", SystemError::EINVAL); let desc_version = efi_manager().desc_version(); if unlikely(desc_version != 1) { - warn!("Unexpected EFI memory map version: {}", desc_version); + kwarn!("Unexpected EFI memory map version: {}", desc_version); } let r = uefi_init(PhysAddr::new(data_from_fdt.systable.unwrap() as usize)); if let Err(e) = r { - error!("Failed to initialize UEFI: {:?}", e); + kerror!("Failed to initialize UEFI: {:?}", e); efi_manager().efi_memmap_unmap(); return; } @@ -98,7 +97,7 @@ pub fn efi_init() { // todo: Initialize screen info - info!("UEFI init done!"); + kinfo!("UEFI init done!"); } fn efi_find_mirror() { @@ -118,7 +117,7 @@ fn efi_find_mirror() { } if mirror_size > 0 { - info!( + kinfo!( "Memory: {}M/{}M mirrored memory", mirror_size >> 20, total_size >> 20 @@ -134,7 +133,7 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> { let err_unmap_systable = |st_vaddr: VirtAddr| { EarlyIoRemap::unmap(st_vaddr) .map_err(|e| { - error!("Failed to unmap system table: {e:?}"); + kerror!("Failed to unmap system table: {e:?}"); }) .ok(); }; @@ -144,7 +143,7 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> { let st_size = size_of::(); let st_vaddr = EarlyIoRemap::map_not_aligned(system_table, st_size, true).map_err(|e| { - warn!("Unable to map EFI system table, e:{e:?}"); + kwarn!("Unable to map EFI system table, e:{e:?}"); e })?; @@ -168,8 +167,9 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> { let st_ptr = st_vaddr.data() as *const uefi_raw::table::system::SystemTable; efi_manager() .check_system_table_header(unsafe { &st_ptr.as_ref().unwrap().header }, 2) - .inspect_err(|_| { + .map_err(|e| { err_unmap_systable(st_vaddr); + e })?; let st_ref = unsafe { st_ptr.as_ref().unwrap() }; @@ -195,7 +195,7 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> { true, ) .map_err(|e| { - warn!("Unable to map EFI configuration table, e:{e:?}"); + kwarn!("Unable to map EFI configuration table, e:{e:?}"); err_unmap_systable(st_vaddr); e })?; @@ -265,11 +265,11 @@ fn reserve_memory_regions() { let phys_start = page_align_down(md.phys_start as usize); let size = (page_count << (MMArch::PAGE_SHIFT as u64)) as usize; - // debug!("Reserve memory region: {:#x}-{:#x}({:#x}), is_memory: {}, is_usable_memory:{}, type: {:?}, att: {:?}", phys_start, phys_start + size, page_count, md.is_memory(), md.is_usable_memory(), md.ty, md.att); + // kdebug!("Reserve memory region: {:#x}-{:#x}({:#x}), is_memory: {}, is_usable_memory:{}, type: {:?}, att: {:?}", phys_start, phys_start + size, page_count, md.is_memory(), md.is_usable_memory(), md.ty, md.att); if md.is_memory() { open_firmware_fdt_driver().early_init_dt_add_memory(phys_start as u64, size as u64); if !md.is_usable_memory() { - // debug!( + // kdebug!( // "Marking non-usable memory as nomap: {:#x}-{:#x}", // phys_start, // phys_start + size diff --git a/kernel/src/driver/firmware/efi/memmap.rs b/kernel/src/driver/firmware/efi/memmap.rs index c213d853b..1bfea15f8 100644 --- a/kernel/src/driver/firmware/efi/memmap.rs +++ b/kernel/src/driver/firmware/efi/memmap.rs @@ -1,6 +1,5 @@ use core::{intrinsics::unlikely, mem::size_of}; -use log::error; use system_error::SystemError; use crate::{ @@ -111,7 +110,7 @@ impl EFIManager { let offset = paddr.data() - page_align_down(paddr.data()); let map_size = data.mmap_size.unwrap() as usize + offset; - // debug!("do_efi_memmap_init: map_size={map_size:#x}"); + // kdebug!("do_efi_memmap_init: map_size={map_size:#x}"); // 映射内存 let mut vaddr = EarlyIoRemap::map( @@ -131,7 +130,7 @@ impl EFIManager { } if inner_guard.mmap.vaddr.is_none() { - error!("Cannot map the EFI memory map!"); + kerror!("Cannot map the EFI memory map!"); return Err(SystemError::ENOMEM); } diff --git a/kernel/src/driver/firmware/efi/mod.rs b/kernel/src/driver/firmware/efi/mod.rs index 4d0f9e3bf..a07d4df26 100644 --- a/kernel/src/driver/firmware/efi/mod.rs +++ b/kernel/src/driver/firmware/efi/mod.rs @@ -1,4 +1,3 @@ -use log::{error, warn}; use system_error::SystemError; use crate::{ @@ -91,14 +90,15 @@ impl EFIManager { min_major: u16, ) -> Result<(), SystemError> { if header.signature != uefi_raw::table::system::SystemTable::SIGNATURE { - error!("System table signature mismatch!"); + kerror!("System table signature mismatch!"); return Err(SystemError::EINVAL); } if header.revision.major() < min_major { - warn!( + kwarn!( "System table version: {:?}, expected {}.00 or greater!", - header.revision, min_major + header.revision, + min_major ); } diff --git a/kernel/src/driver/firmware/efi/tables.rs b/kernel/src/driver/firmware/efi/tables.rs index a6e9c3448..bab605005 100644 --- a/kernel/src/driver/firmware/efi/tables.rs +++ b/kernel/src/driver/firmware/efi/tables.rs @@ -1,7 +1,6 @@ use core::{ffi::CStr, mem::size_of}; use hashbrown::Equivalent; -use log::{debug, error, info, warn}; use system_error::SystemError; use uefi_raw::table::{ boot::{MemoryAttribute, MemoryType}, @@ -67,16 +66,16 @@ impl EFIManager { } EarlyIoRemap::unmap(fw_ptr).map_err(|e|{ - error!("report systable header: failed to unmap systable header, fw_ptr: {fw_ptr:?}, err: {e:?}"); + kerror!("report systable header: failed to unmap systable header, fw_ptr: {fw_ptr:?}, err: {e:?}"); e }).ok(); } else { - warn!("report systable header: failed to map systable header, err: {fw_ptr:?}"); + kwarn!("report systable header: failed to map systable header, err: {fw_ptr:?}"); } let s = CStr::from_bytes_with_nul(&tmp_buf) .unwrap_or_else(|_| CStr::from_bytes_with_nul(b"Unknown\0").unwrap()); - info!("EFI version: {:?}, vendor: {:?}", header.revision, s); + kinfo!("EFI version: {:?}, vendor: {:?}", header.revision, s); } /// 解析EFI config table @@ -87,7 +86,7 @@ impl EFIManager { if let Some(r) = parser.match_table(table) { // 有匹配结果 if let Err(e) = r { - warn!( + kwarn!( "Failed to parse cfg table: '{}', err: {e:?}", parser.table.name() ); @@ -98,7 +97,7 @@ impl EFIManager { } if !flag { - warn!("Cannot find parser for guid: {:?}", table.vendor_guid); + kwarn!("Cannot find parser for guid: {:?}", table.vendor_guid); } } @@ -108,7 +107,7 @@ impl EFIManager { while !prev_paddr.is_null() { let vaddr = EarlyIoRemap::map_not_aligned(prev_paddr, MMArch::PAGE_SIZE, true) .map_err(|e| { - error!( + kerror!( "Failed to map UEFI memreserve table, paddr: {prev_paddr:?}, err: {e:?}" ); @@ -130,7 +129,7 @@ impl EFIManager { + size_of::() * psize, ) .map_err(|e| { - error!("Failed to reserve block, paddr: {prev_paddr:?}, err: {e:?}"); + kerror!("Failed to reserve block, paddr: {prev_paddr:?}, err: {e:?}"); EarlyIoRemap::unmap(vaddr).unwrap(); e })?; @@ -147,7 +146,7 @@ impl EFIManager { mem_block_manager() .reserve_block(PhysAddr::new(entry.base), entry.size) .map_err(|e| { - error!("Failed to reserve block, paddr: {prev_paddr:?}, err: {e:?}"); + kerror!("Failed to reserve block, paddr: {prev_paddr:?}, err: {e:?}"); EarlyIoRemap::unmap(vaddr).unwrap(); e })?; @@ -345,7 +344,7 @@ impl MatchTable for MatchTableMemReserve { ) -> Result<(), SystemError> { efi_manager().inner.write_irqsave().memreserve_table_paddr = Some(PhysAddr::new(table_raw.vendor_table as usize)); - debug!( + kdebug!( "memreserve_table_paddr: {:#x}", table_raw.vendor_table as usize ); @@ -375,7 +374,7 @@ impl MatchTable for MatchTableEsrt { ) -> Result<(), SystemError> { efi_manager().inner.write_irqsave().esrt_table_paddr = Some(PhysAddr::new(table_raw.vendor_table as usize)); - debug!("esrt_table_paddr: {:#x}", table_raw.vendor_table as usize); + kdebug!("esrt_table_paddr: {:#x}", table_raw.vendor_table as usize); return Ok(()); } } diff --git a/kernel/src/driver/input/ps2_dev/ps2_device.rs b/kernel/src/driver/input/ps2_dev/ps2_device.rs index a8a00d90d..26050c7d5 100644 --- a/kernel/src/driver/input/ps2_dev/ps2_device.rs +++ b/kernel/src/driver/input/ps2_dev/ps2_device.rs @@ -1,5 +1,4 @@ use crate::driver::{base::device::Device, input::serio::serio_device::SerioDevice}; // todo: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/libps2.h#33 -#[allow(unused)] pub trait Ps2Device: Device + SerioDevice {} diff --git a/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs b/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs index 8da1c9fd3..e99f683d8 100644 --- a/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs +++ b/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs @@ -6,7 +6,6 @@ use alloc::{ vec::Vec, }; use kdepends::ringbuffer::{AllocRingBuffer, RingBuffer}; -use log::{debug, error}; use system_error::SystemError; use crate::{ @@ -230,7 +229,7 @@ impl Ps2MouseDevice { self.send_command_to_ps2mouse(PsMouseCommand::EnablePacketStreaming) .map_err(|e| { - error!("ps2 mouse init error: {:?}", e); + kerror!("ps2 mouse init error: {:?}", e); e })?; self.read_data_port().ok(); @@ -314,7 +313,7 @@ impl Ps2MouseDevice { guard.current_state.y = self.get_y_movement(packet, flags); } - // debug!( + // kdebug!( // "Ps2MouseDevice packet : flags:{}, x:{}, y:{}\n", // guard.current_state.flags.bits, // guard.current_state.x, @@ -387,7 +386,6 @@ impl Ps2MouseDevice { Ok(()) } - #[allow(dead_code)] fn wait_for_read(&self) -> Result<(), SystemError> { let timeout = 100_000; for _ in 0..timeout { @@ -666,7 +664,7 @@ impl IndexNode for Ps2MouseDevice { impl Ps2Device for Ps2MouseDevice {} pub fn rs_ps2_mouse_device_init(parent: Arc) -> Result<(), SystemError> { - debug!("ps2_mouse_device initializing..."); + kdebug!("ps2_mouse_device initializing..."); let psmouse = Arc::new(Ps2MouseDevice::new()); device_manager().device_default_initialize(&(psmouse.clone() as Arc)); @@ -674,7 +672,7 @@ pub fn rs_ps2_mouse_device_init(parent: Arc) -> Result<(), SystemEr serio_device_manager().register_port(psmouse.clone() as Arc)?; devfs_register(&psmouse.name(), psmouse.clone()).map_err(|e| { - error!( + kerror!( "register psmouse device '{}' to devfs failed: {:?}", psmouse.name(), e diff --git a/kernel/src/driver/input/ps2_mouse/ps_mouse_driver.rs b/kernel/src/driver/input/ps2_mouse/ps_mouse_driver.rs index baedcd4ed..a9b76c338 100644 --- a/kernel/src/driver/input/ps2_mouse/ps_mouse_driver.rs +++ b/kernel/src/driver/input/ps2_mouse/ps_mouse_driver.rs @@ -3,7 +3,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::debug; use system_error::SystemError; use unified_init::macros::unified_init; @@ -277,10 +276,10 @@ impl SerioDriver for Ps2MouseDriver { #[unified_init(INITCALL_DEVICE)] fn ps2_mouse_driver_init() -> Result<(), SystemError> { - debug!("Ps2_mouse_drive initializing..."); + kdebug!("Ps2_mouse_drive initializing..."); let driver = Ps2MouseDriver::new(); serio_driver_manager().register(driver.clone())?; unsafe { PS2_MOUSE_DRIVER = Some(driver) }; - debug!("Ps2_mouse_drive initialized!"); + kdebug!("Ps2_mouse_drive initialized!"); return Ok(()); } diff --git a/kernel/src/driver/input/serio/i8042/mod.rs b/kernel/src/driver/input/serio/i8042/mod.rs index a65453ac3..31a2b1de5 100644 --- a/kernel/src/driver/input/serio/i8042/mod.rs +++ b/kernel/src/driver/input/serio/i8042/mod.rs @@ -1,5 +1,4 @@ use alloc::sync::Arc; -use log::debug; use system_error::SystemError; use unified_init::macros::unified_init; @@ -34,7 +33,7 @@ pub fn i8042_platform_device() -> Arc { // TODO: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/input/serio/i8042.c#1612 #[unified_init(INITCALL_DEVICE)] pub fn i8042_init() -> Result<(), SystemError> { - debug!("i8042 initializing..."); + kdebug!("i8042 initializing..."); let i8042_device = Arc::new(I8042PlatformDevice::new()); device_manager().device_default_initialize(&(i8042_device.clone() as Arc)); platform_device_manager().device_add(i8042_device.clone() as Arc)?; @@ -47,16 +46,14 @@ pub fn i8042_init() -> Result<(), SystemError> { Ok(()) } -/// TODO: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/input/serio/i8042.c#441 -#[allow(dead_code)] +// TODO: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/input/serio/i8042.c#441 pub fn i8042_start(_serio: &Arc) -> Result<(), SystemError> { - todo!("i8042_start") + todo!() } -/// TODO: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/input/serio/i8042.c#471 -#[allow(dead_code)] +// TODO: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/input/serio/i8042.c#471 pub fn i8042_stop(_serio: &Arc) -> Result<(), SystemError> { - todo!("i8042_stop") + todo!() } /// # 函数的功能 diff --git a/kernel/src/driver/input/serio/serio_device.rs b/kernel/src/driver/input/serio/serio_device.rs index a2041f429..cb610ab21 100644 --- a/kernel/src/driver/input/serio/serio_device.rs +++ b/kernel/src/driver/input/serio/serio_device.rs @@ -8,7 +8,6 @@ use super::serio_bus; /// 串行设备,实现该trait的设备实例挂载在serio总线上,同时应该实现Device trait /// /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/serio.h#20 -#[allow(dead_code)] pub trait SerioDevice: Device { /// # 函数功能 /// diff --git a/kernel/src/driver/input/serio/serio_driver.rs b/kernel/src/driver/input/serio/serio_driver.rs index c076a5c44..8cc3c572d 100644 --- a/kernel/src/driver/input/serio/serio_driver.rs +++ b/kernel/src/driver/input/serio/serio_driver.rs @@ -11,7 +11,6 @@ use super::{serio_bus, serio_device::SerioDevice}; /// 实现该trait的设备驱动实例应挂载在serio总线上,同时应该实现Driver trait /// /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/serio.h#67 -#[allow(dead_code)] pub trait SerioDriver: Driver { // 写入时唤醒设备 fn write_wakeup(&self, device: &Arc) -> Result<(), SystemError>; diff --git a/kernel/src/driver/input/serio/subsys.rs b/kernel/src/driver/input/serio/subsys.rs index afdd9349f..d3291a855 100644 --- a/kernel/src/driver/input/serio/subsys.rs +++ b/kernel/src/driver/input/serio/subsys.rs @@ -3,7 +3,6 @@ use alloc::{ sync::{Arc, Weak}, }; use intertrait::cast::CastArc; -use log::error; use system_error::SystemError; use crate::{ @@ -60,7 +59,7 @@ impl Bus for SerioBus { fn probe(&self, device: &Arc) -> Result<(), SystemError> { let drv = device.driver().ok_or(SystemError::EINVAL)?; let pdrv = drv.cast::().map_err(|_| { - error!( + kerror!( "SerioBus::probe() failed: device.driver() is not a SerioDriver. Device: '{:?}'", device.name() ); @@ -68,7 +67,7 @@ impl Bus for SerioBus { })?; let pdev = device.clone().cast::().map_err(|_| { - error!( + kerror!( "SerioBus::probe() failed: device is not a SerioDevice. Device: '{:?}'", device.name() ); diff --git a/kernel/src/driver/irqchip/riscv_intc.rs b/kernel/src/driver/irqchip/riscv_intc.rs index e14d25ad2..2e83dbb3f 100644 --- a/kernel/src/driver/irqchip/riscv_intc.rs +++ b/kernel/src/driver/irqchip/riscv_intc.rs @@ -1,5 +1,4 @@ use alloc::{string::ToString, sync::Arc}; -use log::error; use system_error::SystemError; use crate::{ @@ -156,7 +155,7 @@ pub unsafe fn riscv_intc_init() -> Result<(), SystemError> { RiscvIntcChip::IRQ_SIZE, ) .ok_or_else(|| { - error!("Failed to create riscv-intc domain"); + kerror!("Failed to create riscv-intc domain"); SystemError::ENXIO })?; @@ -198,7 +197,7 @@ pub fn riscv_intc_assicate_irq(hwirq: HardwareIrqNumber) -> Option { irq_domain_manager() .domain_associate( riscv_intc_domain().as_ref().or_else(|| { - error!("riscv_intc_domain is None"); + kerror!("riscv_intc_domain is None"); None })?, virq, diff --git a/kernel/src/driver/irqchip/riscv_sifive_plic.rs b/kernel/src/driver/irqchip/riscv_sifive_plic.rs index 05bc52f0b..83e26916a 100644 --- a/kernel/src/driver/irqchip/riscv_sifive_plic.rs +++ b/kernel/src/driver/irqchip/riscv_sifive_plic.rs @@ -25,7 +25,6 @@ use alloc::{ }; use bitmap::AllocBitmap; use fdt::node::FdtNode; -use log::{debug, warn}; use system_error::SystemError; use crate::{ @@ -188,7 +187,7 @@ impl PlicHandler { fn plic_irq_toggle(cpumask: &CpuMask, irq_data: &Arc, enable: bool) { cpumask.iter_cpu().for_each(|cpu| { - debug!("plic: irq_toggle: cpu: {cpu:?}"); + kdebug!("plic: irq_toggle: cpu: {cpu:?}"); let handler = unsafe { plic_handlers().force_get(cpu) }; handler.toggle(irq_data.hardware_irq(), enable); }); @@ -241,7 +240,7 @@ impl IrqChip for PlicIrqChip { "SiFive PLIC" } fn irq_enable(&self, irq_data: &Arc) -> Result<(), SystemError> { - // warn!("plic: irq_enable"); + // kwarn!("plic: irq_enable"); let common_data = irq_data.common_data(); let inner_guard = common_data.inner(); let mask = inner_guard.effective_affinity(); @@ -253,7 +252,7 @@ impl IrqChip for PlicIrqChip { } fn irq_unmask(&self, irq_data: &Arc) -> Result<(), SystemError> { - // warn!("plic: irq_unmask"); + // kwarn!("plic: irq_unmask"); let chip_data = irq_data .chip_info_read_irqsave() @@ -301,7 +300,7 @@ impl IrqChip for PlicIrqChip { } fn irq_disable(&self, irq_data: &Arc) { - debug!("plic: irq_disable"); + kdebug!("plic: irq_disable"); let common_data = irq_data.common_data(); let inner_guard = common_data.inner(); let mask = inner_guard.effective_affinity(); @@ -322,7 +321,7 @@ impl IrqChip for PlicIrqChip { handler.toggle(irq_data.hardware_irq(), false); } else { - // debug!("plic: irq_eoi: hwirq: {:?}", irq_data.hardware_irq()); + // kdebug!("plic: irq_eoi: hwirq: {:?}", irq_data.hardware_irq()); unsafe { write_volatile( (handler.inner().hart_base + PlicIrqChip::CONTEXT_CLAIM).data() as *mut u32, @@ -424,7 +423,7 @@ pub fn riscv_sifive_plic_init() -> Result<(), SystemError> { }); for node in all_plics { if let Err(e) = do_riscv_sifive_plic_init(&node) { - warn!("Failed to init SiFive PLIC: node: {node:?} {e:?}"); + kwarn!("Failed to init SiFive PLIC: node: {node:?} {e:?}"); } } @@ -458,7 +457,7 @@ fn do_riscv_sifive_plic_init(fdt_node: &FdtNode) -> Result<(), SystemError> { .ok_or(SystemError::EINVAL)? .as_usize() .ok_or(SystemError::EINVAL)?; - debug!( + kdebug!( "plic: node: {}, irq_num: {irq_num}, paddr: {paddr:?}, size: {size}", fdt_node.name ); @@ -466,7 +465,7 @@ fn do_riscv_sifive_plic_init(fdt_node: &FdtNode) -> Result<(), SystemError> { .interrupts_extended() .ok_or(SystemError::EINVAL)? .count(); - debug!("plic: nr_contexts: {nr_contexts}"); + kdebug!("plic: nr_contexts: {nr_contexts}"); let irq_domain = irq_domain_manager() .create_and_add_linear( @@ -475,7 +474,7 @@ fn do_riscv_sifive_plic_init(fdt_node: &FdtNode) -> Result<(), SystemError> { (irq_num + 1) as u32, ) .ok_or(SystemError::EINVAL)?; - // debug!("plic: irq_domain: {irq_domain:?}"); + // kdebug!("plic: irq_domain: {irq_domain:?}"); let priv_data = PlicChipData::new( Arc::downgrade(&irq_domain), @@ -507,13 +506,13 @@ fn do_riscv_sifive_plic_init(fdt_node: &FdtNode) -> Result<(), SystemError> { let cpu = ProcessorId::new(i as u32); let handler = unsafe { plic_handlers().force_get(cpu) }; if handler.present() { - warn!("plic: handler {i} already present."); + kwarn!("plic: handler {i} already present."); handler.set_threshold(PlicIrqChip::PLIC_ENABLE_THRESHOLD); loop_done_setup(handler); continue; } - debug!("plic: setup lmask {cpu:?}."); + kdebug!("plic: setup lmask {cpu:?}."); priv_data.lmask().set(cpu, true); let mut handler_inner = handler.inner(); handler_inner.hart_base = @@ -561,7 +560,7 @@ fn associate_irq_with_plic_domain( let irq = irq as u32; let virq = IrqNumber::new(irq); let hwirq = HardwareIrqNumber::new(irq); - debug!("plic: associate irq: {irq}, virq: {virq:?}, hwirq: {hwirq:?}"); + kdebug!("plic: associate irq: {irq}, virq: {virq:?}, hwirq: {hwirq:?}"); irq_domain_manager() .domain_associate(irq_domain, virq, hwirq) .ok(); @@ -584,7 +583,7 @@ impl IrqDomainOps for PlicIrqDomainOps { hwirq: HardwareIrqNumber, virq: IrqNumber, ) -> Result<(), SystemError> { - // debug!("plic: map: virq: {virq:?}, hwirq: {hwirq:?}"); + // kdebug!("plic: map: virq: {virq:?}, hwirq: {hwirq:?}"); let chip_data = irq_domain.host_data().ok_or(SystemError::EINVAL)?; let plic_chip_data = chip_data @@ -614,7 +613,7 @@ impl IrqDomainOps for PlicIrqDomainOps { _irq_data: &Arc, _reserve: bool, ) -> Result<(), SystemError> { - warn!("plic: activate"); + kwarn!("plic: activate"); loop {} } @@ -623,7 +622,7 @@ impl IrqDomainOps for PlicIrqDomainOps { /// 处理PLIC中断 pub(super) fn do_plic_irq(trap_frame: &mut TrapFrame) { - // debug!("plic: do_plic_irq"); + // kdebug!("plic: do_plic_irq"); let handler = plic_handlers().get(); let priv_data = handler.priv_data(); @@ -649,11 +648,11 @@ pub(super) fn do_plic_irq(trap_frame: &mut TrapFrame) { if claim == 0 { break; } - debug!("plic: claim: {claim:?}"); + kdebug!("plic: claim: {claim:?}"); let hwirq = HardwareIrqNumber::new(claim); if let Err(e) = GenericIrqHandler::handle_domain_irq(domain.clone(), hwirq, trap_frame) { - warn!("plic: can't find mapping for hwirq {hwirq:?}, {e:?}"); + kwarn!("plic: can't find mapping for hwirq {hwirq:?}, {e:?}"); } } } diff --git a/kernel/src/driver/net/dma.rs b/kernel/src/driver/net/dma.rs index f8c06b74e..11fcf6229 100644 --- a/kernel/src/driver/net/dma.rs +++ b/kernel/src/driver/net/dma.rs @@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags; use crate::arch::MMArch; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, EntryFlags}; +use crate::mm::page::{page_manager_lock_irqsave, PageFlags}; use crate::mm::{ allocator::page_frame::{ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, @@ -25,7 +25,7 @@ pub fn dma_alloc(pages: usize) -> (usize, NonNull) { // 清空这块区域,防止出现脏数据 core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); - let dma_flags: EntryFlags = EntryFlags::mmio_flags(); + let dma_flags: PageFlags = PageFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let kernel_mapper = kernel_mapper.as_mut().unwrap(); diff --git a/kernel/src/driver/net/e1000e/e1000e.rs b/kernel/src/driver/net/e1000e/e1000e.rs index a144b1386..c2a27806d 100644 --- a/kernel/src/driver/net/e1000e/e1000e.rs +++ b/kernel/src/driver/net/e1000e/e1000e.rs @@ -9,7 +9,6 @@ use core::mem::size_of; use core::ptr::NonNull; use core::slice::{from_raw_parts, from_raw_parts_mut}; use core::sync::atomic::{compiler_fence, Ordering}; -use log::{debug, info}; use super::e1000e_driver::e1000e_driver_init; use crate::driver::base::device::DeviceId; @@ -24,6 +23,8 @@ use crate::exception::IrqNumber; use crate::libs::volatile::{ReadOnly, Volatile, WriteOnly}; +use crate::{kdebug, kinfo}; + const PAGE_SIZE: usize = 4096; const NETWORK_CLASS: u8 = 0x2; const ETHERNET_SUBCLASS: u8 = 0x0; @@ -283,7 +284,7 @@ impl E1000EDevice { volwrite!(general_regs, ctrl, ctrl | E1000E_CTRL_SLU); } let status = unsafe { volread!(general_regs, status) }; - debug!("Status: {status:#X}"); + kdebug!("Status: {status:#X}"); // 读取设备的mac地址 // Read mac address @@ -441,7 +442,7 @@ impl E1000EDevice { buffer.set_length(desc.len as usize); rdt = index; unsafe { volwrite!(self.receive_regs, rdt0, rdt as u32) }; - // debug!("e1000e: receive packet"); + // kdebug!("e1000e: receive packet"); return Some(buffer); } @@ -558,7 +559,7 @@ impl Drop for E1000EDevice { fn drop(&mut self) { // 释放已分配的所有dma页 // free all dma pages we have allocated - debug!("droping..."); + kdebug!("droping..."); let recv_ring_length = PAGE_SIZE / size_of::(); let trans_ring_length = PAGE_SIZE / size_of::(); unsafe { @@ -589,10 +590,10 @@ impl Drop for E1000EDevice { pub fn e1000e_init() { match e1000e_probe() { Ok(_code) => { - info!("Successfully init e1000e device!"); + kinfo!("Successfully init e1000e device!"); } Err(_error) => { - info!("Error occurred!"); + kinfo!("Error occurred!"); } } } @@ -609,7 +610,7 @@ pub fn e1000e_probe() -> Result { if header.vendor_id == 0x8086 { // intel if E1000E_DEVICE_ID.contains(&header.device_id) { - debug!( + kdebug!( "Detected e1000e PCI device with device id {:#x}", header.device_id ); @@ -780,8 +781,7 @@ const E1000E_TXD_CMD_EOP: u8 = 1 << 0; const E1000E_TXD_CMD_IFCS: u8 = 1 << 1; const E1000E_TXD_CMD_RS: u8 = 1 << 3; -/// E1000E驱动初始化过程中可能的错误 -#[allow(dead_code)] +// E1000E驱动初始化过程中可能的错误 pub enum E1000EPciError { // 获取到错误类型的BAR(IO BAR) // An IO BAR was provided rather than a memory BAR. diff --git a/kernel/src/driver/net/e1000e/e1000e_driver.rs b/kernel/src/driver/net/e1000e/e1000e_driver.rs index fbac2f834..92ff60c45 100644 --- a/kernel/src/driver/net/e1000e/e1000e_driver.rs +++ b/kernel/src/driver/net/e1000e/e1000e_driver.rs @@ -10,6 +10,7 @@ use crate::{ }, net::NetDevice, }, + kinfo, libs::spinlock::SpinLock, net::{generate_iface_id, NET_DEVICES}, time::Instant, @@ -23,7 +24,6 @@ use core::{ fmt::Debug, ops::{Deref, DerefMut}, }; -use log::info; use smoltcp::{ phy, wire::{self, HardwareAddress}, @@ -367,5 +367,5 @@ pub fn e1000e_driver_init(device: E1000EDevice) { NET_DEVICES .write_irqsave() .insert(iface.nic_id(), iface.clone()); - info!("e1000e driver init successfully!\tMAC: [{}]", mac); + kinfo!("e1000e driver init successfully!\tMAC: [{}]", mac); } diff --git a/kernel/src/driver/net/loopback.rs b/kernel/src/driver/net/loopback.rs deleted file mode 100644 index 32e4f07bb..000000000 --- a/kernel/src/driver/net/loopback.rs +++ /dev/null @@ -1,483 +0,0 @@ -use crate::arch::rand::rand; -use crate::driver::base::class::Class; -use crate::driver::base::device::bus::Bus; -use crate::driver::base::device::driver::Driver; -use crate::driver::base::device::{Device, DeviceType, IdTable}; -use crate::driver::base::kobject::{KObjType, KObject, KObjectState}; -use crate::init::initcall::INITCALL_DEVICE; -use crate::libs::spinlock::SpinLock; -use crate::net::{generate_iface_id, NET_DEVICES}; -use crate::time::Instant; -use alloc::collections::VecDeque; -use alloc::fmt::Debug; -use alloc::string::{String, ToString}; -use alloc::sync::{Arc, Weak}; -use alloc::vec::Vec; -use core::cell::UnsafeCell; -use core::ops::{Deref, DerefMut}; -use smoltcp::wire::HardwareAddress; -use smoltcp::{ - phy::{self}, - wire::{IpAddress, IpCidr}, -}; -use system_error::SystemError; -use unified_init::macros::unified_init; - -use super::NetDevice; - -const DEVICE_NAME: &str = "loopback"; - -/// ## 环回接收令牌 -/// 用于储存lo网卡接收到的数据 -pub struct LoopbackRxToken { - buffer: Vec, -} - -impl phy::RxToken for LoopbackRxToken { - /// ## 实现Rxtoken的consume函数 - /// 接受一个函数 `f`,并在 `self.buffer` 上调用它。 - /// - /// ## 参数 - /// - mut self :一个可变的 `LoopbackRxToken` 实例。 - /// - f :接受一个可变的 u8 切片,并返回类型 `R` 的结果。 - /// - /// ## 返回值 - /// 返回函数 `f` 在 `self.buffer` 上的调用结果。 - fn consume(mut self, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - f(self.buffer.as_mut_slice()) - } -} - -/// ## 环回发送令牌 -/// 返回驱动用于操作lo设备 -pub struct LoopbackTxToken { - driver: LoopbackDriver, -} - -impl phy::TxToken for LoopbackTxToken { - /// ## 实现TxToken的consume函数 - /// 向lo的队列推入待发送的数据报,实现环回 - /// - /// ## 参数 - /// - self - /// - len:数据包的长度 - /// - f:接受一个可变的 u8 切片,并返回类型 `R` 的结果。 - /// - /// ## 返回值 - /// 返回f对数据包操纵的结果 - fn consume(self, len: usize, f: F) -> R - where - F: FnOnce(&mut [u8]) -> R, - { - let mut buffer = vec![0; len]; - let result = f(buffer.as_mut_slice()); - let mut device = self.driver.inner.lock(); - device.loopback_transmit(buffer); - result - } -} - -/// ## Loopback设备 -/// 成员是一个队列,用来存放接受到的数据包。 -/// 当使用lo发送数据包时,不会把数据包传到link层,而是直接发送到该队列,实现环回。 -pub struct Loopback { - //回环设备的缓冲区,接受的数据包会存放在这里,发送的数据包也会发送到这里,实现环回 - queue: VecDeque>, -} - -impl Loopback { - /// ## Loopback创建函数 - /// 创建lo设备 - pub fn new() -> Self { - let queue = VecDeque::new(); - Loopback { queue } - } - /// ## Loopback处理接受到的数据包函数 - /// Loopback接受到数据后会调用这个函数来弹出接收的数据,返回给协议栈 - /// - /// ## 参数 - /// - &mut self :自身可变引用 - /// - /// ## 返回值 - /// - queue的头部数据包 - pub fn loopback_receive(&mut self) -> Vec { - let buffer = self.queue.pop_front(); - match buffer { - Some(buffer) => { - //debug!("lo receive:{:?}", buffer); - return buffer; - } - None => { - return Vec::new(); - } - } - } - /// ## Loopback发送数据包的函数 - /// Loopback发送数据包给自己的接收队列,实现环回 - /// - /// ## 参数 - /// - &mut self:自身可变引用 - /// - buffer:需要发送的数据包 - pub fn loopback_transmit(&mut self, buffer: Vec) { - //debug!("lo transmit!"); - self.queue.push_back(buffer) - } -} - -/// ## driver的包裹器 -/// 为实现获得不可变引用的Interface的内部可变性,故为Driver提供UnsafeCell包裹器 -/// -/// 参考virtio_net.rs -struct LoopbackDriverWapper(UnsafeCell); -unsafe impl Send for LoopbackDriverWapper {} -unsafe impl Sync for LoopbackDriverWapper {} - -/// ## deref 方法返回一个指向 `LoopbackDriver` 的引用。 -impl Deref for LoopbackDriverWapper { - type Target = LoopbackDriver; - fn deref(&self) -> &Self::Target { - unsafe { &*self.0.get() } - } -} -/// ## `deref_mut` 方法返回一个指向可变 `LoopbackDriver` 的引用。 -impl DerefMut for LoopbackDriverWapper { - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.0.get() } - } -} - -impl LoopbackDriverWapper { - /// ## force_get_mut返回一个指向可变 `LoopbackDriver` 的引用。 - #[allow(clippy::mut_from_ref)] - #[allow(clippy::mut_from_ref)] - fn force_get_mut(&self) -> &mut LoopbackDriver { - unsafe { &mut *self.0.get() } - } -} - -/// ## Loopback驱动 -/// 负责操作Loopback设备实现基本的网卡功能 -pub struct LoopbackDriver { - pub inner: Arc>, -} - -impl LoopbackDriver { - /// ## LoopbackDriver创建函数 - pub fn new() -> Self { - let inner = Arc::new(SpinLock::new(Loopback::new())); - LoopbackDriver { inner } - } -} - -impl Clone for LoopbackDriver { - fn clone(&self) -> Self { - LoopbackDriver { - inner: self.inner.clone(), - } - } -} - -impl phy::Device for LoopbackDriver { - type RxToken<'a> = LoopbackRxToken where Self: 'a; - type TxToken<'a> = LoopbackTxToken where Self: 'a; - /// ## 返回设备的物理层特性。 - /// lo设备的最大传输单元为65535,最大突发大小为1,传输介质默认为Ethernet - fn capabilities(&self) -> phy::DeviceCapabilities { - let mut result = phy::DeviceCapabilities::default(); - result.max_transmission_unit = 65535; - result.max_burst_size = Some(1); - result.medium = smoltcp::phy::Medium::Ethernet; - return result; - } - /// ## Loopback驱动处理接受数据事件 - /// 驱动调用Loopback的receive函数,处理buffer封装成(rx,tx)返回给上层 - /// - /// ## 参数 - /// - `&mut self` :自身可变引用 - /// - `_timestamp` - /// - /// ## 返回值 - /// - None: 如果接收队列为空,返回 `None`,以通知上层没有可以接收的包 - /// - Option::Some((rx, tx)):如果接收队列不为空,返回 `Some`,其中包含一个接收令牌 `rx` 和一个发送令牌 `tx` - fn receive( - &mut self, - _timestamp: smoltcp::time::Instant, - ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - let buffer = self.inner.lock().loopback_receive(); - //receive队列为为空,返回NONE值以通知上层没有可以receive的包 - if buffer.is_empty() { - return Option::None; - } - let rx = LoopbackRxToken { buffer }; - let tx = LoopbackTxToken { - driver: self.clone(), - }; - return Option::Some((rx, tx)); - } - /// ## Loopback驱动处理发送数据包事件 - /// Loopback驱动在需要发送数据时会调用这个函数来获取一个发送令牌。 - /// - /// ## 参数 - /// - `&mut self` :自身可变引用 - /// - `_timestamp` - /// - /// ## 返回值 - /// - 返回一个 `Some`,其中包含一个发送令牌,该令牌包含一个对自身的克隆引用 - fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { - Some(LoopbackTxToken { - driver: self.clone(), - }) - } -} - -/// ## LoopbackInterface结构 -/// 封装驱动包裹器和iface,设置接口名称 -pub struct LoopbackInterface { - driver: LoopbackDriverWapper, - iface_id: usize, - iface: SpinLock, - name: String, -} - -impl LoopbackInterface { - /// ## `new` 是一个公共函数,用于创建一个新的 `LoopbackInterface` 实例。 - /// 生成一个新的接口 ID。创建一个新的接口配置,设置其硬件地址和随机种子,使用接口配置和驱动器创建一个新的 `smoltcp::iface::Interface` 实例。 - /// 设置接口的 IP 地址为 127.0.0.1。 - /// 创建一个新的 `LoopbackDriverWapper` 实例,包装驱动器。 - /// 创建一个新的 `LoopbackInterface` 实例,包含驱动器、接口 ID、接口和名称,并将其封装在一个 `Arc` 中。 - /// ## 参数 - /// - `driver`:一个 `LoopbackDriver` 实例,用于驱动网络环回操作。 - /// - /// ## 返回值 - /// 返回一个 `Arc`,即一个指向新创建的 `LoopbackInterface` 实例的智能指针。 - pub fn new(mut driver: LoopbackDriver) -> Arc { - let iface_id = generate_iface_id(); - let mut iface_config = smoltcp::iface::Config::new(HardwareAddress::Ethernet( - smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]), - )); - iface_config.random_seed = rand() as u64; - - let mut iface = - smoltcp::iface::Interface::new(iface_config, &mut driver, Instant::now().into()); - //设置网卡地址为127.0.0.1 - iface.update_ip_addrs(|ip_addrs| { - ip_addrs - .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) - .unwrap(); - }); - let driver = LoopbackDriverWapper(UnsafeCell::new(driver)); - Arc::new(LoopbackInterface { - driver, - iface_id, - iface: SpinLock::new(iface), - name: "lo".to_string(), - }) - } -} - -impl Debug for LoopbackInterface { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("LoopbackInterface") - .field("iface_id", &self.iface_id) - .field("iface", &"smtoltcp::iface::Interface") - .field("name", &self.name) - .finish() - } -} -//TODO: 向sysfs注册lo设备 -impl KObject for LoopbackInterface { - fn as_any_ref(&self) -> &dyn core::any::Any { - self - } - - fn set_inode(&self, _inode: Option>) { - todo!() - } - - fn inode(&self) -> Option> { - todo!() - } - - fn parent(&self) -> Option> { - todo!() - } - - fn set_parent(&self, _parent: Option>) { - todo!() - } - - fn kset(&self) -> Option> { - todo!() - } - - fn set_kset(&self, _kset: Option>) { - todo!() - } - - fn kobj_type(&self) -> Option<&'static dyn crate::driver::base::kobject::KObjType> { - todo!() - } - - fn name(&self) -> String { - self.name.clone() - } - - fn set_name(&self, _name: String) { - todo!() - } - - fn kobj_state( - &self, - ) -> crate::libs::rwlock::RwLockReadGuard { - todo!() - } - - fn kobj_state_mut( - &self, - ) -> crate::libs::rwlock::RwLockWriteGuard { - todo!() - } - - fn set_kobj_state(&self, _state: KObjectState) { - todo!() - } - - fn set_kobj_type(&self, _ktype: Option<&'static dyn KObjType>) { - todo!() - } -} - -impl Device for LoopbackInterface { - fn dev_type(&self) -> DeviceType { - DeviceType::Net - } - - fn id_table(&self) -> IdTable { - IdTable::new(DEVICE_NAME.to_string(), None) - } - - fn set_bus(&self, _bus: Option>) { - todo!() - } - - fn set_class(&self, _class: Option>) { - todo!() - } - - fn driver(&self) -> Option> { - todo!() - } - - fn set_driver(&self, _driver: Option>) { - todo!() - } - - fn is_dead(&self) -> bool { - todo!() - } - - fn can_match(&self) -> bool { - todo!() - } - - fn set_can_match(&self, _can_match: bool) { - todo!() - } - - fn state_synced(&self) -> bool { - true - } -} - -impl NetDevice for LoopbackInterface { - /// 由于lo网卡设备不是实际的物理设备,其mac地址需要手动设置为一个默认值,这里默认为0200000001 - fn mac(&self) -> smoltcp::wire::EthernetAddress { - let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01]; - smoltcp::wire::EthernetAddress(mac) - } - - #[inline] - fn nic_id(&self) -> usize { - self.iface_id - } - - #[inline] - fn name(&self) -> String { - self.name.clone() - } - /// ## `update_ip_addrs` 用于更新接口的 IP 地址。 - /// - /// ## 参数 - /// - `&self` :自身引用 - /// - `ip_addrs` :一个包含 `smoltcp::wire::IpCidr` 的切片,表示要设置的 IP 地址和子网掩码 - /// - /// ## 返回值 - /// - 如果 `ip_addrs` 的长度不为 1,返回 `Err(SystemError::EINVAL)`,表示输入参数无效 - /// - 如果更新成功,返回 `Ok(())` - fn update_ip_addrs( - &self, - ip_addrs: &[smoltcp::wire::IpCidr], - ) -> Result<(), system_error::SystemError> { - if ip_addrs.len() != 1 { - return Err(SystemError::EINVAL); - } - - self.iface.lock().update_ip_addrs(|addrs| { - let dest = addrs.iter_mut().next(); - - if let Some(dest) = dest { - *dest = ip_addrs[0]; - } else { - addrs.push(ip_addrs[0]).expect("Push ipCidr failed: full"); - } - }); - return Ok(()); - } - /// ## `poll` 用于轮询接口的状态。 - /// - /// ## 参数 - /// - `&self` :自身引用 - /// - `sockets` :一个可变引用到 `smoltcp::iface::SocketSet`,表示要轮询的套接字集 - /// - /// ## 返回值 - /// - 如果轮询成功,返回 `Ok(())` - /// - 如果轮询失败,返回 `Err(SystemError::EAGAIN_OR_EWOULDBLOCK)`,表示需要再次尝试或者操作会阻塞 - fn poll(&self, sockets: &mut smoltcp::iface::SocketSet) -> Result<(), SystemError> { - let timestamp: smoltcp::time::Instant = Instant::now().into(); - let mut guard = self.iface.lock(); - let poll_res = guard.poll(timestamp, self.driver.force_get_mut(), sockets); - if poll_res { - return Ok(()); - } - return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); - } - - #[inline(always)] - fn inner_iface(&self) -> &SpinLock { - return &self.iface; - } -} - -pub fn loopback_probe() { - loopback_driver_init(); -} -/// ## lo网卡设备初始化函数 -/// 创建驱动和iface,初始化一个lo网卡,添加到全局NET_DEVICES中 -pub fn loopback_driver_init() { - let driver = LoopbackDriver::new(); - let iface = LoopbackInterface::new(driver); - - NET_DEVICES - .write_irqsave() - .insert(iface.iface_id, iface.clone()); -} - -/// ## lo网卡设备的注册函数 -#[unified_init(INITCALL_DEVICE)] -pub fn loopback_init() -> Result<(), SystemError> { - loopback_probe(); - return Ok(()); -} diff --git a/kernel/src/driver/net/mod.rs b/kernel/src/driver/net/mod.rs index 18e5a3fe1..ce1a149ba 100644 --- a/kernel/src/driver/net/mod.rs +++ b/kernel/src/driver/net/mod.rs @@ -11,9 +11,8 @@ use system_error::SystemError; mod dma; pub mod e1000e; pub mod irq_handle; -pub mod loopback; pub mod virtio_net; -#[allow(dead_code)] + pub trait NetDevice: Device { /// @brief 获取网卡的MAC地址 fn mac(&self) -> EthernetAddress; diff --git a/kernel/src/driver/net/virtio_net.rs b/kernel/src/driver/net/virtio_net.rs index 58551e4a9..1bc1f6a6d 100644 --- a/kernel/src/driver/net/virtio_net.rs +++ b/kernel/src/driver/net/virtio_net.rs @@ -10,7 +10,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::{debug, error}; use smoltcp::{iface, phy, wire}; use unified_init::macros::unified_init; use virtio_drivers::device::net::VirtIONet; @@ -40,6 +39,7 @@ use crate::{ exception::{irqdesc::IrqReturn, IrqNumber}, filesystem::kernfs::KernFSInode, init::initcall::INITCALL_POSTCORE, + kerror, libs::{ rwlock::{RwLockReadGuard, RwLockWriteGuard}, spinlock::{SpinLock, SpinLockGuard}, @@ -357,12 +357,12 @@ impl phy::Device for VirtIONicDeviceInner { } fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { - // debug!("VirtioNet: transmit"); + // kdebug!("VirtioNet: transmit"); if self.inner.lock_irqsave().can_send() { - // debug!("VirtioNet: can send"); + // kdebug!("VirtioNet: can send"); return Some(VirtioNetToken::new(self.clone(), None)); } else { - // debug!("VirtioNet: can not send"); + // kdebug!("VirtioNet: can not send"); return None; } } @@ -418,14 +418,14 @@ pub fn virtio_net(transport: VirtIOTransport, dev_id: Arc) { match VirtIONet::::new(transport, 4096) { Ok(net) => net, Err(_) => { - error!("VirtIONet init failed"); + kerror!("VirtIONet init failed"); return; } }; let mac = wire::EthernetAddress::from_bytes(&driver_net.mac_address()); let dev_inner = VirtIONicDeviceInner::new(driver_net); let iface = VirtioInterface::new(dev_inner, dev_id); - debug!("To add virtio net: {}, mac: {}", iface.device_name(), mac); + kdebug!("To add virtio net: {}, mac: {}", iface.device_name(), mac); virtio_device_manager() .device_add(iface.clone() as Arc) .expect("Add virtio net failed"); @@ -471,7 +471,7 @@ impl NetDevice for VirtioInterface { let mut guard = self.iface.lock(); let poll_res = guard.poll(timestamp, self.device_inner.force_get_mut(), sockets); // todo: notify!!! - // debug!("Virtio Interface poll:{poll_res}"); + // kdebug!("Virtio Interface poll:{poll_res}"); if poll_res { return Ok(()); } @@ -596,7 +596,7 @@ impl VirtIODriver for VirtIONetDriver { .arc_any() .downcast::() .map_err(|_| { - error!( + kerror!( "VirtIONetDriver::probe() failed: device is not a VirtioInterface. Device: '{:?}'", device.name() ); diff --git a/kernel/src/driver/open_firmware/fdt.rs b/kernel/src/driver/open_firmware/fdt.rs index aff55017d..2189ef5a3 100644 --- a/kernel/src/driver/open_firmware/fdt.rs +++ b/kernel/src/driver/open_firmware/fdt.rs @@ -4,7 +4,6 @@ use fdt::{ node::{FdtNode, NodeProperty}, Fdt, }; -use log::{debug, error, warn}; use system_error::SystemError; use crate::{ @@ -80,7 +79,7 @@ impl OpenFirmwareFdtDriver { let fdt_vaddr = boot_params().read().fdt().ok_or(SystemError::ENODEV)?; let fdt: Fdt<'_> = unsafe { fdt::Fdt::from_ptr(fdt_vaddr.as_ptr()).map_err(|e| { - error!("failed to parse fdt, err={:?}", e); + kerror!("failed to parse fdt, err={:?}", e); SystemError::EINVAL }) }?; @@ -92,7 +91,7 @@ impl OpenFirmwareFdtDriver { .expect("Failed to scan fdt root node."); self.early_init_scan_chosen(fdt).unwrap_or_else(|_| { - warn!("No `chosen` node found"); + kwarn!("No `chosen` node found"); }); self.early_init_scan_memory(fdt); @@ -107,13 +106,13 @@ impl OpenFirmwareFdtDriver { if let Some(prop) = node.property("#size-cells") { guard.root_size_cells = prop.as_usize().unwrap() as u32; - // debug!("fdt_root_size_cells={}", guard.root_size_cells); + // kdebug!("fdt_root_size_cells={}", guard.root_size_cells); } if let Some(prop) = node.property("#address-cells") { guard.root_addr_cells = prop.as_usize().unwrap() as u32; - // debug!("fdt_root_addr_cells={}", guard.root_addr_cells); + // kdebug!("fdt_root_addr_cells={}", guard.root_addr_cells); } return Ok(()); @@ -145,7 +144,7 @@ impl OpenFirmwareFdtDriver { // TODO: 拼接内核自定义的command line参数 - debug!("Command line: {}", boot_params().read().boot_cmdline_str()); + kdebug!("Command line: {}", boot_params().read().boot_cmdline_str()); return Ok(()); } @@ -195,7 +194,7 @@ impl OpenFirmwareFdtDriver { continue; } - debug!("Found memory: base={:#x}, size={:#x}", base, size); + kdebug!("Found memory: base={:#x}, size={:#x}", base, size); self.early_init_dt_add_memory(base, size); found_memory = true; } @@ -206,7 +205,7 @@ impl OpenFirmwareFdtDriver { #[cfg(target_arch = "x86_64")] pub fn early_init_dt_add_memory(&self, _base: u64, _size: u64) { - panic!("x86_64 should not call early_init_dt_add_memory"); + kBUG!("x86_64 should not call early_init_dt_add_memory"); } #[cfg(not(target_arch = "x86_64"))] @@ -221,7 +220,7 @@ impl OpenFirmwareFdtDriver { let mut size = size as usize; if size < (MMArch::PAGE_SIZE - (base & (!MMArch::PAGE_MASK))) { - warn!("Ignoring memory block {:#x}-{:#x}", base, base + size); + kwarn!("Ignoring memory block {:#x}-{:#x}", base, base + size); } if PhysAddr::new(base).check_aligned(MMArch::PAGE_SIZE) == false { @@ -232,11 +231,11 @@ impl OpenFirmwareFdtDriver { size = page_align_down(size); if base > MemBlockManager::MAX_MEMBLOCK_ADDR.data() { - warn!("Ignoring memory block {:#x}-{:#x}", base, base + size); + kwarn!("Ignoring memory block {:#x}-{:#x}", base, base + size); } if base + size - 1 > MemBlockManager::MAX_MEMBLOCK_ADDR.data() { - warn!( + kwarn!( "Ignoring memory range {:#x}-{:#x}", MemBlockManager::MAX_MEMBLOCK_ADDR.data() + 1, base + size @@ -245,13 +244,13 @@ impl OpenFirmwareFdtDriver { } if base + size < MemBlockManager::MIN_MEMBLOCK_ADDR.data() { - warn!("Ignoring memory range {:#x}-{:#x}", base, base + size); + kwarn!("Ignoring memory range {:#x}-{:#x}", base, base + size); return; } if base < MemBlockManager::MIN_MEMBLOCK_ADDR.data() { { - warn!( + kwarn!( "Ignoring memory range {:#x}-{:#x}", base, MemBlockManager::MIN_MEMBLOCK_ADDR.data() @@ -313,7 +312,7 @@ impl OpenFirmwareFdtDriver { if node.size() != 0 { let address = PhysAddr::new(node.address() as usize); let size = node.size(); - debug!("Reserve memory: {:?}-{:?}", address, address + size); + kdebug!("Reserve memory: {:?}-{:?}", address, address + size); mem_block_manager().reserve_block(address, size).unwrap(); } } @@ -407,7 +406,7 @@ fn reserved_mem_reserve_reg(node: &FdtNode<'_, '_>) -> Result<(), SystemError> { let mut reg_size = reg.value.len(); if reg_size > 0 && reg_size % t_len != 0 { - error!( + kerror!( "Reserved memory: invalid reg property in '{}', skipping node.", node.name ); @@ -432,14 +431,18 @@ fn reserved_mem_reserve_reg(node: &FdtNode<'_, '_>) -> Result<(), SystemError> { .early_init_dt_reserve_memory(PhysAddr::new(base as usize), size as usize, nomap) .is_ok() { - debug!( + kdebug!( "Reserved memory: base={:#x}, size={:#x}, nomap={}", - base, size, nomap + base, + size, + nomap ); } else { - error!( + kerror!( "Failed to reserve memory: base={:#x}, size={:#x}, nomap={}", - base, size, nomap + base, + size, + nomap ); } @@ -468,7 +471,8 @@ fn read_cell(reg_value: &[u8], base_index: usize, cells: usize) -> (u64, usize) 1 => { return ( u32::from_be_bytes(reg_value[base_index..base_index + 4].try_into().unwrap()) - .into(), + .try_into() + .unwrap(), next_base_index, ); } diff --git a/kernel/src/driver/pci/attr.rs b/kernel/src/driver/pci/attr.rs deleted file mode 100644 index 6d59122be..000000000 --- a/kernel/src/driver/pci/attr.rs +++ /dev/null @@ -1,160 +0,0 @@ -use alloc::sync::Arc; -use intertrait::cast::CastArc; -use log::warn; -use system_error::SystemError; - -use crate::{ - driver::base::kobject::KObject, - filesystem::{ - sysfs::{ - file::sysfs_emit_str, Attribute, AttributeGroup, SysFSOpsSupport, SYSFS_ATTR_MODE_RO, - }, - vfs::syscall::ModeType, - }, -}; - -use super::device::PciDevice; -#[derive(Debug)] -pub struct BasicPciReadOnlyAttrs; - -impl AttributeGroup for BasicPciReadOnlyAttrs { - fn name(&self) -> Option<&str> { - None - } - - fn attrs(&self) -> &[&'static dyn Attribute] { - &[&Vendor, &DeviceID, &SubsystemVendor, &SubsystemDevice] - } - - fn is_visible( - &self, - _kobj: Arc, - attr: &'static dyn Attribute, - ) -> Option { - return Some(attr.mode()); - } -} - -#[derive(Debug)] -pub struct Vendor; - -impl Attribute for Vendor { - fn mode(&self) -> ModeType { - SYSFS_ATTR_MODE_RO - } - - fn name(&self) -> &str { - "vendor" - } - - fn show(&self, _kobj: Arc, _buf: &mut [u8]) -> Result { - let dev = _kobj - .cast::() - .map_err(|e: Arc| { - warn!("device:{:?} is not a pci device!", e); - SystemError::EINVAL - })?; - return sysfs_emit_str(_buf, &format!("0x{:04x}", dev.vendor())); - } - - fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - todo!() - } - - fn support(&self) -> SysFSOpsSupport { - SysFSOpsSupport::ATTR_SHOW - } -} - -#[derive(Debug)] -pub struct DeviceID; - -impl Attribute for DeviceID { - fn mode(&self) -> ModeType { - SYSFS_ATTR_MODE_RO - } - - fn name(&self) -> &str { - "device" - } - - fn show(&self, _kobj: Arc, _buf: &mut [u8]) -> Result { - let dev = _kobj - .cast::() - .map_err(|e: Arc| { - warn!("device:{:?} is not a pci device!", e); - SystemError::EINVAL - })?; - return sysfs_emit_str(_buf, &format!("0x{:04x}", dev.device_id())); - } - - fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - todo!() - } - - fn support(&self) -> SysFSOpsSupport { - SysFSOpsSupport::ATTR_SHOW - } -} - -#[derive(Debug)] -pub struct SubsystemVendor; - -impl Attribute for SubsystemVendor { - fn mode(&self) -> ModeType { - SYSFS_ATTR_MODE_RO - } - - fn name(&self) -> &str { - "subsystem_vendor" - } - - fn show(&self, _kobj: Arc, _buf: &mut [u8]) -> Result { - let dev = _kobj - .cast::() - .map_err(|e: Arc| { - warn!("device:{:?} is not a pci device!", e); - SystemError::EINVAL - })?; - return sysfs_emit_str(_buf, &format!("0x{:04x}", dev.subsystem_vendor())); - } - - fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - todo!() - } - - fn support(&self) -> SysFSOpsSupport { - SysFSOpsSupport::ATTR_SHOW - } -} - -#[derive(Debug)] -pub struct SubsystemDevice; - -impl Attribute for SubsystemDevice { - fn mode(&self) -> ModeType { - SYSFS_ATTR_MODE_RO - } - - fn name(&self) -> &str { - "subsystem_device" - } - - fn show(&self, _kobj: Arc, _buf: &mut [u8]) -> Result { - let dev = _kobj - .cast::() - .map_err(|e: Arc| { - warn!("device:{:?} is not a pci device!", e); - SystemError::EINVAL - })?; - return sysfs_emit_str(_buf, &format!("0x{:04x}", dev.subsystem_device())); - } - - fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - todo!() - } - - fn support(&self) -> SysFSOpsSupport { - SysFSOpsSupport::ATTR_SHOW - } -} diff --git a/kernel/src/driver/pci/dev_id.rs b/kernel/src/driver/pci/dev_id.rs deleted file mode 100644 index f79904179..000000000 --- a/kernel/src/driver/pci/dev_id.rs +++ /dev/null @@ -1,109 +0,0 @@ -use alloc::sync::Arc; - -use super::device::PciDevice; -const PCI_ANY_ID: u32 = 0xffff_ffff; - -/// # 结构功能 -/// 该结构用于驱动和设备之间的识别,驱动会有一个支持的设备ID列表,而设备会自带一个ID,如果设备的ID在驱动的支持列表中,则驱动和设备就可以识别了 -/// 见https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/mod_devicetable.h#43 -#[derive(Debug, Copy, Clone)] -pub struct PciDeviceID { - vendor: u32, - device_id: u32, - subvendor: u32, - subdevice: u32, - class: u32, - class_mask: u32, - _driver_data: u64, - _override_only: u32, - /// 可能有些设备的识别方式比较特殊,那么可以通过设置该字段进行自定义的识别方式,只需要在PciSpecifiedData枚举中加入一个类型即可 - /// 若该字段不为None,则优先使用special_data进行识别; - /// 该字段是为了增加灵活性 - special_data: Option, -} - -impl PciDeviceID { - #[allow(dead_code)] - pub fn set_special(&mut self, data: PciSpecifiedData) { - self.special_data = Some(data); - } - - pub fn dummpy() -> Self { - return Self { - vendor: PCI_ANY_ID, - device_id: PCI_ANY_ID, - subvendor: PCI_ANY_ID, - subdevice: PCI_ANY_ID, - class: PCI_ANY_ID, - class_mask: PCI_ANY_ID, - _driver_data: 0, - _override_only: PCI_ANY_ID, - special_data: None, - }; - } - pub fn match_dev(&self, dev: &Arc) -> bool { - if let Some(d_data) = &dev.dynid().special_data { - return d_data.match_dev(self.special_data); - } - if let Some(s_data) = &self.special_data { - return s_data.match_dev(dev.dynid().special_data); - } else { - let d_id = dev.dynid(); - return self.general_match(d_id); - } - } - - /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/pci/pci.h?fi=pci_match_one_device#195 - pub fn general_match(&self, id: PciDeviceID) -> bool { - if (self.vendor == id.vendor() || self.vendor == PCI_ANY_ID) - && (self.device_id == id.device_id() || self.device_id == PCI_ANY_ID) - && (self.subvendor == id.subvendor() || self.subvendor == PCI_ANY_ID) - && (self.subdevice == id.subdevice() || self.subdevice == PCI_ANY_ID) - && self.class_check(&id) - { - return true; - } - return false; - } - - pub fn class_check(&self, id: &Self) -> bool { - return (self.class ^ id.class()) & self.class_mask == 0; - } - - pub fn vendor(&self) -> u32 { - self.vendor - } - - pub fn device_id(&self) -> u32 { - self.device_id - } - - pub fn subvendor(&self) -> u32 { - self.subvendor - } - - pub fn subdevice(&self) -> u32 { - self.subdevice - } - - pub fn class(&self) -> u32 { - self.class - } - - pub fn _class_mask(&self) -> u32 { - self.class_mask - } -} - -#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)] -pub enum PciSpecifiedData {} - -impl PciSpecifiedData { - pub fn match_dev(&self, data: Option) -> bool { - if let Some(data) = data { - return *self == data; - } else { - return false; - } - } -} diff --git a/kernel/src/driver/pci/device.rs b/kernel/src/driver/pci/device.rs deleted file mode 100644 index 2d139f52a..000000000 --- a/kernel/src/driver/pci/device.rs +++ /dev/null @@ -1,219 +0,0 @@ -use alloc::{ - string::{String, ToString}, - sync::{Arc, Weak}, -}; -use system_error::SystemError; - -use crate::{ - driver::base::{ - device::{ - bus::Bus, device_manager, driver::Driver, Device, DeviceCommonData, DeviceType, IdTable, - }, - kobject::{KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState}, - kset::KSet, - }, - filesystem::kernfs::KernFSInode, - libs::{rwlock::RwLockWriteGuard, spinlock::SpinLock}, -}; - -use super::{ - dev_id::PciDeviceID, - subsys::{pci_bus, pci_bus_device}, -}; - -/// # 结构功能 -/// 该结构为Pci设备的管理器,使用该结构可以将pci设备添加到sysfs中 -pub struct PciDeviceManager; - -pub fn pci_device_manager() -> &'static PciDeviceManager { - &PciDeviceManager -} - -impl PciDeviceManager { - /// #函数的功能 - /// 将pci设备注册到sysfs中 - /// - /// ## 参数: - /// - 'pci_dev':需要添加的pci设备 - /// - /// ## 返回值: - /// - OK(()) :表示成功 - /// - Err(e) :失败原因 - pub fn device_add(&self, pci_dev: Arc) -> Result<(), SystemError> { - // pci设备一般放置在/sys/device/pci:xxxx下 - if pci_dev.parent().is_none() { - pci_dev.set_parent(Some(Arc::downgrade( - &(pci_bus_device() as Arc), - ))); - } - // 设置设备的总线 - pci_dev.set_bus(Some(Arc::downgrade(&(pci_bus() as Arc)))); - // 对设备进行默认的初始化 - device_manager().device_default_initialize(&(pci_dev.clone() as Arc)); - // 使用设备管理器注册设备,当设备被注册后,会根据它的总线字段,在对应的总线上扫描驱动,并尝试进行匹配 - let r = device_manager().add_device(pci_dev.clone() as Arc); - - if r.is_ok() { - //todo:这里可能还要处理一些设置成功后设备状态的变化 - return Ok(()); - } else { - //todo:这里可能有一些添加失败的处理 - return r; - } - } -} - -/// #trait功能 -/// 要进入sysfs的Pci设备应当实现的trait -pub trait PciDevice: Device { - /// # 函数的功能 - /// 返回本设备的PciDeviceID,该ID用于driver和device之间的匹配 - /// - /// ## 返回值 - /// - 'PciDeviceID' :本设备的PciDeviceID - fn dynid(&self) -> PciDeviceID; - - /// # 函数的功能 - /// 返回本设备的供应商(vendor)ID - /// - /// ## 返回值 - /// - u16 :表示供应商ID - fn vendor(&self) -> u16; - fn device_id(&self) -> u16; - fn subsystem_vendor(&self) -> u16; - fn subsystem_device(&self) -> u16; -} - -/// #结构功能 -/// 由于Pci总线本身就属于一个设备,故该结构代表Pci总线(控制器)本身 -/// 它对应/sys/device/pci -#[derive(Debug)] -#[cast_to([sync] Device)] -pub struct PciBusDevice { - // inner: SpinLock, - device_data: SpinLock, - kobj_data: SpinLock, - kobj_state: LockedKObjectState, - name: String, -} - -impl PciBusDevice { - pub fn new(parent: Option>) -> Arc { - let common_device = DeviceCommonData::default(); - let common_kobj = KObjectCommonData::default(); - let bus_device = Self { - device_data: SpinLock::new(common_device), - kobj_data: SpinLock::new(common_kobj), - kobj_state: LockedKObjectState::new(None), - name: "pci".to_string(), - }; - bus_device.set_parent(parent); - return Arc::new(bus_device); - } -} - -impl KObject for PciBusDevice { - fn as_any_ref(&self) -> &dyn core::any::Any { - self - } - - fn parent(&self) -> Option> { - self.kobj_data.lock().parent.clone() - } - - fn inode(&self) -> Option> { - self.kobj_data.lock().kern_inode.clone() - } - - fn set_inode(&self, inode: Option>) { - self.kobj_data.lock().kern_inode = inode; - } - - fn kobj_type(&self) -> Option<&'static dyn KObjType> { - self.kobj_data.lock().kobj_type - } - - fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { - self.kobj_data.lock().kobj_type = ktype - } - - fn kset(&self) -> Option> { - self.kobj_data.lock().kset.clone() - } - - fn kobj_state( - &self, - ) -> crate::libs::rwlock::RwLockReadGuard { - self.kobj_state.read() - } - - fn kobj_state_mut(&self) -> RwLockWriteGuard { - self.kobj_state.write() - } - - fn set_kobj_state(&self, state: KObjectState) { - *self.kobj_state.write() = state; - } - - fn name(&self) -> String { - self.name.clone() - } - - fn set_name(&self, _name: String) { - //do nothing; it's not supposed to change this struct's name - } - - fn set_kset(&self, kset: Option>) { - self.kobj_data.lock().kset = kset; - } - - fn set_parent(&self, parent: Option>) { - self.kobj_data.lock().parent = parent; - } -} - -impl Device for PciBusDevice { - fn dev_type(&self) -> DeviceType { - return DeviceType::Bus; - } - - fn id_table(&self) -> IdTable { - IdTable::new("pci".to_string(), None) - } - - fn bus(&self) -> Option> { - self.device_data.lock().bus.clone() - } - - fn set_bus(&self, bus: Option>) { - self.device_data.lock().bus = bus - } - - fn driver(&self) -> Option> { - self.device_data.lock().driver.clone()?.upgrade() - } - - fn is_dead(&self) -> bool { - false - } - - fn set_driver(&self, driver: Option>) { - self.device_data.lock().driver = driver; - } - - fn can_match(&self) -> bool { - todo!() - } - - fn set_can_match(&self, _can_match: bool) { - todo!() - } - - fn set_class(&self, _class: Option>) { - todo!() - } - - fn state_synced(&self) -> bool { - todo!() - } -} diff --git a/kernel/src/driver/pci/driver.rs b/kernel/src/driver/pci/driver.rs deleted file mode 100644 index 0643f2cba..000000000 --- a/kernel/src/driver/pci/driver.rs +++ /dev/null @@ -1,84 +0,0 @@ -use alloc::{sync::Arc, vec::Vec}; -use system_error::SystemError; - -use crate::driver::base::device::{ - bus::Bus, - driver::{driver_manager, Driver}, -}; - -use super::{dev_id::PciDeviceID, device::PciDevice, subsys::pci_bus}; - -/// # trait功能 -/// Pci驱动应该实现的trait -/// -/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/pci.h#907 -#[allow(dead_code)] -pub trait PciDriver: Driver { - /// # 函数的功能 - /// 对设备进行probe操作 - /// - /// ## 参数: - /// - 'device' :要进行probe的设备 - /// - 'id' :设备的ID(暂时不清楚为什么需要这个,依Linux实现是有ID的) - /// - /// ## 返回值: - /// - Ok:probe成功 - /// - Err:probe失败 - fn probe(&self, device: &Arc, id: &PciDeviceID) -> Result<(), SystemError>; - fn remove(&self, device: &Arc) -> Result<(), SystemError>; - fn shutdown(&self, device: &Arc) -> Result<(), SystemError>; - fn suspend(&self, device: &Arc) -> Result<(), SystemError>; - fn resume(&self, device: &Arc) -> Result<(), SystemError>; - /// # 函数的功能 - /// 向驱动中加入一个PciDeviceID,表示该驱动可以支持该ID的设备 - /// - /// ## 参数: - /// - 'id' :要添加的ID - /// - /// ## 返回值: - /// - 'Ok':添加成功 - /// - 'Err':添加失败 - fn add_dynid(&mut self, id: PciDeviceID) -> Result<(), SystemError>; - /// # 函数的功能 - /// 每个Pci驱动都应该持有一个支持ID的列表,并通过该函数进行访问 - /// - /// ## 返回值: - /// - 'Some(Vec)': 支持ID的列表 - /// - 'None':未能获取列表 - fn locked_dynid_list(&self) -> Option>>; - /// # 函数的功能 - /// 检测当前驱动是否支持目标设备 - /// - /// ## 参数: - /// - 'dev' :要检测的设备 - /// - /// ## 返回值: - /// - 'Some(Arc)': 如果支持,则返回支持的ID - /// - 'None': 不支持的设备 - fn match_dev(&self, dev: &Arc) -> Option> { - for i in self.locked_dynid_list()?.iter() { - if i.match_dev(dev) { - return Some(i.clone()); - } - } - return None; - } -} - -pub struct PciDriverManager; - -pub fn pci_driver_manager() -> &'static PciDriverManager { - &PciDriverManager -} - -impl PciDriverManager { - pub fn register(&self, driver: Arc) -> Result<(), SystemError> { - driver.set_bus(Some(Arc::downgrade(&(pci_bus() as Arc)))); - return driver_manager().register(driver as Arc); - } - - #[allow(dead_code)] - pub fn unregister(&self, driver: &Arc) { - driver_manager().unregister(&(driver.clone() as Arc)); - } -} diff --git a/kernel/src/driver/pci/ecam.rs b/kernel/src/driver/pci/ecam.rs index e5f1f86c2..de1529c9c 100644 --- a/kernel/src/driver/pci/ecam.rs +++ b/kernel/src/driver/pci/ecam.rs @@ -1,5 +1,3 @@ -use log::{error, warn}; - use crate::mm::PhysAddr; use super::{ @@ -13,32 +11,27 @@ pub fn pci_ecam_root_info_manager() -> &'static EcamRootInfoManager { } /// Ecam pci root info -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Copy)] pub struct EcamRootInfo { - /// 段组号 - pub segment_group_number: SegmentGroupNumber, - /// 该分组中的最小bus + pub segement_group_number: SegmentGroupNumber, pub bus_begin: u8, - /// 该分组中的最大bus pub bus_end: u8, - /// 物理基地址 pub physical_address_base: PhysAddr, } impl EcamRootInfo { pub fn new( - segment_group_number: SegmentGroupNumber, + segement_group_number: SegmentGroupNumber, bus_begin: u8, bus_end: u8, physical_address_base: PhysAddr, ) -> Self { - let ecam_root_info = Self { - segment_group_number, + Self { + segement_group_number, bus_begin, bus_end, physical_address_base, - }; - return ecam_root_info; + } } } @@ -53,24 +46,25 @@ impl EcamRootInfoManager { /// /// - `ecam_root_info`: EcamRootInfo - 要添加的EcamRootInfo实例 pub fn add_ecam_root_info(&self, ecam_root_info: EcamRootInfo) { - if !pci_root_manager().has_root(ecam_root_info.segment_group_number) { + if !pci_root_manager().has_root(ecam_root_info.segement_group_number) { let root = PciRoot::new( - Some(ecam_root_info), + ecam_root_info.segement_group_number, PciCam::Ecam, + ecam_root_info.physical_address_base, ecam_root_info.bus_begin, ecam_root_info.bus_end, ); if let Err(err) = root { - error!("add_ecam_root_info(): failed to create PciRoot: {:?}", err); + kerror!("add_ecam_root_info(): failed to create PciRoot: {:?}", err); return; } pci_root_manager().add_pci_root(root.unwrap()); } else { - warn!( + kwarn!( "add_ecam_root_info(): root {} already exists", - ecam_root_info.segment_group_number + ecam_root_info.segement_group_number ); } } diff --git a/kernel/src/driver/pci/mod.rs b/kernel/src/driver/pci/mod.rs index d443a4123..9973b1225 100644 --- a/kernel/src/driver/pci/mod.rs +++ b/kernel/src/driver/pci/mod.rs @@ -1,12 +1,5 @@ -pub mod attr; -pub mod dev_id; -pub mod device; -pub mod driver; pub mod ecam; #[allow(clippy::module_inception)] pub mod pci; pub mod pci_irq; -pub mod raw_device; pub mod root; -pub mod subsys; -pub mod test; diff --git a/kernel/src/driver/pci/pci.rs b/kernel/src/driver/pci/pci.rs index b339529f9..0f37e59ec 100644 --- a/kernel/src/driver/pci/pci.rs +++ b/kernel/src/driver/pci/pci.rs @@ -1,26 +1,20 @@ #![allow(dead_code)] // 目前仅支持单主桥单Segment -use super::device::pci_device_manager; use super::pci_irq::{IrqType, PciIrqError}; -use super::raw_device::PciGeneralDevice; use super::root::{pci_root_0, PciRoot}; - use crate::arch::{PciArch, TraitPciArch}; -use crate::driver::pci::subsys::pci_bus_subsys_init; use crate::exception::IrqNumber; use crate::libs::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::mm::mmio_buddy::{mmio_pool, MMIOSpaceGuard}; use crate::mm::VirtAddr; - -use alloc::string::String; +use crate::{kdebug, kerror, kinfo, kwarn}; use alloc::sync::Arc; use alloc::vec::Vec; use alloc::{boxed::Box, collections::LinkedList}; use bitflags::bitflags; -use log::{debug, error, info, warn}; use core::{ convert::TryFrom, @@ -639,8 +633,6 @@ impl PciDeviceStructure for PciDeviceStructurePciToCardbusBridge { /// 用于访问PCI设备的功能配置空间的一组机制。 #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum PciCam { - /// PortIO配置访问机制 - Portiocam, /// PCI内存映射配置访问机制 /// /// 为每个设备功能提供256字节的配置空间访问。 @@ -655,7 +647,6 @@ impl PciCam { /// Returns the total size in bytes of the memory-mapped region. pub const fn size(self) -> u32 { match self { - Self::Portiocam => 0x100000, Self::MmioCam => 0x1000000, Self::Ecam => 0x10000000, } @@ -725,18 +716,11 @@ fn pci_read_header( }; match HeaderType::from(header_type & 0x7f) { HeaderType::Standard => { - let general_device: PciDeviceStructureGeneralDevice = - pci_read_general_device_header(header, &bus_device_function); - let box_general_device = Box::new(general_device.clone()); + let general_device = pci_read_general_device_header(header, &bus_device_function); + let box_general_device = Box::new(general_device); let box_general_device_clone = box_general_device.clone(); if add_to_list { PCI_DEVICE_LINKEDLIST.add(box_general_device); - //这里实际上不应该使用clone,因为raw是用于sysfs的结构,但是实际上pci设备是在PCI_DEVICE_LINKEDLIST链表上的, - //这就导致sysfs呈现的对pci设备的操控接口实际上操控的是pci设备描述符是一个副本 - //但是无奈这里没有使用Arc - //todo:修改pci设备描述符在静态链表中存在的方式,并修改这里的clone操作 - let raw = PciGeneralDevice::from(&general_device); - let _ = pci_device_manager().device_add(Arc::new(raw)); } Ok(box_general_device_clone) } @@ -966,7 +950,7 @@ fn pci_read_pci_to_cardbus_bridge_header( /// @brief 检查所有bus上的设备并将其加入链表 /// @return 成功返回ok(),失败返回失败原因 fn pci_check_all_buses() -> Result { - info!("Checking all devices in PCI bus..."); + kinfo!("Checking all devices in PCI bus..."); let busdevicefunction = BusDeviceFunction { bus: 0, device: 0, @@ -985,7 +969,7 @@ fn pci_check_all_buses() -> Result { /// @brief 检查特定设备并将其加入链表 /// @return 成功返回ok(),失败返回失败原因 fn pci_check_function(busdevicefunction: BusDeviceFunction) -> Result { - //debug!("PCI check function {}", busdevicefunction.function); + //kdebug!("PCI check function {}", busdevicefunction.function); let header = match pci_read_header(busdevicefunction, true) { Ok(header) => header, Err(PciError::GetWrongHeader) => { @@ -1011,7 +995,7 @@ fn pci_check_function(busdevicefunction: BusDeviceFunction) -> Result Result { - //debug!("PCI check device {}", device); + //kdebug!("PCI check device {}", device); let busdevicefunction = BusDeviceFunction { bus, device, @@ -1030,9 +1014,10 @@ fn pci_check_device(bus: u8, device: u8) -> Result { pci_check_function(busdevicefunction)?; let common_header = header.common_header(); if common_header.header_type & 0x80 != 0 { - debug!( + kdebug!( "Detected multi func device in bus{},device{}", - busdevicefunction.bus, busdevicefunction.device + busdevicefunction.bus, + busdevicefunction.device ); // 这是一个多function的设备,因此查询剩余的function for function in 1..8 { @@ -1049,7 +1034,7 @@ fn pci_check_device(bus: u8, device: u8) -> Result { /// @brief 检查该bus上的设备并将其加入链表 /// @return 成功返回ok(),失败返回失败原因 fn pci_check_bus(bus: u8) -> Result { - //debug!("PCI check bus {}", bus); + //kdebug!("PCI check bus {}", bus); for device in 0..32 { pci_check_device(bus, device)?; } @@ -1059,13 +1044,12 @@ fn pci_check_bus(bus: u8) -> Result { /// pci初始化函数 #[inline(never)] pub fn pci_init() { - info!("Initializing PCI bus..."); - pci_bus_subsys_init().expect("Failed to init pci bus subsystem"); + kinfo!("Initializing PCI bus..."); if let Err(e) = pci_check_all_buses() { - error!("pci init failed when checking bus because of error: {}", e); + kerror!("pci init failed when checking bus because of error: {}", e); return; } - info!( + kinfo!( "Total pci device and function num = {}", PCI_DEVICE_LINKEDLIST.num() ); @@ -1074,34 +1058,39 @@ pub fn pci_init() { let common_header = box_pci_device.common_header(); match box_pci_device.header_type() { HeaderType::Standard if common_header.status & 0x10 != 0 => { - info!("Found pci standard device with class code ={} subclass={} status={:#x} cap_pointer={:#x} vendor={:#x}, device id={:#x},bdf={}", common_header.class_code, common_header.subclass, common_header.status, box_pci_device.as_standard_device().unwrap().capabilities_pointer,common_header.vendor_id, common_header.device_id,common_header.bus_device_function); + kinfo!("Found pci standard device with class code ={} subclass={} status={:#x} cap_pointer={:#x} vendor={:#x}, device id={:#x},bdf={}", common_header.class_code, common_header.subclass, common_header.status, box_pci_device.as_standard_device().unwrap().capabilities_pointer,common_header.vendor_id, common_header.device_id,common_header.bus_device_function); } HeaderType::Standard => { - info!( + kinfo!( "Found pci standard device with class code ={} subclass={} status={:#x} ", - common_header.class_code, common_header.subclass, common_header.status + common_header.class_code, + common_header.subclass, + common_header.status ); } HeaderType::PciPciBridge if common_header.status & 0x10 != 0 => { - info!("Found pci-to-pci bridge device with class code ={} subclass={} status={:#x} cap_pointer={:#x}", common_header.class_code, common_header.subclass, common_header.status, box_pci_device.as_pci_to_pci_bridge_device().unwrap().capability_pointer); + kinfo!("Found pci-to-pci bridge device with class code ={} subclass={} status={:#x} cap_pointer={:#x}", common_header.class_code, common_header.subclass, common_header.status, box_pci_device.as_pci_to_pci_bridge_device().unwrap().capability_pointer); } HeaderType::PciPciBridge => { - info!( + kinfo!( "Found pci-to-pci bridge device with class code ={} subclass={} status={:#x} ", - common_header.class_code, common_header.subclass, common_header.status + common_header.class_code, + common_header.subclass, + common_header.status ); } HeaderType::PciCardbusBridge => { - info!( + kinfo!( "Found pcicardbus bridge device with class code ={} subclass={} status={:#x} ", - common_header.class_code, common_header.subclass, common_header.status + common_header.class_code, + common_header.subclass, + common_header.status ); } HeaderType::Unrecognised(_) => {} } } - - info!("PCI bus initialized."); + kinfo!("PCI bus initialized."); } /// An identifier for a PCI bus, device and function. @@ -1125,19 +1114,6 @@ impl BusDeviceFunction { self.device < 32 && self.function < 8 } } - -impl From for String { - /// # 函数的功能 - /// 这里提供一个由BusDeviceFunction到dddd:bb:vv.f字符串的转换函数,主要用于转换成设备的名称(pci设备的名称一般是诸如0000:00:00.1这种) - fn from(value: BusDeviceFunction) -> Self { - //需要注意,这里的0000应该是所谓的“域号”(Domain ID),但是尚不知道是如何获得的,故硬编码在这里 - //todo:实现域号的获取 - format!( - "0000:{:02x}:{:02x}.{}", - value.bus, value.device, value.function - ) - } -} ///实现BusDeviceFunction的Display trait,使其可以直接输出 impl Display for BusDeviceFunction { fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -1337,7 +1313,7 @@ pub fn pci_bar_init( // A wrapping add is necessary to correctly handle the case of unused BARs, which read back // as 0, and should be treated as size 0. let size = (!(size_mask & 0xfffffff0)).wrapping_add(1); - //debug!("bar_orig:{:#x},size: {:#x}", bar_orig,size); + //kdebug!("bar_orig:{:#x},size: {:#x}", bar_orig,size); // Restore the original value. pci_root_0().write_config( bus_device_function, @@ -1377,7 +1353,7 @@ pub fn pci_bar_init( .create_mmio(size_want) .map_err(|_| PciError::CreateMmioError)?; space_guard = Arc::new(tmp); - //debug!("Pci bar init: mmio space: {space_guard:?}, paddr={paddr:?}, size_want={size_want}"); + //kdebug!("Pci bar init: mmio space: {space_guard:?}, paddr={paddr:?}, size_want={size_want}"); assert!( space_guard.map_phys(paddr, size_want).is_ok(), "pci_bar_init: map_phys failed" @@ -1413,7 +1389,7 @@ pub fn pci_bar_init( _ => {} } } - //debug!("pci_device_bar:{}", device_bar); + //kdebug!("pci_device_bar:{}", device_bar); return Ok(device_bar); } @@ -1451,7 +1427,7 @@ impl Iterator for CapabilityIterator { self.next_capability_offset = if next_offset == 0 { None } else if next_offset < 64 || next_offset & 0x3 != 0 { - warn!("Invalid next capability offset {:#04x}", next_offset); + kwarn!("Invalid next capability offset {:#04x}", next_offset); None } else { Some(next_offset) @@ -1499,7 +1475,7 @@ impl<'a> Iterator for ExternalCapabilityIterator<'a> { self.next_capability_offset = if next_offset == 0 { None } else if next_offset < 0x100 || next_offset & 0x3 != 0 { - warn!("Invalid next capability offset {:#04x}", next_offset); + kwarn!("Invalid next capability offset {:#04x}", next_offset); None } else { Some(next_offset) diff --git a/kernel/src/driver/pci/pci_irq.rs b/kernel/src/driver/pci/pci_irq.rs index e31082dd1..2246f0c9c 100644 --- a/kernel/src/driver/pci/pci_irq.rs +++ b/kernel/src/driver/pci/pci_irq.rs @@ -6,7 +6,6 @@ use core::ptr::NonNull; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; -use log::error; use system_error::SystemError; use super::pci::{PciDeviceStructure, PciDeviceStructureGeneralDevice, PciError}; @@ -386,7 +385,7 @@ pub trait PciInterrupt: PciDeviceStructure { } Err(_) => { - error!( + kerror!( "Failed to request pci irq {} for device {}", irq_num.data(), &common_msg.irq_name @@ -549,7 +548,7 @@ pub trait PciInterrupt: PciDeviceStructure { } Err(_) => { - error!( + kerror!( "Failed to request pci irq {} for device {}", irq_num.data(), &common_msg.irq_name diff --git a/kernel/src/driver/pci/raw_device.rs b/kernel/src/driver/pci/raw_device.rs deleted file mode 100644 index c2be0700e..000000000 --- a/kernel/src/driver/pci/raw_device.rs +++ /dev/null @@ -1,194 +0,0 @@ -use core::any::Any; - -use alloc::{ - string::{String, ToString}, - sync::{Arc, Weak}, -}; - -use crate::{ - driver::base::{ - class::Class, - device::{bus::Bus, driver::Driver, Device, DeviceCommonData, DeviceType, IdTable}, - kobject::{KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState}, - kset::KSet, - }, - filesystem::{kernfs::KernFSInode, sysfs::AttributeGroup}, - libs::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, -}; - -use super::{ - attr::BasicPciReadOnlyAttrs, dev_id::PciDeviceID, device::PciDevice, - pci::PciDeviceStructureGeneralDevice, -}; -#[derive(Debug)] -#[cast_to([sync] Device)] -#[cast_to([sync] PciDevice)] -pub struct PciGeneralDevice { - device_data: RwLock, - kobj_data: RwLock, - name: RwLock>, - kobj_state: LockedKObjectState, - dev_id: PciDeviceID, - header: Arc, -} - -impl From<&PciDeviceStructureGeneralDevice> for PciGeneralDevice { - fn from(value: &PciDeviceStructureGeneralDevice) -> Self { - let value = Arc::new(value.clone()); - let name: String = value.common_header.bus_device_function.into(); - let kobj_state = LockedKObjectState::new(None); - let common_dev = RwLock::new(DeviceCommonData::default()); - let common_kobj = RwLock::new(KObjectCommonData::default()); - let dev_id = PciDeviceID::dummpy(); - - // dev_id.set_special(PciSpecifiedData::Virtio()); - let res = Self { - device_data: common_dev, - kobj_data: common_kobj, - kobj_state, - dev_id, - header: value, - name: RwLock::new(None), - }; - res.set_name(name); - res - } -} - -impl PciDevice for PciGeneralDevice { - fn dynid(&self) -> PciDeviceID { - self.dev_id - } - - fn vendor(&self) -> u16 { - self.header.common_header.vendor_id - } - - fn device_id(&self) -> u16 { - self.header.common_header.device_id - } - - fn subsystem_vendor(&self) -> u16 { - self.header.subsystem_vendor_id - } - - fn subsystem_device(&self) -> u16 { - self.header.subsystem_id - } -} - -impl Device for PciGeneralDevice { - fn attribute_groups(&self) -> Option<&'static [&'static dyn AttributeGroup]> { - Some(&[&BasicPciReadOnlyAttrs]) - } - - fn bus(&self) -> Option> { - self.device_data.read().bus.clone() - } - - fn class(&self) -> Option> { - let mut guard = self.device_data.write(); - let r = guard.class.clone()?.upgrade(); - if r.is_none() { - guard.class = None; - } - - return r; - } - - fn driver(&self) -> Option> { - self.device_data.read().driver.clone()?.upgrade() - } - - fn dev_type(&self) -> DeviceType { - DeviceType::Pci - } - - fn id_table(&self) -> IdTable { - IdTable::new("testPci".to_string(), None) - } - - fn can_match(&self) -> bool { - true - } - - fn is_dead(&self) -> bool { - false - } - - fn set_bus(&self, bus: Option>) { - self.device_data.write().bus = bus; - } - - fn set_can_match(&self, _can_match: bool) {} - - fn set_class(&self, class: Option>) { - self.device_data.write().class = class; - } - - fn set_driver(&self, driver: Option>) { - self.device_data.write().driver = driver - } - - fn state_synced(&self) -> bool { - true - } -} - -impl KObject for PciGeneralDevice { - fn as_any_ref(&self) -> &dyn Any { - self - } - - fn set_inode(&self, inode: Option>) { - self.kobj_data.write().kern_inode = inode; - } - - fn inode(&self) -> Option> { - self.kobj_data.read().kern_inode.clone() - } - - fn parent(&self) -> Option> { - self.kobj_data.read().parent.clone() - } - - fn set_parent(&self, parent: Option>) { - self.kobj_data.write().parent = parent; - } - - fn kset(&self) -> Option> { - self.kobj_data.read().kset.clone() - } - - fn set_kset(&self, kset: Option>) { - self.kobj_data.write().kset = kset; - } - - fn kobj_type(&self) -> Option<&'static dyn KObjType> { - self.kobj_data.read().kobj_type - } - - fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { - self.kobj_data.write().kobj_type = ktype; - } - - fn name(&self) -> String { - self.name.read().clone().unwrap() - } - - fn set_name(&self, name: String) { - *self.name.write() = Some(name); - } - - fn kobj_state(&self) -> RwLockReadGuard { - self.kobj_state.read() - } - - fn kobj_state_mut(&self) -> RwLockWriteGuard { - self.kobj_state.write() - } - - fn set_kobj_state(&self, state: KObjectState) { - *self.kobj_state.write() = state; - } -} diff --git a/kernel/src/driver/pci/root.rs b/kernel/src/driver/pci/root.rs index c74c6cf9b..2d7928847 100644 --- a/kernel/src/driver/pci/root.rs +++ b/kernel/src/driver/pci/root.rs @@ -4,17 +4,16 @@ use alloc::sync::Arc; use hashbrown::HashMap; use crate::{ - arch::{PciArch, TraitPciArch}, libs::spinlock::{SpinLock, SpinLockGuard}, mm::{ mmio_buddy::{mmio_pool, MMIOSpaceGuard}, page::PAGE_2M_SIZE, + PhysAddr, }, }; -use super::{ - ecam::EcamRootInfo, - pci::{BusDeviceFunction, ExternalCapabilityIterator, PciCam, PciError, SegmentGroupNumber}, +use super::pci::{ + BusDeviceFunction, ExternalCapabilityIterator, PciCam, PciError, SegmentGroupNumber, }; lazy_static! { @@ -29,14 +28,13 @@ pub fn pci_root_manager() -> &'static PciRootManager { /// 代表一个PCI segement greoup. #[derive(Clone, Debug)] pub struct PciRoot { - pub ecam_root_info: Option, - pub mmio_guard: Option>, //映射后的虚拟地址,为方便访问数据这里转化成指针 + pub physical_address_base: PhysAddr, //物理地址,acpi获取 + pub mmio_guard: Option>, //映射后的虚拟地址,为方便访问数据这里转化成指针 + pub segment_group_number: SegmentGroupNumber, //segement greoup的id + pub bus_begin: u8, //该分组中的最小bus + pub bus_end: u8, //该分组中的最大bus /// 配置空间访问机制 pub cam: PciCam, - /// bus起始位置 - pub bus_begin: u8, - /// bus结束位置 - pub bus_end: u8, } ///线程间共享需要,该结构体只需要在初始化时写入数据,无需读写锁保证线程安全 @@ -45,15 +43,11 @@ unsafe impl Sync for PciRoot {} ///实现PciRoot的Display trait,自定义输出 impl core::fmt::Display for PciRoot { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - if let Some(ecam_root_info) = &self.ecam_root_info { - write!( - f, - "PCI Eacm Root with segment:{}, bus begin at {}, bus end at {}, physical address at {:?},mapped at {:?}", - ecam_root_info.segment_group_number, ecam_root_info.bus_begin, ecam_root_info.bus_end, ecam_root_info.physical_address_base, self.mmio_guard - ) - } else { - write!(f, "PCI Root cam is {:?}", self.cam,) - } + write!( + f, + "PCI Root with segement:{}, bus begin at {}, bus end at {}, physical address at {:?},mapped at {:?}", + self.segment_group_number, self.bus_begin, self.bus_end, self.physical_address_base, self.mmio_guard + ) } } @@ -75,30 +69,29 @@ impl PciRoot { /// /// - 成功执行后,结构体的内部状态将被初始化为包含映射后的虚拟地址。 pub fn new( - ecam_root_info: Option, + segment_group_number: SegmentGroupNumber, cam: PciCam, + phys_base: PhysAddr, bus_begin: u8, bus_end: u8, ) -> Result, PciError> { + assert_eq!(cam, PciCam::Ecam); let mut pci_root = Self { - ecam_root_info, + physical_address_base: phys_base, mmio_guard: None, - cam, + segment_group_number, bus_begin, bus_end, + cam, }; - - if ecam_root_info.is_some() { - pci_root.map()?; - } + pci_root.map()?; Ok(Arc::new(pci_root)) } - - /// # 完成物理地址到虚拟地址的映射,并将虚拟地址加入mmio_base变量 - /// ## return 返回错误或Ok(0) + /// @brief 完成物理地址到虚拟地址的映射,并将虚拟地址加入mmio_base变量 + /// @return 返回错误或Ok(0) fn map(&mut self) -> Result { - //debug!("bus_begin={},bus_end={}", self.bus_begin,self.bus_end); + //kdebug!("bus_begin={},bus_end={}", self.bus_begin,self.bus_end); let bus_number = (self.bus_end - self.bus_begin) as u32 + 1; let bus_number_double = (bus_number - 1) / 2 + 1; //一个bus占据1MB空间,计算全部bus占据空间相对于2MB空间的个数 @@ -111,7 +104,7 @@ impl PciRoot { self.mmio_guard = Some(space_guard.clone()); assert!(space_guard - .map_phys(self.ecam_root_info.unwrap().physical_address_base, size) + .map_phys(self.physical_address_base, size) .is_ok()); } return Ok(0); @@ -136,12 +129,11 @@ impl PciRoot { /// - 此函数计算出的地址需要是字对齐的(即地址与0x3对齐)。如果不是,将panic。 fn cam_offset(&self, bus_device_function: BusDeviceFunction, register_offset: u16) -> u32 { assert!(bus_device_function.valid()); - let bdf = ((bus_device_function.bus - self.ecam_root_info.unwrap().bus_begin) as u32) << 8 + let bdf = ((bus_device_function.bus - self.bus_begin) as u32) << 8 | (bus_device_function.device as u32) << 3 | bus_device_function.function as u32; let address = bdf << match self.cam { - PciCam::Portiocam => 4, PciCam::MmioCam => 8, PciCam::Ecam => 12, } | register_offset as u32; @@ -149,7 +141,6 @@ impl PciRoot { assert!(address & 0x3 == 0); address } - /// # read_config - 通过bus_device_function和offset读取相应位置寄存器的值(32位) /// /// 此函数用于通过指定的bus_device_function和register_offset读取PCI设备中相应位置的寄存器值。 @@ -163,16 +154,12 @@ impl PciRoot { /// /// - `u32`: 寄存器读值结果 pub fn read_config(&self, bus_device_function: BusDeviceFunction, register_offset: u16) -> u32 { - if self.ecam_root_info.is_some() { - let address = self.cam_offset(bus_device_function, register_offset); - unsafe { - // Right shift to convert from byte offset to word offset. - ((self.mmio_guard.as_ref().unwrap().vaddr().data() as *mut u32) - .add((address >> 2) as usize)) - .read_volatile() - } - } else { - PciArch::read_config(&bus_device_function, register_offset as u8) + let address = self.cam_offset(bus_device_function, register_offset); + unsafe { + // Right shift to convert from byte offset to word offset. + ((self.mmio_guard.as_ref().unwrap().vaddr().data() as *mut u32) + .add((address >> 2) as usize)) + .read_volatile() } } @@ -191,21 +178,16 @@ impl PciRoot { register_offset: u16, data: u32, ) { - if self.ecam_root_info.is_some() { - let address = self.cam_offset(bus_device_function, register_offset); - // Safe because both the `mmio_base` and the address offset are properly aligned, and the - // resulting pointer is within the MMIO range of the CAM. - unsafe { - // Right shift to convert from byte offset to word offset. - ((self.mmio_guard.as_ref().unwrap().vaddr().data() as *mut u32) - .add((address >> 2) as usize)) - .write_volatile(data) - } - } else { - PciArch::write_config(&bus_device_function, register_offset as u8, data); + let address = self.cam_offset(bus_device_function, register_offset); + // Safe because both the `mmio_base` and the address offset are properly aligned, and the + // resulting pointer is within the MMIO range of the CAM. + unsafe { + // Right shift to convert from byte offset to word offset. + ((self.mmio_guard.as_ref().unwrap().vaddr().data() as *mut u32) + .add((address >> 2) as usize)) + .write_volatile(data) } } - /// 返回迭代器,遍历pcie设备的external_capabilities #[allow(dead_code)] pub fn external_capabilities( @@ -251,14 +233,9 @@ impl PciRootManager { /// - `pci_root`: Arc,要添加的PciRoot的Arc指针 pub fn add_pci_root(&self, pci_root: Arc) { let mut inner = self.inner.lock(); - - if let Some(ecam_root_info) = pci_root.ecam_root_info { - inner - .pci_root - .insert(ecam_root_info.segment_group_number, pci_root); - } else { - inner.pci_root.insert(pci_root.bus_begin as u16, pci_root); - } + inner + .pci_root + .insert(pci_root.segment_group_number, pci_root); } /// # 检查是否存在PciRoot - 检查PciRootManager中是否存在指定segment_group_number的PciRoot diff --git a/kernel/src/driver/pci/subsys.rs b/kernel/src/driver/pci/subsys.rs deleted file mode 100644 index c2e5ee393..000000000 --- a/kernel/src/driver/pci/subsys.rs +++ /dev/null @@ -1,191 +0,0 @@ -use alloc::{ - string::{String, ToString}, - sync::{Arc, Weak}, -}; -use intertrait::cast::CastArc; -use log::error; -use system_error::SystemError; - -use crate::{ - driver::base::{ - device::{ - bus::{bus_register, Bus}, - device_register, - driver::Driver, - sys_devices_kset, Device, - }, - kobject::KObject, - subsys::SubSysPrivate, - }, - filesystem::sysfs::AttributeGroup, -}; - -use super::{ - device::{PciBusDevice, PciDevice}, - driver::PciDriver, - test::pt_init, -}; - -static mut PCI_BUS_DEVICE: Option> = None; -static mut PCI_BUS: Option> = None; - -pub(super) fn set_pci_bus_device(device: Arc) { - unsafe { - PCI_BUS_DEVICE = Some(device); - } -} - -pub(super) fn set_pci_bus(bus: Arc) { - unsafe { - PCI_BUS = Some(bus); - } -} - -pub fn pci_bus_device() -> Arc { - unsafe { - return PCI_BUS_DEVICE.clone().unwrap(); - } -} - -pub fn pci_bus() -> Arc { - unsafe { - return PCI_BUS.clone().unwrap(); - } -} - -/// # 结构功能 -/// 该结构为Pci总线,由于总线也属于设备,故设此结构; -/// 此结构对应/sys/bus/pci -#[derive(Debug)] -pub struct PciBus { - private: SubSysPrivate, -} - -impl PciBus { - pub fn new() -> Arc { - let w: Weak = Weak::new(); - let private = SubSysPrivate::new("pci".to_string(), Some(w), None, &[]); - let bus = Arc::new(Self { private }); - bus - } -} - -impl Bus for PciBus { - fn name(&self) -> String { - return "pci".to_string(); - } - - fn dev_name(&self) -> String { - return self.name(); - } - - fn dev_groups(&self) -> &'static [&'static dyn AttributeGroup] { - return &[&PciDeviceAttrGroup]; - } - - fn subsystem(&self) -> &SubSysPrivate { - return &self.private; - } - - fn probe(&self, device: &Arc) -> Result<(), SystemError> { - let drv = device.driver().ok_or(SystemError::EINVAL)?; - let pci_drv = drv.cast::().map_err(|_| { - error!( - "PciBus::probe() failed: device.driver() is not a PciDriver. Device: '{:?}'", - device.name() - ); - SystemError::EINVAL - })?; - let pci_dev = device.clone().cast::().map_err(|_| { - error!( - "PciBus::probe() failed: device is not a PciDevice. Device: '{:?}'", - device.name() - ); - SystemError::EINVAL - })?; - //见https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/pci/pci-driver.c#324 - let id = pci_drv.match_dev(&pci_dev).ok_or(SystemError::EINVAL)?; - pci_drv.probe(&pci_dev, &id) - } - - fn remove(&self, _device: &Arc) -> Result<(), SystemError> { - todo!() - } - - fn sync_state(&self, _device: &Arc) { - todo!() - } - - fn shutdown(&self, _device: &Arc) { - todo!() - } - - fn resume(&self, _device: &Arc) -> Result<(), SystemError> { - todo!() - } - - fn match_device( - &self, - device: &Arc, - driver: &Arc, - ) -> Result { - //首先将设备和驱动映射为pci设备和pci驱动 - let pci_driver = driver.clone().cast::().map_err(|_| { - return SystemError::EINVAL; - })?; - let pci_dev = device.clone().cast::().map_err(|_| { - return SystemError::EINVAL; - })?; - //pci_driver需要实现一个match_dev函数,即driver需要识别是否支持给定的pci设备 - //这是主要的match方式 - if pci_driver.match_dev(&pci_dev).is_some() { - return Ok(true); - } - - //todo:这里似乎需要一个driver_override_only的支持,但是目前不清楚driver_override_only 的用途,故暂时参考platform总线的match方法 - //override_only相关代码在 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/pci/pci-driver.c#159 - if let Some(driver_id_table) = driver.id_table() { - if driver_id_table.name().eq(&pci_dev.name()) { - return Ok(true); - } - }; - return Ok(pci_dev.name().eq(&pci_driver.name())); - } -} - -#[derive(Debug)] -pub struct PciDeviceAttrGroup; - -impl AttributeGroup for PciDeviceAttrGroup { - fn name(&self) -> Option<&str> { - return None; - } - - fn attrs(&self) -> &[&'static dyn crate::filesystem::sysfs::Attribute] { - return &[]; - } - - fn is_visible( - &self, - _kobj: Arc, - attr: &'static dyn crate::filesystem::sysfs::Attribute, - ) -> Option { - return Some(attr.mode()); - } -} - -pub(super) fn pci_bus_subsys_init() -> Result<(), SystemError> { - let pci_bus_device: Arc = PciBusDevice::new(Some(Arc::downgrade( - &(sys_devices_kset() as Arc), - ))); - - set_pci_bus_device(pci_bus_device.clone()); - - device_register(pci_bus_device.clone())?; - let pci_bus = PciBus::new(); - - set_pci_bus(pci_bus.clone()); - let r = bus_register(pci_bus.clone() as Arc); - pt_init()?; - return r; -} diff --git a/kernel/src/driver/pci/test/mod.rs b/kernel/src/driver/pci/test/mod.rs deleted file mode 100644 index 4f0da79f2..000000000 --- a/kernel/src/driver/pci/test/mod.rs +++ /dev/null @@ -1,30 +0,0 @@ -use alloc::sync::Arc; -use system_error::SystemError; - -use self::{pt_device::TestDevice, pt_driver::TestDriver}; - -use super::{ - dev_id::PciDeviceID, - device::pci_device_manager, - driver::{pci_driver_manager, PciDriver}, -}; - -pub mod pt_device; -pub mod pt_driver; - -static mut TEST_DRIVER: Option> = None; -static mut TEST_DEVICE: Option> = None; -pub fn pt_init() -> Result<(), SystemError> { - let tdev = Arc::new(TestDevice::new()); - let mut drv = TestDriver::new(); - drv.add_dynid(PciDeviceID::dummpy())?; - let tdrv = Arc::new(drv); - - let _ = pci_device_manager().device_add(tdev.clone()); - let _ = pci_driver_manager().register(tdrv.clone()); - unsafe { - TEST_DEVICE = Some(tdev); - TEST_DRIVER = Some(tdrv); - } - Ok(()) -} diff --git a/kernel/src/driver/pci/test/pt_device.rs b/kernel/src/driver/pci/test/pt_device.rs deleted file mode 100644 index 65d1a05b0..000000000 --- a/kernel/src/driver/pci/test/pt_device.rs +++ /dev/null @@ -1,236 +0,0 @@ -use core::any::Any; - -use alloc::{ - string::{String, ToString}, - sync::{Arc, Weak}, -}; -use system_error::SystemError; - -use crate::{ - driver::{ - base::{ - class::Class, - device::{bus::Bus, driver::Driver, Device, DeviceCommonData, DeviceType, IdTable}, - kobject::{KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState}, - kset::KSet, - }, - pci::{dev_id::PciDeviceID, device::PciDevice}, - }, - filesystem::{ - kernfs::KernFSInode, - sysfs::{ - file::sysfs_emit_str, Attribute, AttributeGroup, SysFSOpsSupport, SYSFS_ATTR_MODE_RO, - }, - vfs::syscall::ModeType, - }, - libs::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, -}; -#[derive(Debug)] -#[cast_to([sync] Device)] -#[cast_to([sync] PciDevice)] -/// # 结构功能 -/// 这是一个测试用的PciDevice,也可以作为新PciDevice的参考 -/// 它需要实现KObject PciDevice Device这些接口 -/// 并通过函数pci_device_manager().device_add()来将设备进行接入 -pub struct TestDevice { - device_data: RwLock, - kobj_data: RwLock, - kobj_state: LockedKObjectState, -} - -impl TestDevice { - pub fn new() -> Self { - let common_dev = RwLock::new(DeviceCommonData::default()); - let common_kobj = RwLock::new(KObjectCommonData::default()); - Self { - device_data: common_dev, - kobj_data: common_kobj, - kobj_state: LockedKObjectState::new(None), - } - } -} - -impl PciDevice for TestDevice { - fn dynid(&self) -> PciDeviceID { - PciDeviceID::dummpy() - } - - fn vendor(&self) -> u16 { - return 0xffff; - } - - fn device_id(&self) -> u16 { - return 0xffff; - } - - fn subsystem_vendor(&self) -> u16 { - return 0xffff; - } - - fn subsystem_device(&self) -> u16 { - return 0xffff; - } -} - -impl Device for TestDevice { - fn attribute_groups(&self) -> Option<&'static [&'static dyn AttributeGroup]> { - Some(&[&HelloAttr]) - } - - fn bus(&self) -> Option> { - self.device_data.read().bus.clone() - } - - fn class(&self) -> Option> { - let mut guard = self.device_data.write(); - let r = guard.class.clone()?.upgrade(); - if r.is_none() { - guard.class = None; - } - - return r; - } - - fn driver(&self) -> Option> { - self.device_data.read().driver.clone()?.upgrade() - } - - fn dev_type(&self) -> DeviceType { - DeviceType::Pci - } - - fn id_table(&self) -> IdTable { - IdTable::new("testPci".to_string(), None) - } - - fn can_match(&self) -> bool { - true - } - - fn is_dead(&self) -> bool { - false - } - - fn set_bus(&self, bus: Option>) { - self.device_data.write().bus = bus - } - - fn set_can_match(&self, _can_match: bool) { - //todo - } - - fn set_class(&self, class: Option>) { - self.device_data.write().class = class - } - - fn set_driver(&self, driver: Option>) { - self.device_data.write().driver = driver - } - - fn state_synced(&self) -> bool { - true - } -} - -impl KObject for TestDevice { - fn as_any_ref(&self) -> &dyn Any { - self - } - - fn set_inode(&self, inode: Option>) { - self.kobj_data.write().kern_inode = inode; - } - - fn inode(&self) -> Option> { - self.kobj_data.read().kern_inode.clone() - } - - fn parent(&self) -> Option> { - self.kobj_data.read().parent.clone() - } - - fn set_parent(&self, parent: Option>) { - self.kobj_data.write().parent = parent; - } - - fn kset(&self) -> Option> { - self.kobj_data.read().kset.clone() - } - - fn set_kset(&self, kset: Option>) { - self.kobj_data.write().kset = kset; - } - - fn kobj_type(&self) -> Option<&'static dyn KObjType> { - self.kobj_data.read().kobj_type - } - - fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { - self.kobj_data.write().kobj_type = ktype; - } - - fn name(&self) -> String { - "PciTest".to_string() - } - - fn set_name(&self, _name: String) { - // do nothing - } - - fn kobj_state(&self) -> RwLockReadGuard { - self.kobj_state.read() - } - - fn kobj_state_mut(&self) -> RwLockWriteGuard { - self.kobj_state.write() - } - - fn set_kobj_state(&self, state: KObjectState) { - *self.kobj_state.write() = state; - } -} - -#[derive(Debug)] -pub struct HelloAttr; - -impl AttributeGroup for HelloAttr { - fn name(&self) -> Option<&str> { - return Some("TestAttr"); - } - - fn attrs(&self) -> &[&'static dyn Attribute] { - &[&Hello] - } - - fn is_visible( - &self, - _kobj: Arc, - attr: &'static dyn Attribute, - ) -> Option { - return Some(attr.mode()); - } -} -#[derive(Debug)] -pub struct Hello; - -impl Attribute for Hello { - fn mode(&self) -> ModeType { - SYSFS_ATTR_MODE_RO - } - - fn name(&self) -> &str { - "Hello" - } - - fn show(&self, _kobj: Arc, _buf: &mut [u8]) -> Result { - return sysfs_emit_str(_buf, "Hello Pci"); - } - - fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - todo!() - } - - fn support(&self) -> SysFSOpsSupport { - SysFSOpsSupport::ATTR_SHOW - } -} diff --git a/kernel/src/driver/pci/test/pt_driver.rs b/kernel/src/driver/pci/test/pt_driver.rs deleted file mode 100644 index 65affee28..000000000 --- a/kernel/src/driver/pci/test/pt_driver.rs +++ /dev/null @@ -1,171 +0,0 @@ -use alloc::{ - string::{String, ToString}, - sync::{Arc, Weak}, - vec::Vec, -}; - -use crate::{ - driver::{ - base::{ - device::{ - bus::Bus, - driver::{Driver, DriverCommonData}, - Device, IdTable, - }, - kobject::{KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState}, - kset::KSet, - }, - pci::{dev_id::PciDeviceID, device::PciDevice, driver::PciDriver}, - }, - filesystem::kernfs::KernFSInode, - libs::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, -}; -#[derive(Debug)] -#[cast_to([sync] PciDriver)] -pub struct TestDriver { - driver_data: RwLock, - kobj_data: RwLock, - kobj_state: LockedKObjectState, - pub locked_dynid_list: RwLock>>, -} - -/// # 结构功能 -/// 本结构体是测试用的驱动,目前暂时保留,否则将出现大量dead code -/// 在编写了实际的pci驱动后,可将该驱动删除 -impl TestDriver { - pub fn new() -> Self { - Self { - driver_data: RwLock::new(DriverCommonData::default()), - kobj_data: RwLock::new(KObjectCommonData::default()), - kobj_state: LockedKObjectState::new(None), - locked_dynid_list: RwLock::new(vec![]), - } - } -} - -impl PciDriver for TestDriver { - fn add_dynid(&mut self, id: PciDeviceID) -> Result<(), system_error::SystemError> { - let id = Arc::new(id); - self.locked_dynid_list.write().push(id); - Ok(()) - } - - fn locked_dynid_list(&self) -> Option>> { - Some(self.locked_dynid_list.read().clone()) - } - - fn probe( - &self, - _device: &Arc, - _id: &PciDeviceID, - ) -> Result<(), system_error::SystemError> { - Ok(()) - } - - fn remove(&self, _device: &Arc) -> Result<(), system_error::SystemError> { - Ok(()) - } - - fn resume(&self, _device: &Arc) -> Result<(), system_error::SystemError> { - Ok(()) - } - - fn shutdown(&self, _device: &Arc) -> Result<(), system_error::SystemError> { - Ok(()) - } - - fn suspend(&self, _device: &Arc) -> Result<(), system_error::SystemError> { - Ok(()) - } -} - -impl Driver for TestDriver { - fn id_table(&self) -> Option { - Some(IdTable::new("PciTestDriver".to_string(), None)) - } - - fn devices(&self) -> Vec> { - self.driver_data.read().devices.clone() - } - - fn add_device(&self, device: Arc) { - let mut guard = self.driver_data.write(); - // check if the device is already in the list - if guard.devices.iter().any(|dev| Arc::ptr_eq(dev, &device)) { - return; - } - - guard.devices.push(device); - } - - fn delete_device(&self, device: &Arc) { - let mut guard = self.driver_data.write(); - guard.devices.retain(|dev| !Arc::ptr_eq(dev, device)); - } - - fn set_bus(&self, bus: Option>) { - self.driver_data.write().bus = bus; - } - - fn bus(&self) -> Option> { - self.driver_data.read().bus.clone() - } -} - -impl KObject for TestDriver { - fn as_any_ref(&self) -> &dyn core::any::Any { - self - } - - fn set_inode(&self, inode: Option>) { - self.kobj_data.write().kern_inode = inode; - } - - fn inode(&self) -> Option> { - self.kobj_data.read().kern_inode.clone() - } - - fn parent(&self) -> Option> { - self.kobj_data.read().parent.clone() - } - - fn set_parent(&self, parent: Option>) { - self.kobj_data.write().parent = parent; - } - - fn kset(&self) -> Option> { - self.kobj_data.read().kset.clone() - } - - fn set_kset(&self, kset: Option>) { - self.kobj_data.write().kset = kset; - } - - fn kobj_type(&self) -> Option<&'static dyn KObjType> { - self.kobj_data.read().kobj_type - } - - fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { - self.kobj_data.write().kobj_type = ktype; - } - - fn name(&self) -> String { - "PciTestDriver".to_string() - } - - fn set_name(&self, _name: String) { - // do nothing - } - - fn kobj_state(&self) -> RwLockReadGuard { - self.kobj_state.read() - } - - fn kobj_state_mut(&self) -> RwLockWriteGuard { - self.kobj_state.write() - } - - fn set_kobj_state(&self, state: KObjectState) { - *self.kobj_state.write() = state; - } -} diff --git a/kernel/src/driver/rtc/class.rs b/kernel/src/driver/rtc/class.rs index d10f11cff..12184e561 100644 --- a/kernel/src/driver/rtc/class.rs +++ b/kernel/src/driver/rtc/class.rs @@ -2,7 +2,6 @@ use alloc::{ string::ToString, sync::{Arc, Weak}, }; -use log::info; use system_error::SystemError; use unified_init::macros::unified_init; @@ -101,7 +100,7 @@ fn rtc_hctosys(dev: &Arc) { let r = do_settimeofday64(timespec64); dev.set_hc2sys_result(r); - info!( + kinfo!( "Setting system clock to {} {} UTC ({})", time.date_string(), time.time_string(), diff --git a/kernel/src/driver/rtc/mod.rs b/kernel/src/driver/rtc/mod.rs index 80f99d5b0..7031c0ba3 100644 --- a/kernel/src/driver/rtc/mod.rs +++ b/kernel/src/driver/rtc/mod.rs @@ -47,7 +47,6 @@ pub trait RtcDevice: Device { fn class_ops(&self) -> &'static dyn RtcClassOps; } -#[allow(dead_code)] pub trait RtcClassOps: Send + Sync + Debug { fn read_time(&self, dev: &Arc) -> Result; fn set_time(&self, dev: &Arc, time: &RtcTime) -> Result<(), SystemError>; diff --git a/kernel/src/driver/serial/mod.rs b/kernel/src/driver/serial/mod.rs index 77bc956ff..53af56d85 100644 --- a/kernel/src/driver/serial/mod.rs +++ b/kernel/src/driver/serial/mod.rs @@ -9,7 +9,6 @@ use self::serial8250::serial8250_manager; pub mod serial8250; -#[allow(dead_code)] pub trait UartDriver: Debug + Send + Sync { fn device_number(&self) -> DeviceNumber; @@ -22,7 +21,6 @@ pub trait UartDriver: Debug + Send + Sync { /// 串口端口应当实现的trait /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/serial_core.h#428 -#[allow(dead_code)] pub trait UartPort { fn iobase(&self) -> Option { None diff --git a/kernel/src/driver/serial/serial8250/mod.rs b/kernel/src/driver/serial/serial8250/mod.rs index f12fe63c8..48c82c07f 100644 --- a/kernel/src/driver/serial/serial8250/mod.rs +++ b/kernel/src/driver/serial/serial8250/mod.rs @@ -8,7 +8,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::error; use system_error::SystemError; use crate::{ @@ -141,7 +140,6 @@ impl Serial8250Manager { } /// 所有的8250串口设备都应该实现的trait -#[allow(dead_code)] trait Serial8250Port: UartPort { fn device(&self) -> Option> { None @@ -412,7 +410,7 @@ impl PlatformDriver for Serial8250ISADriver { .arc_any() .downcast::() .map_err(|_| { - error!("Serial8250ISADriver::probe: device is not a Serial8250ISADevices"); + kerror!("Serial8250ISADriver::probe: device is not a Serial8250ISADevices"); SystemError::EINVAL })?; isa_dev.set_driver(Some(self.self_ref.clone())); diff --git a/kernel/src/driver/serial/serial8250/serial8250_pio.rs b/kernel/src/driver/serial/serial8250/serial8250_pio.rs index 54760ad9a..fbf909ce7 100644 --- a/kernel/src/driver/serial/serial8250/serial8250_pio.rs +++ b/kernel/src/driver/serial/serial8250/serial8250_pio.rs @@ -20,7 +20,6 @@ static mut PIO_PORTS: [Option; 8] = [None, None, None, None, None, None, None, None]; impl Serial8250Manager { - #[allow(static_mut_refs)] pub(super) fn bind_pio_ports( &self, uart_driver: &Arc, @@ -252,7 +251,6 @@ impl Serial8250PIOPortInner { Self { device: None } } - #[allow(dead_code)] pub fn device(&self) -> Option> { if let Some(device) = self.device.as_ref() { return device.upgrade(); diff --git a/kernel/src/driver/tty/console.rs b/kernel/src/driver/tty/console.rs index f127900d4..a9064ccba 100644 --- a/kernel/src/driver/tty/console.rs +++ b/kernel/src/driver/tty/console.rs @@ -10,7 +10,6 @@ pub trait ConsoleSwitch: Sync + Send { fn con_init(&self, vc_data: &mut VirtualConsoleData, init: bool) -> Result<(), SystemError>; /// 进行释放等系列操作,目前未使用 - #[allow(dead_code)] fn con_deinit(&self) -> Result<(), SystemError>; /// ## 清空console的一片区域 diff --git a/kernel/src/driver/tty/tty_core.rs b/kernel/src/driver/tty/tty_core.rs index 90da3be6a..ecd7ee0c1 100644 --- a/kernel/src/driver/tty/tty_core.rs +++ b/kernel/src/driver/tty/tty_core.rs @@ -7,6 +7,7 @@ use alloc::{ collections::LinkedList, string::String, sync::{Arc, Weak}, + vec::Vec, }; use system_error::SystemError; @@ -276,6 +277,14 @@ pub struct TtyContorlInfo { pub packet: bool, } +#[derive(Debug, Default)] +pub struct TtyCoreWriteData { + /// 写缓冲区 + pub write_buf: Vec, + /// 写入数量 + pub write_cnt: usize, +} + #[derive(Debug, Default)] pub struct TtyFlowState { /// 表示流控是否被停止 @@ -456,6 +465,9 @@ impl TtyCoreData { } } +/// TTY 核心接口,不同的tty需要各自实现这个trait +pub trait TtyCoreFuncs: Debug + Send + Sync {} + impl TtyOperation for TtyCore { #[inline] fn open(&self, tty: &TtyCoreData) -> Result<(), SystemError> { diff --git a/kernel/src/driver/tty/tty_device.rs b/kernel/src/driver/tty/tty_device.rs index 56a053b40..e2458607c 100644 --- a/kernel/src/driver/tty/tty_device.rs +++ b/kernel/src/driver/tty/tty_device.rs @@ -34,7 +34,7 @@ use crate::{ spinlock::SpinLockGuard, }, mm::VirtAddr, - net::event_poll::{EPollItem, KernelIoctlData}, + net::event_poll::{EPollItem, EventPoll}, process::ProcessManager, syscall::user_access::{UserBufferReader, UserBufferWriter}, }; @@ -308,35 +308,6 @@ impl IndexNode for TtyDevice { Ok(()) } - fn kernel_ioctl( - &self, - arg: Arc, - data: &FilePrivateData, - ) -> Result { - let epitem = arg - .arc_any() - .downcast::() - .map_err(|_| SystemError::EFAULT)?; - - let _ = UserBufferReader::new( - &epitem as *const Arc, - core::mem::size_of::>(), - false, - )?; - - let (tty, _) = if let FilePrivateData::Tty(tty_priv) = data { - (tty_priv.tty(), tty_priv.mode) - } else { - return Err(SystemError::EIO); - }; - - let core = tty.core(); - - core.add_epitem(epitem.clone()); - - return Ok(0); - } - fn ioctl(&self, cmd: u32, arg: usize, data: &FilePrivateData) -> Result { let (tty, _) = if let FilePrivateData::Tty(tty_priv) = data { (tty_priv.tty(), tty_priv.mode) @@ -355,6 +326,20 @@ impl IndexNode for TtyDevice { todo!() } } + EventPoll::ADD_EPOLLITEM => { + let _ = UserBufferReader::new( + arg as *const Arc, + core::mem::size_of::>(), + false, + )?; + let epitem = unsafe { &*(arg as *const Arc) }; + + let core = tty.core(); + + core.add_epitem(epitem.clone()); + + return Ok(0); + } _ => {} } diff --git a/kernel/src/driver/tty/tty_driver.rs b/kernel/src/driver/tty/tty_driver.rs index 6acb4cbde..532250678 100644 --- a/kernel/src/driver/tty/tty_driver.rs +++ b/kernel/src/driver/tty/tty_driver.rs @@ -6,7 +6,6 @@ use alloc::{ vec::Vec, }; use hashbrown::HashMap; -use log::warn; use system_error::SystemError; use crate::{ @@ -285,7 +284,7 @@ impl TtyDriver { Some(tty) => { // TODO: 暂时这么写,因为还没写TtyPort if tty.core().port().is_none() { - warn!("{} port is None", tty.core().name()); + kwarn!("{} port is None", tty.core().name()); } else if tty.core().port().unwrap().state() == TtyPortState::KOPENED { return Err(SystemError::EBUSY); } diff --git a/kernel/src/driver/tty/tty_ldisc/ntty.rs b/kernel/src/driver/tty/tty_ldisc/ntty.rs index 4f2958064..9253a0e50 100644 --- a/kernel/src/driver/tty/tty_ldisc/ntty.rs +++ b/kernel/src/driver/tty/tty_ldisc/ntty.rs @@ -388,9 +388,9 @@ impl NTtyData { continue; } - if ((c as usize) < self.char_map.size()) && self.char_map.get(c as usize).unwrap() { + if self.char_map.get(c as usize).unwrap() { // 特殊字符 - self.receive_special_char(c, tty.clone(), lookahead_done); + self.receive_special_char(c, tty.clone(), lookahead_done) } else { self.receive_char(c, tty.clone()); } @@ -1344,7 +1344,7 @@ impl NTtyData { tty.write(core, &[8], 1)?; } if tty.put_char(tty.core(), b' ').is_err() { - tty.write(core, b" ", 1)?; + tty.write(core, &[b' '], 1)?; } self.cursor_column -= 1; space -= 1; @@ -1357,7 +1357,7 @@ impl NTtyData { } if tty.put_char(tty.core(), b'^').is_err() { - tty.write(core, b"^", 1)?; + tty.write(core, &[b'^'], 1)?; } if tty.put_char(tty.core(), ch ^ 0o100).is_err() { diff --git a/kernel/src/driver/tty/virtual_terminal/virtual_console.rs b/kernel/src/driver/tty/virtual_terminal/virtual_console.rs index 992659c4b..3fc94b323 100644 --- a/kernel/src/driver/tty/virtual_terminal/virtual_console.rs +++ b/kernel/src/driver/tty/virtual_terminal/virtual_console.rs @@ -5,7 +5,6 @@ use alloc::{ vec::Vec, }; use bitmap::{traits::BitMapOps, StaticBitmap}; -use log::warn; use crate::{ driver::{ @@ -132,6 +131,7 @@ pub struct VirtualConsoleData { pub utf_char: u32, /// 构建utf时需要的参数,表示目前接收了多少个字节的数据来构建utf字符 pub npar: u32, + /// pub par: [u32; NPAR], /// 字符转换表 用于将输入字符映射到特定的字符 @@ -936,7 +936,7 @@ impl VirtualConsoleData { 'c' => { if self.par[0] == 0 { - warn!("respone ID todo"); + kwarn!("respone ID todo"); } return; } @@ -1612,7 +1612,7 @@ impl VirtualConsoleData { tc |= ((attr as u32) << 8) & (!himask as u32); - // warn!( + // kwarn!( // "ch {} pos {} x {} y {} cols {}", // c as u8 as char, // self.pos, @@ -1785,7 +1785,6 @@ impl VirtualConsoleData { draw.size = 0; } - #[allow(clippy::manual_rotate)] fn build_attr( &self, color: u8, diff --git a/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs b/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs index 4a193f284..d0a3040e6 100644 --- a/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs +++ b/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs @@ -1,5 +1,4 @@ use alloc::{sync::Arc, vec::Vec}; -use log::warn; use system_error::SystemError; use crate::{ @@ -201,7 +200,7 @@ impl ConsoleSwitch for BlittingFbConsole { vc_data.font.height = font.height; vc_data.font.count = font.char_count; } else { - warn!("The frontend Framebuffer is not implemented"); + kwarn!("The frontend Framebuffer is not implemented"); } vc_data.color_mode = fb.color_depth() != 1; diff --git a/kernel/src/driver/video/fbdev/base/fbcon/mod.rs b/kernel/src/driver/video/fbdev/base/fbcon/mod.rs index ac6e50cfb..3d95e85a4 100644 --- a/kernel/src/driver/video/fbdev/base/fbcon/mod.rs +++ b/kernel/src/driver/video/fbdev/base/fbcon/mod.rs @@ -3,7 +3,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::warn; use system_error::SystemError; use crate::{ @@ -166,7 +165,7 @@ impl KObject for FbConsoleDevice { fn set_name(&self, _name: String) { // 不允许修改 - warn!("fbcon name can not be changed"); + kwarn!("fbcon name can not be changed"); } fn kobj_state(&self) -> RwLockReadGuard { @@ -200,7 +199,7 @@ impl Device for FbConsoleDevice { fn set_class(&self, _class: Option>) { // 不允许修改 - warn!("fbcon's class can not be changed"); + kwarn!("fbcon's class can not be changed"); } fn class(&self) -> Option> { @@ -280,13 +279,13 @@ impl Attribute for AttrRotate { /// https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/video/fbdev/core/fbcon.c#3226 fn show(&self, _kobj: Arc, buf: &mut [u8]) -> Result { - warn!("fbcon rotate show not implemented"); + kwarn!("fbcon rotate show not implemented"); return sysfs_emit_str(buf, "0\n"); } /// https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/video/fbdev/core/fbcon.c#3182 fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - warn!("fbcon rotate store not implemented"); + kwarn!("fbcon rotate store not implemented"); return Err(SystemError::ENOSYS); } } @@ -309,7 +308,7 @@ impl Attribute for AttrRotateAll { /// https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/video/fbdev/core/fbcon.c#3204 fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - warn!("fbcon rotate_all store not implemented"); + kwarn!("fbcon rotate_all store not implemented"); return Err(SystemError::ENOSYS); } } @@ -341,12 +340,12 @@ impl Attribute for AttrCursorBlink { } #[derive(Debug, Default)] -#[allow(dead_code)] pub struct FrameBufferConsoleData { /// 光标闪烁间隔 pub cursor_blink_jiffies: i64, /// 是否刷新光标 pub cursor_flash: bool, + /// pub display: FbConsoleDisplay, /// 光标状态 pub cursor_state: FbCursor, diff --git a/kernel/src/driver/video/fbdev/base/fbmem.rs b/kernel/src/driver/video/fbdev/base/fbmem.rs index 8031fc6d3..bb404ab30 100644 --- a/kernel/src/driver/video/fbdev/base/fbmem.rs +++ b/kernel/src/driver/video/fbdev/base/fbmem.rs @@ -6,7 +6,6 @@ use alloc::{ vec::Vec, }; -use log::error; use system_error::SystemError; use unified_init::macros::unified_init; @@ -153,7 +152,7 @@ impl FrameBufferManager { device_manager().add_device(fb_device.clone() as Arc)?; // 添加到devfs devfs_register(&fb_device.name(), fb_device.clone()).map_err(|e| { - error!( + kerror!( "register fb device '{}' to devfs failed: {:?}", fb_device.name(), e diff --git a/kernel/src/driver/video/fbdev/base/fbsysfs.rs b/kernel/src/driver/video/fbdev/base/fbsysfs.rs index 7b6025703..0020ec1d9 100644 --- a/kernel/src/driver/video/fbdev/base/fbsysfs.rs +++ b/kernel/src/driver/video/fbdev/base/fbsysfs.rs @@ -1,5 +1,4 @@ use alloc::sync::Arc; -use log::warn; use system_error::SystemError; use crate::{ @@ -86,7 +85,7 @@ impl Attribute for AttrBitsPerPixel { } fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - warn!("attr bits_per_pixel store not implemented"); + kwarn!("attr bits_per_pixel store not implemented"); return Err(SystemError::ENOSYS); } @@ -117,7 +116,7 @@ impl Attribute for AttrBlank { // todo: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/video/fbdev/core/fbsysfs.c#309 fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - warn!("attr blank store not implemented"); + kwarn!("attr blank store not implemented"); return Err(SystemError::ENOSYS); } } diff --git a/kernel/src/driver/video/fbdev/base/mod.rs b/kernel/src/driver/video/fbdev/base/mod.rs index a266b3635..e20b08711 100644 --- a/kernel/src/driver/video/fbdev/base/mod.rs +++ b/kernel/src/driver/video/fbdev/base/mod.rs @@ -301,7 +301,6 @@ impl FrameBufferInfoData { } /// 帧缓冲区信息 -#[allow(dead_code)] pub trait FrameBufferInfo: FrameBufferOps { fn framebuffer_info_data(&self) -> &RwLock; @@ -342,7 +341,7 @@ pub trait FrameBufferInfo: FrameBufferOps { // && var.green.offset == var.blue.offset // && var.green.offset == var.red.offset // { - // error!("return {}", var.green.length); + // kerror!("return {}", var.green.length); // return var.green.length; // } else { // return var.green.length + var.blue.length + var.red.length; @@ -378,7 +377,6 @@ pub trait FrameBufferInfo: FrameBufferOps { /// 帧缓冲区操作 /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/fb.h#237 -#[allow(dead_code)] pub trait FrameBufferOps { fn fb_open(&self, user: bool); fn fb_release(&self, user: bool); @@ -1065,7 +1063,6 @@ pub enum FbAccel { // Add other accelerators here } -#[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub struct BootTimeScreenInfo { pub origin_x: u8, diff --git a/kernel/src/driver/video/fbdev/vesafb.rs b/kernel/src/driver/video/fbdev/vesafb.rs index a5c486964..2472973d7 100644 --- a/kernel/src/driver/video/fbdev/vesafb.rs +++ b/kernel/src/driver/video/fbdev/vesafb.rs @@ -9,7 +9,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::{info, warn}; use system_error::SystemError; use unified_init::macros::unified_init; @@ -391,7 +390,7 @@ impl FrameBufferOps for VesaFb { /// ## 填充矩形 fn fb_fillrect(&self, rect: super::base::FillRectData) -> Result<(), SystemError> { - // warn!("rect {rect:?}"); + // kwarn!("rect {rect:?}"); let boot_param = boot_params().read(); let screen_base = boot_param @@ -1007,7 +1006,7 @@ fn vesa_fb_device_init() -> Result<(), SystemError> { static INIT: Once = Once::new(); INIT.call_once(|| { - info!("vesa fb device init"); + kinfo!("vesa fb device init"); let device = Arc::new(VesaFb::new()); let mut fb_fix = VESAFB_FIX_INFO.write_irqsave(); @@ -1069,7 +1068,7 @@ fn vesa_fb_device_init() -> Result<(), SystemError> { // 加入全局fb表 let mut guard = FRAME_BUFFER_SET.write(); if guard.get(device.fb_id().data() as usize).unwrap().is_some() { - warn!( + kwarn!( "vesa_fb_device_init: There is already an element {:?} in the FRAME_BUFFER_SET", device.fb_id() ); diff --git a/kernel/src/driver/video/mod.rs b/kernel/src/driver/video/mod.rs index e9a705140..6a8c406d9 100644 --- a/kernel/src/driver/video/mod.rs +++ b/kernel/src/driver/video/mod.rs @@ -3,6 +3,7 @@ use core::sync::atomic::{AtomicBool, Ordering}; use crate::{ arch::MMArch, init::boot_params, + kinfo, libs::{ align::page_align_up, lib_ui::screen_manager::{ScmBuffer, ScmBufferFlag, ScmBufferInfo}, @@ -10,13 +11,12 @@ use crate::{ spinlock::SpinLock, }, mm::{ - allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::EntryFlags, + allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::PageFlags, MemoryManagementArch, }, time::timer::{Timer, TimerFunction}, }; use alloc::{boxed::Box, sync::Arc}; -use log::info; use system_error::SystemError; pub mod console; @@ -78,7 +78,7 @@ impl VideoRefreshManager { * 将帧缓存区映射到地址SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE处 */ fn init_frame_buffer(&self) { - info!("Re-mapping VBE frame buffer..."); + kinfo!("Re-mapping VBE frame buffer..."); let buf_vaddr = boot_params() .read_irqsave() .screen_info @@ -95,7 +95,7 @@ impl VideoRefreshManager { let count = PageFrameCount::new( page_align_up(frame_buffer_info_guard.buf_size()) / MMArch::PAGE_SIZE, ); - let page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); + let page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); let mut kernel_mapper = KernelMapper::lock(); let mut kernel_mapper = kernel_mapper.as_mut(); @@ -115,7 +115,7 @@ impl VideoRefreshManager { } } - info!("VBE frame buffer successfully Re-mapped!"); + kinfo!("VBE frame buffer successfully Re-mapped!"); } /** diff --git a/kernel/src/driver/virtio/irq.rs b/kernel/src/driver/virtio/irq.rs index aa727670e..70f823ab1 100644 --- a/kernel/src/driver/virtio/irq.rs +++ b/kernel/src/driver/virtio/irq.rs @@ -1,6 +1,5 @@ use alloc::sync::Arc; use hashbrown::HashMap; -use log::warn; use system_error::SystemError; use unified_init::macros::unified_init; @@ -121,7 +120,7 @@ impl IrqHandler for DefaultVirtioIrqHandler { return dev.handle_irq(irq); } else { // 未绑定具体设备,因此无法处理中断 - warn!("No device found for IRQ: {:?}", irq); + kwarn!("No device found for IRQ: {:?}", irq); return Ok(IrqReturn::NotHandled); } } diff --git a/kernel/src/driver/virtio/mmio.rs b/kernel/src/driver/virtio/mmio.rs index 815cce58d..027a70bd4 100644 --- a/kernel/src/driver/virtio/mmio.rs +++ b/kernel/src/driver/virtio/mmio.rs @@ -1,5 +1,4 @@ use fdt::node::FdtNode; -use log::error; use system_error::SystemError; use crate::driver::{ @@ -10,7 +9,7 @@ use super::{transport::VirtIOTransport, virtio::virtio_device_init}; pub(super) fn virtio_probe_mmio() { if let Err(e) = do_probe_virtio_mmio() { - error!("virtio_probe_mmio failed: {:?}", e); + kerror!("virtio_probe_mmio failed: {:?}", e); } } diff --git a/kernel/src/driver/virtio/mod.rs b/kernel/src/driver/virtio/mod.rs index 024d44074..9498dcb25 100644 --- a/kernel/src/driver/virtio/mod.rs +++ b/kernel/src/driver/virtio/mod.rs @@ -18,7 +18,6 @@ pub mod virtio_impl; /// virtio 设备厂商ID pub const VIRTIO_VENDOR_ID: u16 = 0x1af4; -#[allow(dead_code)] pub trait VirtIODevice: Device { fn handle_irq(&self, _irq: IrqNumber) -> Result; diff --git a/kernel/src/driver/virtio/sysfs.rs b/kernel/src/driver/virtio/sysfs.rs index dc1d0ea1e..526a88ade 100644 --- a/kernel/src/driver/virtio/sysfs.rs +++ b/kernel/src/driver/virtio/sysfs.rs @@ -4,7 +4,6 @@ use alloc::{ }; use ida::IdAllocator; use intertrait::cast::CastArc; -use log::error; use system_error::SystemError; use unified_init::macros::unified_init; @@ -79,7 +78,7 @@ impl Bus for VirtIOBus { fn probe(&self, device: &Arc) -> Result<(), SystemError> { let drv = device.driver().ok_or(SystemError::EINVAL)?; let virtio_drv = drv.cast::().map_err(|_| { - error!( + kerror!( "VirtIOBus::probe() failed: device.driver() is not a VirtioDriver. Device: '{:?}'", device.name() ); @@ -87,7 +86,7 @@ impl Bus for VirtIOBus { })?; let virtio_dev = device.clone().cast::().map_err(|_| { - error!( + kerror!( "VirtIOBus::probe() failed: device is not a VirtIODevice. Device: '{:?}'", device.name() ); @@ -168,7 +167,7 @@ impl VirtIODeviceManager { let drv = dev.driver().ok_or(SystemError::EINVAL)?; let virtio_drv = drv.cast::().map_err(|_| { - error!( + kerror!( "VirtIODeviceManager::device_add() failed: device.driver() is not a VirtioDriver. Device: '{:?}'", dev.name() ); @@ -200,7 +199,7 @@ impl VirtIODeviceManager { IrqHandleFlags::IRQF_SHARED, Some(dev.dev_id().clone()), ) { - error!( + kerror!( "Failed to request irq for virtio device '{}': irq: {:?}, error {:?}", dev.device_name(), irq, @@ -212,7 +211,7 @@ impl VirtIODeviceManager { virtio_irq_manager() .register_device(dev.clone()) .map_err(|e| { - error!( + kerror!( "Failed to register virtio device's irq, dev: '{}', irq: {:?}, error {:?}", dev.device_name(), irq, @@ -303,7 +302,7 @@ impl Attribute for AttrDevice { fn show(&self, kobj: Arc, buf: &mut [u8]) -> Result { let dev = kobj.cast::().map_err(|_| { - error!("AttrDevice::show() failed: kobj is not a VirtIODevice"); + kerror!("AttrDevice::show() failed: kobj is not a VirtIODevice"); SystemError::EINVAL })?; let device_type_id = dev.device_type_id(); @@ -330,7 +329,7 @@ impl Attribute for AttrVendor { fn show(&self, kobj: Arc, buf: &mut [u8]) -> Result { let dev = kobj.cast::().map_err(|_| { - error!("AttrVendor::show() failed: kobj is not a VirtIODevice"); + kerror!("AttrVendor::show() failed: kobj is not a VirtIODevice"); SystemError::EINVAL })?; let vendor = dev.vendor(); diff --git a/kernel/src/driver/virtio/transport_mmio.rs b/kernel/src/driver/virtio/transport_mmio.rs index 0ee3864d4..435f90fc8 100644 --- a/kernel/src/driver/virtio/transport_mmio.rs +++ b/kernel/src/driver/virtio/transport_mmio.rs @@ -2,7 +2,6 @@ use core::ptr::NonNull; use alloc::sync::Arc; use fdt::node::FdtNode; -use log::info; use system_error::SystemError; use virtio_drivers::transport::{ mmio::{MmioTransport, VirtIOHeader}, @@ -55,7 +54,7 @@ impl VirtIOMmioTransport { match unsafe { MmioTransport::new(header) } { Ok(mmio_transport) => { - info!( "Detected virtio MMIO device with vendor id {:#X}, device type {:?}, version {:?}, hw irq: {}", + kinfo!( "Detected virtio MMIO device with vendor id {:#X}, device type {:?}, version {:?}, hw irq: {}", mmio_transport.vendor_id(), mmio_transport.device_type(), mmio_transport.version(), @@ -70,7 +69,7 @@ impl VirtIOMmioTransport { }) } Err(_) => { - // warn!("MmioTransport::new failed: {:?}", e); + // kwarn!("MmioTransport::new failed: {:?}", e); Err(SystemError::EINVAL) } } diff --git a/kernel/src/driver/virtio/transport_pci.rs b/kernel/src/driver/virtio/transport_pci.rs index 88f4fcab8..071f7edd0 100644 --- a/kernel/src/driver/virtio/transport_pci.rs +++ b/kernel/src/driver/virtio/transport_pci.rs @@ -211,7 +211,7 @@ impl PciTransport { notify_off_multiplier, )); } - //debug!("notify.offset={},notify.length={}",notify_cfg.offset,notify_cfg.length); + //kdebug!("notify.offset={},notify.length={}",notify_cfg.offset,notify_cfg.length); let notify_region = get_bar_region_slice::<_>(&device.standard_device_bar, ¬ify_cfg)?; let isr_status = get_bar_region::<_>( &device.standard_device_bar, @@ -532,7 +532,7 @@ fn get_bar_region( { return Err(VirtioPciError::BarOffsetOutOfRange); } - //debug!("Chossed bar ={},used={}",struct_info.bar,struct_info.offset + struct_info.length); + //kdebug!("Chossed bar ={},used={}",struct_info.bar,struct_info.offset + struct_info.length); let vaddr = (bar_info .virtual_address() .ok_or(VirtioPciError::BarGetVaddrFailed)?) diff --git a/kernel/src/driver/virtio/virtio.rs b/kernel/src/driver/virtio/virtio.rs index 9253542ef..4314edbe2 100644 --- a/kernel/src/driver/virtio/virtio.rs +++ b/kernel/src/driver/virtio/virtio.rs @@ -10,11 +10,10 @@ use crate::driver::pci::pci::{ }; use crate::driver::virtio::transport::VirtIOTransport; use crate::libs::rwlock::RwLockWriteGuard; - +use crate::{kdebug, kerror, kwarn}; use alloc::sync::Arc; use alloc::vec::Vec; use alloc::{boxed::Box, collections::LinkedList}; -use log::{debug, error, warn}; use virtio_drivers::transport::{DeviceType, Transport}; ///@brief 寻找并加载所有virtio设备的驱动(目前只有virtio-net,但其他virtio设备也可添加) @@ -33,7 +32,7 @@ fn virtio_probe_pci() { let dev_id = DeviceId::new(None, Some(format!("{dev_id}"))).unwrap(); match PciTransport::new::(virtio_device, dev_id.clone()) { Ok(mut transport) => { - debug!( + kdebug!( "Detected virtio PCI device with device type {:?}, features {:#018x}", transport.device_type(), transport.read_device_features(), @@ -42,7 +41,7 @@ fn virtio_probe_pci() { virtio_device_init(transport, dev_id); } Err(err) => { - error!("Pci transport create failed because of error: {}", err); + kerror!("Pci transport create failed because of error: {}", err); } } } @@ -53,14 +52,14 @@ pub(super) fn virtio_device_init(transport: VirtIOTransport, dev_id: Arc virtio_blk(transport, dev_id), DeviceType::GPU => { - warn!("Not support virtio_gpu device for now"); + kwarn!("Not support virtio_gpu device for now"); } DeviceType::Input => { - warn!("Not support virtio_input device for now"); + kwarn!("Not support virtio_input device for now"); } DeviceType::Network => virtio_net(transport, dev_id), t => { - warn!("Unrecognized virtio device: {:?}", t); + kwarn!("Unrecognized virtio device: {:?}", t); } } } diff --git a/kernel/src/driver/virtio/virtio_impl.rs b/kernel/src/driver/virtio/virtio_impl.rs index 0166b138b..92aa07cc1 100644 --- a/kernel/src/driver/virtio/virtio_impl.rs +++ b/kernel/src/driver/virtio/virtio_impl.rs @@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags; use crate::arch::MMArch; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, EntryFlags}; +use crate::mm::page::{page_manager_lock_irqsave, PageFlags}; use crate::mm::{ allocator::page_frame::{ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, @@ -32,7 +32,7 @@ unsafe impl Hal for HalImpl { // 清空这块区域,防止出现脏数据 core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); - let dma_flags: EntryFlags = EntryFlags::mmio_flags(); + let dma_flags: PageFlags = PageFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let kernel_mapper = kernel_mapper.as_mut().unwrap(); @@ -90,7 +90,7 @@ unsafe impl Hal for HalImpl { _direction: BufferDirection, ) -> virtio_drivers::PhysAddr { let vaddr = VirtAddr::new(buffer.as_ptr() as *mut u8 as usize); - //debug!("virt:{:x}", vaddr); + //kdebug!("virt:{:x}", vaddr); // Nothing to do, as the host already has access to all memory. return MMArch::virt_2_phys(vaddr).unwrap().data(); } diff --git a/kernel/src/exception/handle.rs b/kernel/src/exception/handle.rs index e39ab02ac..fcfea1e41 100644 --- a/kernel/src/exception/handle.rs +++ b/kernel/src/exception/handle.rs @@ -1,7 +1,6 @@ use core::{intrinsics::unlikely, ops::BitAnd}; use alloc::sync::Arc; -use log::{debug, error, warn}; use system_error::SystemError; use crate::{ @@ -101,7 +100,7 @@ impl IrqFlowHandler for EdgeIrqHandler { fn handle(&self, irq_desc: &Arc, _trap_frame: &mut TrapFrame) { let mut desc_inner_guard: SpinLockGuard<'_, InnerIrqDesc> = irq_desc.inner(); if !irq_may_run(&desc_inner_guard) { - // debug!("!irq_may_run"); + // kdebug!("!irq_may_run"); desc_inner_guard .internal_state_mut() .insert(IrqDescState::IRQS_PENDING); @@ -110,7 +109,7 @@ impl IrqFlowHandler for EdgeIrqHandler { } if desc_inner_guard.common_data().disabled() { - // debug!("desc_inner_guard.common_data().disabled()"); + // kdebug!("desc_inner_guard.common_data().disabled()"); desc_inner_guard .internal_state_mut() .insert(IrqDescState::IRQS_PENDING); @@ -124,7 +123,7 @@ impl IrqFlowHandler for EdgeIrqHandler { loop { if unlikely(desc_inner_guard.actions().is_empty()) { - debug!("no action for irq {}", irq_data.irq().data()); + kdebug!("no action for irq {}", irq_data.irq().data()); irq_manager().mask_irq(&irq_data); return; } @@ -137,12 +136,12 @@ impl IrqFlowHandler for EdgeIrqHandler { { let status = desc_inner_guard.common_data().status(); if !status.disabled() && status.masked() { - // debug!("re-enable irq"); + // kdebug!("re-enable irq"); irq_manager().unmask_irq(&desc_inner_guard); } } - // debug!("handle_irq_event"); + // kdebug!("handle_irq_event"); desc_inner_guard = handle_irq_event(irq_desc, desc_inner_guard); @@ -269,7 +268,7 @@ fn do_handle_irq_event(desc: &Arc) -> Result<(), SystemError> { for action in actions { let mut action_inner: SpinLockGuard<'_, InnerIrqAction> = action.inner(); - // debug!("do_handle_irq_event: action: {:?}", action_inner.name()); + // kdebug!("do_handle_irq_event: action: {:?}", action_inner.name()); let dynamic_data = action_inner .dev_id() .clone() @@ -315,17 +314,17 @@ fn cond_unmask_eoi_irq( && desc_inner_guard.common_data().masked() && desc_inner_guard.threads_oneshot() == 0 { - debug!( + kdebug!( "eoi unmask irq {}", desc_inner_guard.irq_data().irq().data() ); chip.irq_eoi(desc_inner_guard.irq_data()); unmask_irq(desc_inner_guard.irq_data()); } else if !chip.flags().contains(IrqChipFlags::IRQCHIP_EOI_THREADED) { - debug!("eoi irq {}", desc_inner_guard.irq_data().irq().data()); + kdebug!("eoi irq {}", desc_inner_guard.irq_data().irq().data()); chip.irq_eoi(desc_inner_guard.irq_data()); } else { - warn!( + kwarn!( "irq {} eoi failed", desc_inner_guard.irq_data().irq().data() ); @@ -341,7 +340,7 @@ fn warn_no_thread(irq: IrqNumber, action_inner: &mut SpinLockGuard<'_, InnerIrqA return; } - warn!( + kwarn!( "irq {}, device {} returned IRQ_WAKE_THREAD, but no threaded handler", irq.data(), action_inner.name() @@ -403,7 +402,7 @@ impl IrqFlowHandler for PerCpuDevIdIrqHandler { static ONCE: Once = Once::new(); ONCE.call_once(|| { - error!( + kerror!( "Spurious percpu irq {} on cpu {:?}, enabled: {}", irq.data(), cpu, diff --git a/kernel/src/exception/irqchip.rs b/kernel/src/exception/irqchip.rs index 064994867..93e2aa093 100644 --- a/kernel/src/exception/irqchip.rs +++ b/kernel/src/exception/irqchip.rs @@ -5,7 +5,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::warn; use system_error::SystemError; use crate::{ @@ -35,7 +34,6 @@ use super::{ }; /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#506 -#[allow(dead_code)] pub trait IrqChip: Sync + Send + Any + Debug { fn name(&self) -> &'static str; /// start up the interrupt (defaults to ->enable if ENOSYS) @@ -128,7 +126,6 @@ pub trait IrqChip: Sync + Send + Any + Debug { } /// enable/disable power management wake-on of an interrupt - #[allow(dead_code)] fn irq_set_wake(&self, _irq_data: &Arc, _on: bool) -> Result<(), SystemError> { Err(SystemError::ENOSYS) } @@ -283,7 +280,6 @@ struct InnerIrqChipGeneric { chip_types: Vec, } -#[allow(dead_code)] pub trait IrqChipGenericOps: Debug + Send + Sync { /// Alternate I/O accessor (defaults to readl if NULL) unsafe fn reg_readl(&self, addr: VirtAddr) -> u32; @@ -437,7 +433,7 @@ impl IrqManager { * 则放弃。 */ if unlikely(is_chained) { - warn!( + kwarn!( "Chained handler for irq {} is not supported", dt.irq().data() ); @@ -457,7 +453,7 @@ impl IrqManager { &no_irq_chip(), ), ) { - warn!("No irq chip for irq {}", desc_inner.irq_data().irq().data()); + kwarn!("No irq chip for irq {}", desc_inner.irq_data().irq().data()); return; } } @@ -576,7 +572,7 @@ impl IrqHandler for ChainedActionHandler { ) -> Result { static ONCE: Once = Once::new(); ONCE.call_once(|| { - warn!("Chained irq {} should not call an action.", irq.data()); + kwarn!("Chained irq {} should not call an action.", irq.data()); }); Ok(IrqReturn::NotHandled) diff --git a/kernel/src/exception/irqdomain.rs b/kernel/src/exception/irqdomain.rs index 021979dd8..92267aa17 100644 --- a/kernel/src/exception/irqdomain.rs +++ b/kernel/src/exception/irqdomain.rs @@ -6,7 +6,6 @@ use alloc::{ vec::Vec, }; use hashbrown::HashMap; -use log::{info, warn}; use system_error::SystemError; use crate::{ @@ -158,7 +157,7 @@ impl IrqDomainManager { ) { for i in 0..count { if let Err(e) = self.domain_associate(domain, first_irq + i, first_hwirq + i) { - warn!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), (first_hwirq + i).data(), (first_irq + i).data()); + kwarn!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), (first_hwirq + i).data(), (first_irq + i).data()); } } } @@ -173,7 +172,7 @@ impl IrqDomainManager { hwirq: HardwareIrqNumber, ) -> Result<(), SystemError> { if hwirq >= domain.revmap.read_irqsave().hwirq_max { - warn!( + kwarn!( "hwirq {} is out of range for domain {:?}", hwirq.data(), domain.name() @@ -183,12 +182,12 @@ impl IrqDomainManager { let irq_data = irq_desc_manager() .lookup(irq) .ok_or_else(|| { - warn!("irq_desc not found for irq {}", irq.data()); + kwarn!("irq_desc not found for irq {}", irq.data()); SystemError::EINVAL })? .irq_data(); if irq_data.domain().is_some() { - warn!( + kwarn!( "irq {} is already associated with domain {:?}", irq.data(), irq_data.domain().unwrap().name() @@ -204,7 +203,7 @@ impl IrqDomainManager { if let Err(e) = r { if e != SystemError::ENOSYS { if e != SystemError::EPERM { - info!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), hwirq.data(), irq.data()); + kinfo!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), hwirq.data(), irq.data()); } let mut irq_data_guard = irq_data.inner(); irq_data_guard.set_domain(None); @@ -247,7 +246,7 @@ impl IrqDomainManager { /// 这是调用 domain_ops->activate 以编程中断控制器的第二步,以便中断实际上可以被传递。 pub fn activate_irq(&self, irq_data: &Arc, reserve: bool) -> Result<(), SystemError> { let mut r = Ok(()); - // debug!( + // kdebug!( // "activate_irq: irq_data.common_data().status().is_activated()={}", // irq_data.common_data().status().is_activated() // ); @@ -271,9 +270,9 @@ impl IrqDomainManager { let mut r = Ok(()); if let Some(irq_data) = irq_data { - // debug!("do_activate_irq: irq_data={:?}", irq_data); + // kdebug!("do_activate_irq: irq_data={:?}", irq_data); if let Some(domain) = irq_data.domain() { - // debug!("do_activate_irq: domain={:?}", domain.name()); + // kdebug!("do_activate_irq: domain={:?}", domain.name()); let parent_data = irq_data.parent_data().and_then(|x| x.upgrade()); if let Some(parent_data) = parent_data.clone() { r = self.do_activate_irq(Some(parent_data), reserve); @@ -644,7 +643,6 @@ pub enum IrqDomainBusToken { /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#107 pub trait IrqDomainOps: Debug + Send + Sync { /// 匹配一个中断控制器设备节点到一个主机。 - #[allow(dead_code)] fn match_node( &self, _irq_domain: &Arc, @@ -668,7 +666,6 @@ pub trait IrqDomainOps: Debug + Send + Sync { } /// 删除一个虚拟中断号与一个硬件中断号之间的映射。 - #[allow(dead_code)] fn unmap(&self, irq_domain: &Arc, virq: IrqNumber); fn activate( diff --git a/kernel/src/exception/manage.rs b/kernel/src/exception/manage.rs index 85a47b336..80a632498 100644 --- a/kernel/src/exception/manage.rs +++ b/kernel/src/exception/manage.rs @@ -2,7 +2,6 @@ use core::ops::{BitXor, Deref, DerefMut}; use alloc::{string::String, sync::Arc}; -use log::{debug, error, warn}; use system_error::SystemError; use crate::{ @@ -136,7 +135,7 @@ impl IrqManager { } let desc = irq_desc_manager().lookup(irq).ok_or(SystemError::EINVAL)?; if !desc.can_request() { - warn!("desc {} can not request", desc.irq().data()); + kwarn!("desc {} can not request", desc.irq().data()); return Err(SystemError::EINVAL); } @@ -156,7 +155,7 @@ impl IrqManager { *action_guard.flags_mut() = flags; *action_guard.dev_id_mut() = dev_id; drop(action_guard); - debug!("to inner_setup_irq"); + kdebug!("to inner_setup_irq"); return self.inner_setup_irq(irq, irqaction, desc); } @@ -216,7 +215,7 @@ impl IrqManager { .flags() .contains(IrqHandleFlags::IRQF_PROBE_SHARED) { - error!("Flags mismatch for irq {} (name: {}, flags: {:?}). old action name: {}, old flags: {:?}", irq.data(), action_guard.name(), action_guard.flags(), old_action_guard.name(), old_action_guard.flags()); + kerror!("Flags mismatch for irq {} (name: {}, flags: {:?}). old action name: {}, old flags: {:?}", irq.data(), action_guard.name(), action_guard.flags(), old_action_guard.name(), old_action_guard.flags()); } return err_out_unlock( SystemError::EBUSY, @@ -301,7 +300,7 @@ impl IrqManager { // 如果当前中断线上还没有irqaction, 则先为中断线申请资源 if desc.actions().is_empty() { if let Err(e) = self.irq_request_resources(desc.clone()) { - error!( + kerror!( "Failed to request resources for {} (irq {}) on irqchip {}, error {:?}", action_guard.name(), irq.data(), @@ -330,7 +329,7 @@ impl IrqManager { .internal_state() .contains(IrqDescState::IRQS_NMI) { - error!( + kerror!( "Invalid attempt to share NMI for {} (irq {}) on irqchip {}", action_guard.name(), irq.data(), @@ -371,7 +370,7 @@ impl IrqManager { || ((old_guard.flags().bitxor(*action_guard.flags())) .contains(IrqHandleFlags::IRQF_ONESHOT)) { - debug!( + kdebug!( "Flags mismatch for irq {} (name: {}, flags: {:?}). old action name: {}, old flags: {:?}", irq.data(), action_guard.name(), @@ -393,7 +392,7 @@ impl IrqManager { if *old_guard.flags() & IrqHandleFlags::IRQF_PERCPU != *action_guard.flags() & IrqHandleFlags::IRQF_PERCPU { - debug!( + kdebug!( "Per-cpu mismatch for irq {} (name: {}, flags: {:?})", irq.data(), action_guard.name(), @@ -430,7 +429,7 @@ impl IrqManager { // 因为我们不能确定这个中断实际上具有什么类型。 // 由于底层芯片实现可能会覆盖它们,所以类型标志并不可靠. - error!( + kerror!( "Requesting irq {} without a handler, and ONESHOT flags not set for irqaction: {}", irq.data(), action_guard.name() @@ -452,7 +451,7 @@ impl IrqManager { if let Err(e) = self.do_set_irq_trigger(desc.clone(), &mut desc_inner_guard, trigger_type) { - debug!( + kdebug!( "Failed to set trigger type for irq {} (name: {}, flags: {:?}), error {:?}", irq.data(), action_guard.name(), @@ -468,10 +467,10 @@ impl IrqManager { )); } } - debug!("to irq_activate"); + kdebug!("to irq_activate"); // 激活中断。这种激活必须独立于IRQ_NOAUTOEN进行*desc_inner_guard.internal_state_mut() |= IrqDescState::IRQS_NOREQUEST;uest. if let Err(e) = self.irq_activate(&desc, &mut desc_inner_guard) { - debug!( + kdebug!( "Failed to activate irq {} (name: {}, flags: {:?}), error {:?}", irq.data(), action_guard.name(), @@ -538,7 +537,7 @@ impl IrqManager { static mut WARNED: bool = false; if action_guard.flags().contains(IrqHandleFlags::IRQF_SHARED) && unsafe { !WARNED } { - warn!( + kwarn!( "Shared interrupt {} for {} requested but not auto enabled", irq.data(), action_guard.name() @@ -552,7 +551,7 @@ impl IrqManager { let new_trigger_type = action_guard.flags().trigger_type(); let old_trigger_type = desc_inner_guard.common_data().trigger_type(); if new_trigger_type != old_trigger_type { - warn!("Irq {} uses trigger type: {old_trigger_type:?}, but requested trigger type: {new_trigger_type:?}.", irq.data()); + kwarn!("Irq {} uses trigger type: {old_trigger_type:?}, but requested trigger type: {new_trigger_type:?}.", irq.data()); } } @@ -613,7 +612,7 @@ impl IrqManager { .thread_completion() .wait_for_completion() .map_err(|e| { - warn!( + kwarn!( "Failed to wait for irq thread ready for {} (irq {:?}), error {:?}", action.inner().name(), desc.irq_data().irq(), @@ -629,7 +628,7 @@ impl IrqManager { desc_inner_guard: &mut SpinLockGuard<'_, InnerIrqDesc>, resend: bool, ) -> Result<(), SystemError> { - debug!( + kdebug!( "irq_activate_and_startup: irq: {}, name: {:?}", desc.irq().data(), desc_inner_guard.name() @@ -660,7 +659,7 @@ impl IrqManager { resend: bool, force: bool, ) -> Result<(), SystemError> { - debug!( + kdebug!( "irq_startup: irq: {}, name: {:?}", desc_inner_guard.irq_data().irq().data(), desc_inner_guard.name() @@ -711,7 +710,7 @@ impl IrqManager { if resend { if let Err(e) = self.irq_check_and_resend(desc_inner_guard, false) { - error!( + kerror!( "Failed to check and resend irq {}, error {:?}", irq_data.irq().data(), e @@ -735,7 +734,7 @@ impl IrqManager { if e == SystemError::ENOSYS { self.unmask_irq(desc_inner_guard); } - error!( + kerror!( "Failed to enable irq {} (name: {:?}), error {:?}", desc_inner_guard.irq_data().irq().data(), desc_inner_guard.name(), @@ -931,7 +930,7 @@ impl IrqManager { let mut to_unmask = false; if !chip.can_set_flow_type() { - // debug!( + // kdebug!( // "No set_irq_type function for irq {}, chip {}", // desc_inner_guard.irq_data().irq().data(), // chip.name() @@ -980,7 +979,7 @@ impl IrqManager { ret = Ok(()); } else { - error!( + kerror!( "Failed to set irq {} trigger type to {:?} on irqchip {}, error {:?}", desc_inner_guard.irq_data().irq().data(), trigger_type, @@ -1060,7 +1059,7 @@ impl IrqManager { if let Err(e) = r { if e != SystemError::ENOSYS { - error!( + kerror!( "Failed to unmask irq {} on irqchip {}, error {:?}", desc_inner_guard.irq_data().irq().data(), desc_inner_guard @@ -1101,7 +1100,7 @@ impl IrqManager { /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/kernel/irq/manage.c#2026 pub fn free_irq(&self, _irq: IrqNumber, _dev_id: Option>) { - warn!("Unimplemented free_irq"); + kwarn!("Unimplemented free_irq"); } } @@ -1139,7 +1138,7 @@ impl IrqHandler for IrqNestedPrimaryHandler { _static_data: Option<&dyn IrqHandlerData>, _dynamic_data: Option>, ) -> Result { - warn!("Primary handler called for nested irq {}", irq.data()); + kwarn!("Primary handler called for nested irq {}", irq.data()); return Ok(IrqReturn::NotHandled); } } diff --git a/kernel/src/exception/msi.rs b/kernel/src/exception/msi.rs index ef0993eb1..f521d117f 100644 --- a/kernel/src/exception/msi.rs +++ b/kernel/src/exception/msi.rs @@ -124,7 +124,6 @@ impl MsiDesc { } } -#[allow(dead_code)] pub trait MsiDescFunc: Debug + Send + Sync { /// Callback that may be called when the MSI message /// address or data changes. diff --git a/kernel/src/exception/softirq.rs b/kernel/src/exception/softirq.rs index f7d9513a5..06e882e80 100644 --- a/kernel/src/exception/softirq.rs +++ b/kernel/src/exception/softirq.rs @@ -7,13 +7,13 @@ use core::{ }; use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use log::{debug, info}; use num_traits::FromPrimitive; use system_error::SystemError; use crate::{ arch::CurrentIrqArch, exception::InterruptArch, + kdebug, kinfo, libs::rwlock::RwLock, mm::percpu::{PerCpu, PerCpuVar}, process::ProcessManager, @@ -35,7 +35,7 @@ pub extern "C" fn rs_softirq_init() { #[inline(never)] pub fn softirq_init() -> Result<(), SystemError> { - info!("Initializing softirq..."); + kinfo!("Initializing softirq..."); unsafe { __SORTIRQ_VECTORS = Box::leak(Box::new(Softirq::new())); __CPU_PENDING = Some(Box::new( @@ -46,7 +46,7 @@ pub fn softirq_init() -> Result<(), SystemError> { cpu_pending[i as usize] = VecStatus::default(); } } - info!("Softirq initialized."); + kinfo!("Softirq initialized."); return Ok(()); } @@ -143,20 +143,20 @@ impl Softirq { softirq_num: SoftirqNumber, handler: Arc, ) -> Result { - // debug!("register_softirq softirq_num = {:?}", softirq_num as u64); + // kdebug!("register_softirq softirq_num = {:?}", softirq_num as u64); // let self = &mut SOFTIRQ_VECTORS.lock(); // 判断该软中断向量是否已经被注册 let mut table_guard = self.table.write_irqsave(); if table_guard[softirq_num as usize].is_some() { - // debug!("register_softirq failed"); + // kdebug!("register_softirq failed"); return Err(SystemError::EINVAL); } table_guard[softirq_num as usize] = Some(handler); drop(table_guard); - // debug!( + // kdebug!( // "register_softirq successfully, softirq_num = {:?}", // softirq_num as u64 // ); @@ -169,7 +169,7 @@ impl Softirq { /// @param irq_num 中断向量号码 #[allow(dead_code)] pub fn unregister_softirq(&self, softirq_num: SoftirqNumber) { - // debug!("unregister_softirq softirq_num = {:?}", softirq_num as u64); + // kdebug!("unregister_softirq softirq_num = {:?}", softirq_num as u64); let mut table_guard = self.table.write_irqsave(); // 将软中断向量清空 table_guard[softirq_num as usize] = None; @@ -219,7 +219,7 @@ impl Softirq { softirq_func.as_ref().unwrap().run(); if unlikely(prev_count != ProcessManager::current_pcb().preempt_count()) { - debug!( + kdebug!( "entered softirq {:?} with preempt_count {:?},exited with {:?}", i, prev_count, @@ -255,7 +255,7 @@ impl Softirq { compiler_fence(Ordering::SeqCst); drop(guard); - // debug!("raise_softirq exited"); + // kdebug!("raise_softirq exited"); } #[allow(dead_code)] diff --git a/kernel/src/exception/sysfs.rs b/kernel/src/exception/sysfs.rs index 1e499bf56..390e65a9a 100644 --- a/kernel/src/exception/sysfs.rs +++ b/kernel/src/exception/sysfs.rs @@ -1,5 +1,4 @@ use alloc::{string::ToString, sync::Arc}; -use log::warn; use system_error::SystemError; use unified_init::macros::unified_init; @@ -118,7 +117,7 @@ fn irq_sysfs_add(irq: &IrqNumber, desc: &Arc) { let kset = sys_kernel_irq_kset(); KObjectManager::add_kobj(desc.clone() as Arc, Some(kset)).unwrap_or_else(|e| { - warn!("Failed to add irq({irq:?}) kobject to sysfs: {:?}", e); + kwarn!("Failed to add irq({irq:?}) kobject to sysfs: {:?}", e); }); desc.mark_in_sysfs(); diff --git a/kernel/src/filesystem/devfs/mod.rs b/kernel/src/filesystem/devfs/mod.rs index 92f602a22..2a3ffd2e7 100644 --- a/kernel/src/filesystem/devfs/mod.rs +++ b/kernel/src/filesystem/devfs/mod.rs @@ -11,6 +11,7 @@ use super::vfs::{ }; use crate::{ driver::base::device::device_number::DeviceNumber, + kerror, kinfo, libs::{ once::Once, spinlock::{SpinLock, SpinLockGuard}, @@ -23,7 +24,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::{error, info}; use system_error::SystemError; const DEVFS_BLOCK_SIZE: u64 = 512; @@ -97,7 +97,7 @@ impl DevFS { .expect("DevFS: Failed to create /dev/block"); devfs.register_bultinin_device(); - // debug!("ls /dev: {:?}", root.list()); + // kdebug!("ls /dev: {:?}", root.list()); return devfs; } @@ -541,7 +541,7 @@ impl IndexNode for LockedDevFSInode { _buf: &mut [u8], _data: SpinLockGuard, ) -> Result { - error!("DevFS: read_at is not supported!"); + kerror!("DevFS: read_at is not supported!"); Err(SystemError::ENOSYS) } @@ -580,7 +580,7 @@ macro_rules! devfs_exact_ref { () => {{ let devfs_inode: Result, SystemError> = ROOT_INODE().find("dev"); if let Err(e) = devfs_inode { - error!("failed to get DevFS ref. errcode = {:?}", e); + kerror!("failed to get DevFS ref. errcode = {:?}", e); return Err(SystemError::ENOENT); } @@ -611,7 +611,7 @@ pub fn devfs_init() -> Result<(), SystemError> { static INIT: Once = Once::new(); let mut result = None; INIT.call_once(|| { - info!("Initializing DevFS..."); + kinfo!("Initializing DevFS..."); // 创建 devfs 实例 let devfs: Arc = DevFS::new(); // devfs 挂载 @@ -620,7 +620,7 @@ pub fn devfs_init() -> Result<(), SystemError> { .expect("Unabled to find /dev") .mount(devfs) .expect("Failed to mount at /dev"); - info!("DevFS mounted."); + kinfo!("DevFS mounted."); result = Some(Ok(())); }); diff --git a/kernel/src/filesystem/devpts/mod.rs b/kernel/src/filesystem/devpts/mod.rs index c019d81ac..1fc57f5c6 100644 --- a/kernel/src/filesystem/devpts/mod.rs +++ b/kernel/src/filesystem/devpts/mod.rs @@ -7,7 +7,6 @@ use alloc::{ vec::Vec, }; use ida::IdAllocator; -use log::info; use system_error::SystemError; use unified_init::macros::unified_init; @@ -279,7 +278,7 @@ pub fn devpts_init() -> Result<(), SystemError> { let ptsfs: Arc = DevPtsFs::new(); do_mount_mkdir(ptsfs, "/dev/pts").expect("Failed to mount DevPtsFS"); - info!("DevPtsFs mounted."); + kinfo!("DevPtsFs mounted."); Ok(()) } diff --git a/kernel/src/filesystem/eventfd.rs b/kernel/src/filesystem/eventfd.rs deleted file mode 100644 index b35f608af..000000000 --- a/kernel/src/filesystem/eventfd.rs +++ /dev/null @@ -1,268 +0,0 @@ -use crate::filesystem::vfs::file::{File, FileMode}; -use crate::filesystem::vfs::syscall::ModeType; -use crate::filesystem::vfs::{FilePrivateData, FileSystem, FileType, IndexNode, Metadata}; -use crate::libs::spinlock::{SpinLock, SpinLockGuard}; -use crate::libs::wait_queue::WaitQueue; -use crate::net::event_poll::{EPollEventType, EPollItem, EventPoll, KernelIoctlData}; -use crate::process::ProcessManager; -use crate::syscall::Syscall; -use alloc::collections::LinkedList; -use alloc::string::String; -use alloc::sync::Arc; -use alloc::sync::Weak; -use alloc::vec::Vec; -use core::any::Any; -use ida::IdAllocator; -use system_error::SystemError; - -static EVENTFD_ID_ALLOCATOR: IdAllocator = IdAllocator::new(0, u32::MAX as usize); - -bitflags! { - pub struct EventFdFlags: u32{ - /// Provide semaphore-like semantics for reads from the new - /// file descriptor. - const EFD_SEMAPHORE = 0o1; - /// Set the close-on-exec (FD_CLOEXEC) flag on the new file - /// descriptor - const EFD_CLOEXEC = 0o2000000; - /// Set the O_NONBLOCK file status flag on the open file - /// description (see open(2)) referred to by the new file - /// descriptor - const EFD_NONBLOCK = 0o0004000; - } -} - -#[derive(Debug)] -pub struct EventFd { - count: u64, - flags: EventFdFlags, - #[allow(unused)] - id: u32, -} - -impl EventFd { - pub fn new(count: u64, flags: EventFdFlags, id: u32) -> Self { - EventFd { count, flags, id } - } -} - -#[derive(Debug)] -pub struct EventFdInode { - eventfd: SpinLock, - wait_queue: WaitQueue, - epitems: SpinLock>>, -} - -impl EventFdInode { - pub fn new(eventfd: EventFd) -> Self { - EventFdInode { - eventfd: SpinLock::new(eventfd), - wait_queue: WaitQueue::default(), - epitems: SpinLock::new(LinkedList::new()), - } - } - pub fn remove_epoll(&self, epoll: &Weak>) -> Result<(), SystemError> { - let is_remove = !self - .epitems - .lock_irqsave() - .extract_if(|x| x.epoll().ptr_eq(epoll)) - .collect::>() - .is_empty(); - - if is_remove { - return Ok(()); - } - - Err(SystemError::ENOENT) - } -} - -impl IndexNode for EventFdInode { - fn open( - &self, - _data: SpinLockGuard, - _mode: &FileMode, - ) -> Result<(), SystemError> { - Ok(()) - } - - fn close(&self, _data: SpinLockGuard) -> Result<(), SystemError> { - Ok(()) - } - - /// # 从 counter 里读取一个 8 字节的int值 - /// - /// 1. counter !=0 - /// - EFD_SEMAPHORE 如果没有被设置,从 eventfd read,会得到 counter,并将它归0 - /// - EFD_SEMAPHORE 如果被设置,从 eventfd read,会得到值 1,并将 counter - 1 - /// 2. counter == 0 - /// - EFD_NONBLOCK 如果被设置,那么会以 EAGAIN 的错失败 - /// - 否则 read 会被阻塞,直到为非0。 - fn read_at( - &self, - _offset: usize, - len: usize, - buf: &mut [u8], - data: SpinLockGuard, - ) -> Result { - if len < 8 { - return Err(SystemError::EINVAL); - } - let mut val = loop { - let val = self.eventfd.lock().count; - if val != 0 { - break val; - } - if self - .eventfd - .lock() - .flags - .contains(EventFdFlags::EFD_NONBLOCK) - { - return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); - } - self.wait_queue.sleep(); - }; - - let mut eventfd = self.eventfd.lock(); - if eventfd.flags.contains(EventFdFlags::EFD_SEMAPHORE) { - eventfd.count -= 1; - val = 1; - } else { - eventfd.count = 0; - } - let val_bytes = val.to_ne_bytes(); - buf[..8].copy_from_slice(&val_bytes); - - let pollflag = EPollEventType::from_bits_truncate(self.poll(&data)? as u32); - // 唤醒epoll中等待的进程 - EventPoll::wakeup_epoll(&self.epitems, pollflag)?; - - return Ok(8); - } - - /// # 把一个 8 字节的int值写入到 counter 里 - /// - /// - counter 最大值是 2^64 - 1 - /// - 如果写入时会发生溢出,则write会被阻塞 - /// - 如果 EFD_NONBLOCK 被设置,那么以 EAGAIN 失败 - /// - 以不合法的值写入时,会以 EINVAL 失败 - /// - 比如 0xffffffffffffffff 不合法 - /// - 比如 写入的值 size 小于8字节 - fn write_at( - &self, - _offset: usize, - len: usize, - buf: &[u8], - data: SpinLockGuard, - ) -> Result { - if len < 8 { - return Err(SystemError::EINVAL); - } - let val = u64::from_ne_bytes(buf[..8].try_into().unwrap()); - if val == u64::MAX { - return Err(SystemError::EINVAL); - } - loop { - let eventfd = self.eventfd.lock(); - if u64::MAX - eventfd.count > val { - break; - } - // block until a read() is performed on the - // file descriptor, or fails with the error EAGAIN if the - // file descriptor has been made nonblocking. - if eventfd.flags.contains(EventFdFlags::EFD_NONBLOCK) { - return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); - } - drop(eventfd); - self.wait_queue.sleep(); - } - let mut eventfd = self.eventfd.lock(); - eventfd.count += val; - self.wait_queue.wakeup_all(None); - - let pollflag = EPollEventType::from_bits_truncate(self.poll(&data)? as u32); - // 唤醒epoll中等待的进程 - EventPoll::wakeup_epoll(&self.epitems, pollflag)?; - return Ok(8); - } - - /// # 检查 eventfd 的状态 - /// - /// - 如果 counter 的值大于 0 ,那么 fd 的状态就是可读的 - /// - 如果能无阻塞地写入一个至少为 1 的值,那么 fd 的状态就是可写的 - fn poll(&self, _private_data: &FilePrivateData) -> Result { - let mut events = EPollEventType::empty(); - if self.eventfd.lock().count != 0 { - events |= EPollEventType::EPOLLIN | EPollEventType::EPOLLRDNORM; - } - if self.eventfd.lock().count != u64::MAX { - events |= EPollEventType::EPOLLOUT | EPollEventType::EPOLLWRNORM; - } - return Ok(events.bits() as usize); - } - - fn metadata(&self) -> Result { - let meta = Metadata { - mode: ModeType::from_bits_truncate(0o755), - file_type: FileType::File, - ..Default::default() - }; - Ok(meta) - } - - fn resize(&self, _len: usize) -> Result<(), SystemError> { - Ok(()) - } - fn kernel_ioctl( - &self, - arg: Arc, - _data: &FilePrivateData, - ) -> Result { - let epitem = arg - .arc_any() - .downcast::() - .map_err(|_| SystemError::EFAULT)?; - self.epitems.lock().push_back(epitem); - Ok(0) - } - fn fs(&self) -> Arc { - panic!("EventFd does not have a filesystem") - } - fn as_any_ref(&self) -> &dyn Any { - self - } - fn list(&self) -> Result, SystemError> { - Err(SystemError::EINVAL) - } -} - -impl Syscall { - /// # 创建一个 eventfd 文件描述符 - /// - /// ## 参数 - /// - `init_val`: u32: eventfd 的初始值 - /// - `flags`: u32: eventfd 的标志 - /// - /// ## 返回值 - /// - `Ok(usize)`: 成功创建的文件描述符 - /// - `Err(SystemError)`: 创建失败 - /// - /// See: https://man7.org/linux/man-pages/man2/eventfd2.2.html - pub fn sys_eventfd(init_val: u32, flags: u32) -> Result { - let flags = EventFdFlags::from_bits(flags).ok_or(SystemError::EINVAL)?; - let id = EVENTFD_ID_ALLOCATOR.alloc().ok_or(SystemError::ENOMEM)? as u32; - let eventfd = EventFd::new(init_val as u64, flags, id); - let inode = Arc::new(EventFdInode::new(eventfd)); - let filemode = if flags.contains(EventFdFlags::EFD_CLOEXEC) { - FileMode::O_RDWR | FileMode::O_CLOEXEC - } else { - FileMode::O_RDWR - }; - let file = File::new(inode, filemode)?; - let binding = ProcessManager::current_pcb().fd_table(); - let mut fd_table_guard = binding.write(); - let fd = fd_table_guard.alloc_fd(file, None).map(|x| x as usize); - return fd; - } -} diff --git a/kernel/src/filesystem/fat/bpb.rs b/kernel/src/filesystem/fat/bpb.rs index 2f8d6ec77..1c7b8cc7c 100644 --- a/kernel/src/filesystem/fat/bpb.rs +++ b/kernel/src/filesystem/fat/bpb.rs @@ -1,10 +1,10 @@ #![allow(dead_code)] use alloc::sync::Arc; -use log::error; use system_error::SystemError; use crate::{ driver::base::block::{block_device::LBA_SIZE, disk_info::Partition, SeekFrom}, + kerror, libs::vec_cursor::VecCursor, }; @@ -190,27 +190,27 @@ impl BiosParameterBlockFAT32 { /// @brief 验证BPB32的信息是否合法 fn validate(&self, bpb: &BiosParameterBlock) -> Result<(), SystemError> { if bpb.fat_size_16 != 0 { - error!("Invalid fat_size_16 value in BPB (should be zero for FAT32)"); + kerror!("Invalid fat_size_16 value in BPB (should be zero for FAT32)"); return Err(SystemError::EINVAL); } if bpb.root_entries_cnt != 0 { - error!("Invalid root_entries value in BPB (should be zero for FAT32)"); + kerror!("Invalid root_entries value in BPB (should be zero for FAT32)"); return Err(SystemError::EINVAL); } if bpb.total_sectors_16 != 0 { - error!("Invalid total_sectors_16 value in BPB (should be zero for FAT32)"); + kerror!("Invalid total_sectors_16 value in BPB (should be zero for FAT32)"); return Err(SystemError::EINVAL); } if self.fat_size_32 == 0 { - error!("Invalid fat_size_32 value in BPB (should be non-zero for FAT32)"); + kerror!("Invalid fat_size_32 value in BPB (should be non-zero for FAT32)"); return Err(SystemError::EINVAL); } if self.fs_version != 0 { - error!("Unknown FAT FS version"); + kerror!("Unknown FAT FS version"); return Err(SystemError::EINVAL); } @@ -315,28 +315,28 @@ impl BiosParameterBlock { pub fn validate(&self) -> Result<(), SystemError> { // 校验每扇区字节数是否合法 if self.bytes_per_sector.count_ones() != 1 { - error!("Invalid bytes per sector(not a power of 2)"); + kerror!("Invalid bytes per sector(not a power of 2)"); return Err(SystemError::EINVAL); } else if self.bytes_per_sector < 512 { - error!("Invalid bytes per sector (value < 512)"); + kerror!("Invalid bytes per sector (value < 512)"); return Err(SystemError::EINVAL); } else if self.bytes_per_sector > 4096 { - error!("Invalid bytes per sector (value > 4096)"); + kerror!("Invalid bytes per sector (value > 4096)"); return Err(SystemError::EINVAL); } if self.rsvd_sec_cnt < 1 { - error!("Invalid rsvd_sec_cnt value in BPB"); + kerror!("Invalid rsvd_sec_cnt value in BPB"); return Err(SystemError::EINVAL); } if self.num_fats == 0 { - error!("Invalid fats value in BPB"); + kerror!("Invalid fats value in BPB"); return Err(SystemError::EINVAL); } if (self.total_sectors_16 == 0) && (self.total_sectors_32 == 0) { - error!("Invalid BPB (total_sectors_16 or total_sectors_32 should be non-zero)"); + kerror!("Invalid BPB (total_sectors_16 or total_sectors_32 should be non-zero)"); return Err(SystemError::EINVAL); } @@ -367,7 +367,7 @@ impl BiosParameterBlock { // 总扇区数应当大于第一个数据扇区的扇区号 if total_sectors <= first_data_sector { - error!("Total sectors lesser than first data sector"); + kerror!("Total sectors lesser than first data sector"); return Err(SystemError::EINVAL); } diff --git a/kernel/src/filesystem/fat/entry.rs b/kernel/src/filesystem/fat/entry.rs index cafb3dc4c..322486aeb 100644 --- a/kernel/src/filesystem/fat/entry.rs +++ b/kernel/src/filesystem/fat/entry.rs @@ -1,10 +1,10 @@ #![allow(dead_code)] use core::{cmp::min, intrinsics::unlikely}; -use log::{debug, warn}; use system_error::SystemError; use crate::{ driver::base::block::{block_device::LBA_SIZE, SeekFrom}, + kwarn, libs::vec_cursor::VecCursor, }; use alloc::{ @@ -265,7 +265,7 @@ impl FATFile { let last_cluster = if let Some(c) = fs.get_last_cluster(self.first_cluster) { c } else { - warn!("FAT: last cluster not found, File = {self:?}"); + kwarn!("FAT: last cluster not found, File = {self:?}"); return Err(SystemError::EINVAL); }; // 申请簇 @@ -450,7 +450,7 @@ impl FATDir { free += 1; if free == num_free { - // debug!("first_free = {first_free:?}, current_free = ({current_cluster:?}, {offset})"); + // kdebug!("first_free = {first_free:?}, current_free = ({current_cluster:?}, {offset})"); return Ok(first_free); } } @@ -472,7 +472,7 @@ impl FATDir { / fs.bytes_per_cluster(); let mut first_cluster = Cluster::default(); let mut prev_cluster = current_cluster; - // debug!( + // kdebug!( // "clusters_required={clusters_required}, prev_cluster={prev_cluster:?}, free ={free}" // ); // 申请簇 @@ -592,7 +592,7 @@ impl FATDir { pub fn create_dir(&self, name: &str, fs: &Arc) -> Result { let r: Result = self.check_existence(name, Some(true), fs.clone()); - // debug!("check existence ok"); + // kdebug!("check existence ok"); // 检查错误码,如果能够表明目录项已经存在,则返回-EEXIST if let Err(err_val) = r { if err_val == (SystemError::EISDIR) || err_val == (SystemError::ENOTDIR) { @@ -639,7 +639,7 @@ impl FATDir { dot_dot_entry.flush(fs, fs.cluster_bytes_offset(first_cluster) + offset)?; - // debug!("to create dentries"); + // kdebug!("to create dentries"); // 在当前目录下创建目标目录项 let res = self .create_dir_entries( @@ -652,7 +652,7 @@ impl FATDir { fs.clone(), ) .map(|e| e.to_dir())?; - // debug!("create dentries ok"); + // kdebug!("create dentries ok"); return res; } FATDirEntryOrShortName::DirEntry(_) => { @@ -732,7 +732,7 @@ impl FATDir { LongNameEntryGenerator::new(long_name, short_dentry.checksum()); let num_entries = long_name_gen.num_entries() as u64; - // debug!("to find free entries"); + // kdebug!("to find free entries"); let free_entries: Option<(Cluster, u64)> = self.find_free_entries(num_entries, fs.clone())?; // 目录项开始位置 @@ -1119,7 +1119,7 @@ impl LongDirEntry { | '^' | '#' | '&' => {} '+' | ',' | ';' | '=' | '[' | ']' | '.' | ' ' => {} _ => { - debug!("error char: {}", c); + kdebug!("error char: {}", c); return Err(SystemError::EILSEQ); } } @@ -1328,7 +1328,6 @@ impl ShortDirEntry { } /// @brief 计算短目录项的名称的校验和 - #[allow(clippy::manual_rotate)] fn checksum(&self) -> u8 { let mut result = 0; @@ -1575,7 +1574,7 @@ impl FATDirIter { } } } - // debug!("collect dentries done. long_name_entries={long_name_entries:?}"); + // kdebug!("collect dentries done. long_name_entries={long_name_entries:?}"); let dir_entry: Result = FATDirEntry::new( long_name_entries, ( @@ -1583,16 +1582,16 @@ impl FATDirIter { (self.current_cluster, self.offset), ), ); - // debug!("dir_entry={:?}", dir_entry); + // kdebug!("dir_entry={:?}", dir_entry); match dir_entry { Ok(d) => { - // debug!("dir_entry ok"); + // kdebug!("dir_entry ok"); self.offset += FATRawDirEntry::DIR_ENTRY_LEN; return Ok((self.current_cluster, self.offset, Some(d))); } Err(_) => { - // debug!("dir_entry err, e={}", e); + // kdebug!("dir_entry err, e={}", e); self.offset += FATRawDirEntry::DIR_ENTRY_LEN; } } @@ -2412,7 +2411,7 @@ pub fn get_raw_dir_entry( // let step1 = fs.get_in_partition_bytes_offset(in_disk_bytes_offset); // let step2 = fs.bytes_to_sector(step1); // let lba = fs.get_lba_from_offset(step2); - // debug!("step1={step1}, step2={step2}, lba={lba}"); + // kdebug!("step1={step1}, step2={step2}, lba={lba}"); let mut v: Vec = vec![0; LBA_SIZE]; fs.partition.disk().read_at(lba, 1, &mut v)?; diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index 9e6fc0126..df6af8ce8 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -2,7 +2,6 @@ use alloc::string::ToString; use core::cmp::Ordering; use core::intrinsics::unlikely; use core::{any::Any, fmt::Debug}; -use log::error; use system_error::SystemError; use alloc::{ @@ -13,12 +12,9 @@ use alloc::{ }; use crate::driver::base::device::device_number::DeviceNumber; -use crate::filesystem::vfs::file::PageCache; use crate::filesystem::vfs::utils::DName; use crate::filesystem::vfs::{Magic, SpecialNodeData, SuperBlock}; use crate::ipc::pipe::LockedPipeInode; -use crate::mm::fault::{PageFaultHandler, PageFaultMessage}; -use crate::mm::VmFaultReason; use crate::{ driver::base::block::{block_device::LBA_SIZE, disk_info::Partition, SeekFrom}, filesystem::vfs::{ @@ -27,6 +23,7 @@ use crate::{ syscall::ModeType, FileSystem, FileType, IndexNode, InodeId, Metadata, }, + kerror, libs::{ spinlock::{SpinLock, SpinLockGuard}, vec_cursor::VecCursor, @@ -48,7 +45,6 @@ pub const MAX_FILE_SIZE: u64 = 0xffff_ffff; /// @brief 表示当前簇和上一个簇的关系的结构体 /// 定义这样一个结构体的原因是,FAT文件系统的文件中,前后两个簇具有关联关系。 -#[allow(dead_code)] #[derive(Debug, Clone, Copy, Default)] pub struct Cluster { pub cluster_num: u64, @@ -121,9 +117,6 @@ pub struct FATInode { /// 目录名 dname: DName, - - /// 页缓存 - page_cache: Option>, } impl FATInode { @@ -138,7 +131,7 @@ impl FATInode { self.metadata.size = d.size(&self.fs.upgrade().unwrap().clone()) as i64; } FATDirEntry::UnInit => { - error!("update_metadata: Uninitialized FATDirEntry: {:?}", self); + kerror!("update_metadata: Uninitialized FATDirEntry: {:?}", self); return; } }; @@ -221,14 +214,8 @@ impl LockedFATInode { }, special_node: None, dname, - page_cache: None, }))); - if !inode.0.lock().inode_type.is_dir() { - let page_cache = PageCache::new(Some(Arc::downgrade(&inode) as Weak)); - inode.0.lock().page_cache = Some(page_cache); - } - inode.0.lock().self_ref = Arc::downgrade(&inode); inode.0.lock().update_metadata(); @@ -283,19 +270,6 @@ impl FileSystem for FATFileSystem { FAT_MAX_NAMELEN, ) } - - unsafe fn fault(&self, pfm: &mut PageFaultMessage) -> VmFaultReason { - PageFaultHandler::filemap_fault(pfm) - } - - unsafe fn map_pages( - &self, - pfm: &mut PageFaultMessage, - start_pgoff: usize, - end_pgoff: usize, - ) -> VmFaultReason { - PageFaultHandler::filemap_map_pages(pfm, start_pgoff, end_pgoff) - } } impl FATFileSystem { @@ -335,7 +309,7 @@ impl FATFileSystem { match bpb.fat_type { FATType::FAT32(x) => x.fat_size_32 as u64, _ => { - error!("FAT12 and FAT16 volumes should have non-zero BPB_FATSz16"); + kerror!("FAT12 and FAT16 volumes should have non-zero BPB_FATSz16"); return Err(SystemError::EINVAL); } } @@ -373,7 +347,6 @@ impl FATFileSystem { }, special_node: None, dname: DName::default(), - page_cache: None, }))); let result: Arc = Arc::new(FATFileSystem { @@ -484,7 +457,7 @@ impl FATFileSystem { match entry { _n if (0x0ffffff7..=0x0fffffff).contains(¤t_cluster) => { // 当前簇号不是一个能被获得的簇(可能是文件系统出错了) - error!("FAT32 get fat entry: current cluster number [{}] is not an allocatable cluster number.", current_cluster); + kerror!("FAT32 get fat entry: current cluster number [{}] is not an allocatable cluster number.", current_cluster); FATEntry::Bad } 0 => FATEntry::Unused, @@ -640,7 +613,7 @@ impl FATFileSystem { // 如果这个空闲簇不是簇链的第一个簇,那么把当前簇跟前一个簇连上。 if let Some(prev_cluster) = prev_cluster { - // debug!("set entry, prev ={prev_cluster:?}, next = {free_cluster:?}"); + // kdebug!("set entry, prev ={prev_cluster:?}, next = {free_cluster:?}"); self.set_entry(prev_cluster, FATEntry::Next(free_cluster))?; } // 清空新获取的这个簇 @@ -669,12 +642,12 @@ impl FATFileSystem { self.set_entry(cluster, FATEntry::Unused)?; self.fs_info.0.lock().update_free_count_delta(1); // 安全选项:清空被释放的簇 - #[cfg(feature = "fatfs-secure")] + #[cfg(feature = "secure")] self.zero_cluster(cluster)?; return Ok(()); } else { // 不能释放坏簇 - error!("Bad clusters cannot be freed."); + kerror!("Bad clusters cannot be freed."); return Err(SystemError::EFAULT); } } @@ -1163,14 +1136,14 @@ impl FATFileSystem { } else { self.bpb.num_fats as u64 }; - // debug!("set entry, bound={bound}, fat_size={fat_size}"); + // kdebug!("set entry, bound={bound}, fat_size={fat_size}"); for i in 0..bound { // 当前操作的FAT表在磁盘上的字节偏移量 let f_offset: u64 = fat_part_bytes_offset + i * fat_size; let in_block_offset: u64 = self.get_in_block_offset(f_offset); let lba = self.get_lba_from_offset(self.bytes_to_sector(f_offset)); - // debug!("set entry, lba={lba}, in_block_offset={in_block_offset}"); + // kdebug!("set entry, lba={lba}, in_block_offset={in_block_offset}"); let mut v: Vec = vec![0; LBA_SIZE]; self.partition.disk().read_at(lba, 1, &mut v)?; @@ -1184,7 +1157,7 @@ impl FATFileSystem { && cluster.cluster_num >= 0x0ffffff7 && cluster.cluster_num <= 0x0fffffff { - error!( + kerror!( "FAT32: Reserved Cluster {:?} cannot be marked as free", cluster ); @@ -1202,7 +1175,7 @@ impl FATFileSystem { // 恢复保留位 raw_val |= old_bits; - // debug!("sent entry, raw_val={raw_val}"); + // kdebug!("sent entry, raw_val={raw_val}"); cursor.seek(SeekFrom::SeekSet(in_block_offset as i64))?; cursor.write_u32(raw_val)?; @@ -1233,7 +1206,7 @@ impl Drop for FATFileSystem { fn drop(&mut self) { let r = self.umount(); if r.is_err() { - error!( + kerror!( "Umount FAT filesystem failed: errno={:?}, FS detail:{self:?}", r.as_ref().unwrap_err() ); @@ -1284,7 +1257,7 @@ impl FATFsInfo { if fsinfo.is_valid() { return Ok(fsinfo); } else { - error!("Error occurred while parsing FATFsInfo."); + kerror!("Error occurred while parsing FATFsInfo."); return Err(SystemError::EINVAL); } } @@ -1422,7 +1395,7 @@ impl IndexNode for LockedFATInode { return Err(SystemError::EISDIR); } FATDirEntry::UnInit => { - error!("FATFS: param: Inode_type uninitialized."); + kerror!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } } @@ -1448,7 +1421,7 @@ impl IndexNode for LockedFATInode { return Err(SystemError::EISDIR); } FATDirEntry::UnInit => { - error!("FATFS: param: Inode_type uninitialized."); + kerror!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } } @@ -1482,7 +1455,7 @@ impl IndexNode for LockedFATInode { _ => return Err(SystemError::EINVAL), }, FATDirEntry::UnInit => { - error!("FATFS: param: Inode_type uninitialized."); + kerror!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } } @@ -1499,16 +1472,6 @@ impl IndexNode for LockedFATInode { fn metadata(&self) -> Result { return Ok(self.0.lock().metadata.clone()); } - fn set_metadata(&self, metadata: &Metadata) -> Result<(), SystemError> { - let inode = &mut self.0.lock(); - inode.metadata.atime = metadata.atime; - inode.metadata.mtime = metadata.mtime; - inode.metadata.ctime = metadata.ctime; - inode.metadata.mode = metadata.mode; - inode.metadata.uid = metadata.uid; - inode.metadata.gid = metadata.gid; - Ok(()) - } fn resize(&self, len: usize) -> Result<(), SystemError> { let mut guard: SpinLockGuard = self.0.lock(); let fs: &Arc = &guard.fs.upgrade().unwrap(); @@ -1546,7 +1509,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(_) => return Err(SystemError::ENOSYS), FATDirEntry::UnInit => { - error!("FATFS: param: Inode_type uninitialized."); + kerror!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } } @@ -1579,7 +1542,7 @@ impl IndexNode for LockedFATInode { // ====== 生成inode缓存,存入B树 let name = DName::from(ent.name().to_uppercase()); - // debug!("name={name}"); + // kdebug!("name={name}"); if !guard.children.contains_key(&name) && name.as_ref() != "." @@ -1599,7 +1562,7 @@ impl IndexNode for LockedFATInode { return Ok(ret); } FATDirEntry::UnInit => { - error!("FATFS: param: Inode_type uninitialized."); + kerror!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } } @@ -1645,7 +1608,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(d) => d, FATDirEntry::UnInit => { - error!("FATFS: param: Inode_type uninitialized."); + kerror!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } }; @@ -1672,7 +1635,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(d) => d, FATDirEntry::UnInit => { - error!("FATFS: param: Inode_type uninitialized."); + kerror!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } }; @@ -1720,7 +1683,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(d) => d, FATDirEntry::UnInit => { - error!("FATFS: param: Inode_type uninitialized."); + kerror!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } }; @@ -1749,7 +1712,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(d) => d, FATDirEntry::UnInit => { - error!("FATFS: param: Inode_type uninitialized."); + kerror!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } }; @@ -1759,7 +1722,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(d) => d, FATDirEntry::UnInit => { - error!("FATFA: param: Inode_type uninitialized."); + kerror!("FATFA: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } }; @@ -1868,10 +1831,6 @@ impl IndexNode for LockedFATInode { .map(|item| item as Arc) .ok_or(SystemError::EINVAL) } - - fn page_cache(&self) -> Option> { - self.0.lock().page_cache.clone() - } } impl Default for FATFsInfo { diff --git a/kernel/src/filesystem/kernfs/mod.rs b/kernel/src/filesystem/kernfs/mod.rs index 91765e577..ff29cf2ac 100644 --- a/kernel/src/filesystem/kernfs/mod.rs +++ b/kernel/src/filesystem/kernfs/mod.rs @@ -6,7 +6,6 @@ use alloc::{ vec::Vec, }; use hashbrown::HashMap; -use log::warn; use system_error::SystemError; use crate::{ @@ -346,7 +345,7 @@ impl IndexNode for KernFSInode { } if self.callback.is_none() { - warn!("kernfs: callback is none"); + kwarn!("kernfs: callback is none"); return Err(SystemError::ENOSYS); } @@ -591,7 +590,7 @@ impl KernFSInode { target: &Arc, target_absolute_path: String, ) -> Result, SystemError> { - // debug!("kernfs add link: name:{name}, target path={target_absolute_path}"); + // kdebug!("kernfs add link: name:{name}, target path={target_absolute_path}"); let inode = self.inner_create( name, KernInodeType::SymLink, diff --git a/kernel/src/filesystem/mbr.rs b/kernel/src/filesystem/mbr.rs index b3e900de3..eb3af9af8 100644 --- a/kernel/src/filesystem/mbr.rs +++ b/kernel/src/filesystem/mbr.rs @@ -4,7 +4,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::debug; use system_error::SystemError; use crate::{ @@ -55,7 +54,7 @@ impl MbrDiskPartitionTableEntry { #[repr(packed)] #[derive(Debug, Clone, Copy)] pub struct MbrDiskPartionTable { - pub _reserved: [u8; 446], + pub reserved: [u8; 446], pub dpte: [MbrDiskPartitionTableEntry; 4], // 磁盘分区表项 pub bs_trailsig: u16, } @@ -63,7 +62,7 @@ pub struct MbrDiskPartionTable { impl Default for MbrDiskPartionTable { fn default() -> Self { MbrDiskPartionTable { - _reserved: [0; 446], + reserved: [0; 446], dpte: [Default::default(); 4], bs_trailsig: Default::default(), } @@ -106,10 +105,10 @@ impl MbrDiskPartionTable { table.dpte[i].starting_lba = cursor.read_u32()?; table.dpte[i].total_sectors = cursor.read_u32()?; - debug!("dpte[{i}] = {:?}", table.dpte[i]); + kdebug!("dpte[{i}] = {:?}", table.dpte[i]); } table.bs_trailsig = cursor.read_u16()?; - // debug!("bs_trailsig = {}", unsafe { + // kdebug!("bs_trailsig = {}", unsafe { // read_unaligned(addr_of!(table.bs_trailsig)) // }); diff --git a/kernel/src/filesystem/mod.rs b/kernel/src/filesystem/mod.rs index 90dcc51bf..3af494b0b 100644 --- a/kernel/src/filesystem/mod.rs +++ b/kernel/src/filesystem/mod.rs @@ -1,6 +1,5 @@ pub mod devfs; pub mod devpts; -pub mod eventfd; pub mod fat; pub mod kernfs; pub mod mbr; diff --git a/kernel/src/filesystem/procfs/kmsg.rs b/kernel/src/filesystem/procfs/kmsg.rs index c2181dc80..79f78c0aa 100644 --- a/kernel/src/filesystem/procfs/kmsg.rs +++ b/kernel/src/filesystem/procfs/kmsg.rs @@ -8,7 +8,6 @@ use alloc::{borrow::ToOwned, string::ToString, vec::Vec}; use kdepends::ringbuffer::{AllocRingBuffer, RingBuffer}; -use log::info; use system_error::SystemError; /// 缓冲区容量 @@ -19,13 +18,13 @@ pub static mut KMSG: Option> = None; /// 初始化KMSG pub fn kmsg_init() { - info!("kmsg_init"); + kinfo!("kmsg_init"); let kmsg = SpinLock::new(Kmsg::new()); compiler_fence(Ordering::SeqCst); unsafe { KMSG = Some(kmsg) }; compiler_fence(Ordering::SeqCst); - info!("kmsg_init done"); + kinfo!("kmsg_init done"); } /// 日志 diff --git a/kernel/src/filesystem/procfs/mod.rs b/kernel/src/filesystem/procfs/mod.rs index 0defc3e0e..07dcd1795 100644 --- a/kernel/src/filesystem/procfs/mod.rs +++ b/kernel/src/filesystem/procfs/mod.rs @@ -1,6 +1,5 @@ use core::intrinsics::size_of; -use ::log::{error, info}; use alloc::{ borrow::ToOwned, collections::BTreeMap, @@ -18,6 +17,7 @@ use crate::{ core::{generate_inode_id, ROOT_INODE}, FileType, }, + kerror, kinfo, libs::{ once::Once, rwlock::RwLock, @@ -149,7 +149,7 @@ impl ProcFSInode { let pcb = if let Some(pcb) = pcb { pcb } else { - error!( + kerror!( "ProcFS: Cannot find pcb for pid {:?} when opening its 'status' file.", pid ); @@ -157,7 +157,7 @@ impl ProcFSInode { }; // 传入数据 let pdata: &mut Vec = &mut pdata.data; - // name + pdata.append( &mut format!("Name:\t{}", pcb.basic().name()) .as_bytes() @@ -174,32 +174,17 @@ impl ProcFSInode { let priority = sched_info_guard.policy(); let vrtime = sched_info_guard.sched_entity.vruntime; - // State pdata.append(&mut format!("\nState:\t{:?}", state).as_bytes().to_owned()); - - // Tgid - pdata.append(&mut format!("\nTgid:\t{}", pcb.tgid().into()).into()); - - // pid pdata.append( &mut format!("\nPid:\t{}", pcb.pid().into()) .as_bytes() .to_owned(), ); - - // ppid pdata.append( &mut format!("\nPpid:\t{}", pcb.basic().ppid().into()) .as_bytes() .to_owned(), ); - - // fdsize - pdata.append(&mut format!("\nFDSize:\t{}", pcb.fd_table().read().fd_open_count()).into()); - - // kthread - pdata.append(&mut format!("\nKthread:\t{}", pcb.is_kthread() as usize).into()); - pdata.append(&mut format!("\ncpu_id:\t{}", cpu_id).as_bytes().to_owned()); pdata.append(&mut format!("\npriority:\t{:?}", priority).as_bytes().to_owned()); pdata.append( @@ -207,7 +192,6 @@ impl ProcFSInode { .as_bytes() .to_owned(), ); - pdata.append(&mut format!("\nvrtime:\t{}", vrtime).as_bytes().to_owned()); if let Some(user_vm) = pcb.basic().user_vm() { @@ -837,7 +821,7 @@ pub fn procfs_init() -> Result<(), SystemError> { static INIT: Once = Once::new(); let mut result = None; INIT.call_once(|| { - info!("Initializing ProcFS..."); + kinfo!("Initializing ProcFS..."); // 创建 procfs 实例 let procfs: Arc = ProcFS::new(); // procfs 挂载 @@ -846,7 +830,7 @@ pub fn procfs_init() -> Result<(), SystemError> { .expect("Unabled to find /proc") .mount(procfs) .expect("Failed to mount at /proc"); - info!("ProcFS mounted."); + kinfo!("ProcFS mounted."); result = Some(Ok(())); }); diff --git a/kernel/src/filesystem/procfs/syscall.rs b/kernel/src/filesystem/procfs/syscall.rs index f80365cc0..2c4aa0416 100644 --- a/kernel/src/filesystem/procfs/syscall.rs +++ b/kernel/src/filesystem/procfs/syscall.rs @@ -1,3 +1,5 @@ +use core::usize; + use system_error::SystemError; use crate::syscall::Syscall; diff --git a/kernel/src/filesystem/ramfs/mod.rs b/kernel/src/filesystem/ramfs/mod.rs index 5f1ec4864..c2435ff3e 100644 --- a/kernel/src/filesystem/ramfs/mod.rs +++ b/kernel/src/filesystem/ramfs/mod.rs @@ -25,9 +25,6 @@ use super::vfs::{ file::FilePrivateData, syscall::ModeType, utils::DName, FileSystem, FileSystemMaker, FsInfo, IndexNode, InodeId, Metadata, SpecialNodeData, }; - -use linkme::distributed_slice; - use super::vfs::{Magic, SuperBlock}; /// RamFS的inode名称的最大长度 diff --git a/kernel/src/filesystem/sysfs/file.rs b/kernel/src/filesystem/sysfs/file.rs index 1d65be42b..01450a8ae 100644 --- a/kernel/src/filesystem/sysfs/file.rs +++ b/kernel/src/filesystem/sysfs/file.rs @@ -4,7 +4,6 @@ use alloc::{ string::ToString, sync::{Arc, Weak}, }; -use log::warn; use system_error::SystemError; use crate::{ @@ -17,6 +16,7 @@ use crate::{ sysfs::{SysFSOps, SysFSOpsSupport}, vfs::{syscall::ModeType, PollStatus}, }, + kwarn, }; use super::{Attribute, BinAttribute, SysFS, SysFSKernPrivateData}; @@ -130,7 +130,7 @@ impl SysFS { drop(x); let sysfs_ops: &dyn SysFSOps = kobj.kobj_type().unwrap().sysfs_ops().ok_or_else(|| { - warn!("missing sysfs attribute operations for kobject: {kobj:?}"); + kwarn!("missing sysfs attribute operations for kobject: {kobj:?}"); SystemError::EINVAL })?; @@ -184,7 +184,7 @@ impl SysFS { if let Some(parent) = parent { let r = parent.remove(attr.name()); if unlikely(r.is_err()) { - warn!( + kwarn!( "failed to remove file '{}' from '{}'", attr.name(), kobj.name() @@ -220,7 +220,7 @@ impl SysFS { if let Some(parent) = parent { let r = parent.remove(attr.name()); if unlikely(r.is_err()) { - warn!( + kwarn!( "failed to remove file '{}' from '{}'", attr.name(), kobj.name() diff --git a/kernel/src/filesystem/sysfs/group.rs b/kernel/src/filesystem/sysfs/group.rs index f1dff750c..fb45ccb9f 100644 --- a/kernel/src/filesystem/sysfs/group.rs +++ b/kernel/src/filesystem/sysfs/group.rs @@ -1,7 +1,6 @@ use core::intrinsics::unlikely; use alloc::{string::ToString, sync::Arc}; -use log::{error, warn}; use system_error::SystemError; use crate::{ @@ -11,6 +10,7 @@ use crate::{ sysfs::{dir::SysKernDirPriv, sysfs_instance, SysFSKernPrivateData}, vfs::{syscall::ModeType, IndexNode}, }, + kwarn, libs::casting::DowncastArc, }; @@ -38,7 +38,7 @@ impl SysFS { continue; } if let Err(e) = self.do_create_group(kobj, group, update) { - error!( + kerror!( "Failed to create group '{}', err={e:?}", group.name().unwrap_or("") ); @@ -136,8 +136,9 @@ impl SysFS { if let Some(name) = group.name() { parent_inode = inode .find(name) - .inspect_err(|_e| { - warn!("sysfs group '{name}' not found for kobject {kobj:?}"); + .map_err(|e| { + kwarn!("sysfs group '{name}' not found for kobject {kobj:?}"); + e })? .downcast_arc() .unwrap(); @@ -188,7 +189,7 @@ impl SysFS { } if unlikely((mode.bits() & (!0o644)) != 0) { - warn!( + kwarn!( "Attribute '{name}' has invalid mode 0{mode:o}", name = attr.name(), mode = mode @@ -203,7 +204,7 @@ impl SysFS { } if let Err(e) = e { - error!( + kerror!( "Failed to create sysfs files for group '{}', err={e:?}", group.name().unwrap_or("") ); diff --git a/kernel/src/filesystem/sysfs/mod.rs b/kernel/src/filesystem/sysfs/mod.rs index 97579f007..2c2dce136 100644 --- a/kernel/src/filesystem/sysfs/mod.rs +++ b/kernel/src/filesystem/sysfs/mod.rs @@ -9,10 +9,10 @@ use super::{ use crate::{ driver::base::kobject::KObject, filesystem::vfs::ROOT_INODE, + kinfo, kwarn, libs::{casting::DowncastArc, once::Once}, }; use alloc::sync::Arc; -use log::{info, warn}; use system_error::SystemError; pub mod dir; @@ -34,7 +34,7 @@ pub fn sysfs_init() -> Result<(), SystemError> { static INIT: Once = Once::new(); let mut result = None; INIT.call_once(|| { - info!("Initializing SysFS..."); + kinfo!("Initializing SysFS..."); // 创建 sysfs 实例 // let sysfs: Arc = OldSysFS::new(); @@ -47,9 +47,9 @@ pub fn sysfs_init() -> Result<(), SystemError> { .expect("Unabled to find /sys") .mount(sysfs_instance().fs().clone()) .expect("Failed to mount at /sys"); - info!("SysFS mounted."); + kinfo!("SysFS mounted."); - // debug!("sys_bus_init result: {:?}", SYS_BUS_INODE().list()); + // kdebug!("sys_bus_init result: {:?}", SYS_BUS_INODE().list()); result = Some(Ok(())); }); @@ -227,6 +227,6 @@ impl SysFS { /// 警告:重复的sysfs entry pub(self) fn warn_duplicate(&self, parent: &Arc, name: &str) { let path = self.kernfs_path(parent); - warn!("duplicate sysfs entry: {path}/{name}"); + kwarn!("duplicate sysfs entry: {path}/{name}"); } } diff --git a/kernel/src/filesystem/sysfs/symlink.rs b/kernel/src/filesystem/sysfs/symlink.rs index 66e4b33e7..f9fb27124 100644 --- a/kernel/src/filesystem/sysfs/symlink.rs +++ b/kernel/src/filesystem/sysfs/symlink.rs @@ -72,7 +72,7 @@ impl SysFS { let target_abs_path = "/sys".to_string() + &self.kernfs_path(&target_inode).to_owned(); // let current_path = self.kernfs_path(inode); - // debug!("sysfs: create link {} to {}", current_path, target_abs_path); + // kdebug!("sysfs: create link {} to {}", current_path, target_abs_path); let kn = inode.add_link(name.clone(), &target_inode, target_abs_path); if kn.is_ok() { diff --git a/kernel/src/filesystem/vfs/core.rs b/kernel/src/filesystem/vfs/core.rs index 5a1d55176..bc2d0ebba 100644 --- a/kernel/src/filesystem/vfs/core.rs +++ b/kernel/src/filesystem/vfs/core.rs @@ -1,7 +1,6 @@ use core::{hint::spin_loop, sync::atomic::Ordering}; use alloc::sync::Arc; -use log::{error, info}; use system_error::SystemError; use crate::{ @@ -14,6 +13,7 @@ use crate::{ sysfs::sysfs_init, vfs::{mount::MountFS, syscall::ModeType, AtomicInodeId, FileSystem, FileType}, }, + kerror, kinfo, process::ProcessManager, }; @@ -67,7 +67,7 @@ pub fn vfs_init() -> Result<(), SystemError> { let root_entries = ROOT_INODE().list().expect("VFS init failed"); if !root_entries.is_empty() { - info!("Successfully initialized VFS!"); + kinfo!("Successfully initialized VFS!"); } return Ok(()); } @@ -75,7 +75,7 @@ pub fn vfs_init() -> Result<(), SystemError> { /// @brief 迁移伪文件系统的inode /// 请注意,为了避免删掉了伪文件系统内的信息,因此没有在原root inode那里调用unlink. fn migrate_virtual_filesystem(new_fs: Arc) -> Result<(), SystemError> { - info!("VFS: Migrating filesystems..."); + kinfo!("VFS: Migrating filesystems..."); let new_fs = MountFS::new(new_fs, None); // 获取新的根文件系统的根节点的引用 @@ -108,7 +108,7 @@ fn migrate_virtual_filesystem(new_fs: Arc) -> Result<(), SystemE drop(old_root_inode); } - info!("VFS: Migrate filesystems done!"); + kinfo!("VFS: Migrate filesystems done!"); return Ok(()); } @@ -131,7 +131,7 @@ fn root_partition() -> Arc { let virtio0 = crate::driver::block::virtio_blk::virtio_blk_0(); if virtio0.is_none() { - error!("Failed to get virtio_blk_0"); + kerror!("Failed to get virtio_blk_0"); loop { spin_loop(); } @@ -142,12 +142,12 @@ fn root_partition() -> Arc { } } pub fn mount_root_fs() -> Result<(), SystemError> { - info!("Try to mount FAT32 as root fs..."); + kinfo!("Try to mount FAT32 as root fs..."); let partiton: Arc = root_partition(); let fatfs: Result, SystemError> = FATFileSystem::new(partiton); if fatfs.is_err() { - error!( + kerror!( "Failed to initialize fatfs, code={:?}", fatfs.as_ref().err() ); @@ -158,12 +158,12 @@ pub fn mount_root_fs() -> Result<(), SystemError> { let fatfs: Arc = fatfs.unwrap(); let r = migrate_virtual_filesystem(fatfs); if r.is_err() { - error!("Failed to migrate virtual filesystem to FAT32!"); + kerror!("Failed to migrate virtual filesystem to FAT32!"); loop { spin_loop(); } } - info!("Successfully migrate rootfs to FAT32!"); + kinfo!("Successfully migrate rootfs to FAT32!"); return Ok(()); } @@ -174,14 +174,14 @@ pub fn do_mkdir_at( path: &str, mode: FileMode, ) -> Result, SystemError> { - // debug!("Call do mkdir at"); + // kdebug!("Call do mkdir at"); let (mut current_inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path.trim())?; let (name, parent) = rsplit_path(&path); if let Some(parent) = parent { current_inode = current_inode.lookup(parent)?; } - // debug!("mkdir at {:?}", current_inode.metadata()?.inode_id); + // kdebug!("mkdir at {:?}", current_inode.metadata()?.inode_id); return current_inode.mkdir(name, ModeType::from_bits_truncate(mode.bits())); } @@ -239,7 +239,7 @@ pub fn do_unlink_at(dirfd: i32, path: &str) -> Result { return Err(SystemError::EPERM); } - let (filename, parent_path) = rsplit_path(&remain_path); + let (filename, parent_path) = rsplit_path(path); // 查找父目录 let parent_inode: Arc = inode_begin .lookup_follow_symlink(parent_path.unwrap_or("/"), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 69994b73f..c867d4ca7 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -5,29 +5,26 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::error; use system_error::SystemError; -use xarray::XArray; -use super::{Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; -use crate::filesystem::eventfd::EventFdInode; use crate::{ - arch::MMArch, driver::{ base::{block::SeekFrom, device::DevicePrivateData}, tty::tty_device::TtyFilePrivateData, }, filesystem::procfs::ProcfsFilePrivateData, ipc::pipe::{LockedPipeInode, PipeFsPrivateData}, + kerror, libs::{rwlock::RwLock, spinlock::SpinLock}, - mm::{page::Page, MemoryManagementArch}, net::{ event_poll::{EPollItem, EPollPrivateData, EventPoll}, socket::SocketInode, }, - process::{cred::Cred, ProcessManager}, + process::ProcessManager, }; +use super::{Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; + /// 文件私有信息的枚举类型 #[derive(Debug, Clone)] #[allow(dead_code)] @@ -121,66 +118,6 @@ impl FileMode { return self.bits() & FileMode::O_ACCMODE.bits(); } } - -/// 页面缓存 -pub struct PageCache { - xarray: SpinLock>>, - inode: Option>, -} - -impl core::fmt::Debug for PageCache { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - f.debug_struct("PageCache") - .field( - "xarray", - &self - .xarray - .lock() - .range(0..((MMArch::PAGE_ADDRESS_SIZE >> MMArch::PAGE_SHIFT) as u64)) - .map(|(_, r)| (*r).clone()) - .collect::>>(), - ) - .finish() - } -} - -impl PageCache { - pub fn new(inode: Option>) -> Arc { - let page_cache = Self { - xarray: SpinLock::new(XArray::new()), - inode, - }; - Arc::new(page_cache) - } - - pub fn inode(&self) -> Option> { - self.inode.clone() - } - - pub fn add_page(&self, offset: usize, page: &Arc) { - let mut guard = self.xarray.lock(); - let mut cursor = guard.cursor_mut(offset as u64); - cursor.store(page.clone()); - } - - pub fn get_page(&self, offset: usize) -> Option> { - let mut guard = self.xarray.lock(); - let mut cursor = guard.cursor_mut(offset as u64); - let page = cursor.load().map(|r| (*r).clone()); - page - } - - pub fn remove_page(&self, offset: usize) { - let mut guard = self.xarray.lock(); - let mut cursor = guard.cursor_mut(offset as u64); - cursor.remove(); - } - - pub fn set_inode(&mut self, inode: Weak) { - self.inode = Some(inode) - } -} - /// @brief 抽象文件结构体 #[derive(Debug)] pub struct File { @@ -194,8 +131,6 @@ pub struct File { /// readdir时候用的,暂存的本次循环中,所有子目录项的名字的数组 readdir_subdirs_name: SpinLock>, pub private_data: SpinLock, - /// 文件的凭证 - cred: Cred, } impl File { @@ -219,7 +154,6 @@ impl File { file_type, readdir_subdirs_name: SpinLock::new(Vec::default()), private_data: SpinLock::new(FilePrivateData::default()), - cred: ProcessManager::current_pcb().cred(), }; f.inode.open(f.private_data.lock(), &mode)?; @@ -299,14 +233,7 @@ impl File { let len = self .inode - .read_at(offset, len, buf, self.private_data.lock()) - .map_err(|e| { - if e == SystemError::ERESTARTSYS { - SystemError::EINTR - } else { - e - } - })?; + .read_at(offset, len, buf, self.private_data.lock())?; if update_offset { self.offset @@ -331,24 +258,11 @@ impl File { // 如果文件指针已经超过了文件大小,则需要扩展文件大小 if offset > self.inode.metadata()?.size as usize { - self.inode.resize(offset).map_err(|e| { - if e == SystemError::ERESTARTSYS { - SystemError::EINTR - } else { - e - } - })?; + self.inode.resize(offset)?; } let len = self .inode - .write_at(offset, len, buf, self.private_data.lock()) - .map_err(|e| { - if e == SystemError::ERESTARTSYS { - SystemError::EINTR - } else { - e - } - })?; + .write_at(offset, len, buf, self.private_data.lock())?; if update_offset { self.offset @@ -435,7 +349,7 @@ impl File { *readdir_subdirs_name = inode.list()?; readdir_subdirs_name.sort(); } - // debug!("sub_entries={sub_entries:?}"); + // kdebug!("sub_entries={sub_entries:?}"); // 已经读到末尾 if offset == readdir_subdirs_name.len() { @@ -446,7 +360,7 @@ impl File { let sub_inode: Arc = match inode.find(name) { Ok(i) => i, Err(e) => { - error!( + kerror!( "Readdir error: Failed to find sub inode:{name:?}, file={self:?}, error={e:?}" ); return Err(e); @@ -494,7 +408,6 @@ impl File { file_type: self.file_type, readdir_subdirs_name: SpinLock::new(self.readdir_subdirs_name.lock().clone()), private_data: SpinLock::new(self.private_data.lock().clone()), - cred: self.cred.clone(), }; // 调用inode的open方法,让inode知道有新的文件打开了这个inode if self @@ -579,7 +492,11 @@ impl File { return inode.inner().lock().add_epoll(epitem); } _ => { - let r = self.inode.kernel_ioctl(epitem, &self.private_data.lock()); + let r = self.inode.ioctl( + EventPoll::ADD_EPOLLITEM, + &epitem as *const Arc as usize, + &self.private_data.lock(), + ); if r.is_err() { return Err(SystemError::ENOSYS); } @@ -596,19 +513,9 @@ impl File { let inode = self.inode.downcast_ref::().unwrap(); let mut socket = inode.inner(); - socket.remove_epoll(epoll) - } - FileType::Pipe => { - let inode = self.inode.downcast_ref::().unwrap(); - inode.inner().lock().remove_epoll(epoll) - } - _ => { - let inode = self - .inode - .downcast_ref::() - .ok_or(SystemError::ENOSYS)?; - inode.remove_epoll(epoll) + return socket.remove_epoll(epoll); } + _ => return Err(SystemError::ENOSYS), } } @@ -622,7 +529,7 @@ impl Drop for File { let r: Result<(), SystemError> = self.inode.close(self.private_data.lock()); // 打印错误信息 if r.is_err() { - error!( + kerror!( "pid: {:?} failed to close file: {:?}, errno={:?}", ProcessManager::current_pcb().pid(), self, @@ -638,11 +545,7 @@ pub struct FileDescriptorVec { /// 当前进程打开的文件描述符 fds: Vec>>, } -impl Default for FileDescriptorVec { - fn default() -> Self { - Self::new() - } -} + impl FileDescriptorVec { pub const PROCESS_MAX_FD: usize = 1024; @@ -670,17 +573,6 @@ impl FileDescriptorVec { return res; } - /// 返回 `已经打开的` 文件描述符的数量 - pub fn fd_open_count(&self) -> usize { - let mut size = 0; - for fd in &self.fds { - if fd.is_some() { - size += 1; - } - } - return size; - } - /// @brief 判断文件描述符序号是否合法 /// /// @return true 合法 @@ -740,13 +632,14 @@ impl FileDescriptorVec { /// ## 参数 /// /// - `fd` 文件描述符序号 - pub fn drop_fd(&mut self, fd: i32) -> Result, SystemError> { + pub fn drop_fd(&mut self, fd: i32) -> Result<(), SystemError> { self.get_file_by_fd(fd).ok_or(SystemError::EBADF)?; // 把文件描述符数组对应位置设置为空 let file = self.fds[fd as usize].take().unwrap(); - return Ok(file); + assert!(Arc::strong_count(&file) == 1); + return Ok(()); } #[allow(dead_code)] @@ -760,7 +653,7 @@ impl FileDescriptorVec { let to_drop = file.close_on_exec(); if to_drop { if let Err(r) = self.drop_fd(i as i32) { - error!( + kerror!( "Failed to close file: pid = {:?}, fd = {}, error = {:?}", ProcessManager::current_pcb().pid(), i, diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 00ce6ba50..482d4a884 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -20,16 +20,10 @@ use crate::{ casting::DowncastArc, spinlock::{SpinLock, SpinLockGuard}, }, - mm::{fault::PageFaultMessage, VmFaultReason}, time::PosixTimeSpec, }; -use self::{ - core::generate_inode_id, - file::{FileMode, PageCache}, - syscall::ModeType, - utils::DName, -}; +use self::{core::generate_inode_id, file::FileMode, syscall::ModeType, utils::DName}; pub use self::{core::ROOT_INODE, file::FilePrivateData, mount::MountFS}; /// vfs容许的最大的路径名称长度 @@ -356,14 +350,6 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { return Err(SystemError::ENOSYS); } - fn kernel_ioctl( - &self, - _arg: Arc, - _data: &FilePrivateData, - ) -> Result { - return Err(SystemError::ENOSYS); - } - /// @brief 获取inode所在的文件系统的指针 fn fs(&self) -> Arc; @@ -562,14 +548,6 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { fn parent(&self) -> Result, SystemError> { return self.find(".."); } - - fn page_cache(&self) -> Option> { - log::error!( - "function page_cache() has not yet been implemented for inode:{}", - crate::libs::name::get_type_name(&self) - ); - None - } } impl DowncastArc for dyn IndexNode { @@ -819,25 +797,6 @@ pub trait FileSystem: Any + Sync + Send + Debug { fn name(&self) -> &str; fn super_block(&self) -> SuperBlock; - - unsafe fn fault(&self, _pfm: &mut PageFaultMessage) -> VmFaultReason { - panic!( - "fault() has not yet been implemented for filesystem: {}", - crate::libs::name::get_type_name(&self) - ) - } - - unsafe fn map_pages( - &self, - _pfm: &mut PageFaultMessage, - _start_pgoff: usize, - _end_pgoff: usize, - ) -> VmFaultReason { - panic!( - "map_pages() has not yet been implemented for filesystem: {}", - crate::libs::name::get_type_name(&self) - ) - } } impl DowncastArc for dyn FileSystem { @@ -923,7 +882,7 @@ macro_rules! producefs { match $initializer_slice.iter().find(|&m| m.name == $filesystem) { Some(maker) => maker.call(), None => { - log::error!("mismatch filesystem type : {}", $filesystem); + kerror!("mismatch filesystem type : {}", $filesystem); Err(SystemError::EINVAL) } } diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index cc479883b..7d7515833 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -19,14 +19,11 @@ use crate::{ rwlock::RwLock, spinlock::{SpinLock, SpinLockGuard}, }, - mm::{fault::PageFaultMessage, VmFaultReason}, }; use super::{ - file::{FileMode, PageCache}, - syscall::ModeType, - utils::DName, - FilePrivateData, FileSystem, FileType, IndexNode, InodeId, Magic, SuperBlock, + file::FileMode, syscall::ModeType, utils::DName, FilePrivateData, FileSystem, FileType, + IndexNode, InodeId, Magic, SuperBlock, }; const MOUNTFS_BLOCK_SIZE: u64 = 512; @@ -163,6 +160,25 @@ impl MountFSInode { } } + /// 将新的挂载点-挂载文件系统添加到父级的挂载树 + pub(super) fn do_mount( + &self, + inode_id: InodeId, + new_mount_fs: Arc, + ) -> Result<(), SystemError> { + let mut guard = self.mount_fs.mountpoints.lock(); + if guard.contains_key(&inode_id) { + return Err(SystemError::EBUSY); + } + guard.insert(inode_id, new_mount_fs); + + return Ok(()); + } + + pub(super) fn inode_id(&self) -> InodeId { + self.metadata().map(|x| x.inode_id).unwrap() + } + fn do_find(&self, name: &str) -> Result, SystemError> { // 直接调用当前inode所在的文件系统的find方法进行查找 // 由于向下查找可能会跨越文件系统的边界,因此需要尝试替换inode @@ -440,7 +456,7 @@ impl IndexNode for MountFSInode { if self.is_mountpoint_root()? { return Err(SystemError::EBUSY); } - // debug!("from {:?}, to {:?}", from, self); + // kdebug!("from {:?}, to {:?}", from, self); let new_mount_fs = from.umount()?; self.mount_fs .mountpoints @@ -504,10 +520,6 @@ impl IndexNode for MountFSInode { fn parent(&self) -> Result, SystemError> { return self.do_parent().map(|inode| inode as Arc); } - - fn page_cache(&self) -> Option> { - self.inner_inode.page_cache() - } } impl FileSystem for MountFS { @@ -535,19 +547,6 @@ impl FileSystem for MountFS { fn super_block(&self) -> SuperBlock { SuperBlock::new(Magic::MOUNT_MAGIC, MOUNTFS_BLOCK_SIZE, MOUNTFS_MAX_NAMELEN) } - - unsafe fn fault(&self, pfm: &mut PageFaultMessage) -> VmFaultReason { - self.inner_filesystem.fault(pfm) - } - - unsafe fn map_pages( - &self, - pfm: &mut PageFaultMessage, - start_pgoff: usize, - end_pgoff: usize, - ) -> VmFaultReason { - self.inner_filesystem.map_pages(pfm, start_pgoff, end_pgoff) - } } /// MountList diff --git a/kernel/src/filesystem/vfs/open.rs b/kernel/src/filesystem/vfs/open.rs index 14f31fa10..3e2e5660c 100644 --- a/kernel/src/filesystem/vfs/open.rs +++ b/kernel/src/filesystem/vfs/open.rs @@ -1,7 +1,11 @@ use alloc::sync::Arc; -use log::warn; use system_error::SystemError; +use crate::{ + driver::base::block::SeekFrom, process::ProcessManager, + syscall::user_access::check_and_clone_cstr, +}; + use super::{ fcntl::AtFlags, file::{File, FileMode}, @@ -9,13 +13,6 @@ use super::{ utils::{rsplit_path, user_path_at}, FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; -use crate::filesystem::vfs::syscall::UtimensFlags; -use crate::time::{syscall::PosixTimeval, PosixTimeSpec}; -use crate::{ - driver::base::block::SeekFrom, process::ProcessManager, - syscall::user_access::check_and_clone_cstr, -}; -use alloc::string::String; pub(super) fn do_faccessat( dirfd: i32, @@ -38,9 +35,8 @@ pub(super) fn do_faccessat( // let follow_symlink = flags & AtFlags::AT_SYMLINK_NOFOLLOW.bits() as u32 == 0; let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; - let path = path.to_str().map_err(|_| SystemError::EINVAL)?; - let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; + let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, &path)?; // 如果找不到文件,则返回错误码ENOENT let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; @@ -51,14 +47,13 @@ pub(super) fn do_faccessat( pub fn do_fchmodat(dirfd: i32, path: *const u8, _mode: ModeType) -> Result { let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; - let path = path.to_str().map_err(|_| SystemError::EINVAL)?; - let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; + let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, &path)?; // 如果找不到文件,则返回错误码ENOENT let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; - warn!("do_fchmodat: not implemented yet\n"); + kwarn!("do_fchmodat: not implemented yet\n"); // todo: 真正去改变文件的权限 return Ok(0); @@ -81,7 +76,7 @@ fn do_sys_openat2( how: OpenHow, follow_symlink: bool, ) -> Result { - // debug!("open path: {}, how: {:?}", path, how); + // kdebug!("open path: {}, how: {:?}", path, how); let path = path.trim(); let (inode_begin, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; @@ -151,84 +146,3 @@ fn do_sys_openat2( return r; } - -/// On Linux, futimens() is a library function implemented on top of -/// the utimensat() system call. To support this, the Linux -/// utimensat() system call implements a nonstandard feature: if -/// pathname is NULL, then the call modifies the timestamps of the -/// file referred to by the file descriptor dirfd (which may refer to -/// any type of file). -pub fn do_utimensat( - dirfd: i32, - pathname: Option, - times: Option<[PosixTimeSpec; 2]>, - flags: UtimensFlags, -) -> Result { - const UTIME_NOW: i64 = (1i64 << 30) - 1i64; - const UTIME_OMIT: i64 = (1i64 << 30) - 2i64; - // log::debug!("do_utimensat: dirfd:{}, pathname:{:?}, times:{:?}, flags:{:?}", dirfd, pathname, times, flags); - let inode = match pathname { - Some(path) => { - let (inode_begin, path) = - user_path_at(&ProcessManager::current_pcb(), dirfd, path.as_str())?; - let inode = if flags.contains(UtimensFlags::AT_SYMLINK_NOFOLLOW) { - inode_begin.lookup(path.as_str())? - } else { - inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)? - }; - inode - } - None => { - let binding = ProcessManager::current_pcb().fd_table(); - let fd_table_guard = binding.write(); - let file = fd_table_guard - .get_file_by_fd(dirfd) - .ok_or(SystemError::EBADF)?; - file.inode() - } - }; - let now = PosixTimeSpec::now(); - let mut meta = inode.metadata()?; - - if let Some([atime, mtime]) = times { - if atime.tv_nsec == UTIME_NOW { - meta.atime = now; - } else if atime.tv_nsec != UTIME_OMIT { - meta.atime = atime; - } - if mtime.tv_nsec == UTIME_NOW { - meta.mtime = now; - } else if mtime.tv_nsec != UTIME_OMIT { - meta.mtime = mtime; - } - inode.set_metadata(&meta).unwrap(); - } else { - meta.atime = now; - meta.mtime = now; - inode.set_metadata(&meta).unwrap(); - } - return Ok(0); -} - -pub fn do_utimes(path: &str, times: Option<[PosixTimeval; 2]>) -> Result { - // log::debug!("do_utimes: path:{:?}, times:{:?}", path, times); - let (inode_begin, path) = user_path_at( - &ProcessManager::current_pcb(), - AtFlags::AT_FDCWD.bits(), - path, - )?; - let inode = inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; - let mut meta = inode.metadata()?; - - if let Some([atime, mtime]) = times { - meta.atime = PosixTimeSpec::from(atime); - meta.mtime = PosixTimeSpec::from(mtime); - inode.set_metadata(&meta)?; - } else { - let now = PosixTimeSpec::now(); - meta.atime = now; - meta.mtime = now; - inode.set_metadata(&meta)?; - } - return Ok(0); -} diff --git a/kernel/src/filesystem/vfs/syscall.rs b/kernel/src/filesystem/vfs/syscall.rs index 53e674f47..7449f5acb 100644 --- a/kernel/src/filesystem/vfs/syscall.rs +++ b/kernel/src/filesystem/vfs/syscall.rs @@ -1,15 +1,15 @@ use core::ffi::c_void; use core::mem::size_of; +use alloc::string::ToString; use alloc::{string::String, sync::Arc, vec::Vec}; -use log::warn; use system_error::SystemError; use crate::producefs; -use crate::syscall::user_access::UserBufferReader; use crate::{ driver::base::{block::SeekFrom, device::device_number::DeviceNumber}, filesystem::vfs::{core as Vcore, file::FileDescriptorVec}, + kerror, libs::rwlock::RwLockWriteGuard, mm::{verify_area, VirtAddr}, process::ProcessManager, @@ -17,18 +17,19 @@ use crate::{ user_access::{self, check_and_clone_cstr, UserBufferWriter}, Syscall, }, - time::{syscall::PosixTimeval, PosixTimeSpec}, + time::PosixTimeSpec, }; use super::{ core::{do_mkdir_at, do_remove_dir, do_unlink_at}, fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC}, file::{File, FileMode}, - open::{do_faccessat, do_fchmodat, do_sys_open, do_utimensat, do_utimes}, + open::{do_faccessat, do_fchmodat, do_sys_open}, utils::{rsplit_path, user_path_at}, Dirent, FileType, IndexNode, SuperBlock, FSMAKER, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; +// use crate::kdebug; pub const SEEK_SET: u32 = 0; pub const SEEK_CUR: u32 = 1; @@ -323,13 +324,6 @@ bitflags! { } } -bitflags! { - pub struct UtimensFlags: u32 { - /// 不需要解释符号链接 - const AT_SYMLINK_NOFOLLOW = 0x100; - } -} - #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct PosixStatfs { @@ -397,7 +391,6 @@ impl PosixOpenHow { } } -#[allow(dead_code)] #[derive(Debug, Clone, Copy)] pub struct OpenHow { pub o_flags: FileMode, @@ -483,10 +476,7 @@ impl Syscall { mode: u32, follow_symlink: bool, ) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; - + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; let open_flags: FileMode = FileMode::from_bits(o_flags).ok_or(SystemError::EINVAL)?; let mode = ModeType::from_bits(mode).ok_or(SystemError::EINVAL)?; return do_sys_open( @@ -505,10 +495,7 @@ impl Syscall { mode: u32, follow_symlink: bool, ) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; - + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; let open_flags: FileMode = FileMode::from_bits(o_flags).ok_or(SystemError::EINVAL)?; let mode = ModeType::from_bits(mode).ok_or(SystemError::EINVAL)?; return do_sys_open(dirfd, &path, open_flags, mode, follow_symlink); @@ -522,9 +509,8 @@ impl Syscall { pub fn close(fd: usize) -> Result { let binding = ProcessManager::current_pcb().fd_table(); let mut fd_table_guard = binding.write(); - let _file = fd_table_guard.drop_fd(fd as i32)?; - drop(fd_table_guard); - Ok(0) + + fd_table_guard.drop_fd(fd as i32).map(|_| 0) } /// @brief 发送命令到文件描述符对应的设备, @@ -688,10 +674,7 @@ impl Syscall { return Err(SystemError::EFAULT); } - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; - + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; let proc = ProcessManager::current_pcb(); // Copy path to kernel space to avoid some security issues let mut new_path = String::from(""); @@ -795,10 +778,7 @@ impl Syscall { /// /// @return uint64_t 负数错误码 / 0表示成功 pub fn mkdir(path: *const u8, mode: usize) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; - + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; do_mkdir_at( AtFlags::AT_FDCWD.bits(), &path, @@ -873,10 +853,7 @@ impl Syscall { pub fn link(old: *const u8, new: *const u8) -> Result { let get_path = |cstr: *const u8| -> Result { - let res = check_and_clone_cstr(cstr, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; - + let res = check_and_clone_cstr(cstr, Some(MAX_PATHLEN))?; if res.len() >= MAX_PATHLEN { return Err(SystemError::ENAMETOOLONG); } @@ -903,12 +880,8 @@ impl Syscall { new: *const u8, flags: i32, ) -> Result { - let old = check_and_clone_cstr(old, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; - let new = check_and_clone_cstr(new, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let old = check_and_clone_cstr(old, Some(MAX_PATHLEN))?; + let new = check_and_clone_cstr(new, Some(MAX_PATHLEN))?; if old.len() >= MAX_PATHLEN || new.len() >= MAX_PATHLEN { return Err(SystemError::ENAMETOOLONG); } @@ -932,12 +905,10 @@ impl Syscall { pub fn unlinkat(dirfd: i32, path: *const u8, flags: u32) -> Result { let flags = AtFlags::from_bits(flags as i32).ok_or(SystemError::EINVAL)?; - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; if flags.contains(AtFlags::AT_REMOVEDIR) { - // debug!("rmdir"); + // kdebug!("rmdir"); match do_remove_dir(dirfd, &path) { Err(err) => { return Err(err); @@ -959,16 +930,12 @@ impl Syscall { } pub fn rmdir(path: *const u8) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; return do_remove_dir(AtFlags::AT_FDCWD.bits(), &path).map(|v| v as usize); } pub fn unlink(path: *const u8) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; return do_unlink_at(AtFlags::AT_FDCWD.bits(), &path).map(|v| v as usize); } @@ -995,14 +962,8 @@ impl Syscall { filename_to: *const u8, _flags: u32, ) -> Result { - let filename_from = check_and_clone_cstr(filename_from, Some(MAX_PATHLEN)) - .unwrap() - .into_string() - .map_err(|_| SystemError::EINVAL)?; - let filename_to = check_and_clone_cstr(filename_to, Some(MAX_PATHLEN)) - .unwrap() - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let filename_from = check_and_clone_cstr(filename_from, Some(MAX_PATHLEN)).unwrap(); + let filename_to = check_and_clone_cstr(filename_to, Some(MAX_PATHLEN)).unwrap(); // 文件名过长 if filename_from.len() > MAX_PATHLEN || filename_to.len() > MAX_PATHLEN { return Err(SystemError::ENAMETOOLONG); @@ -1131,7 +1092,7 @@ impl Syscall { /// - `cmd`:命令 /// - `arg`:参数 pub fn fcntl(fd: i32, cmd: FcntlCommand, arg: i32) -> Result { - // debug!("fcntl ({cmd:?}) fd: {fd}, arg={arg}"); + // kdebug!("fcntl ({cmd:?}) fd: {fd}, arg={arg}"); match cmd { FcntlCommand::DupFd | FcntlCommand::DupFdCloexec => { if arg < 0 || arg as usize >= FileDescriptorVec::PROCESS_MAX_FD { @@ -1225,7 +1186,7 @@ impl Syscall { // TODO: unimplemented // 未实现的命令,返回0,不报错。 - warn!("fcntl: unimplemented command: {:?}, defaults to 0.", cmd); + kwarn!("fcntl: unimplemented command: {:?}, defaults to 0.", cmd); return Err(SystemError::ENOSYS); } } @@ -1346,10 +1307,7 @@ impl Syscall { ModeType::empty().bits(), true, )?; - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN)) - .unwrap() - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN)).unwrap(); let pcb = ProcessManager::current_pcb(); let (_inode_begin, remain_path) = user_path_at(&pcb, fd as i32, &path)?; let inode = ROOT_INODE().lookup_follow_symlink(&remain_path, MAX_PATHLEN)?; @@ -1484,9 +1442,7 @@ impl Syscall { mode: ModeType, dev_t: DeviceNumber, ) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; let path = path.as_str().trim(); let inode: Result, SystemError> = @@ -1535,9 +1491,7 @@ impl Syscall { user_buf: *mut u8, buf_size: usize, ) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; let path = path.as_str().trim(); let mut user_buf = UserBufferWriter::new(user_buf, buf_size, true)?; @@ -1614,7 +1568,7 @@ impl Syscall { // fchmod没完全实现,因此不修改文件的权限 // todo: 实现fchmod - warn!("fchmod not fully implemented"); + kwarn!("fchmod not fully implemented"); return Ok(0); } /// #挂载文件系统 @@ -1639,16 +1593,13 @@ impl Syscall { _mountflags: usize, _data: *const c_void, ) -> Result { - let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))?; - let fstype_str = user_access::check_and_clone_cstr(filesystemtype, Some(MAX_PATHLEN))?; - let fstype_str = fstype_str.to_str().map_err(|_| SystemError::EINVAL)?; + let filesystemtype = user_access::check_and_clone_cstr(filesystemtype, Some(MAX_PATHLEN))?; - let fstype = producefs!(FSMAKER, fstype_str)?; + let filesystemtype = producefs!(FSMAKER, filesystemtype)?; - Vcore::do_mount(fstype, &target)?; + Vcore::do_mount(filesystemtype, target.to_string().as_str())?; return Ok(0); } @@ -1662,9 +1613,7 @@ impl Syscall { /// /// [umount(2) — Linux manual page](https://www.man7.org/linux/man-pages/man2/umount.2.html) pub fn umount2(target: *const u8, flags: i32) -> Result<(), SystemError> { - let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))?; Vcore::do_umount2( AtFlags::AT_FDCWD.bits(), &target, @@ -1672,48 +1621,6 @@ impl Syscall { )?; return Ok(()); } - - pub fn sys_utimensat( - dirfd: i32, - pathname: *const u8, - times: *const PosixTimeSpec, - flags: u32, - ) -> Result { - let pathname = if pathname.is_null() { - None - } else { - let pathname = check_and_clone_cstr(pathname, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; - Some(pathname) - }; - let flags = UtimensFlags::from_bits(flags).ok_or(SystemError::EINVAL)?; - let times = if times.is_null() { - None - } else { - let times_reader = UserBufferReader::new(times, size_of::() * 2, true)?; - let times = times_reader.read_from_user::(0)?; - Some([times[0], times[1]]) - }; - do_utimensat(dirfd, pathname, times, flags) - } - - pub fn sys_utimes( - pathname: *const u8, - times: *const PosixTimeval, - ) -> Result { - let pathname = check_and_clone_cstr(pathname, Some(MAX_PATHLEN))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; - let times = if times.is_null() { - None - } else { - let times_reader = UserBufferReader::new(times, size_of::() * 2, true)?; - let times = times_reader.read_from_user::(0)?; - Some([times[0], times[1]]) - }; - do_utimes(&pathname, times) - } } #[repr(C)] @@ -1755,7 +1662,8 @@ impl IoVecs { // 将用户空间的IoVec转换为引用(注意:这里的引用是静态的,因为用户空间的IoVec不会被释放) let iovs: &[IoVec] = core::slice::from_raw_parts(iov, iovcnt); - let mut slices: Vec<&mut [u8]> = Vec::with_capacity(iovs.len()); + let mut slices: Vec<&mut [u8]> = vec![]; + slices.reserve(iovs.len()); for iov in iovs.iter() { if iov.iov_len == 0 { diff --git a/kernel/src/filesystem/vfs/utils.rs b/kernel/src/filesystem/vfs/utils.rs index 770de48d1..7e9ce6c79 100644 --- a/kernel/src/filesystem/vfs/utils.rs +++ b/kernel/src/filesystem/vfs/utils.rs @@ -1,7 +1,8 @@ use core::cmp::Ordering; -use core::fmt::{self, Debug}; +use core::fmt::Debug; use core::hash::Hash; +use alloc::string::ToString; use alloc::{string::String, sync::Arc}; use system_error::SystemError; @@ -134,9 +135,9 @@ impl Clone for DName { } } -impl fmt::Display for DName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) +impl ToString for DName { + fn to_string(&self) -> String { + (*self.0).clone() } } diff --git a/kernel/src/init/init.rs b/kernel/src/init/init.rs index c42fa4cc7..309dd753b 100644 --- a/kernel/src/init/init.rs +++ b/kernel/src/init/init.rs @@ -75,11 +75,12 @@ fn do_start_kernel() { time_init(); timer_init(); kthread_init(); - setup_arch_post().expect("setup_arch_post failed"); clocksource_boot_finish(); Futex::init(); + setup_arch_post().expect("setup_arch_post failed"); + #[cfg(all(target_arch = "x86_64", feature = "kvm"))] crate::virt::kvm::kvm_init(); } diff --git a/kernel/src/init/initial_kthread.rs b/kernel/src/init/initial_kthread.rs index 0f7056a4c..3ff270694 100644 --- a/kernel/src/init/initial_kthread.rs +++ b/kernel/src/init/initial_kthread.rs @@ -2,14 +2,14 @@ use core::sync::atomic::{compiler_fence, Ordering}; -use alloc::{ffi::CString, string::ToString}; -use log::{debug, error}; +use alloc::string::{String, ToString}; use system_error::SystemError; use crate::{ arch::{interrupt::TrapFrame, process::arch_switch_to_user}, driver::{net::e1000e::e1000e::e1000e_init, virtio::virtio::virtio_probe}, filesystem::vfs::core::mount_root_fs, + kdebug, kerror, net::net_core::net_init, process::{kthread::KernelThreadMechanism, stdio::stdio_init, ProcessFlags, ProcessManager}, smp::smp_init, @@ -35,14 +35,16 @@ fn kernel_init() -> Result<(), SystemError> { #[cfg(target_arch = "x86_64")] crate::driver::disk::ahci::ahci_init().expect("Failed to initialize AHCI"); + virtio_probe(); mount_root_fs().expect("Failed to mount root fs"); + e1000e_init(); net_init().unwrap_or_else(|err| { - error!("Failed to initialize network: {:?}", err); + kerror!("Failed to initialize network: {:?}", err); }); - debug!("initial kernel thread done."); + kdebug!("initial kernel thread done."); return Ok(()); } @@ -86,23 +88,25 @@ fn switch_to_user() -> ! { } fn try_to_run_init_process(path: &str, trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - if let Err(e) = run_init_process(path, trap_frame) { + if let Err(e) = run_init_process(path.to_string(), trap_frame) { if e != SystemError::ENOENT { - error!( + kerror!( "Failed to run init process: {path} exists but couldn't execute it (error {:?})", e ); } return Err(e); } + Ok(()) } -fn run_init_process(path: &str, trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - let argv = vec![CString::new(path).unwrap()]; - let envp = vec![CString::new("PATH=/").unwrap()]; +fn run_init_process(path: String, trap_frame: &mut TrapFrame) -> Result<(), SystemError> { + let argv = vec![path.clone()]; + let envp = vec![String::from("PATH=/")]; compiler_fence(Ordering::SeqCst); - Syscall::do_execve(path.to_string(), argv, envp, trap_frame)?; + Syscall::do_execve(path, argv, envp, trap_frame)?; + Ok(()) } diff --git a/kernel/src/init/mod.rs b/kernel/src/init/mod.rs index b52416100..c67a9001e 100644 --- a/kernel/src/init/mod.rs +++ b/kernel/src/init/mod.rs @@ -27,7 +27,6 @@ fn init_intertrait() { #[derive(Debug)] pub struct BootParams { pub screen_info: BootTimeScreenInfo, - #[allow(dead_code)] pub arch: ArchBootParams, boot_command_line: [u8; Self::BOOT_COMMAND_LINE_SIZE], } diff --git a/kernel/src/ipc/pipe.rs b/kernel/src/ipc/pipe.rs index 864bc750c..a0349a4e2 100644 --- a/kernel/src/ipc/pipe.rs +++ b/kernel/src/ipc/pipe.rs @@ -1,4 +1,6 @@ use crate::{ + arch::CurrentIrqArch, + exception::InterruptArch, filesystem::vfs::{ core::generate_inode_id, file::FileMode, syscall::ModeType, FilePrivateData, FileSystem, FileType, IndexNode, Metadata, @@ -9,14 +11,13 @@ use crate::{ }, net::event_poll::{EPollEventType, EPollItem, EventPoll}, process::ProcessState, - sched::SchedMode, + sched::{schedule, SchedMode}, time::PosixTimeSpec, }; use alloc::{ collections::LinkedList, sync::{Arc, Weak}, - vec::Vec, }; use system_error::SystemError; @@ -103,25 +104,6 @@ impl InnerPipeInode { self.epitems.lock().push_back(epitem); Ok(()) } - - fn buf_full(&self) -> bool { - return self.valid_cnt as usize == PIPE_BUFF_SIZE; - } - - pub fn remove_epoll(&self, epoll: &Weak>) -> Result<(), SystemError> { - let is_remove = !self - .epitems - .lock_irqsave() - .extract_if(|x| x.epoll().ptr_eq(epoll)) - .collect::>() - .is_empty(); - - if is_remove { - return Ok(()); - } - - Err(SystemError::ENOENT) - } } impl LockedPipeInode { @@ -168,16 +150,6 @@ impl LockedPipeInode { pub fn inner(&self) -> &SpinLock { &self.inner } - - fn readable(&self) -> bool { - let inode = self.inner.lock(); - return inode.valid_cnt > 0 || inode.writer == 0; - } - - fn writeable(&self) -> bool { - let inode = self.inner.lock(); - return !inode.buf_full() || inode.reader == 0; - } } impl IndexNode for LockedPipeInode { @@ -201,7 +173,6 @@ impl IndexNode for LockedPipeInode { if buf.len() < len { return Err(SystemError::EINVAL); } - // log::debug!("pipe mode: {:?}", mode); // 加锁 let mut inode = self.inner.lock(); @@ -222,12 +193,14 @@ impl IndexNode for LockedPipeInode { } // 否则在读等待队列中睡眠,并释放锁 - drop(inode); - let r = wq_wait_event_interruptible!(self.read_wait_queue, self.readable(), {}); - if r.is_err() { - return Err(SystemError::ERESTARTSYS); - } + unsafe { + let irq_guard = CurrentIrqArch::save_and_disable_irq(); + drop(inode); + self.read_wait_queue.sleep_without_schedule(); + drop(irq_guard); + } + schedule(SchedMode::SM_NONE); inode = self.inner.lock(); } @@ -378,11 +351,13 @@ impl IndexNode for LockedPipeInode { } // 解锁并睡眠 - drop(inode); - let r = wq_wait_event_interruptible!(self.write_wait_queue, self.writeable(), {}); - if r.is_err() { - return Err(SystemError::ERESTARTSYS); + unsafe { + let irq_guard = CurrentIrqArch::save_and_disable_irq(); + drop(inode); + self.write_wait_queue.sleep_without_schedule(); + drop(irq_guard); } + schedule(SchedMode::SM_NONE); inode = self.inner.lock(); } diff --git a/kernel/src/ipc/shm.rs b/kernel/src/ipc/shm.rs index 99cce2e45..6e4b7224d 100644 --- a/kernel/src/ipc/shm.rs +++ b/kernel/src/ipc/shm.rs @@ -14,11 +14,10 @@ use crate::{ syscall::user_access::{UserBufferReader, UserBufferWriter}, time::PosixTimeSpec, }; -use alloc::{sync::Arc, vec::Vec}; +use alloc::vec::Vec; use core::sync::atomic::{compiler_fence, Ordering}; use hashbrown::{HashMap, HashSet}; use ida::IdAllocator; -use log::info; use num::ToPrimitive; use system_error::SystemError; @@ -29,14 +28,14 @@ pub const IPC_PRIVATE: ShmKey = ShmKey::new(0); /// 初始化SHM_MANAGER pub fn shm_manager_init() { - info!("shm_manager_init"); + kinfo!("shm_manager_init"); let shm_manager = SpinLock::new(ShmManager::new()); compiler_fence(Ordering::SeqCst); unsafe { SHM_MANAGER = Some(shm_manager) }; compiler_fence(Ordering::SeqCst); - info!("shm_manager_init done"); + kinfo!("shm_manager_init done"); } pub fn shm_manager_lock() -> SpinLockGuard<'static, ShmManager> { @@ -165,10 +164,10 @@ impl ShmManager { let mut page_manager_guard = page_manager_lock_irqsave(); let mut cur_phys = PhysPageFrame::new(phys_page.0); for _ in 0..page_count.data() { - let page = Arc::new(Page::new(true, cur_phys.phys_address())); - page.write_irqsave().set_shm_id(shm_id); + let mut page = Page::new(true); + page.set_shm_id(shm_id); let paddr = cur_phys.phys_address(); - page_manager_guard.insert(paddr, &page); + page_manager_guard.insert(paddr, page); cur_phys = cur_phys.next(); } @@ -324,8 +323,8 @@ impl ShmManager { if map_count > 0 { // 设置共享内存物理页当映射计数等于0时可被回收 for _ in 0..count.data() { - let page = page_manager_guard.get_unwrap(&cur_phys.phys_address()); - page.write_irqsave().set_dealloc_when_zero(true); + let page = page_manager_guard.get_mut(&cur_phys.phys_address()); + page.set_dealloc_when_zero(true); cur_phys = cur_phys.next(); } @@ -436,7 +435,7 @@ impl KernelShm { /// 共享内存段的映射计数(有多少个不同的VMA映射) pub fn map_count(&self) -> usize { - let mut page_manager_guard = page_manager_lock_irqsave(); + let page_manager_guard = page_manager_lock_irqsave(); let mut id_set: HashSet = HashSet::new(); let mut cur_phys = PhysPageFrame::new(self.shm_start_paddr); let page_count = PageFrameCount::from_bytes(page_align_up(self.shm_size)).unwrap(); @@ -444,8 +443,7 @@ impl KernelShm { for _ in 0..page_count.data() { let page = page_manager_guard.get(&cur_phys.phys_address()).unwrap(); id_set.extend( - page.read_irqsave() - .anon_vma() + page.anon_vma() .iter() .map(|vma| vma.id()) .collect::>(), diff --git a/kernel/src/ipc/signal.rs b/kernel/src/ipc/signal.rs index fe91bd68d..df4f0f466 100644 --- a/kernel/src/ipc/signal.rs +++ b/kernel/src/ipc/signal.rs @@ -1,12 +1,12 @@ use core::sync::atomic::compiler_fence; use alloc::sync::Arc; -use log::warn; use system_error::SystemError; use crate::{ arch::ipc::signal::{SigCode, SigFlags, SigSet, Signal}, ipc::signal_types::SigactionType, + kwarn, libs::spinlock::SpinLockGuard, process::{pid::PidType, Pid, ProcessControlBlock, ProcessFlags, ProcessManager}, }; @@ -16,35 +16,6 @@ use super::signal_types::{ }; impl Signal { - pub fn signal_pending_state( - interruptible: bool, - task_wake_kill: bool, - pcb: &Arc, - ) -> bool { - if !interruptible && !task_wake_kill { - return false; - } - - if !pcb.has_pending_signal() { - return false; - } - - return interruptible || Self::fatal_signal_pending(pcb); - } - - /// 判断当前进程是否收到了SIGKILL信号 - pub fn fatal_signal_pending(pcb: &Arc) -> bool { - let guard = pcb.sig_info_irqsave(); - if guard - .sig_pending() - .signal() - .contains(Signal::SIGKILL.into()) - { - return true; - } - - return false; - } /// 向目标进程发送信号 /// /// ## 参数 @@ -64,7 +35,7 @@ impl Signal { // 如果 pid 等于 -1,那么信号的发送范围是:调用进程有权将信号发往的每个目标进程,除去 init(进程 ID 为 1)和调用进程自身。如果特权级进程发起这一调用,那么会发送信号给系统中的所有进程,上述两个进程除外。显而易见,有时也将这种信号发送方式称之为广播信号 // 如果并无进程与指定的 pid 相匹配,那么 kill() 调用失败,同时将 errno 置为 ESRCH(“查无此进程”) if pid.lt(&Pid::from(0)) { - warn!("Kill operation not support: pid={:?}", pid); + kwarn!("Kill operation not support: pid={:?}", pid); return Err(SystemError::ENOSYS); } compiler_fence(core::sync::atomic::Ordering::SeqCst); @@ -76,7 +47,7 @@ impl Signal { let pcb = ProcessManager::find(pid); if pcb.is_none() { - warn!("No such process."); + kwarn!("No such process."); return retval; } @@ -114,7 +85,7 @@ impl Signal { if !self.prepare_sianal(pcb.clone(), force_send) { return Err(SystemError::EINVAL); } - // debug!("force send={}", force_send); + // kdebug!("force send={}", force_send); let pcb_info = pcb.sig_info_irqsave(); let pending = if matches!(pt, PidType::PID) { pcb_info.sig_shared_pending() @@ -170,7 +141,7 @@ impl Signal { /// @param pt siginfo结构体中,pid字段代表的含义 #[allow(clippy::if_same_then_else)] fn complete_signal(&self, pcb: Arc, pt: PidType) { - // debug!("complete_signal"); + // kdebug!("complete_signal"); compiler_fence(core::sync::atomic::Ordering::SeqCst); // ===== 寻找需要wakeup的目标进程 ===== @@ -313,14 +284,14 @@ impl Signal { fn signal_wake_up(pcb: Arc, _guard: SpinLockGuard, fatal: bool) { // 如果是 fatal 的话就唤醒 stop 和 block 的进程来响应,因为唤醒后就会终止 // 如果不是 fatal 的就只唤醒 stop 的进程来响应 - // debug!("signal_wake_up"); + // kdebug!("signal_wake_up"); // 如果目标进程已经在运行,则发起一个ipi,使得它陷入内核 let state = pcb.sched_info().inner_lock_read_irqsave().state(); let mut wakeup_ok = true; if state.is_blocked_interruptable() { ProcessManager::wakeup(&pcb).unwrap_or_else(|e| { wakeup_ok = false; - warn!( + kwarn!( "Current pid: {:?}, signal_wake_up target {:?} error: {:?}", ProcessManager::current_pcb().pid(), pcb.pid(), @@ -330,7 +301,7 @@ fn signal_wake_up(pcb: Arc, _guard: SpinLockGuard, force_default: bool) { compiler_fence(core::sync::atomic::Ordering::SeqCst); - // debug!("hand=0x{:018x}", hand as *const sighand_struct as usize); + // kdebug!("hand=0x{:018x}", hand as *const sighand_struct as usize); let actions = &mut pcb.sig_struct_irqsave().handlers; for sigaction in actions.iter_mut() { diff --git a/kernel/src/ipc/signal_types.rs b/kernel/src/ipc/signal_types.rs index 6ebfbf611..1ed085470 100644 --- a/kernel/src/ipc/signal_types.rs +++ b/kernel/src/ipc/signal_types.rs @@ -64,7 +64,6 @@ pub struct SignalStruct { } #[derive(Debug)] -#[allow(dead_code)] pub struct InnerSignalStruct { pub cnt: AtomicI64, /// 如果对应linux,这部分会有一个引用计数,但是没发现在哪里有用到需要计算引用的地方,因此 @@ -81,12 +80,6 @@ impl SignalStruct { } } -impl Default for SignalStruct { - fn default() -> Self { - Self::new() - } -} - impl Deref for SignalStruct { type Target = InnerSignalStruct; @@ -437,7 +430,7 @@ impl SigPending { /// @brief 从当前进程的sigpending中取出下一个待处理的signal,并返回给调用者。(调用者应当处理这个信号) /// 请注意,进入本函数前,当前进程应当持有current_pcb().sighand.siglock pub fn dequeue_signal(&mut self, sig_mask: &SigSet) -> (Signal, Option) { - // debug!("dequeue signal"); + // kdebug!("dequeue signal"); // 获取下一个要处理的信号的编号 let sig = self.next_signal(sig_mask); diff --git a/kernel/src/ipc/syscall.rs b/kernel/src/ipc/syscall.rs index 2d0b9b6b2..995dcfbd5 100644 --- a/kernel/src/ipc/syscall.rs +++ b/kernel/src/ipc/syscall.rs @@ -3,7 +3,6 @@ use core::{ sync::atomic::compiler_fence, }; -use log::{error, warn}; use system_error::SystemError; use crate::{ @@ -16,11 +15,12 @@ use crate::{ FilePrivateData, }, ipc::shm::{shm_manager_lock, IPC_PRIVATE}, + kerror, kwarn, libs::align::page_align_up, libs::spinlock::SpinLock, mm::{ allocator::page_frame::{PageFrameCount, PhysPageFrame, VirtPageFrame}, - page::{page_manager_lock_irqsave, EntryFlags, PageFlushAll}, + page::{page_manager_lock_irqsave, PageFlags, PageFlushAll}, syscall::ProtFlags, ucontext::{AddressSpace, VMA}, VirtAddr, VmFlags, @@ -96,7 +96,7 @@ impl Syscall { let sig = Signal::from(sig); if sig == Signal::INVALID { // 传入的signal数值不合法 - warn!("Not a valid signal number"); + kwarn!("Not a valid signal number"); return Err(SystemError::EINVAL); } @@ -173,12 +173,12 @@ impl Syscall { } // TODO 如果为空,赋默认值? - // debug!("new_ka={:?}", new_ka); + // kdebug!("new_ka={:?}", new_ka); // 如果用户手动给了sa_restorer,那么就置位SA_FLAG_RESTORER,否则报错。(用户必须手动指定restorer) if new_ka.restorer().is_some() { new_ka.flags_mut().insert(SigFlags::SA_RESTORER); } else if new_ka.action().is_customized() { - error!( + kerror!( "pid:{:?}: in sys_sigaction: User must manually sprcify a sa_restorer for signal {}.", ProcessManager::current_pcb().pid(), sig @@ -229,7 +229,7 @@ impl Syscall { } } SigactionType::SaSigaction(_) => { - error!("unsupported type: SaSigaction"); + kerror!("unsupported type: SaSigaction"); VirtAddr::new(USER_SIG_DFL as usize) } }; @@ -261,7 +261,7 @@ impl Syscall { pub fn shmget(key: ShmKey, size: usize, shmflg: ShmFlags) -> Result { // 暂不支持巨页 if shmflg.contains(ShmFlags::SHM_HUGETLB) { - error!("shmget: not support huge page"); + kerror!("shmget: not support huge page"); return Err(SystemError::ENOSYS); } @@ -324,8 +324,8 @@ impl Syscall { .ok_or(SystemError::EINVAL)?; let vm_flags = VmFlags::from(shmflg); let destination = VirtPageFrame::new(region.start()); - let page_flags: EntryFlags = - EntryFlags::from_prot_flags(ProtFlags::from(vm_flags), true); + let page_flags: PageFlags = + PageFlags::from_prot_flags(ProtFlags::from(vm_flags), true); let flusher: PageFlushAll = PageFlushAll::new(); // 将共享内存映射到对应虚拟区域 @@ -351,14 +351,14 @@ impl Syscall { .mappings .contains(vaddr) .ok_or(SystemError::EINVAL)?; - if vma.lock_irqsave().region().start() != vaddr { + if vma.lock().region().start() != vaddr { return Err(SystemError::EINVAL); } // 验证用户虚拟内存区域是否有效 let _ = UserBufferReader::new(vaddr.data() as *const u8, size, true)?; - // 必须在取消映射前获取到EntryFlags + // 必须在取消映射前获取到PageFlags let page_flags = address_write_guard .user_mapper .utable @@ -386,8 +386,7 @@ impl Syscall { // 将vma加入到对应Page的anon_vma page_manager_guard - .get_unwrap(&phys.phys_address()) - .write_irqsave() + .get_mut(&phys.phys_address()) .insert_vma(vma.clone()); phys = phys.next(); @@ -395,7 +394,7 @@ impl Syscall { } // 更新vma的映射状态 - vma.lock_irqsave().set_mapped(true); + vma.lock().set_mapped(true); vaddr.data() } @@ -428,7 +427,7 @@ impl Syscall { .ok_or(SystemError::EINVAL)?; // 判断vaddr是否为起始地址 - if vma.lock_irqsave().region().start() != vaddr { + if vma.lock().region().start() != vaddr { return Err(SystemError::EINVAL); } @@ -441,9 +440,9 @@ impl Syscall { .0; // 如果物理页的shm_id为None,代表不是共享页 - let mut page_manager_guard = page_manager_lock_irqsave(); + let page_manager_guard = page_manager_lock_irqsave(); let page = page_manager_guard.get(&paddr).ok_or(SystemError::EINVAL)?; - let shm_id = page.read_irqsave().shm_id().ok_or(SystemError::EINVAL)?; + let shm_id = page.shm_id().ok_or(SystemError::EINVAL)?; drop(page_manager_guard); // 获取对应共享页管理信息 diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 99af0cc42..b26a7ca71 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -7,21 +7,22 @@ #![feature(const_for)] #![feature(const_mut_refs)] #![feature(const_trait_impl)] +#![feature(const_transmute_copy)] #![feature(const_refs_to_cell)] #![feature(core_intrinsics)] #![feature(c_void_variant)] #![feature(extract_if)] #![feature(fn_align)] -#![feature(linked_list_retain)] +#![feature(inline_const)] #![feature(naked_functions)] #![feature(new_uninit)] +#![feature(panic_info_message)] #![feature(ptr_internals)] +#![feature(ptr_to_from_bits)] #![feature(trait_upcasting)] #![feature(slice_ptr_get)] -#![feature(sync_unsafe_cell)] #![feature(vec_into_raw_parts)] #![cfg_attr(target_os = "none", no_std)] -#![allow(internal_features)] // clippy的配置 #![deny(clippy::all)] #![allow(clippy::bad_bit_mask)] @@ -84,8 +85,6 @@ extern crate x86; extern crate klog_types; extern crate uefi; extern crate uefi_raw; -#[macro_use] -extern crate wait_queue_macros; use crate::mm::allocator::kernel_allocator::KernelAllocator; @@ -107,9 +106,7 @@ pub static KERNEL_ALLOCATOR: KernelAllocator = KernelAllocator; #[panic_handler] #[no_mangle] pub fn panic(info: &PanicInfo) -> ! { - use log::error; - - error!("Kernel Panic Occurred."); + kerror!("Kernel Panic Occurred."); match info.location() { Some(loc) => { @@ -124,7 +121,15 @@ pub fn panic(info: &PanicInfo) -> ! { println!("No location info"); } } - println!("Message:\n\t{}", info.message()); + + match info.message() { + Some(msg) => { + println!("Message:\n\t{}", msg); + } + None => { + println!("No panic message."); + } + } #[cfg(all(feature = "backtrace", target_arch = "x86_64"))] { diff --git a/kernel/src/libs/elf.rs b/kernel/src/libs/elf.rs index 6c74dec1a..71bb91e37 100644 --- a/kernel/src/libs/elf.rs +++ b/kernel/src/libs/elf.rs @@ -12,12 +12,12 @@ use elf::{ file::FileHeader, segment::{ProgramHeader, SegmentTable}, }; -use log::error; use system_error::SystemError; use crate::{ arch::{CurrentElfArch, MMArch}, driver::base::block::SeekFrom, + kdebug, kerror, libs::align::page_align_up, mm::{ allocator::page_frame::{PageFrameCount, VirtPageFrame}, @@ -124,7 +124,7 @@ impl ElfLoader { ) -> Result<(), ExecError> { let start = Self::elf_page_start(start); let end = Self::elf_page_align_up(end); - // debug!("set_elf_brk: start={:?}, end={:?}", start, end); + if end > start { let r = user_vm_guard.map_anonymous( start, @@ -134,9 +134,9 @@ impl ElfLoader { false, true, ); - // debug!("set_elf_brk: map_anonymous: r={:?}", r); + // kdebug!("set_elf_brk: map_anonymous: r={:?}", r); if r.is_err() { - error!("set_elf_brk: map_anonymous failed, err={:?}", r); + kerror!("set_elf_brk: map_anonymous failed, err={:?}", r); return Err(ExecError::OutOfMemory); } } @@ -207,7 +207,7 @@ impl ElfLoader { map_flags: &MapFlags, total_size: usize, ) -> Result<(VirtAddr, bool), SystemError> { - // debug!("load_elf_segment: addr_to_map={:?}", addr_to_map); + // kdebug!("load_elf_segment: addr_to_map={:?}", addr_to_map); // 映射位置的偏移量(页内偏移) let beginning_page_offset = Self::elf_page_offset(addr_to_map); @@ -228,7 +228,7 @@ impl ElfLoader { let map_err_handler = |err: SystemError| { if err == SystemError::EEXIST { - error!( + kerror!( "Pid: {:?}, elf segment at {:p} overlaps with existing mapping", ProcessManager::current_pcb().pid(), addr_to_map.as_ptr::() @@ -255,7 +255,7 @@ impl ElfLoader { if total_size != 0 { let total_size = Self::elf_page_align_up(VirtAddr::new(total_size)).data(); - // log::debug!("total_size={}", total_size); + // kdebug!("total_size={}", total_size); map_addr = user_vm_guard .map_anonymous(addr_to_map, total_size, tmp_prot, *map_flags, false, true) @@ -285,12 +285,12 @@ impl ElfLoader { )?; } } else { - // debug!("total size = 0"); + // kdebug!("total size = 0"); map_addr = user_vm_guard .map_anonymous(addr_to_map, map_size, tmp_prot, *map_flags, false, true)? .virt_address(); - // debug!( + // kdebug!( // "map ok: addr_to_map={:?}, map_addr={map_addr:?},beginning_page_offset={beginning_page_offset:?}", // addr_to_map // ); @@ -311,7 +311,7 @@ impl ElfLoader { )?; } } - // debug!("load_elf_segment OK: map_addr={:?}", map_addr); + // kdebug!("load_elf_segment OK: map_addr={:?}", map_addr); return Ok((map_addr, true)); } @@ -329,7 +329,7 @@ impl ElfLoader { interp_elf_ex: &mut ExecParam, load_bias: usize, ) -> Result { - log::debug!("loading elf interp"); + kdebug!("loading elf interp"); let mut head_buf = [0u8; 512]; interp_elf_ex .file_mut() @@ -361,28 +361,29 @@ impl ElfLoader { let mut last_bss: VirtAddr = VirtAddr::new(0); let mut bss_prot: Option = None; for section in phdr_table { + kdebug!("loading {:?}", section); if section.p_type == PT_LOAD { - log::debug!("loading {:?}", section); let mut elf_type = MapFlags::MAP_PRIVATE; let elf_prot = Self::make_prot(section.p_flags, true, true); let vaddr = TryInto::::try_into(section.p_vaddr).unwrap(); - let mut addr_to_map = load_addr + vaddr; if interp_hdr.e_type == ET_EXEC || load_addr_set { elf_type.insert(MapFlags::MAP_FIXED) //TODO 应当为MapFlags::MAP_FIXED,暂时未支持 - } else if load_bias != 0 && interp_hdr.e_type == ET_DYN { - addr_to_map = VirtAddr::new(0); + } + load_addr += vaddr; + if load_bias != 0 && interp_hdr.e_type == ET_DYN { + load_addr -= vaddr; } let map_addr = Self::load_elf_segment( &mut interp_elf_ex.vm().clone().write(), interp_elf_ex, §ion, - addr_to_map, + load_addr, &elf_prot, &elf_type, total_size, ) .map_err(|e| { - log::error!("Failed to load elf interpreter :{:?}", e); + kerror!("Failed to load elf interpreter :{:?}", e); return ExecError::InvalidParemeter; })?; if !map_addr.1 { @@ -451,13 +452,14 @@ impl ElfLoader { _ => return ExecError::InvalidParemeter, })?; } - load_addr += TryInto::::try_into(interp_hdr.e_entry).unwrap(); - if load_addr > MMArch::USER_END_VADDR { + if load_addr + TryInto::::try_into(interp_hdr.e_entry).unwrap() + > MMArch::USER_END_VADDR + { return Err(ExecError::BadAddress(Some( load_addr + TryInto::::try_into(interp_hdr.e_entry).unwrap(), ))); } - log::debug!("sucessfully load elf interp"); + kdebug!("sucessfully load elf interp"); return Ok(BinaryLoaderResult::new(load_addr)); } @@ -489,7 +491,7 @@ impl ElfLoader { while remain > 0 { let read_size = min(remain, buf_size); file.read(read_size, &mut buf[..read_size])?; - // debug!("copy_to_user: vaddr={:?}, read_size = {read_size}", vaddr); + // kdebug!("copy_to_user: vaddr={:?}, read_size = {read_size}", vaddr); unsafe { copy_to_user(vaddr, &buf[..read_size]).map_err(|_| SystemError::EFAULT)?; } @@ -706,14 +708,14 @@ impl BinaryLoader for ElfLoader { // https://code.dragonos.org.cn/xref/linux-5.19.10/fs/binfmt_elf.c?r=&mo=22652&fi=824#1034 let elf_type = ElfType::from(ehdr.e_type); - // debug!("ehdr = {:?}", ehdr); + // kdebug!("ehdr = {:?}", ehdr); let binding = param.vm().clone(); let mut user_vm = binding.write(); // todo: 增加对user stack上的内存是否具有可执行权限的处理(方法:寻找phdr里面的PT_GNU_STACK段) - // debug!("to parse segments"); + // kdebug!("to parse segments"); // 加载ELF文件并映射到用户空间 let mut phdr_buf = Vec::new(); let phdr_table = Self::parse_segments(param, &ehdr, &mut phdr_buf) @@ -736,8 +738,9 @@ impl BinaryLoader for ElfLoader { if seg.p_filesz > 4096 || seg.p_filesz < 2 { return Err(ExecError::NotExecutable); } - log::debug!("seg:{:?}", seg); - let mut buffer = vec![0; seg.p_filesz.try_into().unwrap()]; + kdebug!("seg:{:?}", seg); + let mut buffer = Vec::new(); + buffer.resize(seg.p_filesz.try_into().unwrap(), 0); let r = param .file_mut() .pread( @@ -746,11 +749,11 @@ impl BinaryLoader for ElfLoader { buffer.as_mut_slice(), ) .map_err(|e| { - log::error!("Failed to load interpreter :{:?}", e); + kerror!("Failed to load interpreter :{:?}", e); return ExecError::NotSupported; })?; if r != seg.p_filesz.try_into().unwrap() { - log::error!("Failed to load interpreter "); + kerror!("Failed to load interpreter "); return Err(ExecError::NotSupported); } let interpreter_path = core::str::from_utf8( @@ -762,11 +765,11 @@ impl BinaryLoader for ElfLoader { e )) })?; - log::debug!("opening interpreter at :{}", interpreter_path); + kdebug!("opening interpreter at :{}", interpreter_path); interpreter = Some( - ExecParam::new(interpreter_path, param.vm().clone(), ExecParamFlags::EXEC) + ExecParam::new(&interpreter_path, param.vm().clone(), ExecParamFlags::EXEC) .map_err(|e| { - log::error!("Failed to load interpreter :{:?}", e); + kerror!("Failed to load interpreter :{:?}", e); return ExecError::NotSupported; })?, ); @@ -801,9 +804,9 @@ impl BinaryLoader for ElfLoader { .filter(|seg| seg.p_type == elf::abi::PT_LOAD); for seg_to_load in loadable_sections { - log::debug!("seg_to_load = {:?}", seg_to_load); + kdebug!("seg_to_load = {:?}", seg_to_load); if unlikely(elf_brk > elf_bss) { - // debug!( + // kdebug!( // "to set brk, elf_brk = {:?}, elf_bss = {:?}", // elf_brk, // elf_bss @@ -866,7 +869,6 @@ impl BinaryLoader for ElfLoader { // 加载这个段到用户空间 - log::debug!("bias: {load_bias}"); let e = Self::load_elf_segment( &mut user_vm, param, @@ -881,7 +883,7 @@ impl BinaryLoader for ElfLoader { SystemError::ENOMEM => ExecError::OutOfMemory, _ => ExecError::Other(format!("load_elf_segment failed: {:?}", e)), })?; - log::debug!("e.0={:?}", e.0); + // 如果地址不对,那么就报错 if !e.1 { return Err(ExecError::BadAddress(Some(e.0))); @@ -900,9 +902,9 @@ impl BinaryLoader for ElfLoader { } } - // debug!("seg_to_load.p_offset={}", seg_to_load.p_offset); - // debug!("e_phoff={}", ehdr.e_phoff); - // debug!("seg_to_load.p_filesz={}", seg_to_load.p_filesz); + // kdebug!("seg_to_load.p_offset={}", seg_to_load.p_offset); + // kdebug!("e_phoff={}", ehdr.e_phoff); + // kdebug!("seg_to_load.p_filesz={}", seg_to_load.p_filesz); // Figure out which segment in the file contains the Program Header Table, // and map to the associated virtual address. if (seg_to_load.p_offset <= ehdr.e_phoff) @@ -932,7 +934,7 @@ impl BinaryLoader for ElfLoader { || Self::elf_page_align_up(p_vaddr + seg_to_load.p_memsz as usize) >= MMArch::USER_END_VADDR { - // debug!("ERR: p_vaddr={p_vaddr:?}"); + // kdebug!("ERR: p_vaddr={p_vaddr:?}"); return Err(ExecError::InvalidParemeter); } @@ -965,7 +967,7 @@ impl BinaryLoader for ElfLoader { elf_brk = seg_end_vaddr; } } - // debug!("elf load: phdr_vaddr={phdr_vaddr:?}"); + // kdebug!("elf load: phdr_vaddr={phdr_vaddr:?}"); let program_entrypoint = VirtAddr::new(ehdr.e_entry as usize + load_bias); let phdr_vaddr = phdr_vaddr.map(|phdr_vaddr| phdr_vaddr + load_bias); @@ -976,7 +978,7 @@ impl BinaryLoader for ElfLoader { start_data = start_data.map(|v| v + load_bias); end_data = end_data.map(|v| v + load_bias); let mut interp_load_addr: Option = None; - // debug!( + // kdebug!( // "to set brk: elf_bss: {:?}, elf_brk: {:?}, bss_prot_flags: {:?}", // elf_bss, // elf_brk, @@ -985,7 +987,7 @@ impl BinaryLoader for ElfLoader { self.set_elf_brk(&mut user_vm, elf_bss, elf_brk, bss_prot_flags)?; if likely(elf_bss != elf_brk) && unlikely(Self::pad_zero(elf_bss).is_err()) { - // debug!("elf_bss = {elf_bss:?}, elf_brk = {elf_brk:?}"); + // kdebug!("elf_bss = {elf_bss:?}, elf_brk = {elf_brk:?}"); return Err(ExecError::BadAddress(Some(elf_bss))); } drop(user_vm); @@ -1003,17 +1005,22 @@ impl BinaryLoader for ElfLoader { .map(|fd| fd as usize) .map_err(|_| ExecError::InvalidParemeter)?; } - // debug!("to create auxv"); + // kdebug!("to create auxv"); let mut user_vm = binding.write(); - self.create_auxv(param, program_entrypoint, phdr_vaddr, &ehdr)?; + self.create_auxv( + param, + interp_load_addr.unwrap_or(program_entrypoint), + phdr_vaddr, + &ehdr, + )?; - // debug!("auxv create ok"); + // kdebug!("auxv create ok"); user_vm.start_code = start_code.unwrap_or(VirtAddr::new(0)); user_vm.end_code = end_code.unwrap_or(VirtAddr::new(0)); user_vm.start_data = start_data.unwrap_or(VirtAddr::new(0)); user_vm.end_data = end_data.unwrap_or(VirtAddr::new(0)); - let result = BinaryLoaderResult::new(interp_load_addr.unwrap_or(program_entrypoint)); + let result = BinaryLoaderResult::new(program_entrypoint); // kdebug!("elf load OK!!!"); return Ok(result); } diff --git a/kernel/src/libs/ffi_convert.rs b/kernel/src/libs/ffi_convert.rs new file mode 100644 index 000000000..4e39d51a7 --- /dev/null +++ b/kernel/src/libs/ffi_convert.rs @@ -0,0 +1,15 @@ +/// @brief 由bindgen生成的结构体转换成rust原生定义的结构体的特性 +pub trait FFIBind2Rust { + /// 转换为不可变引用 + fn convert_ref(src: *const T) -> Option<&'static Self>; + /// 转换为可变引用 + fn convert_mut(src: *mut T) -> Option<&'static mut Self>; +} + +pub fn __convert_mut<'a, S, D>(src: *mut S) -> Option<&'a mut D> { + return unsafe { core::mem::transmute::<*mut S, *mut D>(src).as_mut() }; +} + +pub fn __convert_ref<'a, S, D>(src: *const S) -> Option<&'a D> { + return unsafe { core::mem::transmute::<*const S, *const D>(src).as_ref() }; +} diff --git a/kernel/src/libs/font/mod.rs b/kernel/src/libs/font/mod.rs index 90eeeb354..02309e632 100644 --- a/kernel/src/libs/font/mod.rs +++ b/kernel/src/libs/font/mod.rs @@ -2,7 +2,6 @@ use self::font_type::vga8x16::FONT_VGA_8X16; pub mod font_type; -#[allow(dead_code)] pub struct FontDesc { pub index: usize, pub name: &'static str, diff --git a/kernel/src/libs/futex/futex.rs b/kernel/src/libs/futex/futex.rs index a3a26d7db..c1897f968 100644 --- a/kernel/src/libs/futex/futex.rs +++ b/kernel/src/libs/futex/futex.rs @@ -8,7 +8,6 @@ use core::{ mem, sync::atomic::AtomicU64, }; -use log::warn; use hashbrown::HashMap; use system_error::SystemError; @@ -289,7 +288,7 @@ impl Futex { let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; // 满足条件则将当前进程在该bucket上挂起 bucket_mut.sleep_no_sched(futex_q.clone()).map_err(|e| { - warn!("error:{e:?}"); + kwarn!("error:{e:?}"); e })?; drop(futex_map_guard); @@ -559,7 +558,7 @@ impl Futex { let cmparg = sign_extend32(encoded_op & 0x00000fff, 11); if (encoded_op & (FutexOP::FUTEX_OP_OPARG_SHIFT.bits() << 28) != 0) && oparg > 31 { - warn!( + kwarn!( "futex_wake_op: pid:{} tries to shift op by {}; fix this program", ProcessManager::current_pcb().pid().data(), oparg diff --git a/kernel/src/libs/ida/src/lib.rs b/kernel/src/libs/ida/src/lib.rs index 8a7082ead..eb7d8622b 100644 --- a/kernel/src/libs/ida/src/lib.rs +++ b/kernel/src/libs/ida/src/lib.rs @@ -1,6 +1,5 @@ #![no_std] #![feature(core_intrinsics)] -#![allow(internal_features)] #![allow(clippy::needless_return)] use core::intrinsics::unlikely; diff --git a/kernel/crates/intertrait/.gitignore b/kernel/src/libs/intertrait/.gitignore similarity index 100% rename from kernel/crates/intertrait/.gitignore rename to kernel/src/libs/intertrait/.gitignore diff --git a/kernel/crates/intertrait/Cargo.toml b/kernel/src/libs/intertrait/Cargo.toml similarity index 97% rename from kernel/crates/intertrait/Cargo.toml rename to kernel/src/libs/intertrait/Cargo.toml index 697bccc2e..fe7febd78 100644 --- a/kernel/crates/intertrait/Cargo.toml +++ b/kernel/src/libs/intertrait/Cargo.toml @@ -14,7 +14,7 @@ include = ["src/**/*", "Cargo.toml", "LICENSE-*", "README.md"] [dependencies] -linkme = "=0.3.27" +linkme = "0.2" hashbrown = "0.13.2" intertrait-macros = { version = "=0.2.2", path = "macros" } diff --git a/kernel/crates/intertrait/LICENSE-MIT b/kernel/src/libs/intertrait/LICENSE-MIT similarity index 100% rename from kernel/crates/intertrait/LICENSE-MIT rename to kernel/src/libs/intertrait/LICENSE-MIT diff --git a/kernel/crates/intertrait/README.md b/kernel/src/libs/intertrait/README.md similarity index 99% rename from kernel/crates/intertrait/README.md rename to kernel/src/libs/intertrait/README.md index 6f22c6616..f4db29532 100644 --- a/kernel/crates/intertrait/README.md +++ b/kernel/src/libs/intertrait/README.md @@ -23,7 +23,7 @@ Add the following two dependencies to your `Cargo.toml`: ```toml [dependencies] intertrait = "0.2" -linkme = "=0.3.27" +linkme = "0.2" ``` The `linkme` dependency is required due to the use of `linkme` macro in the output of `intertrait` macros. diff --git a/kernel/crates/intertrait/macros/Cargo.toml b/kernel/src/libs/intertrait/macros/Cargo.toml similarity index 97% rename from kernel/crates/intertrait/macros/Cargo.toml rename to kernel/src/libs/intertrait/macros/Cargo.toml index 769db8332..6e05adf2b 100644 --- a/kernel/crates/intertrait/macros/Cargo.toml +++ b/kernel/src/libs/intertrait/macros/Cargo.toml @@ -20,4 +20,4 @@ uuid = { version = "0.8", features = ["v4"] } [dev-dependencies] intertrait = { version = "=0.2.2", path = ".." } -linkme = "=0.3.27" +linkme = "0.2" diff --git a/kernel/crates/intertrait/macros/LICENSE-APACHE b/kernel/src/libs/intertrait/macros/LICENSE-APACHE similarity index 100% rename from kernel/crates/intertrait/macros/LICENSE-APACHE rename to kernel/src/libs/intertrait/macros/LICENSE-APACHE diff --git a/kernel/crates/intertrait/macros/LICENSE-MIT b/kernel/src/libs/intertrait/macros/LICENSE-MIT similarity index 100% rename from kernel/crates/intertrait/macros/LICENSE-MIT rename to kernel/src/libs/intertrait/macros/LICENSE-MIT diff --git a/kernel/crates/intertrait/macros/src/args.rs b/kernel/src/libs/intertrait/macros/src/args.rs similarity index 100% rename from kernel/crates/intertrait/macros/src/args.rs rename to kernel/src/libs/intertrait/macros/src/args.rs diff --git a/kernel/crates/intertrait/macros/src/gen_caster.rs b/kernel/src/libs/intertrait/macros/src/gen_caster.rs similarity index 100% rename from kernel/crates/intertrait/macros/src/gen_caster.rs rename to kernel/src/libs/intertrait/macros/src/gen_caster.rs diff --git a/kernel/crates/intertrait/macros/src/item_impl.rs b/kernel/src/libs/intertrait/macros/src/item_impl.rs similarity index 100% rename from kernel/crates/intertrait/macros/src/item_impl.rs rename to kernel/src/libs/intertrait/macros/src/item_impl.rs diff --git a/kernel/crates/intertrait/macros/src/item_type.rs b/kernel/src/libs/intertrait/macros/src/item_type.rs similarity index 100% rename from kernel/crates/intertrait/macros/src/item_type.rs rename to kernel/src/libs/intertrait/macros/src/item_type.rs diff --git a/kernel/crates/intertrait/macros/src/lib.rs b/kernel/src/libs/intertrait/macros/src/lib.rs similarity index 100% rename from kernel/crates/intertrait/macros/src/lib.rs rename to kernel/src/libs/intertrait/macros/src/lib.rs diff --git a/kernel/crates/intertrait/src/cast.rs b/kernel/src/libs/intertrait/src/cast.rs similarity index 100% rename from kernel/crates/intertrait/src/cast.rs rename to kernel/src/libs/intertrait/src/cast.rs diff --git a/kernel/crates/intertrait/src/cast/cast_arc.rs b/kernel/src/libs/intertrait/src/cast/cast_arc.rs similarity index 100% rename from kernel/crates/intertrait/src/cast/cast_arc.rs rename to kernel/src/libs/intertrait/src/cast/cast_arc.rs diff --git a/kernel/crates/intertrait/src/cast/cast_box.rs b/kernel/src/libs/intertrait/src/cast/cast_box.rs similarity index 100% rename from kernel/crates/intertrait/src/cast/cast_box.rs rename to kernel/src/libs/intertrait/src/cast/cast_box.rs diff --git a/kernel/crates/intertrait/src/cast/cast_mut.rs b/kernel/src/libs/intertrait/src/cast/cast_mut.rs similarity index 100% rename from kernel/crates/intertrait/src/cast/cast_mut.rs rename to kernel/src/libs/intertrait/src/cast/cast_mut.rs diff --git a/kernel/crates/intertrait/src/cast/cast_rc.rs b/kernel/src/libs/intertrait/src/cast/cast_rc.rs similarity index 100% rename from kernel/crates/intertrait/src/cast/cast_rc.rs rename to kernel/src/libs/intertrait/src/cast/cast_rc.rs diff --git a/kernel/crates/intertrait/src/cast/cast_ref.rs b/kernel/src/libs/intertrait/src/cast/cast_ref.rs similarity index 100% rename from kernel/crates/intertrait/src/cast/cast_ref.rs rename to kernel/src/libs/intertrait/src/cast/cast_ref.rs diff --git a/kernel/crates/intertrait/src/hasher.rs b/kernel/src/libs/intertrait/src/hasher.rs similarity index 100% rename from kernel/crates/intertrait/src/hasher.rs rename to kernel/src/libs/intertrait/src/hasher.rs diff --git a/kernel/crates/intertrait/src/lib.rs b/kernel/src/libs/intertrait/src/lib.rs similarity index 100% rename from kernel/crates/intertrait/src/lib.rs rename to kernel/src/libs/intertrait/src/lib.rs diff --git a/kernel/crates/intertrait/tests/castable_to.rs b/kernel/src/libs/intertrait/tests/castable_to.rs similarity index 100% rename from kernel/crates/intertrait/tests/castable_to.rs rename to kernel/src/libs/intertrait/tests/castable_to.rs diff --git a/kernel/crates/intertrait/tests/on-enum.rs b/kernel/src/libs/intertrait/tests/on-enum.rs similarity index 100% rename from kernel/crates/intertrait/tests/on-enum.rs rename to kernel/src/libs/intertrait/tests/on-enum.rs diff --git a/kernel/crates/intertrait/tests/on-struct.rs b/kernel/src/libs/intertrait/tests/on-struct.rs similarity index 100% rename from kernel/crates/intertrait/tests/on-struct.rs rename to kernel/src/libs/intertrait/tests/on-struct.rs diff --git a/kernel/crates/intertrait/tests/on-trait-impl-assoc-type1.rs b/kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type1.rs similarity index 100% rename from kernel/crates/intertrait/tests/on-trait-impl-assoc-type1.rs rename to kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type1.rs diff --git a/kernel/crates/intertrait/tests/on-trait-impl-assoc-type2.rs b/kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type2.rs similarity index 100% rename from kernel/crates/intertrait/tests/on-trait-impl-assoc-type2.rs rename to kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type2.rs diff --git a/kernel/crates/intertrait/tests/on-trait-impl-assoc-type3.rs b/kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type3.rs similarity index 100% rename from kernel/crates/intertrait/tests/on-trait-impl-assoc-type3.rs rename to kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type3.rs diff --git a/kernel/crates/intertrait/tests/on-trait-impl.rs b/kernel/src/libs/intertrait/tests/on-trait-impl.rs similarity index 100% rename from kernel/crates/intertrait/tests/on-trait-impl.rs rename to kernel/src/libs/intertrait/tests/on-trait-impl.rs diff --git a/kernel/crates/intertrait/tests/on-type-multi-traits.rs b/kernel/src/libs/intertrait/tests/on-type-multi-traits.rs similarity index 100% rename from kernel/crates/intertrait/tests/on-type-multi-traits.rs rename to kernel/src/libs/intertrait/tests/on-type-multi-traits.rs diff --git a/kernel/crates/intertrait/tests/run.rs b/kernel/src/libs/intertrait/tests/run.rs similarity index 100% rename from kernel/crates/intertrait/tests/run.rs rename to kernel/src/libs/intertrait/tests/run.rs diff --git a/kernel/crates/intertrait/tests/ui/duplicate-flags.rs b/kernel/src/libs/intertrait/tests/ui/duplicate-flags.rs similarity index 100% rename from kernel/crates/intertrait/tests/ui/duplicate-flags.rs rename to kernel/src/libs/intertrait/tests/ui/duplicate-flags.rs diff --git a/kernel/crates/intertrait/tests/ui/duplicate-flags.stderr b/kernel/src/libs/intertrait/tests/ui/duplicate-flags.stderr similarity index 100% rename from kernel/crates/intertrait/tests/ui/duplicate-flags.stderr rename to kernel/src/libs/intertrait/tests/ui/duplicate-flags.stderr diff --git a/kernel/crates/intertrait/tests/ui/on-generic-type.rs b/kernel/src/libs/intertrait/tests/ui/on-generic-type.rs similarity index 100% rename from kernel/crates/intertrait/tests/ui/on-generic-type.rs rename to kernel/src/libs/intertrait/tests/ui/on-generic-type.rs diff --git a/kernel/crates/intertrait/tests/ui/on-generic-type.stderr b/kernel/src/libs/intertrait/tests/ui/on-generic-type.stderr similarity index 100% rename from kernel/crates/intertrait/tests/ui/on-generic-type.stderr rename to kernel/src/libs/intertrait/tests/ui/on-generic-type.stderr diff --git a/kernel/crates/intertrait/tests/ui/on-type-impl.rs b/kernel/src/libs/intertrait/tests/ui/on-type-impl.rs similarity index 100% rename from kernel/crates/intertrait/tests/ui/on-type-impl.rs rename to kernel/src/libs/intertrait/tests/ui/on-type-impl.rs diff --git a/kernel/crates/intertrait/tests/ui/on-type-impl.stderr b/kernel/src/libs/intertrait/tests/ui/on-type-impl.stderr similarity index 100% rename from kernel/crates/intertrait/tests/ui/on-type-impl.stderr rename to kernel/src/libs/intertrait/tests/ui/on-type-impl.stderr diff --git a/kernel/crates/intertrait/tests/ui/unknown-flag.rs b/kernel/src/libs/intertrait/tests/ui/unknown-flag.rs similarity index 100% rename from kernel/crates/intertrait/tests/ui/unknown-flag.rs rename to kernel/src/libs/intertrait/tests/ui/unknown-flag.rs diff --git a/kernel/crates/intertrait/tests/ui/unknown-flag.stderr b/kernel/src/libs/intertrait/tests/ui/unknown-flag.stderr similarity index 100% rename from kernel/crates/intertrait/tests/ui/unknown-flag.stderr rename to kernel/src/libs/intertrait/tests/ui/unknown-flag.stderr diff --git a/kernel/src/libs/keyboard_parser.rs b/kernel/src/libs/keyboard_parser.rs index 2cfe57b3e..90271d810 100644 --- a/kernel/src/libs/keyboard_parser.rs +++ b/kernel/src/libs/keyboard_parser.rs @@ -64,7 +64,7 @@ pub enum TypeOneFSMState { impl TypeOneFSMState { /// @brief 状态机总控程序 fn parse(&self, scancode: u8, scancode_status: &mut ScanCodeStatus) -> TypeOneFSMState { - // debug!("the code is {:#x}\n", scancode); + // kdebug!("the code is {:#x}\n", scancode); match self { TypeOneFSMState::Start => { return self.handle_start(scancode, scancode_status); @@ -87,7 +87,7 @@ impl TypeOneFSMState { /// @brief 处理起始状态 fn handle_start(&self, scancode: u8, scancode_status: &mut ScanCodeStatus) -> TypeOneFSMState { - //debug!("in handle_start the code is {:#x}\n",scancode); + //kdebug!("in handle_start the code is {:#x}\n",scancode); match scancode { 0xe1 => { return TypeOneFSMState::PauseBreak(1); @@ -96,7 +96,7 @@ impl TypeOneFSMState { return TypeOneFSMState::Func0; } _ => { - //debug!("in _d the code is {:#x}\n",scancode); + //kdebug!("in _d the code is {:#x}\n",scancode); return TypeOneFSMState::Type3.handle_type3(scancode, scancode_status); } } @@ -274,7 +274,7 @@ impl TypeOneFSMState { let mut col: bool = false; let index = scancode & 0x7f; - //debug!("in type3 ch is {:#x}\n",ch); + //kdebug!("in type3 ch is {:#x}\n",ch); let mut key = KeyFlag::OtherKey; // 可视字符 match index { @@ -306,15 +306,14 @@ impl TypeOneFSMState { } _ => { if !flag_make { - // debug!("in type3 ch is {:#x}\n",ch); + // kdebug!("in type3 ch is {:#x}\n",ch); key = KeyFlag::NoneFlag; } } } // shift被按下 - let shift = scancode_status.shift_l || scancode_status.shift_r; - if shift { + if scancode_status.shift_l || scancode_status.shift_r { col = true; } @@ -328,8 +327,9 @@ impl TypeOneFSMState { let mut ch = TYPE1_KEY_CODE_MAPTABLE[col as usize + 2 * index as usize]; if key != KeyFlag::NoneFlag { + // kdebug!("EMIT: ch is '{}', keyflag is {:?}\n", ch as char, key); if scancode_status.ctrl_l || scancode_status.ctrl_r { - ch = Self::to_ctrl(ch, shift); + ch = Self::to_ctrl(ch); } Self::emit(ch); } @@ -337,16 +337,10 @@ impl TypeOneFSMState { } #[inline] - fn to_ctrl(ch: u8, shift: bool) -> u8 { + fn to_ctrl(ch: u8) -> u8 { return match ch as char { - 'a'..='z' => ch - 0x60, - 'A'..='Z' => { - if shift { - ch - } else { - ch - 0x40 - } - } + 'a'..='z' => ch - 0x40, + 'A'..='Z' => ch - 0x40, '@'..='_' => ch - 0x40, _ => ch, }; diff --git a/kernel/src/libs/lib_ui/screen_manager.rs b/kernel/src/libs/lib_ui/screen_manager.rs index 00f0bf96f..5ed9d49a3 100644 --- a/kernel/src/libs/lib_ui/screen_manager.rs +++ b/kernel/src/libs/lib_ui/screen_manager.rs @@ -240,7 +240,6 @@ pub trait ScmUiFramework: Sync + Send + Debug { return Err(SystemError::ENOSYS); } // 卸载ui框架的回调函数 - #[allow(dead_code)] fn uninstall(&self) -> Result { return Err(SystemError::ENOSYS); } diff --git a/kernel/src/libs/lib_ui/textui.rs b/kernel/src/libs/lib_ui/textui.rs index d04b64bb5..59747b15e 100644 --- a/kernel/src/libs/lib_ui/textui.rs +++ b/kernel/src/libs/lib_ui/textui.rs @@ -4,6 +4,7 @@ use crate::{ tty::{tty_port::tty_port, virtual_terminal::virtual_console::CURRENT_VCNUM}, video::video_refresh_manager, }, + kdebug, kinfo, libs::{ lib_ui::font::FONT_8x16, rwlock::RwLock, @@ -19,7 +20,6 @@ use core::{ ptr::copy_nonoverlapping, sync::atomic::{AtomicBool, AtomicI32, AtomicU32, Ordering}, }; -use log::{debug, info}; use system_error::SystemError; use super::{ @@ -73,9 +73,9 @@ pub fn textui_framework() -> Arc { /// 初始化TEXTUI_FRAMEWORK fn textui_framwork_init() { if unsafe { __TEXTUI_FRAMEWORK.is_none() } { - info!("textuiframework init"); + kinfo!("textuiframework init"); let metadata = ScmUiFrameworkMetadata::new("TextUI".to_string(), ScmFramworkType::Text); - debug!("textui metadata: {:?}", metadata); + kdebug!("textui metadata: {:?}", metadata); // 为textui框架生成第一个窗口 let vlines_num = (metadata.buf_info().height() / TEXTUI_CHAR_HEIGHT) as usize; @@ -106,7 +106,7 @@ fn textui_framwork_init() { }; scm_register(textui_framework()).expect("register textui framework failed"); - debug!("textui framework init success"); + kdebug!("textui framework init success"); send_to_default_serial8250_port("\ntext ui initialized\n\0".as_bytes()); unsafe { TEXTUI_IS_INIT = true }; @@ -822,7 +822,7 @@ impl TextuiWindow { //进行换行操作 if character == '\n' { // 换行时还需要输出\r - send_to_default_serial8250_port(b"\r"); + send_to_default_serial8250_port(&[b'\r']); if is_enable_window { self.textui_new_line()?; } @@ -989,8 +989,8 @@ impl ScmUiFramework for TextUiFramework { let mut new_buf = textui_framework().metadata.read().buf_info(); new_buf.copy_from_nonoverlapping(&old_buf); - debug!("textui change buf_info: old: {:?}", old_buf); - debug!("textui change buf_info: new: {:?}", new_buf); + kdebug!("textui change buf_info: old: {:?}", old_buf); + kdebug!("textui change buf_info: new: {:?}", new_buf); return Ok(0); } diff --git a/kernel/src/libs/lib_ui/textui_no_alloc.rs b/kernel/src/libs/lib_ui/textui_no_alloc.rs index 36718f932..9387cdc55 100644 --- a/kernel/src/libs/lib_ui/textui_no_alloc.rs +++ b/kernel/src/libs/lib_ui/textui_no_alloc.rs @@ -54,7 +54,7 @@ pub fn no_init_textui_putchar_window( match character { // 进行换行操作 '\n' => { - send_to_default_serial8250_port(b"\r"); + send_to_default_serial8250_port(&[b'\r']); if is_put_to_window { next_line(); } diff --git a/kernel/src/libs/mod.rs b/kernel/src/libs/mod.rs index 42788bddc..f15630bd3 100644 --- a/kernel/src/libs/mod.rs +++ b/kernel/src/libs/mod.rs @@ -2,6 +2,7 @@ pub mod align; pub mod casting; pub mod cpumask; pub mod elf; +pub mod ffi_convert; #[macro_use] pub mod int_like; pub mod keyboard_parser; @@ -26,4 +27,3 @@ pub mod rand; pub mod wait_queue; pub mod font; -pub mod name; diff --git a/kernel/src/libs/name.rs b/kernel/src/libs/name.rs deleted file mode 100644 index bca4467c1..000000000 --- a/kernel/src/libs/name.rs +++ /dev/null @@ -1,13 +0,0 @@ -use core::any::type_name; - -use alloc::string::{String, ToString}; - -#[allow(dead_code)] -pub fn get_full_type_name(_: &T) -> String { - type_name::().to_string() -} - -pub fn get_type_name(_: &T) -> String { - let full_name = type_name::(); - full_name[(full_name.rfind("::").unwrap_or(0) + 2)..].to_string() -} diff --git a/kernel/src/libs/notifier.rs b/kernel/src/libs/notifier.rs index ee470d174..ea4dcdc76 100644 --- a/kernel/src/libs/notifier.rs +++ b/kernel/src/libs/notifier.rs @@ -1,9 +1,11 @@ #![allow(dead_code)] use core::fmt::Debug; -use crate::libs::{rwlock::RwLock, spinlock::SpinLock}; +use crate::{ + kwarn, + libs::{rwlock::RwLock, spinlock::SpinLock}, +}; use alloc::{sync::Arc, vec::Vec}; -use log::warn; use system_error::SystemError; /// @brief 通知链节点 @@ -37,7 +39,7 @@ impl NotifierChain { for b in self.0.iter() { // 判断之前是否已经注册过该节点 if Arc::ptr_eq(&block, b) { - warn!( + kwarn!( "notifier callback {:?} already registered", Arc::as_ptr(&block) ); diff --git a/kernel/src/libs/printk.rs b/kernel/src/libs/printk.rs index 65f630041..29ed9c165 100644 --- a/kernel/src/libs/printk.rs +++ b/kernel/src/libs/printk.rs @@ -4,7 +4,7 @@ use core::{ }; use alloc::string::ToString; -use log::{info, Level, Log}; +use log::{info, Log}; use super::lib_ui::textui::{textui_putstr, FontColor}; @@ -33,6 +33,49 @@ macro_rules! println { ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } +#[macro_export] +macro_rules! kdebug { + ($($arg:tt)*) => { + $crate::libs::printk::Logger.log(7,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); + $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("[ DEBUG ] ({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))) + } +} + +#[macro_export] +macro_rules! kinfo { + ($($arg:tt)*) => { + $crate::libs::printk::Logger.log(6,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); + $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("[ INFO ] ({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))) + } +} + +#[macro_export] +macro_rules! kwarn { + ($($arg:tt)*) => { + $crate::libs::printk::Logger.log(4,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); + $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("\x1B[1;33m[ WARN ] \x1B[0m")); + $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); + } +} + +#[macro_export] +macro_rules! kerror { + ($($arg:tt)*) => { + $crate::libs::printk::Logger.log(3,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); + $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("\x1B[41m[ ERROR ] \x1B[0m")); + $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); + } +} + +#[macro_export] +macro_rules! kBUG { + ($($arg:tt)*) => { + $crate::libs::printk::Logger.log(1,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); + $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("\x1B[41m[ BUG ] \x1B[0m")); + $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); + } +} + pub struct PrintkWriter; impl PrintkWriter { @@ -90,10 +133,10 @@ impl Logger { /// 内核自定义日志器 /// -/// todo: https://github.com/DragonOS-Community/DragonOS/issues/762 -struct KernelLogger; +/// todo: 完善他的功能,并且逐步把kinfo等宏,迁移到这个logger上面来。 +struct CustomLogger; -impl Log for KernelLogger { +impl Log for CustomLogger { fn enabled(&self, _metadata: &log::Metadata) -> bool { // 这里可以自定义日志过滤规则 true @@ -102,8 +145,17 @@ impl Log for KernelLogger { fn log(&self, record: &log::Record) { if self.enabled(record.metadata()) { // todo: 接入kmsg - Self::kernel_log(record); - Self::iodisplay(record) + + writeln!( + PrintkWriter, + "[ {} ] {} ({}:{}) {}", + record.level(), + record.target(), + record.file().unwrap_or(""), + record.line().unwrap_or(0), + record.args() + ) + .unwrap(); } } @@ -112,77 +164,8 @@ impl Log for KernelLogger { } } -impl KernelLogger { - fn iodisplay(record: &log::Record) { - match record.level() { - Level::Debug | Level::Info | Level::Trace => { - write!(PrintkWriter, "[ {} ] ", record.level(),) - } - Level::Error => { - write!(PrintkWriter, "\x1B[41m[ ERROR ] \x1B[0m",) - } - Level::Warn => { - write!(PrintkWriter, "\x1B[1;33m[ WARN ] \x1B[0m",) - } - } - .unwrap(); - writeln!( - PrintkWriter, - "({}:{})\t {}", - record.file().unwrap_or(""), - record.line().unwrap_or(0), - record.args() - ) - .unwrap(); - } - - fn kernel_log(record: &log::Record) { - match record.level() { - Level::Debug => Logger.log( - 7, - format_args!( - "({}:{})\t {}\n", - record.file().unwrap_or(""), - record.line().unwrap_or(0), - record.args() - ), - ), - Level::Error => Logger.log( - 3, - format_args!( - "({}:{})\t {}\n", - record.file().unwrap_or(""), - record.line().unwrap_or(0), - record.args() - ), - ), - Level::Info => Logger.log( - 6, - format_args!( - "({}:{})\t {}\n", - record.file().unwrap_or(""), - record.line().unwrap_or(0), - record.args() - ), - ), - Level::Warn => Logger.log( - 4, - format_args!( - "({}:{})\t {}\n", - record.file().unwrap_or(""), - record.line().unwrap_or(0), - record.args() - ), - ), - Level::Trace => { - todo!() - } - } - } -} - pub fn early_init_logging() { - log::set_logger(&KernelLogger).unwrap(); + log::set_logger(&CustomLogger).unwrap(); log::set_max_level(log::LevelFilter::Debug); info!("Logging initialized"); } diff --git a/kernel/src/libs/rbtree.rs b/kernel/src/libs/rbtree.rs index f1fed00f0..aed58744f 100644 --- a/kernel/src/libs/rbtree.rs +++ b/kernel/src/libs/rbtree.rs @@ -22,7 +22,8 @@ use core::ops::Index; use core::ptr; use alloc::boxed::Box; -use log::debug; + +use crate::kdebug; #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum Color { @@ -302,7 +303,7 @@ impl NodePtr { /// /// // check for a specific one. /// if !book_reviews.contains_key(&"Les Misérables") { -/// debug!( +/// kdebug!( /// "We've got {} reviews, but Les Misérables ain't one.", /// book_reviews.len() /// ); @@ -315,14 +316,14 @@ impl NodePtr { /// let to_find = ["Pride and Prejudice", "Alice's Adventure in Wonderland"]; /// for book in &to_find { /// match book_reviews.get(book) { -/// Some(review) => debug!("{}: {}", book, review), -/// None => debug!("{} is unreviewed.", book), +/// Some(review) => kdebug!("{}: {}", book, review), +/// None => kdebug!("{} is unreviewed.", book), /// } /// } /// /// // iterate over everything. /// for (book, review) in book_reviews.iter() { -/// debug!("{}: \"{}\"", book, review); +/// kdebug!("{}: \"{}\"", book, review); /// } /// /// book_reviews.print_tree(); @@ -385,12 +386,12 @@ impl RBTree { } if direction == 0 { unsafe { - debug!("'{:?}' is root node", (*node.0)); + kdebug!("'{:?}' is root node", (*node.0)); } } else { let direct = if direction == -1 { "left" } else { "right" }; unsafe { - debug!( + kdebug!( "{:?} is {:?}'s {:?} child ", (*node.0), *node.parent().0, @@ -404,12 +405,12 @@ impl RBTree { pub fn print_tree(&self) { if self.root.is_null() { - debug!("This is a empty tree"); + kdebug!("This is a empty tree"); return; } - debug!("This tree size = {:?}, begin:-------------", self.len()); + kdebug!("This tree size = {:?}, begin:-------------", self.len()); self.tree_print(self.root, 0); - debug!("end--------------------------"); + kdebug!("end--------------------------"); } } @@ -719,7 +720,7 @@ impl<'a, K: Ord + Debug + 'a, V: Debug + 'a> Iterator for Iter<'a, K, V> { impl<'a, K: Ord + Debug + 'a, V: Debug + 'a> DoubleEndedIterator for Iter<'a, K, V> { #[inline] fn next_back(&mut self) -> Option<(&'a K, &'a V)> { - // debug!("len = {:?}", self.len); + // kdebug!("len = {:?}", self.len); if self.len == 0 { return None; } diff --git a/kernel/src/libs/rwlock.rs b/kernel/src/libs/rwlock.rs index 63af797fc..081616044 100644 --- a/kernel/src/libs/rwlock.rs +++ b/kernel/src/libs/rwlock.rs @@ -101,7 +101,7 @@ impl RwLock { #[inline] /// @brief 获取实时的读者数并尝试加1,如果增加值成功则返回增加1后的读者数,否则panic fn current_reader(&self) -> Result { - const MAX_READERS: u32 = u32::MAX >> READER_BIT >> 1; //右移3位 + const MAX_READERS: u32 = core::u32::MAX >> READER_BIT >> 1; //右移3位 let value = self.lock.fetch_add(READER, Ordering::Acquire); //value二进制形式的MSB不能为1, 否则导致溢出 diff --git a/kernel/src/libs/semaphore.rs b/kernel/src/libs/semaphore.rs index 5cf600d3d..163c6dc45 100644 --- a/kernel/src/libs/semaphore.rs +++ b/kernel/src/libs/semaphore.rs @@ -1,9 +1,8 @@ use core::sync::atomic::{AtomicI32, Ordering}; -use log::debug; use system_error::SystemError; -use crate::process::ProcessManager; +use crate::{kdebug, process::ProcessManager}; use super::wait_queue::WaitQueue; @@ -52,7 +51,7 @@ impl Semaphore { //尝试唤醒 if !self.wait_queue.wakeup(None) { //如果唤醒失败,打印错误信息 - debug!( + kdebug!( "Semaphore wakeup failed: current pid= {}, semaphore={:?}", ProcessManager::current_pcb().pid().into(), self diff --git a/kernel/src/libs/wait_queue.rs b/kernel/src/libs/wait_queue.rs index efb3606d3..25e985b9e 100644 --- a/kernel/src/libs/wait_queue.rs +++ b/kernel/src/libs/wait_queue.rs @@ -2,12 +2,11 @@ use core::intrinsics::unlikely; use alloc::{collections::LinkedList, sync::Arc, vec::Vec}; -use log::{error, warn}; -use system_error::SystemError; use crate::{ - arch::{ipc::signal::Signal, CurrentIrqArch}, + arch::CurrentIrqArch, exception::InterruptArch, + kerror, process::{ProcessControlBlock, ProcessManager, ProcessState}, sched::{schedule, SchedMode}, }; @@ -33,34 +32,6 @@ impl WaitQueue { WaitQueue(SpinLock::new(InnerWaitQueue::INIT)) } - pub fn prepare_to_wait_event(&self, interruptible: bool) -> Result<(), SystemError> { - let mut guard: SpinLockGuard = self.0.lock_irqsave(); - let pcb = ProcessManager::current_pcb(); - if Signal::signal_pending_state(interruptible, false, &pcb) { - return Err(SystemError::ERESTARTSYS); - } else { - ProcessManager::mark_sleep(interruptible).unwrap_or_else(|e| { - panic!("sleep error: {:?}", e); - }); - guard.wait_list.push_back(ProcessManager::current_pcb()); - drop(guard); - } - Ok(()) - } - - pub fn finish_wait(&self) { - let pcb = ProcessManager::current_pcb(); - let mut writer = pcb.sched_info().inner_lock_write_irqsave(); - let mut guard: SpinLockGuard = self.0.lock_irqsave(); - - writer.set_state(ProcessState::Runnable); - writer.set_wakeup(); - - guard.wait_list.retain(|x| !Arc::ptr_eq(x, &pcb)); - drop(guard); - drop(writer); - } - /// @brief 让当前进程在等待队列上进行等待,并且,允许被信号打断 pub fn sleep(&self) { before_sleep_check(0); @@ -79,7 +50,7 @@ impl WaitQueue { F: FnOnce(), { before_sleep_check(0); - let mut guard: SpinLockGuard = self.0.lock_irqsave(); + let mut guard: SpinLockGuard = self.0.lock(); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_sleep(true).unwrap_or_else(|e| { panic!("sleep error: {:?}", e); @@ -266,7 +237,7 @@ impl WaitQueue { if wake { ProcessManager::wakeup(&to_wakeup).unwrap_or_else(|e| { - error!("wakeup pid: {:?} error: {:?}", to_wakeup.pid(), e); + kerror!("wakeup pid: {:?} error: {:?}", to_wakeup.pid(), e); }); continue; } else { @@ -294,7 +265,7 @@ impl InnerWaitQueue { fn before_sleep_check(max_preempt: usize) { let pcb = ProcessManager::current_pcb(); if unlikely(pcb.preempt_count() > max_preempt) { - warn!( + kwarn!( "Process {:?}: Try to sleep when preempt count is {}", pcb.pid().data(), pcb.preempt_count() diff --git a/kernel/src/misc/ksysfs.rs b/kernel/src/misc/ksysfs.rs index 5041c650e..f4f45248c 100644 --- a/kernel/src/misc/ksysfs.rs +++ b/kernel/src/misc/ksysfs.rs @@ -7,7 +7,6 @@ use crate::{ init::initcall::INITCALL_CORE, }; use alloc::{string::ToString, sync::Arc}; -use log::error; use system_error::SystemError; use unified_init::macros::unified_init; @@ -30,7 +29,7 @@ fn ksysfs_init() -> Result<(), SystemError> { sysfs_instance() .create_groups(&kernel_kset.as_kobject(), &[&KernelAttrGroup]) .map_err(|e| { - error!("Failed to create sysfs groups for kernel kset: {:?}", e); + kerror!("Failed to create sysfs groups for kernel kset: {:?}", e); kernel_kset.unregister(); SystemError::ENOMEM })?; diff --git a/kernel/src/mm/allocator/buddy.rs b/kernel/src/mm/allocator/buddy.rs index 50ee1a3b1..deff40669 100644 --- a/kernel/src/mm/allocator/buddy.rs +++ b/kernel/src/mm/allocator/buddy.rs @@ -1,5 +1,3 @@ -use log::{debug, warn}; - /// @Author: longjin@dragonos.org /// @Author: kongweichao@dragonos.org /// @Date: 2023-03-28 16:03:47 @@ -9,7 +7,7 @@ use crate::arch::MMArch; use crate::mm::allocator::bump::BumpAllocator; use crate::mm::allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage}; use crate::mm::{MemoryManagementArch, PhysAddr, PhysMemoryArea, VirtAddr}; - +use crate::{kdebug, kwarn}; use core::cmp::min; use core::fmt::Debug; use core::intrinsics::{likely, unlikely}; @@ -80,8 +78,8 @@ impl BuddyAllocator { pub unsafe fn new(mut bump_allocator: BumpAllocator) -> Option { let initial_free_pages = bump_allocator.usage().free(); let total_memory = bump_allocator.usage().total(); - debug!("Free pages before init buddy: {:?}", initial_free_pages); - debug!("Buddy entries: {}", Self::BUDDY_ENTRIES); + kdebug!("Free pages before init buddy: {:?}", initial_free_pages); + kdebug!("Buddy entries: {}", Self::BUDDY_ENTRIES); let mut free_area: [PhysAddr; MAX_ORDER - MIN_ORDER] = [PhysAddr::new(0); MAX_ORDER - MIN_ORDER]; @@ -120,7 +118,7 @@ impl BuddyAllocator { if remain_pages.data() == 0 { continue; } - debug!("area: {area:?}, paddr: {paddr:#x}, remain_pages: {remain_pages:?}"); + kdebug!("area: {area:?}, paddr: {paddr:#x}, remain_pages: {remain_pages:?}"); total_pages_to_buddy += remain_pages; @@ -130,7 +128,7 @@ impl BuddyAllocator { // 先从低阶开始,尽可能地填满空闲链表 for i in MIN_ORDER..MAX_ORDER { - // debug!("i {i}, remain pages={}", remain_pages.data()); + // kdebug!("i {i}, remain pages={}", remain_pages.data()); if remain_pages.data() < (1 << (i - MIN_ORDER)) { break; } @@ -177,7 +175,7 @@ impl BuddyAllocator { assert!(remain_bytes == 0); } - debug!("Total pages to buddy: {:?}", total_pages_to_buddy); + kdebug!("Total pages to buddy: {:?}", total_pages_to_buddy); allocator.total = total_memory; Some(allocator) @@ -240,7 +238,7 @@ impl BuddyAllocator { if !next_page_list_addr.is_null() { // 此时page_list已经没有空闲伙伴块了,又因为非唯一页,需要删除该page_list self.free_area[Self::order2index(spec_order)] = next_page_list_addr; - // debug!("FREE: page_list_addr={:b}", page_list_addr.data()); + // kdebug!("FREE: page_list_addr={:b}", page_list_addr.data()); unsafe { self.buddy_free(page_list_addr, MMArch::PAGE_SHIFT as u8); } @@ -275,7 +273,7 @@ impl BuddyAllocator { page_list.entry_num - 1 ); } - // debug!("entry={entry:?}"); + // kdebug!("entry={entry:?}"); // 更新page_list的entry_num page_list.entry_num -= 1; @@ -304,7 +302,7 @@ impl BuddyAllocator { return None; }; let result: Option = alloc_in_specific_order(order); - // debug!("result={:?}", result); + // kdebug!("result={:?}", result); if result.is_some() { return result; } @@ -314,14 +312,14 @@ impl BuddyAllocator { let mut x: Option = None; while current_order < MAX_ORDER { x = alloc_in_specific_order(current_order as u8); - // debug!("current_order={:?}", current_order); + // kdebug!("current_order={:?}", current_order); if x.is_some() { break; } current_order += 1; } - // debug!("x={:?}", x); + // kdebug!("x={:?}", x); // 如果找到一个大的块,就进行分裂 if x.is_some() { // 分裂到order阶 @@ -330,8 +328,8 @@ impl BuddyAllocator { // 把后面那半块放回空闲链表 let buddy = *x.as_ref().unwrap() + (1 << current_order); - // debug!("x={:?}, buddy={:?}", x, buddy); - // debug!("current_order={:?}, buddy={:?}", current_order, buddy); + // kdebug!("x={:?}, buddy={:?}", x, buddy); + // kdebug!("current_order={:?}, buddy={:?}", current_order, buddy); unsafe { self.buddy_free(buddy, current_order as u8) }; } return x; @@ -361,10 +359,10 @@ impl BuddyAllocator { return None; } - // debug!("buddy_alloc: order = {}", order); + // kdebug!("buddy_alloc: order = {}", order); // 获取该阶数的一个空闲页面 let free_addr = self.pop_front(order); - // debug!( + // kdebug!( // "buddy_alloc: order = {}, free_addr = {:?}", // order, // free_addr @@ -380,7 +378,7 @@ impl BuddyAllocator { /// - `base` - 块的起始地址 /// - `order` - 块的阶数 unsafe fn buddy_free(&mut self, mut base: PhysAddr, order: u8) { - // debug!("buddy_free: base = {:?}, order = {}", base, order); + // kdebug!("buddy_free: base = {:?}, order = {}", base, order); let mut order = order as usize; while order < MAX_ORDER { @@ -559,7 +557,7 @@ impl BuddyAllocator { (first_page_list_paddr, first_page_list) }; - // debug!("to write entry, page_list_base={paddr:?}, page_list.entry_num={}, value={base:?}", page_list.entry_num); + // kdebug!("to write entry, page_list_base={paddr:?}, page_list.entry_num={}, value={base:?}", page_list.entry_num); assert!(page_list.entry_num < Self::BUDDY_ENTRIES); // 把要归还的块,写入到链表项中 unsafe { A::write(Self::entry_virt_addr(paddr, page_list.entry_num), base) } @@ -593,14 +591,14 @@ impl FrameAllocator for BuddyAllocator { unsafe fn free(&mut self, base: PhysAddr, count: PageFrameCount) { // 要求count是2的幂 if unlikely(!count.data().is_power_of_two()) { - warn!("buddy free: count is not power of two"); + kwarn!("buddy free: count is not power of two"); } let mut order = log2(count.data()); if count.data() & ((1 << order) - 1) != 0 { order += 1; } let order = (order + MIN_ORDER) as u8; - // debug!("free: base={:?}, count={:?}", base, count); + // kdebug!("free: base={:?}, count={:?}", base, count); self.buddy_free(base, order); } diff --git a/kernel/src/mm/allocator/kernel_allocator.rs b/kernel/src/mm/allocator/kernel_allocator.rs index 6201fef33..9e55a4ab5 100644 --- a/kernel/src/mm/allocator/kernel_allocator.rs +++ b/kernel/src/mm/allocator/kernel_allocator.rs @@ -20,7 +20,6 @@ use super::{ /// 类kmalloc的分配器应当实现的trait pub trait LocalAlloc { - #[allow(dead_code)] unsafe fn local_alloc(&self, layout: Layout) -> *mut u8; unsafe fn local_alloc_zeroed(&self, layout: Layout) -> *mut u8; unsafe fn local_dealloc(&self, ptr: *mut u8, layout: Layout); diff --git a/kernel/src/mm/allocator/page_frame.rs b/kernel/src/mm/allocator/page_frame.rs index 180a2ac25..75a79529f 100644 --- a/kernel/src/mm/allocator/page_frame.rs +++ b/kernel/src/mm/allocator/page_frame.rs @@ -371,9 +371,8 @@ pub unsafe fn deallocate_page_frames( if let Some(page) = page { // 如果page是共享页,将其共享页信息从SHM_MANAGER中删去 - let page_guard = page.read_irqsave(); - if page_guard.shared() { - shm_manager_lock().free_id(&page_guard.shm_id().unwrap()); + if page.shared() { + shm_manager_lock().free_id(&page.shm_id().unwrap()); } } diff --git a/kernel/src/mm/allocator/slab.rs b/kernel/src/mm/allocator/slab.rs index 710f9b7b3..a21d4ca96 100644 --- a/kernel/src/mm/allocator/slab.rs +++ b/kernel/src/mm/allocator/slab.rs @@ -1,7 +1,6 @@ use core::{alloc::Layout, ptr::NonNull, sync::atomic::AtomicBool}; use alloc::boxed::Box; -use log::debug; use slabmalloc::*; // 全局slab分配器 @@ -19,7 +18,7 @@ pub(crate) struct SlabAllocator { impl SlabAllocator { /// 创建slab分配器 pub fn new() -> SlabAllocator { - debug!("trying to new a slab_allocator"); + kdebug!("trying to new a slab_allocator"); SlabAllocator { zone: ZoneAllocator::new(), } @@ -63,7 +62,7 @@ impl SlabAllocator { /// 初始化slab分配器 pub unsafe fn slab_init() { - debug!("trying to init a slab_allocator"); + kdebug!("trying to init a slab_allocator"); SLABALLOCATOR = Some(SlabAllocator::new()); SLABINITSTATE = true.into(); } diff --git a/kernel/src/mm/c_adapter.rs b/kernel/src/mm/c_adapter.rs index 1c824595d..35ac24de9 100644 --- a/kernel/src/mm/c_adapter.rs +++ b/kernel/src/mm/c_adapter.rs @@ -4,18 +4,18 @@ use core::intrinsics::unlikely; use alloc::vec::Vec; use hashbrown::HashMap; -use log::error; use system_error::SystemError; use crate::{ include::bindings::bindings::{gfp_t, PAGE_U_S}, + kerror, libs::{align::page_align_up, spinlock::SpinLock}, mm::MMArch, }; use super::{ allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, mmio_buddy::mmio_pool, - no_init::pseudo_map_phys, page::EntryFlags, MemoryManagementArch, PhysAddr, VirtAddr, + no_init::pseudo_map_phys, page::PageFlags, MemoryManagementArch, PhysAddr, VirtAddr, }; lazy_static! { @@ -38,9 +38,9 @@ pub unsafe extern "C" fn rs_map_phys(vaddr: usize, paddr: usize, size: usize, fl let mut vaddr = VirtAddr::new(vaddr); let mut paddr = PhysAddr::new(paddr); let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); - // debug!("rs_map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); + // kdebug!("rs_map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); - let mut page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); + let mut page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); if flags & PAGE_U_S as usize != 0 { page_flags = page_flags.set_user(true); } @@ -64,13 +64,13 @@ pub unsafe extern "C" fn rs_map_phys(vaddr: usize, paddr: usize, size: usize, fl #[no_mangle] pub unsafe extern "C" fn kzalloc(size: usize, _gfp: gfp_t) -> usize { - // debug!("kzalloc: size: {size}"); + // kdebug!("kzalloc: size: {size}"); return do_kmalloc(size, true); } #[no_mangle] pub unsafe extern "C" fn kmalloc(size: usize, _gfp: gfp_t) -> usize { - // debug!("kmalloc: size: {size}"); + // kdebug!("kmalloc: size: {size}"); // 由于C代码不规范,因此都全部清空 return do_kmalloc(size, true); } @@ -109,7 +109,7 @@ pub unsafe extern "C" fn kfree(vaddr: usize) -> usize { drop(guard); if p.is_none() { - error!("kfree: vaddr {:?} not found in C Allocation Map", vaddr); + kerror!("kfree: vaddr {:?} not found in C Allocation Map", vaddr); return SystemError::EINVAL.to_posix_errno() as i64 as usize; } let (vaddr, len, cap) = p.unwrap(); @@ -135,7 +135,7 @@ unsafe extern "C" fn rs_mmio_create( res_vaddr: *mut u64, res_length: *mut u64, ) -> i32 { - // debug!("mmio_create"); + // kdebug!("mmio_create"); let r = mmio_pool().create_mmio(size as usize); if let Err(e) = r { return e.to_posix_errno(); diff --git a/kernel/src/mm/early_ioremap.rs b/kernel/src/mm/early_ioremap.rs index 0daec6168..225002b93 100644 --- a/kernel/src/mm/early_ioremap.rs +++ b/kernel/src/mm/early_ioremap.rs @@ -45,7 +45,7 @@ impl EarlyIoRemap { mut size: usize, read_only: bool, ) -> Result { - // debug!("map not aligned phys:{phys:?}, size:{size:?}, read_only:{read_only:?}"); + // kdebug!("map not aligned phys:{phys:?}, size:{size:?}, read_only:{read_only:?}"); let offset = phys.data() - page_align_down(phys.data()); size += offset; @@ -82,7 +82,7 @@ impl EarlyIoRemap { return Err(SystemError::EINVAL); } - // debug!("Early io remap:{phys:?}, size:{size}"); + // kdebug!("Early io remap:{phys:?}, size:{size}"); let mut slot_guard = SLOTS.lock(); @@ -111,7 +111,7 @@ impl EarlyIoRemap { let start_slot = start_slot.ok_or(SystemError::ENOMEM)?; let vaddr = Self::idx_to_virt(start_slot); - // debug!("start_slot:{start_slot}, vaddr: {vaddr:?}, slot_count: {slot_count:?}"); + // kdebug!("start_slot:{start_slot}, vaddr: {vaddr:?}, slot_count: {slot_count:?}"); let page_count = PageFrameCount::new(slot_count); // 执行映射 if read_only { @@ -120,7 +120,7 @@ impl EarlyIoRemap { unsafe { pseudo_map_phys(vaddr, phys, page_count) } } - // debug!("map ok"); + // kdebug!("map ok"); // 更新slot信息 let map_size = slot_count * MMArch::PAGE_SIZE; diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 9055cdf5f..71e0623ae 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -1,17 +1,11 @@ -use core::{ - alloc::Layout, - cmp::{max, min}, - intrinsics::unlikely, - panic, -}; +use core::{alloc::Layout, intrinsics::unlikely, panic}; use alloc::sync::Arc; use crate::{ arch::{mm::PageMapper, MMArch}, - libs::align::align_down, mm::{ - page::{page_manager_lock_irqsave, EntryFlags}, + page::{page_manager_lock_irqsave, PageFlags}, ucontext::LockedVMA, VirtAddr, VmFaultReason, VmFlags, }, @@ -20,68 +14,39 @@ use crate::{ use crate::mm::MemoryManagementArch; -use super::{ - allocator::page_frame::FrameAllocator, - page::{page_reclaimer_lock_irqsave, Page, PageFlags}, -}; - bitflags! { pub struct FaultFlags: u64{ - const FAULT_FLAG_WRITE = 1 << 0; - const FAULT_FLAG_MKWRITE = 1 << 1; - const FAULT_FLAG_ALLOW_RETRY = 1 << 2; - const FAULT_FLAG_RETRY_NOWAIT = 1 << 3; - const FAULT_FLAG_KILLABLE = 1 << 4; - const FAULT_FLAG_TRIED = 1 << 5; - const FAULT_FLAG_USER = 1 << 6; - const FAULT_FLAG_REMOTE = 1 << 7; - const FAULT_FLAG_INSTRUCTION = 1 << 8; - const FAULT_FLAG_INTERRUPTIBLE =1 << 9; - const FAULT_FLAG_UNSHARE = 1 << 10; - const FAULT_FLAG_ORIG_PTE_VALID = 1 << 11; - const FAULT_FLAG_VMA_LOCK = 1 << 12; + const FAULT_FLAG_WRITE = 1 << 0; + const FAULT_FLAG_MKWRITE = 1 << 1; + const FAULT_FLAG_ALLOW_RETRY = 1 << 2; + const FAULT_FLAG_RETRY_NOWAIT = 1 << 3; + const FAULT_FLAG_KILLABLE = 1 << 4; + const FAULT_FLAG_TRIED = 1 << 5; + const FAULT_FLAG_USER = 1 << 6; + const FAULT_FLAG_REMOTE = 1 << 7; + const FAULT_FLAG_INSTRUCTION = 1 << 8; + const FAULT_FLAG_INTERRUPTIBLE =1 << 9; + const FAULT_FLAG_UNSHARE = 1 << 10; + const FAULT_FLAG_ORIG_PTE_VALID = 1 << 11; + const FAULT_FLAG_VMA_LOCK = 1 << 12; } } /// # 缺页异常信息结构体 /// 包含了页面错误处理的相关信息,例如出错的地址、VMA等 #[derive(Debug)] -pub struct PageFaultMessage<'a> { - /// 产生缺页的VMA结构体 +pub struct PageFaultMessage { vma: Arc, - /// 缺页地址 address: VirtAddr, - /// 异常处理标志 flags: FaultFlags, - /// 页表映射器 - mapper: &'a mut PageMapper, - /// 缺页的文件页在文件中的偏移量 - file_pgoff: Option, - /// 缺页对应PageCache中的文件页 - page: Option>, - /// 写时拷贝需要的页面 - cow_page: Option>, } -impl<'a> PageFaultMessage<'a> { - pub fn new( - vma: Arc, - address: VirtAddr, - flags: FaultFlags, - mapper: &'a mut PageMapper, - ) -> Self { - let guard = vma.lock_irqsave(); - let file_pgoff = guard.file_page_offset().map(|file_page_offset| { - ((address - guard.region().start()) >> MMArch::PAGE_SHIFT) + file_page_offset - }); +impl PageFaultMessage { + pub fn new(vma: Arc, address: VirtAddr, flags: FaultFlags) -> Self { Self { vma: vma.clone(), - address: VirtAddr::new(crate::libs::align::page_align_down(address.data())), + address, flags, - file_pgoff, - page: None, - mapper, - cow_page: None, } } @@ -110,6 +75,16 @@ impl<'a> PageFaultMessage<'a> { } } +impl Clone for PageFaultMessage { + fn clone(&self) -> Self { + Self { + vma: self.vma.clone(), + address: self.address, + flags: self.flags, + } + } +} + /// 缺页中断处理结构体 pub struct PageFaultHandler; @@ -122,7 +97,7 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn handle_mm_fault(mut pfm: PageFaultMessage) -> VmFaultReason { + pub unsafe fn handle_mm_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { let flags = pfm.flags(); let vma = pfm.vma(); let current_pcb = ProcessManager::current_pcb(); @@ -138,13 +113,13 @@ impl PageFaultHandler { return VmFaultReason::VM_FAULT_SIGSEGV; } - let guard = vma.lock_irqsave(); + let guard = vma.lock(); let vm_flags = *guard.vm_flags(); drop(guard); if unlikely(vm_flags.contains(VmFlags::VM_HUGETLB)) { //TODO: 添加handle_hugetlb_fault处理大页缺页异常 } else { - Self::handle_normal_fault(&mut pfm); + Self::handle_normal_fault(pfm, mapper); } VmFaultReason::VM_FAULT_COMPLETED @@ -158,16 +133,18 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn handle_normal_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + pub unsafe fn handle_normal_fault( + pfm: PageFaultMessage, + mapper: &mut PageMapper, + ) -> VmFaultReason { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); - let mapper = &mut pfm.mapper; if mapper.get_entry(address, 3).is_none() { mapper .allocate_table(address, 2) .expect("failed to allocate PUD table"); } - let page_flags = vma.lock_irqsave().flags(); + let page_flags = vma.lock().flags(); for level in 2..=3 { let level = MMArch::PAGE_LEVELS - level; @@ -182,7 +159,7 @@ impl PageFaultHandler { } } - Self::handle_pte_fault(pfm) + Self::handle_pte_fault(pfm, mapper) } /// 处理页表项异常 @@ -193,37 +170,35 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn handle_pte_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + pub unsafe fn handle_pte_fault( + pfm: PageFaultMessage, + mapper: &mut PageMapper, + ) -> VmFaultReason { let address = pfm.address_aligned_down(); let flags = pfm.flags; let vma = pfm.vma.clone(); let mut ret = VmFaultReason::VM_FAULT_COMPLETED; - let mapper = &pfm.mapper; - - // pte存在 if let Some(mut entry) = mapper.get_entry(address, 0) { if !entry.present() { - ret = Self::do_swap_page(pfm); + ret = Self::do_swap_page(pfm.clone(), mapper); } - if entry.protnone() && vma.is_accessible() { - ret = Self::do_numa_page(pfm); + ret = Self::do_numa_page(pfm.clone(), mapper); } - if flags.intersects(FaultFlags::FAULT_FLAG_WRITE | FaultFlags::FAULT_FLAG_UNSHARE) { if !entry.write() { - ret = Self::do_wp_page(pfm); + ret = Self::do_wp_page(pfm.clone(), mapper); } else { - entry.set_flags(EntryFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); + entry.set_flags(PageFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); } } } else if vma.is_anonymous() { - ret = Self::do_anonymous_page(pfm); + ret = Self::do_anonymous_page(pfm.clone(), mapper); } else { - ret = Self::do_fault(pfm); + ret = Self::do_fault(pfm.clone(), mapper); } - vma.lock_irqsave().set_mapped(true); + vma.lock().set_mapped(true); return ret; } @@ -236,12 +211,13 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_anonymous_page(pfm: &mut PageFaultMessage) -> VmFaultReason { + pub unsafe fn do_anonymous_page( + pfm: PageFaultMessage, + mapper: &mut PageMapper, + ) -> VmFaultReason { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); - let guard = vma.lock_irqsave(); - let mapper = &mut pfm.mapper; - + let guard = vma.lock(); if let Some(flush) = mapper.map(address, guard.flags()) { flush.flush(); crate::debug::klog::mm::mm_debug_log( @@ -253,9 +229,9 @@ impl PageFaultHandler { klog_types::LogSource::Buddy, ); let paddr = mapper.translate(address).unwrap().0; - let mut page_manager_guard = page_manager_lock_irqsave(); - let page = page_manager_guard.get_unwrap(&paddr); - page.write_irqsave().insert_vma(vma.clone()); + let mut anon_vma_guard = page_manager_lock_irqsave(); + let page = anon_vma_guard.get_mut(&paddr); + page.insert_vma(vma.clone()); VmFaultReason::VM_FAULT_COMPLETED } else { VmFaultReason::VM_FAULT_OOM @@ -270,19 +246,16 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { - if !pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) { - return Self::do_read_fault(pfm); - } else if !pfm - .vma() - .lock_irqsave() - .vm_flags() - .contains(VmFlags::VM_SHARED) - { - return Self::do_cow_fault(pfm); - } else { - return Self::do_shared_fault(pfm); - } + #[allow(unused_variables)] + pub unsafe fn do_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + panic!( + "do_fault has not yet been implemented, + fault message: {:?}, + pid: {}\n", + pfm, + crate::process::ProcessManager::current_pid().data() + ); + // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_fault } /// 处理私有文件映射的写时复制 @@ -293,54 +266,16 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_cow_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { - let mut ret = Self::filemap_fault(pfm); - - if unlikely(ret.intersects( - VmFaultReason::VM_FAULT_ERROR - | VmFaultReason::VM_FAULT_NOPAGE - | VmFaultReason::VM_FAULT_RETRY - | VmFaultReason::VM_FAULT_DONE_COW, - )) { - return ret; - } - - let cache_page = pfm.page.clone().unwrap(); - let mapper = &mut pfm.mapper; - - let cow_page_phys = mapper.allocator_mut().allocate_one(); - if cow_page_phys.is_none() { - return VmFaultReason::VM_FAULT_OOM; - } - let cow_page_phys = cow_page_phys.unwrap(); - - let cow_page = Arc::new(Page::new(false, cow_page_phys)); - pfm.cow_page = Some(cow_page.clone()); - - //复制PageCache内容到新的页内 - let new_frame = MMArch::phys_2_virt(cow_page_phys).unwrap(); - (new_frame.data() as *mut u8).copy_from_nonoverlapping( - MMArch::phys_2_virt(cache_page.read_irqsave().phys_address()) - .unwrap() - .data() as *mut u8, - MMArch::PAGE_SIZE, - ); - - let mut page_manager_guard = page_manager_lock_irqsave(); - - // 新页加入页管理器中 - page_manager_guard.insert(cow_page_phys, &cow_page); - cow_page.write_irqsave().set_page_cache_index( - cache_page.read_irqsave().page_cache(), - cache_page.read_irqsave().index(), + #[allow(dead_code, unused_variables)] + pub unsafe fn do_cow_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + panic!( + "do_cow_fault has not yet been implemented, + fault message: {:?}, + pid: {}\n", + pfm, + crate::process::ProcessManager::current_pid().data() ); - - // 将vma插入页的vma表中 - cow_page.write_irqsave().insert_vma(pfm.vma()); - - ret = ret.union(Self::finish_fault(pfm)); - - ret + // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_cow_fault } /// 处理文件映射页的缺页异常 @@ -351,19 +286,16 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_read_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { - let fs = pfm.vma().lock_irqsave().vm_file().unwrap().inode().fs(); - - let mut ret = Self::do_fault_around(pfm); - if !ret.is_empty() { - return ret; - } - - ret = fs.fault(pfm); - - ret = ret.union(Self::finish_fault(pfm)); - - ret + #[allow(dead_code, unused_variables)] + pub unsafe fn do_read_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + panic!( + "do_read_fault has not yet been implemented, + fault message: {:?}, + pid: {}\n", + pfm, + crate::process::ProcessManager::current_pid().data() + ); + // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_read_fault } /// 处理对共享文件映射区写入引起的缺页 @@ -374,16 +306,16 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_shared_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { - let mut ret = Self::filemap_fault(pfm); - - let cache_page = pfm.page.clone().expect("no cache_page in PageFaultMessage"); - - // 将pagecache页设为脏页,以便回收时能够回写 - cache_page.write_irqsave().add_flags(PageFlags::PG_DIRTY); - ret = ret.union(Self::finish_fault(pfm)); - - ret + #[allow(dead_code, unused_variables)] + pub unsafe fn do_shared_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + panic!( + "do_shared_fault has not yet been implemented, + fault message: {:?}, + pid: {}\n", + pfm, + crate::process::ProcessManager::current_pid().data() + ); + // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_shared_fault } /// 处理被置换页面的缺页异常 @@ -395,7 +327,7 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] - pub unsafe fn do_swap_page(pfm: &mut PageFaultMessage) -> VmFaultReason { + pub unsafe fn do_swap_page(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { panic!( "do_swap_page has not yet been implemented, fault message: {:?}, @@ -415,7 +347,7 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] - pub unsafe fn do_numa_page(pfm: &mut PageFaultMessage) -> VmFaultReason { + pub unsafe fn do_numa_page(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { panic!( "do_numa_page has not yet been implemented, fault message: {:?}, @@ -434,289 +366,43 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_wp_page(pfm: &mut PageFaultMessage) -> VmFaultReason { + pub unsafe fn do_wp_page(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); - let mapper = &mut pfm.mapper; - let old_paddr = mapper.translate(address).unwrap().0; let mut page_manager = page_manager_lock_irqsave(); - let old_page = page_manager.get_unwrap(&old_paddr); - let map_count = old_page.read_irqsave().map_count(); + let map_count = page_manager.get_mut(&old_paddr).map_count(); drop(page_manager); let mut entry = mapper.get_entry(address, 0).unwrap(); - let new_flags = entry.flags().set_write(true).set_dirty(true); + let new_flags = entry.flags().set_write(true); - if vma.lock().vm_flags().contains(VmFlags::VM_SHARED) { - // 共享映射,直接修改页表项保护位,标记为脏页 + if map_count == 1 { let table = mapper.get_table(address, 0).unwrap(); let i = table.index_of(address).unwrap(); entry.set_flags(new_flags); table.set_entry(i, entry); - - old_page.write_irqsave().add_flags(PageFlags::PG_DIRTY); - VmFaultReason::VM_FAULT_COMPLETED - } else if vma.is_anonymous() { - // 私有匿名映射,根据引用计数判断是否拷贝页面 - if map_count == 1 { - let table = mapper.get_table(address, 0).unwrap(); - let i = table.index_of(address).unwrap(); - entry.set_flags(new_flags); - table.set_entry(i, entry); - VmFaultReason::VM_FAULT_COMPLETED - } else if let Some(flush) = mapper.map(address, new_flags) { - let mut page_manager_guard = page_manager_lock_irqsave(); - let old_page = page_manager_guard.get_unwrap(&old_paddr); - old_page.write_irqsave().remove_vma(&vma); - // drop(page_manager_guard); - - flush.flush(); - let paddr = mapper.translate(address).unwrap().0; - // let mut page_manager_guard = page_manager_lock_irqsave(); - let page = page_manager_guard.get_unwrap(&paddr); - page.write_irqsave().insert_vma(vma.clone()); - - (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( - MMArch::phys_2_virt(old_paddr).unwrap().data() as *mut u8, - MMArch::PAGE_SIZE, - ); - - VmFaultReason::VM_FAULT_COMPLETED - } else { - VmFaultReason::VM_FAULT_OOM - } - } else { - // 私有文件映射,必须拷贝页面 - if let Some(flush) = mapper.map(address, new_flags) { - let mut page_manager_guard = page_manager_lock_irqsave(); - let old_page = page_manager_guard.get_unwrap(&old_paddr); - old_page.write_irqsave().remove_vma(&vma); - // drop(page_manager_guard); - - flush.flush(); - let paddr = mapper.translate(address).unwrap().0; - // let mut page_manager_guard = page_manager_lock_irqsave(); - let page = page_manager_guard.get_unwrap(&paddr); - page.write_irqsave().insert_vma(vma.clone()); - - (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( - MMArch::phys_2_virt(old_paddr).unwrap().data() as *mut u8, - MMArch::PAGE_SIZE, - ); - - VmFaultReason::VM_FAULT_COMPLETED - } else { - VmFaultReason::VM_FAULT_OOM - } - } - } + } else if let Some(flush) = mapper.map(address, new_flags) { + let mut page_manager = page_manager_lock_irqsave(); + let old_page = page_manager.get_mut(&old_paddr); + old_page.remove_vma(&vma); + drop(page_manager); - /// 缺页附近页预读 - /// ## 参数 - /// - /// - `pfm`: 缺页异常信息 - /// - `mapper`: 页表映射器 - /// - /// ## 返回值 - /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_fault_around(pfm: &mut PageFaultMessage) -> VmFaultReason { - let vma = pfm.vma(); - let address = pfm.address(); - let mapper = &mut pfm.mapper; - - if mapper.get_table(address, 0).is_none() { - mapper - .allocate_table(address, 0) - .expect("failed to allocate pte table"); - } - let vma_guard = vma.lock_irqsave(); - let vma_region = *vma_guard.region(); - drop(vma_guard); - - // 缺页在VMA中的偏移量 - let vm_pgoff = (address - vma_region.start()) >> MMArch::PAGE_SHIFT; - - // 缺页在PTE中的偏移量 - let pte_pgoff = (address.data() >> MMArch::PAGE_SHIFT) & (1 << MMArch::PAGE_ENTRY_SHIFT); - - // 缺页在文件中的偏移量 - let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); - - let vma_pages_count = (vma_region.end() - vma_region.start()) >> MMArch::PAGE_SHIFT; - - let fault_around_page_number = 16; - - // 开始位置不能超出当前pte和vma头部 - let from_pte = max( - align_down(pte_pgoff, fault_around_page_number), - pte_pgoff - min(vm_pgoff, pte_pgoff), - ); - - // pte结束位置不能超过: - // 1.最大预读上限(默认16) - // 2.最大pte(512) - // 3.vma结束位置(pte_pgoff + (vma_pages_count - vm_pgoff)计算出vma结束页号对当前pte开头的偏移) - let to_pte = min( - from_pte + fault_around_page_number, - min( - 1 << MMArch::PAGE_SHIFT, - pte_pgoff + (vma_pages_count - vm_pgoff), - ), - ); - - // 预先分配pte页表(如果不存在) - if mapper.get_table(address, 0).is_none() && mapper.allocate_table(address, 0).is_none() { - return VmFaultReason::VM_FAULT_OOM; - } - - let fs = pfm.vma().lock_irqsave().vm_file().unwrap().inode().fs(); - // from_pte - pte_pgoff得出预读起始pte相对缺失页的偏移,加上pfm.file_pgoff(缺失页在文件中的偏移)得出起始页在文件中的偏移,结束pte同理 - fs.map_pages( - pfm, - file_pgoff + (from_pte - pte_pgoff), - file_pgoff + (to_pte - pte_pgoff), - ); - - VmFaultReason::empty() - } - - /// 通用的VMA文件映射页面映射函数,将PageCache中的页面映射到进程空间 - /// ## 参数 - /// - /// - `pfm`: 缺页异常信息 - /// - `mapper`: 页表映射器 - /// - /// ## 返回值 - /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn filemap_map_pages( - pfm: &mut PageFaultMessage, - - start_pgoff: usize, - end_pgoff: usize, - ) -> VmFaultReason { - let vma = pfm.vma(); - let vma_guard = vma.lock_irqsave(); - let file = vma_guard.vm_file().expect("no vm_file in vma"); - let page_cache = file.inode().page_cache().unwrap(); - let mapper = &mut pfm.mapper; - - // 起始页地址 - let addr = vma_guard.region().start - + ((start_pgoff - - vma_guard - .file_page_offset() - .expect("file_page_offset is none")) - << MMArch::PAGE_SHIFT); - - for pgoff in start_pgoff..=end_pgoff { - if let Some(page) = page_cache.get_page(pgoff) { - let page_guard = page.read_irqsave(); - if page_guard.flags().contains(PageFlags::PG_UPTODATE) { - let phys = page_guard.phys_address(); - - let address = - VirtAddr::new(addr.data() + ((pgoff - start_pgoff) << MMArch::PAGE_SHIFT)); - mapper - .map_phys(address, phys, vma_guard.flags()) - .unwrap() - .flush(); - } - } - } - VmFaultReason::empty() - } + flush.flush(); + let paddr = mapper.translate(address).unwrap().0; + let mut anon_vma_guard = page_manager_lock_irqsave(); + let page = anon_vma_guard.get_mut(&paddr); + page.insert_vma(vma.clone()); - /// 通用的VMA文件映射错误处理函数 - /// ## 参数 - /// - /// - `pfm`: 缺页异常信息 - /// - `mapper`: 页表映射器 - /// - /// ## 返回值 - /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn filemap_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { - let vma = pfm.vma(); - let vma_guard = vma.lock_irqsave(); - let file = vma_guard.vm_file().expect("no vm_file in vma"); - let page_cache = file.inode().page_cache().unwrap(); - let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); - let mapper = &mut pfm.mapper; - let mut ret = VmFaultReason::empty(); - - if let Some(page) = page_cache.get_page(file_pgoff) { - // TODO 异步从磁盘中预读页面进PageCache - - // 直接将PageCache中的页面作为要映射的页面 - pfm.page = Some(page.clone()); - } else { - // TODO 同步预读 - //涉及磁盘IO,返回标志为VM_FAULT_MAJOR - ret = VmFaultReason::VM_FAULT_MAJOR; - // let mut buf: Vec = vec![0; MMArch::PAGE_SIZE]; - - let allocator = mapper.allocator_mut(); - - // 分配一个物理页面作为加入PageCache的新页 - let new_cache_page = allocator.allocate_one().unwrap(); - // (MMArch::phys_2_virt(new_cache_page).unwrap().data() as *mut u8) - // .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); - file.pread( - file_pgoff * MMArch::PAGE_SIZE, + (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( + MMArch::phys_2_virt(old_paddr).unwrap().data() as *mut u8, MMArch::PAGE_SIZE, - core::slice::from_raw_parts_mut( - MMArch::phys_2_virt(new_cache_page).unwrap().data() as *mut u8, - MMArch::PAGE_SIZE, - ), - ) - .expect("failed to read file to create pagecache page"); - - let page = Arc::new(Page::new(true, new_cache_page)); - pfm.page = Some(page.clone()); - - page.write_irqsave().add_flags(PageFlags::PG_LRU); - page_manager_lock_irqsave().insert(new_cache_page, &page); - page_reclaimer_lock_irqsave().insert_page(new_cache_page, &page); - page_cache.add_page(file_pgoff, &page); - - page.write_irqsave() - .set_page_cache_index(Some(page_cache), Some(file_pgoff)); - } - ret - } + ); - /// 将文件页映射到缺页地址 - /// ## 参数 - /// - /// - `pfm`: 缺页异常信息 - /// - `mapper`: 页表映射器 - /// - /// ## 返回值 - /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn finish_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { - let vma = pfm.vma(); - let vma_guard = vma.lock_irqsave(); - let flags = pfm.flags(); - let cache_page = pfm.page.clone(); - let cow_page = pfm.cow_page.clone(); - let address = pfm.address(); - let mapper = &mut pfm.mapper; - - let page_to_map = if flags.contains(FaultFlags::FAULT_FLAG_WRITE) - && !vma_guard.vm_flags().contains(VmFlags::VM_SHARED) - { - // 私有文件映射的写时复制 - cow_page.expect("no cow_page in PageFaultMessage") + VmFaultReason::VM_FAULT_COMPLETED } else { - // 直接映射到PageCache - cache_page.expect("no cache_page in PageFaultMessage") - }; - - let page_phys = page_to_map.read_irqsave().phys_address(); - - mapper.map_phys(address, page_phys, vma_guard.flags()); - page_to_map.write_irqsave().insert_vma(pfm.vma()); - VmFaultReason::VM_FAULT_COMPLETED + VmFaultReason::VM_FAULT_OOM + } } } diff --git a/kernel/src/mm/init.rs b/kernel/src/mm/init.rs index 3fe0e72fb..0c8549b09 100644 --- a/kernel/src/mm/init.rs +++ b/kernel/src/mm/init.rs @@ -1,18 +1,12 @@ use core::{fmt::Write, sync::atomic::Ordering}; -use log::info; - use crate::{ arch::MMArch, driver::serial::serial8250::send_to_default_serial8250_port, filesystem::procfs::kmsg::kmsg_init, ipc::shm::shm_manager_init, libs::printk::PrintkWriter, - mm::{ - allocator::slab::slab_init, - mmio_buddy::mmio_init, - page::{page_manager_init, page_reclaimer_init}, - }, + mm::{allocator::slab::slab_init, mmio_buddy::mmio_init, page::page_manager_init}, }; use super::MemoryManagementArch; @@ -61,8 +55,6 @@ pub unsafe fn mm_init() { page_manager_init(); // enable SHM_MANAGER shm_manager_init(); - // enable PAGE_RECLAIMER - page_reclaimer_init(); MM_INIT .compare_exchange( @@ -73,7 +65,7 @@ pub unsafe fn mm_init() { ) .unwrap(); MMArch::arch_post_init(); - info!("mm init done."); + kinfo!("mm init done."); } /// 获取内存管理的初始化状态 diff --git a/kernel/src/mm/kernel_mapper.rs b/kernel/src/mm/kernel_mapper.rs index 487d15378..02436189d 100644 --- a/kernel/src/mm/kernel_mapper.rs +++ b/kernel/src/mm/kernel_mapper.rs @@ -1,6 +1,6 @@ use system_error::SystemError; -use super::{page::EntryFlags, PageTableKind, PhysAddr, VirtAddr}; +use super::{page::PageFlags, PageTableKind, PhysAddr, VirtAddr}; use crate::{ arch::{ mm::{LockedFrameAllocator, PageMapper}, @@ -104,7 +104,7 @@ impl KernelMapper { mut vaddr: VirtAddr, mut paddr: PhysAddr, size: usize, - flags: EntryFlags, + flags: PageFlags, flush: bool, ) -> Result<(), SystemError> { if self.readonly { @@ -112,7 +112,7 @@ impl KernelMapper { } let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); - // debug!("kernel mapper: map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); + // kdebug!("kernel mapper: map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); for _ in 0..count.data() { let flusher = self.mapper.map_phys(vaddr, paddr, flags).unwrap(); diff --git a/kernel/src/mm/madvise.rs b/kernel/src/mm/madvise.rs index 9089f88f6..5e9587a40 100644 --- a/kernel/src/mm/madvise.rs +++ b/kernel/src/mm/madvise.rs @@ -12,7 +12,7 @@ impl LockedVMA { _flusher: impl Flusher, ) -> Result<(), SystemError> { //TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/madvise.c?fi=madvise#do_madvise - let mut vma = self.lock_irqsave(); + let mut vma = self.lock(); let mut new_flags = *vma.vm_flags(); match behavior { MadvFlags::MADV_REMOVE => { diff --git a/kernel/src/mm/memblock.rs b/kernel/src/mm/memblock.rs index d17bd3434..4ecbee529 100644 --- a/kernel/src/mm/memblock.rs +++ b/kernel/src/mm/memblock.rs @@ -1,6 +1,5 @@ use core::intrinsics::unlikely; -use log::error; use system_error::SystemError; use crate::libs::{ @@ -89,7 +88,7 @@ impl MemBlockManager { .expect("Failed to count blocks to add!"); if inner.initial_memory_regions_num + blocks_to_add > INITIAL_MEMORY_REGIONS_NUM { - error!("Too many memory regions!"); + kerror!("Too many memory regions!"); return Err(SystemError::ENOMEM); } @@ -204,7 +203,7 @@ impl MemBlockManager { if this.base + this.size != next_base || this.flags != next_flags { if unlikely(this.base + this.size > next_base) { - panic!("this->base + this->size > next->base"); + kBUG!("this->base + this->size > next->base"); } i += 1; continue; diff --git a/kernel/src/mm/mmio_buddy.rs b/kernel/src/mm/mmio_buddy.rs index 8d88f8ace..0c5005721 100644 --- a/kernel/src/mm/mmio_buddy.rs +++ b/kernel/src/mm/mmio_buddy.rs @@ -2,17 +2,19 @@ use crate::libs::align::{page_align_down, page_align_up}; use crate::libs::spinlock::{SpinLock, SpinLockGuard}; use crate::mm::kernel_mapper::KernelMapper; use crate::mm::page::{PAGE_1G_SHIFT, PAGE_4K_SHIFT}; -use crate::mm::{MMArch, MemoryManagementArch}; use crate::process::ProcessManager; - +use crate::{ + kdebug, + mm::{MMArch, MemoryManagementArch}, +}; +use crate::{kerror, kinfo, kwarn}; use alloc::{collections::LinkedList, vec::Vec}; use core::mem; use core::mem::MaybeUninit; use core::sync::atomic::{AtomicBool, Ordering}; -use log::{debug, error, info, warn}; use system_error::SystemError; -use super::page::{EntryFlags, PAGE_4K_SIZE}; +use super::page::{PageFlags, PAGE_4K_SIZE}; use super::{PhysAddr, VirtAddr}; // 最大的伙伴块的幂 @@ -55,12 +57,9 @@ impl MmioBuddyMemPool { free_regions[i as usize] = MaybeUninit::new(SpinLock::new(MmioFreeRegionList::new())); } let free_regions = unsafe { - mem::transmute::< - [core::mem::MaybeUninit< - crate::libs::spinlock::SpinLock, - >; MMIO_BUDDY_REGION_COUNT as usize], - [SpinLock; MMIO_BUDDY_REGION_COUNT as usize], - >(free_regions) + mem::transmute::<_, [SpinLock; MMIO_BUDDY_REGION_COUNT as usize]>( + free_regions, + ) }; let pool = MmioBuddyMemPool { @@ -70,11 +69,11 @@ impl MmioBuddyMemPool { }; assert!(pool.pool_start_addr.data() % PAGE_1G_SIZE == 0); - debug!("MMIO buddy pool init: created"); + kdebug!("MMIO buddy pool init: created"); let mut vaddr_base = MMArch::MMIO_BASE; let mut remain_size = MMArch::MMIO_SIZE; - debug!( + kdebug!( "BASE: {:?}, TOP: {:?}, size: {:?}", MMArch::MMIO_BASE, MMArch::MMIO_TOP, @@ -93,7 +92,7 @@ impl MmioBuddyMemPool { } } - debug!("MMIO buddy pool init success"); + kdebug!("MMIO buddy pool init success"); return pool; } @@ -103,11 +102,11 @@ impl MmioBuddyMemPool { /// /// @return 创建好的地址区域结构体 fn create_region(&self, vaddr: VirtAddr) -> MmioBuddyAddrRegion { - // debug!("create_region for vaddr: {vaddr:?}"); + // kdebug!("create_region for vaddr: {vaddr:?}"); let region: MmioBuddyAddrRegion = MmioBuddyAddrRegion::new(vaddr); - // debug!("create_region for vaddr: {vaddr:?} OK!!!"); + // kdebug!("create_region for vaddr: {vaddr:?} OK!!!"); return region; } @@ -173,7 +172,7 @@ impl MmioBuddyMemPool { ) -> Result { // 申请范围错误 if !(MMIO_BUDDY_MIN_EXP..=MMIO_BUDDY_MAX_EXP).contains(&exp) { - debug!("query_addr_region: exp wrong"); + kdebug!("query_addr_region: exp wrong"); return Err(MmioResult::WRONGEXP); } // 没有恰好符合要求的内存块 @@ -204,7 +203,7 @@ impl MmioBuddyMemPool { } } Err(err) => { - debug!("buddy_pop_region get wrong"); + kdebug!("buddy_pop_region get wrong"); return Err(err); } } @@ -223,7 +222,7 @@ impl MmioBuddyMemPool { } } Err(err) => { - debug!("buddy_pop_region get wrong"); + kdebug!("buddy_pop_region get wrong"); return Err(err); } } @@ -263,7 +262,7 @@ impl MmioBuddyMemPool { ) { Ok(_) => continue, Err(err) => { - debug!("merge_all_exp get wrong"); + kdebug!("merge_all_exp get wrong"); return Err(err); } } @@ -275,7 +274,7 @@ impl MmioBuddyMemPool { ) { Ok(_) => continue, Err(err) => { - debug!("merge_all_exp get wrong"); + kdebug!("merge_all_exp get wrong"); return Err(err); } } @@ -310,7 +309,7 @@ impl MmioBuddyMemPool { match self.query_addr_region(exp, &mut list_guard) { Ok(ret) => return Ok(ret), Err(err) => { - debug!("mmio_buddy_query_addr_region failed"); + kdebug!("mmio_buddy_query_addr_region failed"); return Err(err); } } @@ -434,7 +433,7 @@ impl MmioBuddyMemPool { Err(err) => { // 如果合并失败了要将取出来的元素放回去 self.push_block(copy_region, list_guard); - debug!("merge_all_exp: merge_blocks failed"); + kdebug!("merge_all_exp: merge_blocks failed"); return Err(err); } Ok(_) => continue, @@ -490,7 +489,7 @@ impl MmioBuddyMemPool { // 计算前导0 #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] let mut size_exp: u32 = 63 - size.leading_zeros(); - // debug!("create_mmio: size_exp: {}", size_exp); + // kdebug!("create_mmio: size_exp: {}", size_exp); // 记录最终申请的空间大小 let mut new_size = size; // 对齐要申请的空间大小 @@ -510,7 +509,7 @@ impl MmioBuddyMemPool { return Ok(space_guard); } Err(_) => { - error!( + kerror!( "failed to create mmio. pid = {:?}", ProcessManager::current_pcb().pid() ); @@ -544,7 +543,7 @@ impl MmioBuddyMemPool { let mut bindings = KernelMapper::lock(); let mut kernel_mapper = bindings.as_mut(); if kernel_mapper.is_none() { - warn!("release_mmio: kernel_mapper is read only"); + kwarn!("release_mmio: kernel_mapper is read only"); return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); } @@ -552,7 +551,7 @@ impl MmioBuddyMemPool { unsafe { let x: Option<( PhysAddr, - EntryFlags, + PageFlags, crate::mm::page::PageFlush, )> = kernel_mapper .as_mut() @@ -677,7 +676,7 @@ impl MMIOSpaceGuard { return Err(SystemError::EINVAL); } - let flags = EntryFlags::mmio_flags(); + let flags = PageFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let r = kernel_mapper.map_phys_with_size(self.vaddr, paddr, length, flags, true); @@ -736,11 +735,11 @@ impl Drop for MMIOSpaceGuard { } pub fn mmio_init() { - debug!("Initializing MMIO buddy memory pool..."); + kdebug!("Initializing MMIO buddy memory pool..."); // 初始化mmio内存池 unsafe { __MMIO_POOL = Some(MmioBuddyMemPool::new()); } - info!("MMIO buddy memory pool init done"); + kinfo!("MMIO buddy memory pool init done"); } diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index e95e90198..49e61fcd1 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -1,8 +1,7 @@ use alloc::sync::Arc; -use page::EntryFlags; use system_error::SystemError; -use crate::arch::MMArch; +use crate::{arch::MMArch, include::bindings::bindings::PAGE_OFFSET}; use core::{ cmp, @@ -41,7 +40,7 @@ static mut __IDLE_PROCESS_ADDRESS_SPACE: Option> = None; bitflags! { /// Virtual memory flags #[allow(clippy::bad_bit_mask)] - pub struct VmFlags:usize{ + pub struct VmFlags:u64{ const VM_NONE = 0x00000000; const VM_READ = 0x00000001; @@ -94,27 +93,6 @@ bitflags! { const VM_FAULT_NEEDDSYNC = 0x002000; const VM_FAULT_COMPLETED = 0x004000; const VM_FAULT_HINDEX_MASK = 0x0f0000; - const VM_FAULT_ERROR = 0x000001 | 0x000002 | 0x000040 | 0x000010 | 0x000020 | 0x000800; - } - - pub struct MsFlags:usize { - const MS_ASYNC = 1; - const MS_INVALIDATE = 2; - const MS_SYNC = 4; - } -} - -impl core::ops::Index for [usize] { - type Output = usize; - - fn index(&self, index: VmFlags) -> &Self::Output { - &self[index.bits] - } -} - -impl core::ops::IndexMut for [usize] { - fn index_mut(&mut self, index: VmFlags) -> &mut Self::Output { - &mut self[index.bits] } } @@ -143,6 +121,18 @@ pub unsafe fn set_IDLE_PROCESS_ADDRESS_SPACE(address_space: Arc) { __IDLE_PROCESS_ADDRESS_SPACE = Some(address_space); } +/// @brief 将内核空间的虚拟地址转换为物理地址 +#[inline(always)] +pub fn virt_2_phys(addr: usize) -> usize { + addr - PAGE_OFFSET as usize +} + +/// @brief 将物理地址转换为内核空间的虚拟地址 +#[inline(always)] +pub fn phys_2_virt(addr: usize) -> usize { + addr + PAGE_OFFSET as usize +} + #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] pub enum PageTableKind { /// 用户可访问的页表 @@ -437,7 +427,6 @@ impl Default for PhysMemoryArea { } } -#[allow(dead_code)] pub trait MemoryManagementArch: Clone + Copy + Debug { /// 是否支持缺页中断 const PAGE_FAULT_ENABLED: bool; @@ -621,7 +610,7 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { /// 创建页表项 /// - /// 这是一个低阶api,用于根据物理地址以及指定好的EntryFlags,创建页表项 + /// 这是一个低阶api,用于根据物理地址以及指定好的pageflags,创建页表项 /// /// ## 参数 /// @@ -653,48 +642,6 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { ) -> bool { true } - - const PAGE_NONE: usize; - const PAGE_SHARED: usize; - const PAGE_SHARED_EXEC: usize; - const PAGE_COPY_NOEXEC: usize; - const PAGE_COPY_EXEC: usize; - const PAGE_COPY: usize; - const PAGE_READONLY: usize; - const PAGE_READONLY_EXEC: usize; - - const PAGE_READ: usize; - const PAGE_READ_EXEC: usize; - const PAGE_WRITE: usize; - const PAGE_WRITE_EXEC: usize; - const PAGE_EXEC: usize; - - const PROTECTION_MAP: [EntryFlags; 16]; - - /// 页面保护标志转换函数 - /// ## 参数 - /// - /// - `vm_flags`: VmFlags标志 - /// - /// ## 返回值 - /// - EntryFlags: 页面的保护位 - fn vm_get_page_prot(vm_flags: VmFlags) -> EntryFlags { - let map = Self::PROTECTION_MAP; - let mut ret = map[vm_flags - .intersection( - VmFlags::VM_READ | VmFlags::VM_WRITE | VmFlags::VM_EXEC | VmFlags::VM_SHARED, - ) - .bits()]; - - #[cfg(target_arch = "x86_64")] - { - // 如果xd位被保留,那么将可执行性设置为true - if crate::arch::mm::X86_64MMArch::is_xd_reserved() { - ret = ret.set_execute(true); - } - } - ret - } } /// @brief 虚拟地址范围 diff --git a/kernel/src/mm/no_init.rs b/kernel/src/mm/no_init.rs index fdb8d4d66..855a86767 100644 --- a/kernel/src/mm/no_init.rs +++ b/kernel/src/mm/no_init.rs @@ -19,7 +19,7 @@ use core::marker::PhantomData; use super::{ allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage}, - page::EntryFlags, + page::PageFlags, PageTableKind, VirtAddr, }; @@ -141,7 +141,7 @@ impl FrameAllocator for PseudoAllocator { /// 并且,内核引导文件必须以4K页为粒度,填写了前100M的内存映射关系。(具体以本文件开头的注释为准) #[inline(never)] pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { - let flags: EntryFlags = EntryFlags::new().set_write(true); + let flags: PageFlags = PageFlags::new().set_write(true); pseudo_map_phys_with_flags(vaddr, paddr, count, flags); } @@ -150,7 +150,7 @@ pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrame /// with READ_ONLY and EXECUTE flags. #[inline(never)] pub unsafe fn pseudo_map_phys_ro(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { - let flags: EntryFlags = EntryFlags::new().set_write(false).set_execute(true); + let flags: PageFlags = PageFlags::new().set_write(false).set_execute(true); pseudo_map_phys_with_flags(vaddr, paddr, count, flags); } @@ -160,7 +160,7 @@ pub unsafe fn pseudo_map_phys_with_flags( vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount, - flags: EntryFlags, + flags: PageFlags, ) { assert!(vaddr.check_aligned(MMArch::PAGE_SIZE)); assert!(paddr.check_aligned(MMArch::PAGE_SIZE)); diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 27b399dd4..0bc5d5b51 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -1,4 +1,3 @@ -use alloc::string::ToString; use core::{ fmt::{self, Debug, Error, Formatter}, marker::PhantomData, @@ -6,26 +5,16 @@ use core::{ ops::Add, sync::atomic::{compiler_fence, Ordering}, }; -use system_error::SystemError; -use unified_init::macros::unified_init; use alloc::sync::Arc; use hashbrown::{HashMap, HashSet}; -use log::{error, info}; -use lru::LruCache; use crate::{ - arch::{interrupt::ipi::send_ipi, mm::LockedFrameAllocator, MMArch}, + arch::{interrupt::ipi::send_ipi, MMArch}, exception::ipi::{IpiKind, IpiTarget}, - filesystem::vfs::{file::PageCache, FilePrivateData}, - init::initcall::INITCALL_CORE, ipc::shm::ShmId, - libs::{ - rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, - spinlock::{SpinLock, SpinLockGuard}, - }, - process::{ProcessControlBlock, ProcessManager}, - time::{sleep::usleep, PosixTimeSpec}, + kerror, + libs::spinlock::{SpinLock, SpinLockGuard}, }; use super::{ @@ -48,14 +37,14 @@ pub static mut PAGE_MANAGER: Option> = None; /// 初始化PAGE_MANAGER pub fn page_manager_init() { - info!("page_manager_init"); + kinfo!("page_manager_init"); let page_manager = SpinLock::new(PageManager::new()); compiler_fence(Ordering::SeqCst); unsafe { PAGE_MANAGER = Some(page_manager) }; compiler_fence(Ordering::SeqCst); - info!("page_manager_init done"); + kinfo!("page_manager_init done"); } pub fn page_manager_lock_irqsave() -> SpinLockGuard<'static, PageManager> { @@ -64,7 +53,7 @@ pub fn page_manager_lock_irqsave() -> SpinLockGuard<'static, PageManager> { // 物理页管理器 pub struct PageManager { - phys2page: HashMap>, + phys2page: HashMap, } impl PageManager { @@ -78,21 +67,18 @@ impl PageManager { self.phys2page.contains_key(paddr) } - pub fn get(&mut self, paddr: &PhysAddr) -> Option> { - page_reclaimer_lock_irqsave().get(paddr); - self.phys2page.get(paddr).cloned() + pub fn get(&self, paddr: &PhysAddr) -> Option<&Page> { + self.phys2page.get(paddr) } - pub fn get_unwrap(&mut self, paddr: &PhysAddr) -> Arc { - page_reclaimer_lock_irqsave().get(paddr); + pub fn get_mut(&mut self, paddr: &PhysAddr) -> &mut Page { self.phys2page - .get(paddr) - .unwrap_or_else(|| panic!("Phys Page not found, {:?}", paddr)) - .clone() + .get_mut(paddr) + .unwrap_or_else(|| panic!("{:?}", paddr)) } - pub fn insert(&mut self, paddr: PhysAddr, page: &Arc) { - self.phys2page.insert(paddr, page.clone()); + pub fn insert(&mut self, paddr: PhysAddr, page: Page) { + self.phys2page.insert(paddr, page); } pub fn remove_page(&mut self, paddr: &PhysAddr) { @@ -100,236 +86,8 @@ impl PageManager { } } -pub static mut PAGE_RECLAIMER: Option> = None; - -pub fn page_reclaimer_init() { - info!("page_reclaimer_init"); - let page_reclaimer = SpinLock::new(PageReclaimer::new()); - - compiler_fence(Ordering::SeqCst); - unsafe { PAGE_RECLAIMER = Some(page_reclaimer) }; - compiler_fence(Ordering::SeqCst); - - info!("page_reclaimer_init done"); -} - -/// 页面回收线程 -static mut PAGE_RECLAIMER_THREAD: Option> = None; - -/// 页面回收线程初始化函数 -#[unified_init(INITCALL_CORE)] -fn page_reclaimer_thread_init() -> Result<(), SystemError> { - let closure = crate::process::kthread::KernelThreadClosure::StaticEmptyClosure(( - &(page_reclaim_thread as fn() -> i32), - (), - )); - let pcb = crate::process::kthread::KernelThreadMechanism::create_and_run( - closure, - "page_reclaim".to_string(), - ) - .ok_or("") - .expect("create tty_refresh thread failed"); - unsafe { - PAGE_RECLAIMER_THREAD = Some(pcb); - } - Ok(()) -} - -/// 页面回收线程执行的函数 -fn page_reclaim_thread() -> i32 { - loop { - let usage = unsafe { LockedFrameAllocator.usage() }; - // log::info!("usage{:?}", usage); - - // 保留4096个页面,总计16MB的空闲空间 - if usage.free().data() < 4096 { - let page_to_free = 4096; - page_reclaimer_lock_irqsave().shrink_list(PageFrameCount::new(page_to_free)); - } else { - //TODO 暂时让页面回收线程负责脏页回写任务,后续需要分离 - page_reclaimer_lock_irqsave().flush_dirty_pages(); - // 休眠5秒 - // log::info!("sleep"); - let _ = usleep(PosixTimeSpec::new(5, 0)); - } - } -} - -/// 获取页面回收器 -pub fn page_reclaimer_lock_irqsave() -> SpinLockGuard<'static, PageReclaimer> { - unsafe { PAGE_RECLAIMER.as_ref().unwrap().lock_irqsave() } -} - -/// 页面回收器 -pub struct PageReclaimer { - lru: LruCache>, -} - -impl PageReclaimer { - pub fn new() -> Self { - Self { - lru: LruCache::unbounded(), - } - } - - pub fn get(&mut self, paddr: &PhysAddr) -> Option> { - self.lru.get(paddr).cloned() - } - - pub fn insert_page(&mut self, paddr: PhysAddr, page: &Arc) { - self.lru.put(paddr, page.clone()); - } - - /// lru链表缩减 - /// ## 参数 - /// - /// - `count`: 需要缩减的页面数量 - pub fn shrink_list(&mut self, count: PageFrameCount) { - for _ in 0..count.data() { - let (paddr, page) = self.lru.pop_lru().expect("pagecache is empty"); - let page_cache = page.read_irqsave().page_cache().unwrap(); - for vma in page.read_irqsave().anon_vma() { - let address_space = vma.lock_irqsave().address_space().unwrap(); - let address_space = address_space.upgrade().unwrap(); - let mut guard = address_space.write(); - let mapper = &mut guard.user_mapper.utable; - let virt = vma.lock_irqsave().page_address(&page).unwrap(); - unsafe { - mapper.unmap(virt, false).unwrap().flush(); - } - } - page_cache.remove_page(page.read_irqsave().index().unwrap()); - page_manager_lock_irqsave().remove_page(&paddr); - if page.read_irqsave().flags.contains(PageFlags::PG_DIRTY) { - Self::page_writeback(&page, true); - } - } - } - - /// 唤醒页面回收线程 - pub fn wakeup_claim_thread() { - // log::info!("wakeup_claim_thread"); - let _ = ProcessManager::wakeup(unsafe { PAGE_RECLAIMER_THREAD.as_ref().unwrap() }); - } - - /// 脏页回写函数 - /// ## 参数 - /// - /// - `page`: 需要回写的脏页 - /// - `unmap`: 是否取消映射 - /// - /// ## 返回值 - /// - VmFaultReason: 页面错误处理信息标志 - pub fn page_writeback(page: &Arc, unmap: bool) { - if !unmap { - page.write_irqsave().remove_flags(PageFlags::PG_DIRTY); - } - - for vma in page.read_irqsave().anon_vma() { - let address_space = vma.lock_irqsave().address_space().unwrap(); - let address_space = address_space.upgrade().unwrap(); - let mut guard = address_space.write(); - let mapper = &mut guard.user_mapper.utable; - let virt = vma.lock_irqsave().page_address(page).unwrap(); - if unmap { - unsafe { - mapper.unmap(virt, false).unwrap().flush(); - } - } else { - unsafe { - // 保护位设为只读 - mapper.remap( - virt, - mapper.get_entry(virt, 0).unwrap().flags().set_write(false), - ) - }; - } - } - let inode = page - .read_irqsave() - .page_cache - .clone() - .unwrap() - .inode() - .clone() - .unwrap() - .upgrade() - .unwrap(); - inode - .write_at( - page.read_irqsave().index().unwrap(), - MMArch::PAGE_SIZE, - unsafe { - core::slice::from_raw_parts( - MMArch::phys_2_virt(page.read_irqsave().phys_addr) - .unwrap() - .data() as *mut u8, - MMArch::PAGE_SIZE, - ) - }, - SpinLock::new(FilePrivateData::Unused).lock(), - ) - .unwrap(); - } - - /// lru脏页刷新 - pub fn flush_dirty_pages(&self) { - // log::info!("flush_dirty_pages"); - let iter = self.lru.iter(); - for (_, page) in iter { - if page.read_irqsave().flags().contains(PageFlags::PG_DIRTY) { - Self::page_writeback(page, false); - } - } - } -} - -bitflags! { - pub struct PageFlags: u64 { - const PG_LOCKED = 1 << 0; - const PG_WRITEBACK = 1 << 1; - const PG_REFERENCED = 1 << 2; - const PG_UPTODATE = 1 << 3; - const PG_DIRTY = 1 << 4; - const PG_LRU = 1 << 5; - const PG_HEAD = 1 << 6; - const PG_WAITERS = 1 << 7; - const PG_ACTIVE = 1 << 8; - const PG_WORKINGSET = 1 << 9; - const PG_ERROR = 1 << 10; - const PG_SLAB = 1 << 11; - const PG_RESERVED = 1 << 14; - const PG_PRIVATE = 1 << 15; - const PG_RECLAIM = 1 << 18; - const PG_SWAPBACKED = 1 << 19; - } -} - -#[derive(Debug)] -pub struct Page { - inner: RwLock, -} - -impl Page { - pub fn new(shared: bool, phys_addr: PhysAddr) -> Self { - let inner = InnerPage::new(shared, phys_addr); - Self { - inner: RwLock::new(inner), - } - } - - pub fn read_irqsave(&self) -> RwLockReadGuard { - self.inner.read_irqsave() - } - - pub fn write_irqsave(&self) -> RwLockWriteGuard { - self.inner.write_irqsave() - } -} - -#[derive(Debug)] /// 物理页面信息 -pub struct InnerPage { +pub struct Page { /// 映射计数 map_count: usize, /// 是否为共享页 @@ -340,17 +98,10 @@ pub struct InnerPage { shm_id: Option, /// 映射到当前page的VMA anon_vma: HashSet>, - /// 标志 - flags: PageFlags, - /// 页所在的物理页帧号 - phys_addr: PhysAddr, - /// 在pagecache中的偏移 - index: Option, - page_cache: Option>, } -impl InnerPage { - pub fn new(shared: bool, phys_addr: PhysAddr) -> Self { +impl Page { + pub fn new(shared: bool) -> Self { let dealloc_when_zero = !shared; Self { map_count: 0, @@ -358,10 +109,6 @@ impl InnerPage { free_when_zero: dealloc_when_zero, shm_id: None, anon_vma: HashSet::new(), - flags: PageFlags::empty(), - phys_addr, - index: None, - page_cache: None, } } @@ -390,31 +137,6 @@ impl InnerPage { self.shm_id } - pub fn index(&self) -> Option { - self.index - } - - pub fn page_cache(&self) -> Option> { - self.page_cache.clone() - } - - pub fn set_page_cache(&mut self, page_cache: Option>) { - self.page_cache = page_cache; - } - - pub fn set_index(&mut self, index: Option) { - self.index = index; - } - - pub fn set_page_cache_index( - &mut self, - page_cache: Option>, - index: Option, - ) { - self.page_cache = page_cache; - self.index = index; - } - pub fn set_shm_id(&mut self, shm_id: ShmId) { self.shm_id = Some(shm_id); } @@ -432,31 +154,6 @@ impl InnerPage { pub fn map_count(&self) -> usize { self.map_count } - - #[inline(always)] - pub fn flags(&self) -> &PageFlags { - &self.flags - } - - #[inline(always)] - pub fn set_flags(&mut self, flags: PageFlags) { - self.flags = flags - } - - #[inline(always)] - pub fn add_flags(&mut self, flags: PageFlags) { - self.flags = self.flags.union(flags); - } - - #[inline(always)] - pub fn remove_flags(&mut self, flags: PageFlags) { - self.flags = self.flags.difference(flags); - } - - #[inline(always)] - pub fn phys_address(&self) -> PhysAddr { - self.phys_addr - } } #[derive(Debug)] @@ -632,19 +329,8 @@ impl PageTable { new_table.set_entry(i, entry); } else { let phys = allocator.allocate_one()?; - let mut page_manager_guard = page_manager_lock_irqsave(); - let old_phys = entry.address().unwrap(); - let old_page = page_manager_guard.get_unwrap(&old_phys); - let new_page = - Arc::new(Page::new(old_page.read_irqsave().shared(), phys)); - if let Some(ref page_cache) = old_page.read_irqsave().page_cache() { - new_page.write_irqsave().set_page_cache_index( - Some(page_cache.clone()), - old_page.read_irqsave().index(), - ); - } - - page_manager_guard.insert(phys, &new_page); + let mut anon_vma_guard = page_manager_lock_irqsave(); + anon_vma_guard.insert(phys, Page::new(false)); let old_phys = entry.address().unwrap(); let frame = MMArch::phys_2_virt(phys).unwrap().data() as *mut u8; frame.copy_from_nonoverlapping( @@ -686,7 +372,7 @@ impl Debug for PageEntry { impl PageEntry { #[inline(always)] - pub fn new(paddr: PhysAddr, flags: EntryFlags) -> Self { + pub fn new(paddr: PhysAddr, flags: PageFlags) -> Self { Self { data: MMArch::make_entry(paddr, flags.data()), phantom: PhantomData, @@ -734,12 +420,12 @@ impl PageEntry { } #[inline(always)] - pub fn flags(&self) -> EntryFlags { - unsafe { EntryFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } + pub fn flags(&self) -> PageFlags { + unsafe { PageFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } } #[inline(always)] - pub fn set_flags(&mut self, flags: EntryFlags) { + pub fn set_flags(&mut self, flags: PageFlags) { self.data = (self.data & !Arch::ENTRY_FLAGS_MASK) | flags.data(); } @@ -767,19 +453,13 @@ impl PageEntry { /// 页表项的标志位 #[derive(Copy, Clone, Hash)] -pub struct EntryFlags { +pub struct PageFlags { data: usize, phantom: PhantomData, } -impl Default for EntryFlags { - fn default() -> Self { - Self::new() - } -} - #[allow(dead_code)] -impl EntryFlags { +impl PageFlags { #[inline(always)] pub fn new() -> Self { let mut r = unsafe { @@ -800,19 +480,18 @@ impl EntryFlags { return r; } - /// 根据ProtFlags生成EntryFlags + /// 根据ProtFlags生成PageFlags /// /// ## 参数 /// /// - prot_flags: 页的保护标志 /// - user: 用户空间是否可访问 - pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> Self { - let vm_flags = super::VmFlags::from(prot_flags); - // let flags: EntryFlags = EntryFlags::new() - // .set_user(user) - // .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) - // .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); - let flags = Arch::vm_get_page_prot(vm_flags).set_user(user); + pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> PageFlags { + let flags: PageFlags = PageFlags::new() + .set_user(user) + .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) + .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); + return flags; } @@ -1078,9 +757,9 @@ impl EntryFlags { } } -impl fmt::Debug for EntryFlags { +impl fmt::Debug for PageFlags { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("EntryFlags") + f.debug_struct("PageFlags") .field("bits", &format_args!("{:#0x}", self.data)) .field("present", &self.present()) .field("has_write", &self.has_write()) @@ -1176,23 +855,18 @@ impl PageMapper { pub unsafe fn map( &mut self, virt: VirtAddr, - flags: EntryFlags, + flags: PageFlags, ) -> Option> { compiler_fence(Ordering::SeqCst); let phys: PhysAddr = self.frame_allocator.allocate_one()?; compiler_fence(Ordering::SeqCst); - unsafe { - let vaddr = MMArch::phys_2_virt(phys).unwrap(); - MMArch::write_bytes(vaddr, 0, MMArch::PAGE_SIZE); - } - let mut page_manager_guard: SpinLockGuard<'static, PageManager> = page_manager_lock_irqsave(); if !page_manager_guard.contains(&phys) { - page_manager_guard.insert(phys, &Arc::new(Page::new(false, phys))) + page_manager_guard.insert(phys, Page::new(false)) } - drop(page_manager_guard); + return self.map_phys(virt, phys, flags); } @@ -1201,13 +875,14 @@ impl PageMapper { &mut self, virt: VirtAddr, phys: PhysAddr, - flags: EntryFlags, + flags: PageFlags, ) -> Option> { // 验证虚拟地址和物理地址是否对齐 if !(virt.check_aligned(Arch::PAGE_SIZE) && phys.check_aligned(Arch::PAGE_SIZE)) { - error!( + kerror!( "Try to map unaligned page: virt={:?}, phys={:?}", - virt, phys + virt, + phys ); return None; } @@ -1233,7 +908,7 @@ impl PageMapper { let next_table = table.next_level_table(i); if let Some(next_table) = next_table { table = next_table; - // debug!("Mapping {:?} to next level table...", virt); + // kdebug!("Mapping {:?} to next level table...", virt); } else { // 分配下一级页表 let frame = self.frame_allocator.allocate_one()?; @@ -1241,8 +916,8 @@ impl PageMapper { // 清空这个页帧 MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); // 设置页表项的flags - let flags: EntryFlags = - EntryFlags::new_page_table(virt.kind() == PageTableKind::User); + let flags: PageFlags = + PageFlags::new_page_table(virt.kind() == PageTableKind::User); // 把新分配的页表映射到当前页表 table.set_entry(i, PageEntry::new(frame, flags)); @@ -1258,11 +933,11 @@ impl PageMapper { pub unsafe fn map_huge_page( &mut self, virt: VirtAddr, - flags: EntryFlags, + flags: PageFlags, ) -> Option> { // 验证虚拟地址是否对齐 if !(virt.check_aligned(Arch::PAGE_SIZE)) { - error!("Try to map unaligned page: virt={:?}", virt); + kerror!("Try to map unaligned page: virt={:?}", virt); return None; } @@ -1324,8 +999,7 @@ impl PageMapper { MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); // 设置页表项的flags - let flags: EntryFlags = - EntryFlags::new_page_table(virt.kind() == PageTableKind::User); + let flags: PageFlags = PageFlags::new_page_table(virt.kind() == PageTableKind::User); table.set_entry(i, PageEntry::new(frame, flags)); table.next_level_table(i) @@ -1427,7 +1101,7 @@ impl PageMapper { pub unsafe fn map_linearly( &mut self, phys: PhysAddr, - flags: EntryFlags, + flags: PageFlags, ) -> Option<(VirtAddr, PageFlush)> { let virt: VirtAddr = Arch::phys_2_virt(phys)?; return self.map_phys(virt, phys, flags).map(|flush| (virt, flush)); @@ -1447,7 +1121,7 @@ impl PageMapper { pub unsafe fn remap( &mut self, virt: VirtAddr, - flags: EntryFlags, + flags: PageFlags, ) -> Option> { return self .visit(virt, |p1, i| { @@ -1469,7 +1143,7 @@ impl PageMapper { /// ## 返回值 /// /// 如果查找成功,返回物理地址和页表项的flags,否则返回None - pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, EntryFlags)> { + pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, PageFlags)> { let entry: PageEntry = self.visit(virt, |p1, i| unsafe { p1.entry(i) })??; let paddr = entry.address().ok()?; let flags = entry.flags(); @@ -1508,9 +1182,9 @@ impl PageMapper { &mut self, virt: VirtAddr, unmap_parents: bool, - ) -> Option<(PhysAddr, EntryFlags, PageFlush)> { + ) -> Option<(PhysAddr, PageFlags, PageFlush)> { if !virt.check_aligned(Arch::PAGE_SIZE) { - error!("Try to unmap unaligned page: virt={:?}", virt); + kerror!("Try to unmap unaligned page: virt={:?}", virt); return None; } @@ -1556,7 +1230,7 @@ unsafe fn unmap_phys_inner( table: &PageTable, unmap_parents: bool, allocator: &mut impl FrameAllocator, -) -> Option<(PhysAddr, EntryFlags)> { +) -> Option<(PhysAddr, PageFlags)> { // 获取页表项的索引 let i = table.index_of(vaddr)?; diff --git a/kernel/src/mm/syscall.rs b/kernel/src/mm/syscall.rs index 6c9646a3f..99997ee54 100644 --- a/kernel/src/mm/syscall.rs +++ b/kernel/src/mm/syscall.rs @@ -1,13 +1,12 @@ -use core::{intrinsics::unlikely, slice::from_raw_parts}; +use core::intrinsics::unlikely; use alloc::sync::Arc; -use log::error; use system_error::SystemError; use crate::{ arch::MMArch, - driver::base::block::SeekFrom, ipc::shm::ShmFlags, + kerror, libs::align::{check_aligned, page_align_up}, mm::MemoryManagementArch, syscall::Syscall, @@ -16,7 +15,7 @@ use crate::{ use super::{ allocator::page_frame::{PageFrameCount, VirtPageFrame}, ucontext::{AddressSpace, DEFAULT_MMAP_MIN_ADDR}, - verify_area, MsFlags, VirtAddr, VmFlags, + verify_area, VirtAddr, VmFlags, }; bitflags! { @@ -155,10 +154,6 @@ impl From for VmFlags { vm_flags |= VmFlags::VM_SYNC; } - if map_flags.contains(MapFlags::MAP_SHARED) { - vm_flags |= VmFlags::VM_SHARED; - } - vm_flags } } @@ -251,7 +246,7 @@ impl From for ProtFlags { impl Syscall { pub fn brk(new_addr: VirtAddr) -> Result { - // debug!("brk: new_addr={:?}", new_addr); + // kdebug!("brk: new_addr={:?}", new_addr); let address_space = AddressSpace::current()?; let mut address_space = address_space.write(); @@ -301,8 +296,8 @@ impl Syscall { len: usize, prot_flags: usize, map_flags: usize, - fd: i32, - offset: usize, + _fd: i32, + _offset: usize, ) -> Result { let map_flags = MapFlags::from_bits_truncate(map_flags as u64); let prot_flags = ProtFlags::from_bits_truncate(prot_flags as u64); @@ -310,43 +305,32 @@ impl Syscall { if start_vaddr < VirtAddr::new(DEFAULT_MMAP_MIN_ADDR) && map_flags.contains(MapFlags::MAP_FIXED) { - error!( + kerror!( "mmap: MAP_FIXED is not supported for address below {}", DEFAULT_MMAP_MIN_ADDR ); return Err(SystemError::EINVAL); } + // 暂时不支持除匿名页以外的映射 + if !map_flags.contains(MapFlags::MAP_ANONYMOUS) { + kerror!("mmap: not support file mapping"); + return Err(SystemError::ENOSYS); + } // 暂时不支持巨页映射 if map_flags.contains(MapFlags::MAP_HUGETLB) { - error!("mmap: not support huge page mapping"); + kerror!("mmap: not support huge page mapping"); return Err(SystemError::ENOSYS); } let current_address_space = AddressSpace::current()?; - let start_page = if map_flags.contains(MapFlags::MAP_ANONYMOUS) { - // 匿名映射 - current_address_space.write().map_anonymous( - start_vaddr, - len, - prot_flags, - map_flags, - true, - false, - )? - } else { - // 文件映射 - current_address_space.write().file_mapping( - start_vaddr, - len, - prot_flags, - map_flags, - fd, - offset, - true, - false, - )? - }; - + let start_page = current_address_space.write().map_anonymous( + start_vaddr, + len, + prot_flags, + map_flags, + true, + true, + )?; return Ok(start_page.virt_address().data()); } @@ -406,11 +390,11 @@ impl Syscall { return Err(SystemError::EINVAL); } let vma = vma.unwrap(); - let vm_flags = *vma.lock_irqsave().vm_flags(); + let vm_flags = *vma.lock().vm_flags(); // 暂时不支持巨页映射 if vm_flags.contains(VmFlags::VM_HUGETLB) { - error!("mmap: not support huge page mapping"); + kerror!("mmap: not support huge page mapping"); return Err(SystemError::ENOSYS); } @@ -540,115 +524,4 @@ impl Syscall { .map_err(|_| SystemError::EINVAL)?; return Ok(0); } - - /// ## msync系统调用 - /// - /// ## 参数 - /// - /// - `start`:起始地址(已经对齐到页) - /// - `len`:长度(已经对齐到页) - /// - `flags`:标志 - pub fn msync(start: VirtAddr, len: usize, flags: usize) -> Result { - if !start.check_aligned(MMArch::PAGE_SIZE) || !check_aligned(len, MMArch::PAGE_SIZE) { - return Err(SystemError::EINVAL); - } - - if unlikely(verify_area(start, len).is_err()) { - return Err(SystemError::EINVAL); - } - if unlikely(len == 0) { - return Err(SystemError::EINVAL); - } - - let mut start = start.data(); - let end = start + len; - let flags = MsFlags::from_bits_truncate(flags); - let mut unmapped_error = Ok(0); - - if !flags.intersects(MsFlags::MS_ASYNC | MsFlags::MS_INVALIDATE | MsFlags::MS_SYNC) { - return Err(SystemError::EINVAL); - } - - if flags.contains(MsFlags::MS_ASYNC | MsFlags::MS_SYNC) { - return Err(SystemError::EINVAL); - } - - if end < start { - return Err(SystemError::ENOMEM); - } - - if start == end { - return Ok(0); - } - - let current_address_space = AddressSpace::current()?; - let mut err = Err(SystemError::ENOMEM); - let mut next_vma = current_address_space - .read() - .mappings - .find_nearest(VirtAddr::new(start)); - loop { - if let Some(vma) = next_vma.clone() { - let guard = vma.lock_irqsave(); - let vm_start = guard.region().start().data(); - let vm_end = guard.region().end().data(); - if start < vm_start { - if flags == MsFlags::MS_ASYNC { - break; - } - start = vm_start; - if start >= vm_end { - break; - } - unmapped_error = Err(SystemError::ENOMEM); - } - let vm_flags = *guard.vm_flags(); - if flags.contains(MsFlags::MS_INVALIDATE) && vm_flags.contains(VmFlags::VM_LOCKED) { - err = Err(SystemError::EBUSY); - break; - } - let file = guard.vm_file(); - let fstart = (start - vm_start) - + (guard.file_page_offset().unwrap_or(0) << MMArch::PAGE_SHIFT); - let fend = fstart + (core::cmp::min(end, vm_end) - start) - 1; - let old_start = start; - start = vm_end; - // log::info!("flags: {:?}", flags); - // log::info!("vm_flags: {:?}", vm_flags); - // log::info!("file: {:?}", file); - if flags.contains(MsFlags::MS_SYNC) && vm_flags.contains(VmFlags::VM_SHARED) { - if let Some(file) = file { - let old_pos = file.lseek(SeekFrom::SeekCurrent(0)).unwrap(); - file.lseek(SeekFrom::SeekSet(fstart as i64)).unwrap(); - err = file.write(len, unsafe { - from_raw_parts(old_start as *mut u8, fend - fstart + 1) - }); - file.lseek(SeekFrom::SeekSet(old_pos as i64)).unwrap(); - if err.is_err() { - break; - } else if start >= end { - err = unmapped_error; - break; - } - next_vma = current_address_space - .read() - .mappings - .find_nearest(VirtAddr::new(start)); - } - } else { - if start >= end { - err = unmapped_error; - break; - } - next_vma = current_address_space - .read() - .mappings - .find_nearest(VirtAddr::new(vm_end)); - } - } else { - return Err(SystemError::ENOMEM); - } - } - return err; - } } diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 6b83bfe61..18d8bda4b 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -20,7 +20,6 @@ use system_error::SystemError; use crate::{ arch::{mm::PageMapper, CurrentIrqArch, MMArch}, exception::InterruptArch, - filesystem::vfs::file::File, libs::{ align::page_align_up, rwlock::RwLock, @@ -35,7 +34,7 @@ use super::{ allocator::page_frame::{ deallocate_page_frames, PageFrameCount, PhysPageFrame, VirtPageFrame, VirtPageFrameIter, }, - page::{EntryFlags, Flusher, InactiveFlusher, Page, PageFlushAll}, + page::{Flusher, InactiveFlusher, PageFlags, PageFlushAll}, syscall::{MadvFlags, MapFlags, MremapFlags, ProtFlags}, MemoryManagementArch, PageTableKind, VirtAddr, VirtRegion, VmFlags, }; @@ -144,7 +143,7 @@ impl InnerAddressSpace { end_data: VirtAddr(0), }; if create_stack { - // debug!("to create user stack."); + // kdebug!("to create user stack."); result.new_user_stack(UserStack::DEFAULT_USER_STACK_SIZE)?; } @@ -179,23 +178,23 @@ impl InnerAddressSpace { for vma in self.mappings.vmas.iter() { // TODO: 增加对VMA是否为文件映射的判断,如果是的话,就跳过 - let vma_guard: SpinLockGuard<'_, VMA> = vma.lock_irqsave(); + let vma_guard: SpinLockGuard<'_, VMA> = vma.lock(); // 仅拷贝VMA信息并添加反向映射,因为UserMapper克隆时已经分配了新的物理页 let new_vma = LockedVMA::new(vma_guard.clone_info_only()); new_guard.mappings.vmas.insert(new_vma.clone()); - // debug!("new vma: {:x?}", new_vma); - let new_vma_guard = new_vma.lock_irqsave(); + // kdebug!("new vma: {:x?}", new_vma); + let new_vma_guard = new_vma.lock(); let new_mapper = &new_guard.user_mapper.utable; - let mut page_manager_guard = page_manager_lock_irqsave(); + let mut anon_vma_guard = page_manager_lock_irqsave(); for page in new_vma_guard.pages().map(|p| p.virt_address()) { if let Some((paddr, _)) = new_mapper.translate(page) { - let page = page_manager_guard.get_unwrap(&paddr); - page.write_irqsave().insert_vma(new_vma.clone()); + let page = anon_vma_guard.get_mut(&paddr); + page.insert_vma(new_vma.clone()); } } - drop(page_manager_guard); + drop(anon_vma_guard); drop(vma_guard); drop(new_vma_guard); } @@ -210,7 +209,7 @@ impl InnerAddressSpace { /// - `bytes`: 拓展大小 #[allow(dead_code)] pub fn extend_stack(&mut self, mut bytes: usize) -> Result<(), SystemError> { - // debug!("extend user stack"); + // kdebug!("extend user stack"); let prot_flags = ProtFlags::PROT_READ | ProtFlags::PROT_WRITE | ProtFlags::PROT_EXEC; let map_flags = MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS | MapFlags::MAP_GROWSDOWN; let stack = self.user_stack.as_mut().unwrap(); @@ -260,7 +259,7 @@ impl InnerAddressSpace { let round_hint_to_min = |hint: VirtAddr| { // 先把hint向下对齐到页边界 let addr = hint.data() & (!MMArch::PAGE_OFFSET_MASK); - // debug!("map_anonymous: hint = {:?}, addr = {addr:#x}", hint); + // kdebug!("map_anonymous: hint = {:?}, addr = {addr:#x}", hint); // 如果hint不是0,且hint小于DEFAULT_MMAP_MIN_ADDR,则对齐到DEFAULT_MMAP_MIN_ADDR if (addr != 0) && round_to_min && (addr < DEFAULT_MMAP_MIN_ADDR) { Some(VirtAddr::new(page_align_up(DEFAULT_MMAP_MIN_ADDR))) @@ -270,8 +269,8 @@ impl InnerAddressSpace { Some(VirtAddr::new(addr)) } }; - // debug!("map_anonymous: start_vaddr = {:?}", start_vaddr); - // debug!("map_anonymous: len(no align) = {}", len); + // kdebug!("map_anonymous: start_vaddr = {:?}", start_vaddr); + // kdebug!("map_anonymous: len(no align) = {}", len); let len = page_align_up(len); @@ -281,137 +280,35 @@ impl InnerAddressSpace { | VmFlags::VM_MAYWRITE | VmFlags::VM_MAYEXEC; - // debug!("map_anonymous: len = {}", len); - - let start_page: VirtPageFrame = self.mmap( - round_hint_to_min(start_vaddr), - PageFrameCount::from_bytes(len).unwrap(), - prot_flags, - map_flags, - move |page, count, flags, mapper, flusher| { - if allocate_at_once { - VMA::zeroed(page, count, vm_flags, flags, mapper, flusher, None, None) - } else { + // kdebug!("map_anonymous: len = {}", len); + + let start_page: VirtPageFrame = if allocate_at_once { + self.mmap( + round_hint_to_min(start_vaddr), + PageFrameCount::from_bytes(len).unwrap(), + prot_flags, + map_flags, + move |page, count, flags, mapper, flusher| { + VMA::zeroed(page, count, vm_flags, flags, mapper, flusher) + }, + )? + } else { + self.mmap( + round_hint_to_min(start_vaddr), + PageFrameCount::from_bytes(len).unwrap(), + prot_flags, + map_flags, + move |page, count, flags, _mapper, _flusher| { Ok(LockedVMA::new(VMA::new( VirtRegion::new(page.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, flags, - None, - None, false, ))) - } - }, - )?; - - return Ok(start_page); - } - - /// 进行文件页映射 - /// - /// ## 参数 - /// - /// - `start_vaddr`:映射的起始地址 - /// - `len`:映射的长度 - /// - `prot_flags`:保护标志 - /// - `map_flags`:映射标志 - /// - `fd`:文件描述符 - /// - `offset`:映射偏移量 - /// - `round_to_min`:是否将`start_vaddr`对齐到`mmap_min`,如果为`true`,则当`start_vaddr`不为0时,会对齐到`mmap_min`,否则仅向下对齐到页边界 - /// - `allocate_at_once`:是否立即分配物理空间 - /// - /// ## 返回 - /// - /// 返回映射的起始虚拟页帧 - #[allow(clippy::too_many_arguments)] - pub fn file_mapping( - &mut self, - start_vaddr: VirtAddr, - len: usize, - prot_flags: ProtFlags, - map_flags: MapFlags, - fd: i32, - offset: usize, - round_to_min: bool, - allocate_at_once: bool, - ) -> Result { - let allocate_at_once = if MMArch::PAGE_FAULT_ENABLED { - allocate_at_once - } else { - true + }, + )? }; - // 用于对齐hint的函数 - let round_hint_to_min = |hint: VirtAddr| { - // 先把hint向下对齐到页边界 - let addr = hint.data() & (!MMArch::PAGE_OFFSET_MASK); - // debug!("map_anonymous: hint = {:?}, addr = {addr:#x}", hint); - // 如果hint不是0,且hint小于DEFAULT_MMAP_MIN_ADDR,则对齐到DEFAULT_MMAP_MIN_ADDR - if (addr != 0) && round_to_min && (addr < DEFAULT_MMAP_MIN_ADDR) { - Some(VirtAddr::new(page_align_up(DEFAULT_MMAP_MIN_ADDR))) - } else if addr == 0 { - None - } else { - Some(VirtAddr::new(addr)) - } - }; - // debug!("map_anonymous: start_vaddr = {:?}", start_vaddr); - // debug!("map_anonymous: len(no align) = {}", len); - - let len = page_align_up(len); - - let vm_flags = VmFlags::from(prot_flags) - | VmFlags::from(map_flags) - | VmFlags::VM_MAYREAD - | VmFlags::VM_MAYWRITE - | VmFlags::VM_MAYEXEC; - - // debug!("map_anonymous: len = {}", len); - let binding = ProcessManager::current_pcb().fd_table(); - let fd_table_guard = binding.read(); - - let file = fd_table_guard.get_file_by_fd(fd); - if file.is_none() { - return Err(SystemError::EBADF); - } - // drop guard 以避免无法调度的问题 - drop(fd_table_guard); - - // offset需要4K对齐 - if !offset & (MMArch::PAGE_SIZE - 1) == 0 { - return Err(SystemError::EINVAL); - } - let pgoff = offset >> MMArch::PAGE_SHIFT; - - let start_page: VirtPageFrame = self.mmap( - round_hint_to_min(start_vaddr), - PageFrameCount::from_bytes(len).unwrap(), - prot_flags, - map_flags, - move |page, count, flags, mapper, flusher| { - if allocate_at_once { - VMA::zeroed( - page, - count, - vm_flags, - flags, - mapper, - flusher, - file, - Some(pgoff), - ) - } else { - Ok(LockedVMA::new(VMA::new( - VirtRegion::new(page.virt_address(), count.data() * MMArch::PAGE_SIZE), - vm_flags, - flags, - file, - Some(pgoff), - false, - ))) - } - }, - )?; return Ok(start_page); } @@ -436,7 +333,7 @@ impl InnerAddressSpace { F: FnOnce( VirtPageFrame, PageFrameCount, - EntryFlags, + PageFlags, &mut PageMapper, &mut dyn Flusher, ) -> Result, SystemError>, @@ -451,7 +348,7 @@ impl InnerAddressSpace { if page_count == PageFrameCount::new(0) { return Err(SystemError::EINVAL); } - // debug!("mmap: addr: {addr:?}, page_count: {page_count:?}, prot_flags: {prot_flags:?}, map_flags: {map_flags:?}"); + // kdebug!("mmap: addr: {addr:?}, page_count: {page_count:?}, prot_flags: {prot_flags:?}, map_flags: {map_flags:?}"); // 找到未使用的区域 let region = match addr { @@ -466,7 +363,7 @@ impl InnerAddressSpace { let page = VirtPageFrame::new(region.start()); - // debug!("mmap: page: {:?}, region={region:?}", page.virt_address()); + // kdebug!("mmap: page: {:?}, region={region:?}", page.virt_address()); compiler_fence(Ordering::SeqCst); let (mut active, mut inactive); @@ -482,7 +379,7 @@ impl InnerAddressSpace { self.mappings.insert_vma(map_func( page, page_count, - EntryFlags::from_prot_flags(prot_flags, true), + PageFlags::from_prot_flags(prot_flags, true), &mut self.user_mapper.utable, flusher, )?); @@ -581,9 +478,9 @@ impl InnerAddressSpace { let regions: Vec> = self.mappings.conflicts(to_unmap).collect::>(); for r in regions { - let r = r.lock_irqsave().region; + let r = r.lock().region; let r = self.mappings.remove_vma(&r).unwrap(); - let intersection = r.lock_irqsave().region().intersect(&to_unmap).unwrap(); + let intersection = r.lock().region().intersect(&to_unmap).unwrap(); let split_result = r.extract(intersection, &self.user_mapper.utable).unwrap(); // TODO: 当引入后备页映射后,这里需要增加通知文件的逻辑 @@ -612,13 +509,13 @@ impl InnerAddressSpace { page_count: PageFrameCount, prot_flags: ProtFlags, ) -> Result<(), SystemError> { - // debug!( + // kdebug!( // "mprotect: start_page: {:?}, page_count: {:?}, prot_flags:{prot_flags:?}", // start_page, // page_count // ); let (mut active, mut inactive); - let flusher = if self.is_current() { + let mut flusher = if self.is_current() { active = PageFlushAll::new(); &mut active as &mut dyn Flusher } else { @@ -628,17 +525,17 @@ impl InnerAddressSpace { let mapper = &mut self.user_mapper.utable; let region = VirtRegion::new(start_page.virt_address(), page_count.bytes()); - // debug!("mprotect: region: {:?}", region); + // kdebug!("mprotect: region: {:?}", region); let regions = self.mappings.conflicts(region).collect::>(); - // debug!("mprotect: regions: {:?}", regions); + // kdebug!("mprotect: regions: {:?}", regions); for r in regions { - // debug!("mprotect: r: {:?}", r); - let r = *r.lock_irqsave().region(); + // kdebug!("mprotect: r: {:?}", r); + let r = *r.lock().region(); let r = self.mappings.remove_vma(&r).unwrap(); - let intersection = r.lock_irqsave().region().intersect(®ion).unwrap(); + let intersection = r.lock().region().intersect(®ion).unwrap(); let split_result = r .extract(intersection, mapper) .expect("Failed to extract VMA"); @@ -650,21 +547,20 @@ impl InnerAddressSpace { self.mappings.insert_vma(after); } - let mut r_guard = r.lock_irqsave(); + let mut r_guard = r.lock(); // 如果VMA的保护标志不允许指定的修改,则返回错误 if !r_guard.can_have_flags(prot_flags) { drop(r_guard); self.mappings.insert_vma(r.clone()); return Err(SystemError::EACCES); } - r_guard.set_vm_flags(VmFlags::from(prot_flags)); - let new_flags: EntryFlags = r_guard + let new_flags: PageFlags = r_guard .flags() .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); - r_guard.remap(new_flags, mapper, &mut *flusher)?; + r_guard.remap(new_flags, mapper, &mut flusher)?; drop(r_guard); self.mappings.insert_vma(r); } @@ -679,7 +575,7 @@ impl InnerAddressSpace { behavior: MadvFlags, ) -> Result<(), SystemError> { let (mut active, mut inactive); - let flusher = if self.is_current() { + let mut flusher = if self.is_current() { active = PageFlushAll::new(); &mut active as &mut dyn Flusher } else { @@ -693,10 +589,10 @@ impl InnerAddressSpace { let regions = self.mappings.conflicts(region).collect::>(); for r in regions { - let r = *r.lock_irqsave().region(); + let r = *r.lock().region(); let r = self.mappings.remove_vma(&r).unwrap(); - let intersection = r.lock_irqsave().region().intersect(®ion).unwrap(); + let intersection = r.lock().region().intersect(®ion).unwrap(); let split_result = r .extract(intersection, mapper) .expect("Failed to extract VMA"); @@ -707,7 +603,7 @@ impl InnerAddressSpace { if let Some(after) = split_result.after { self.mappings.insert_vma(after); } - r.do_madvise(behavior, mapper, &mut *flusher)?; + r.do_madvise(behavior, mapper, &mut flusher)?; self.mappings.insert_vma(r); } Ok(()) @@ -919,7 +815,7 @@ impl UserMappings { #[allow(dead_code)] pub fn contains(&self, vaddr: VirtAddr) -> Option> { for v in self.vmas.iter() { - let guard = v.lock_irqsave(); + let guard = v.lock(); if guard.region.contains(vaddr) { return Some(v.clone()); } @@ -939,13 +835,13 @@ impl UserMappings { pub fn find_nearest(&self, vaddr: VirtAddr) -> Option> { let mut nearest: Option> = None; for v in self.vmas.iter() { - let guard = v.lock_irqsave(); + let guard = v.lock(); if guard.region.contains(vaddr) { return Some(v.clone()); } - if guard.region.start >= vaddr + if guard.region.start > vaddr && if let Some(ref nearest) = nearest { - guard.region.start < nearest.lock_irqsave().region.start + guard.region.start < nearest.lock().region.start } else { true } @@ -961,7 +857,7 @@ impl UserMappings { let r = self .vmas .iter() - .filter(move |v| v.lock_irqsave().region.intersect(&request).is_some()) + .filter(move |v| v.lock().region.intersect(&request).is_some()) .cloned(); return r; } @@ -1044,7 +940,7 @@ impl UserMappings { /// 在当前进程的映射关系中,插入一个新的VMA。 pub fn insert_vma(&mut self, vma: Arc) { - let region = vma.lock_irqsave().region; + let region = vma.lock().region; // 要求插入的地址范围必须是空闲的,也就是说,当前进程的地址空间中,不能有任何与之重叠的VMA。 assert!(self.conflicts(region).next().is_none()); self.reserve_hole(®ion); @@ -1064,7 +960,7 @@ impl UserMappings { // 请注意,由于这里会对每个VMA加锁,因此性能很低 let vma: Arc = self .vmas - .drain_filter(|vma| vma.lock_irqsave().region == *region) + .drain_filter(|vma| vma.lock().region == *region) .next()?; self.unreserve_hole(region); @@ -1114,7 +1010,7 @@ impl LockedVMA { id: LOCKEDVMA_ID_ALLOCATOR.alloc().unwrap(), vma: SpinLock::new(vma), }); - r.vma.lock_irqsave().self_ref = Arc::downgrade(&r); + r.vma.lock().self_ref = Arc::downgrade(&r); return r; } @@ -1126,10 +1022,6 @@ impl LockedVMA { return self.vma.lock(); } - pub fn lock_irqsave(&self) -> SpinLockGuard { - return self.vma.lock_irqsave(); - } - /// 调整当前VMA的页面的标志位 /// /// TODO:增加调整虚拟页映射的物理地址的功能 @@ -1140,11 +1032,11 @@ impl LockedVMA { /// pub fn remap( &self, - flags: EntryFlags, + flags: PageFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { - let mut guard = self.lock_irqsave(); + let mut guard = self.lock(); for page in guard.region.pages() { // 暂时要求所有的页帧都已经映射到页表 // TODO: 引入Lazy Mapping, 通过缺页中断来映射页帧,这里就不必要求所有的页帧都已经映射到页表了 @@ -1162,7 +1054,7 @@ impl LockedVMA { pub fn unmap(&self, mapper: &mut PageMapper, mut flusher: impl Flusher) { // todo: 如果当前vma与文件相关,完善文件相关的逻辑 - let mut guard = self.lock_irqsave(); + let mut guard = self.lock(); // 获取物理页的anon_vma的守卫 let mut page_manager_guard: SpinLockGuard<'_, crate::mm::page::PageManager> = @@ -1175,13 +1067,12 @@ impl LockedVMA { .expect("Failed to unmap, beacuse of some page is not mapped"); // 从anon_vma中删除当前VMA - let page = page_manager_guard.get_unwrap(&paddr); - page.write_irqsave().remove_vma(self); + let page = page_manager_guard.get_mut(&paddr); + page.remove_vma(self); // 如果物理页的anon_vma链表长度为0并且不是共享页,则释放物理页. - if page.read_irqsave().can_deallocate() { + if page.can_deallocate() { unsafe { - drop(page); deallocate_page_frames( PhysPageFrame::new(paddr), PageFrameCount::new(1), @@ -1193,19 +1084,10 @@ impl LockedVMA { flusher.consume(flush); } guard.mapped = false; - - // 当vma对应共享文件的写映射时,唤醒脏页回写线程 - if guard.vm_file().is_some() - && guard - .vm_flags() - .contains(VmFlags::VM_SHARED | VmFlags::VM_WRITE) - { - crate::mm::page::PageReclaimer::wakeup_claim_thread(); - } } pub fn mapped(&self) -> bool { - return self.vma.lock_irqsave().mapped; + return self.vma.lock().mapped; } /// 将当前VMA进行切分,切分成3个VMA,分别是: @@ -1217,7 +1099,7 @@ impl LockedVMA { assert!(region.start().check_aligned(MMArch::PAGE_SIZE)); assert!(region.end().check_aligned(MMArch::PAGE_SIZE)); - let mut guard = self.lock_irqsave(); + let mut guard = self.lock(); { // 如果传入的region不在当前VMA的范围内,则直接返回None if unlikely(region.start() < guard.region.start() || region.end() > guard.region.end()) @@ -1244,7 +1126,7 @@ impl LockedVMA { let before: Option> = guard.region.before(®ion).map(|virt_region| { let mut vma: VMA = unsafe { guard.clone() }; vma.region = virt_region; - vma.mapped = false; + let vma: Arc = LockedVMA::new(vma); vma }); @@ -1252,7 +1134,7 @@ impl LockedVMA { let after: Option> = guard.region.after(®ion).map(|virt_region| { let mut vma: VMA = unsafe { guard.clone() }; vma.region = virt_region; - vma.mapped = false; + let vma: Arc = LockedVMA::new(vma); vma }); @@ -1260,27 +1142,27 @@ impl LockedVMA { // 重新设置before、after这两个VMA里面的物理页的anon_vma let mut page_manager_guard = page_manager_lock_irqsave(); if let Some(before) = before.clone() { - let virt_iter = before.lock_irqsave().region.iter_pages(); - for frame in virt_iter { - if let Some((paddr, _)) = utable.translate(frame.virt_address()) { - let page = page_manager_guard.get_unwrap(&paddr); - let mut page_guard = page.write_irqsave(); - page_guard.insert_vma(before.clone()); - page_guard.remove_vma(self); - before.lock_irqsave().mapped = true; + let before_guard = before.lock(); + if before_guard.mapped { + let virt_iter = before_guard.region.iter_pages(); + for frame in virt_iter { + let paddr = utable.translate(frame.virt_address()).unwrap().0; + let page = page_manager_guard.get_mut(&paddr); + page.insert_vma(before.clone()); + page.remove_vma(self); } } } if let Some(after) = after.clone() { - let virt_iter = after.lock_irqsave().region.iter_pages(); - for frame in virt_iter { - if let Some((paddr, _)) = utable.translate(frame.virt_address()) { - let page = page_manager_guard.get_unwrap(&paddr); - let mut page_guard = page.write_irqsave(); - page_guard.insert_vma(after.clone()); - page_guard.remove_vma(self); - after.lock_irqsave().mapped = true; + let after_guard = after.lock(); + if after_guard.mapped { + let virt_iter = after_guard.region.iter_pages(); + for frame in virt_iter { + let paddr = utable.translate(frame.virt_address()).unwrap().0; + let page = page_manager_guard.get_mut(&paddr); + page.insert_vma(after.clone()); + page.remove_vma(self); } } } @@ -1296,7 +1178,7 @@ impl LockedVMA { /// 判断VMA是否为外部(非当前进程空间)的VMA pub fn is_foreign(&self) -> bool { - let guard = self.lock_irqsave(); + let guard = self.lock(); if let Some(space) = guard.user_address_space.clone() { if let Some(space) = space.upgrade() { return AddressSpace::is_current(&space); @@ -1310,15 +1192,15 @@ impl LockedVMA { /// 判断VMA是否可访问 pub fn is_accessible(&self) -> bool { - let guard = self.lock_irqsave(); + let guard = self.lock(); let vm_access_flags: VmFlags = VmFlags::VM_READ | VmFlags::VM_WRITE | VmFlags::VM_EXEC; guard.vm_flags().intersects(vm_access_flags) } /// 判断VMA是否为匿名映射 pub fn is_anonymous(&self) -> bool { - let guard = self.lock_irqsave(); - guard.vm_file.is_none() + //TODO: 实现匿名映射判断逻辑,目前仅支持匿名映射 + true } /// 判断VMA是否为大页映射 @@ -1335,7 +1217,6 @@ impl Drop for LockedVMA { } /// VMA切分结果 -#[allow(dead_code)] pub struct VMASplitResult { pub prev: Option>, pub middle: Arc, @@ -1364,17 +1245,13 @@ pub struct VMA { /// 虚拟内存区域标志 vm_flags: VmFlags, /// VMA内的页帧的标志 - flags: EntryFlags, + flags: PageFlags, /// VMA内的页帧是否已经映射到页表 mapped: bool, /// VMA所属的用户地址空间 user_address_space: Option>, self_ref: Weak, - vm_file: Option>, - /// VMA映射的文件部分相对于整个文件的偏移页数 - file_pgoff: Option, - provider: Provider, } @@ -1397,9 +1274,7 @@ impl VMA { pub fn new( region: VirtRegion, vm_flags: VmFlags, - flags: EntryFlags, - file: Option>, - pgoff: Option, + flags: PageFlags, mapped: bool, ) -> Self { VMA { @@ -1410,8 +1285,6 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, - vm_file: file, - file_pgoff: pgoff, } } @@ -1423,14 +1296,6 @@ impl VMA { return &self.vm_flags; } - pub fn vm_file(&self) -> Option> { - return self.vm_file.clone(); - } - - pub fn address_space(&self) -> Option> { - return self.user_address_space.clone(); - } - pub fn set_vm_flags(&mut self, vm_flags: VmFlags) { self.vm_flags = vm_flags; } @@ -1443,10 +1308,6 @@ impl VMA { self.mapped = mapped; } - pub fn set_flags(&mut self) { - self.flags = MMArch::vm_get_page_prot(self.vm_flags); - } - /// # 拷贝当前VMA的内容 /// /// ### 安全性 @@ -1461,8 +1322,6 @@ impl VMA { user_address_space: self.user_address_space.clone(), self_ref: self.self_ref.clone(), provider: Provider::Allocated, - file_pgoff: self.file_pgoff, - vm_file: self.vm_file.clone(), }; } @@ -1475,21 +1334,14 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, - file_pgoff: self.file_pgoff, - vm_file: self.vm_file.clone(), }; } #[inline(always)] - pub fn flags(&self) -> EntryFlags { + pub fn flags(&self) -> PageFlags { return self.flags; } - #[inline(always)] - pub fn file_page_offset(&self) -> Option { - return self.file_pgoff; - } - pub fn pages(&self) -> VirtPageFrameIter { return VirtPageFrameIter::new( VirtPageFrame::new(self.region.start()), @@ -1499,12 +1351,12 @@ impl VMA { pub fn remap( &mut self, - flags: EntryFlags, + flags: PageFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { for page in self.region.pages() { - // debug!("remap page {:?}", page.virt_address()); + // kdebug!("remap page {:?}", page.virt_address()); if mapper.translate(page.virt_address()).is_some() { let r = unsafe { mapper @@ -1513,8 +1365,8 @@ impl VMA { }; flusher.consume(r); } - // debug!("consume page {:?}", page.virt_address()); - // debug!("remap page {:?} done", page.virt_address()); + // kdebug!("consume page {:?}", page.virt_address()); + // kdebug!("remap page {:?} done", page.virt_address()); } self.flags = flags; return Ok(()); @@ -1552,7 +1404,7 @@ impl VMA { destination: VirtPageFrame, count: PageFrameCount, vm_flags: VmFlags, - flags: EntryFlags, + flags: PageFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result, SystemError> { @@ -1574,22 +1426,23 @@ impl VMA { cur_dest = cur_dest.next(); } - let r: Arc = LockedVMA::new(VMA::new( - VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), + let r: Arc = LockedVMA::new(VMA { + region: VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, flags, - None, - None, - true, - )); + mapped: true, + user_address_space: None, + self_ref: Weak::default(), + provider: Provider::Allocated, + }); // 将VMA加入到anon_vma中 let mut page_manager_guard = page_manager_lock_irqsave(); cur_phy = phys; for _ in 0..count.data() { let paddr = cur_phy.phys_address(); - let page = page_manager_guard.get_unwrap(&paddr); - page.write_irqsave().insert_vma(r.clone()); + let page = page_manager_guard.get_mut(&paddr); + page.insert_vma(r.clone()); cur_phy = cur_phy.next(); } @@ -1597,37 +1450,29 @@ impl VMA { } /// 从页分配器中分配一些物理页,并把它们映射到指定的虚拟地址,然后创建VMA - /// ## 参数 /// - /// - `destination`: 要映射到的虚拟地址 - /// - `page_count`: 要映射的页帧数量 - /// - `vm_flags`: VMA标志位 - /// - `flags`: 页面标志位 - /// - `mapper`: 页表映射器 - /// - `flusher`: 页表项刷新器 - /// - `file`: 映射文件 - /// - `pgoff`: 返回映射后的虚拟内存区域 + /// @param destination 要映射到的虚拟地址 + /// @param count 要映射的页帧数量 + /// @param flags 页面标志位 + /// @param mapper 页表映射器 + /// @param flusher 页表项刷新器 /// - /// ## 返回值 - /// - 页面错误处理信息标志 - #[allow(clippy::too_many_arguments)] + /// @return 返回映射后的虚拟内存区域 pub fn zeroed( destination: VirtPageFrame, page_count: PageFrameCount, vm_flags: VmFlags, - flags: EntryFlags, + flags: PageFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, - file: Option>, - pgoff: Option, ) -> Result, SystemError> { let mut cur_dest: VirtPageFrame = destination; - // debug!( + // kdebug!( // "VMA::zeroed: page_count = {:?}, destination={destination:?}", // page_count // ); for _ in 0..page_count.data() { - // debug!( + // kdebug!( // "VMA::zeroed: cur_dest={cur_dest:?}, vaddr = {:?}", // cur_dest.virt_address() // ); @@ -1646,12 +1491,10 @@ impl VMA { ), vm_flags, flags, - file, - pgoff, true, )); drop(flusher); - // debug!("VMA::zeroed: flusher dropped"); + // kdebug!("VMA::zeroed: flusher dropped"); // 清空这些内存并将VMA加入到anon_vma中 let mut page_manager_guard = page_manager_lock_irqsave(); @@ -1661,24 +1504,17 @@ impl VMA { let paddr = mapper.translate(frame.virt_address()).unwrap().0; // 将VMA加入到anon_vma - let page = page_manager_guard.get_unwrap(&paddr); - page.write_irqsave().insert_vma(r.clone()); - } - // debug!("VMA::zeroed: done"); - return Ok(r); - } + let page = page_manager_guard.get_mut(&paddr); + page.insert_vma(r.clone()); - pub fn page_address(&self, page: &Arc) -> Result { - let page_guard = page.read_irqsave(); - let index = page_guard.index().unwrap(); - if index >= self.file_pgoff.unwrap() { - let address = - self.region.start + ((index - self.file_pgoff.unwrap()) << MMArch::PAGE_SHIFT); - if address <= self.region.end() { - return Ok(address); + // 清空内存 + unsafe { + let vaddr = MMArch::phys_2_virt(paddr).unwrap(); + MMArch::write_bytes(vaddr, 0, MMArch::PAGE_SIZE); } } - return Err(SystemError::EFAULT); + // kdebug!("VMA::zeroed: done"); + return Ok(r); } } @@ -1745,7 +1581,7 @@ impl UserStack { | MapFlags::MAP_ANONYMOUS | MapFlags::MAP_FIXED_NOREPLACE | MapFlags::MAP_GROWSDOWN; - // debug!( + // kdebug!( // "map anonymous stack: {:?} {}", // actual_stack_bottom, // guard_size @@ -1761,7 +1597,7 @@ impl UserStack { // test_buddy(); // 设置保护页只读 prot_flags.remove(ProtFlags::PROT_WRITE); - // debug!( + // kdebug!( // "to mprotect stack guard pages: {:?} {}", // actual_stack_bottom, // guard_size @@ -1772,7 +1608,7 @@ impl UserStack { prot_flags, )?; - // debug!( + // kdebug!( // "mprotect stack guard pages done: {:?} {}", // actual_stack_bottom, // guard_size @@ -1784,10 +1620,10 @@ impl UserStack { current_sp: actual_stack_bottom - guard_size, }; - // debug!("extend user stack: {:?} {}", stack_bottom, stack_size); + // kdebug!("extend user stack: {:?} {}", stack_bottom, stack_size); // 分配用户栈 user_stack.initial_extend(vm, stack_size)?; - // debug!("user stack created: {:?} {}", stack_bottom, stack_size); + // kdebug!("user stack created: {:?} {}", stack_bottom, stack_size); return Ok(user_stack); } diff --git a/kernel/src/net/event_poll/mod.rs b/kernel/src/net/event_poll/mod.rs index a23209297..0691278ba 100644 --- a/kernel/src/net/event_poll/mod.rs +++ b/kernel/src/net/event_poll/mod.rs @@ -1,5 +1,4 @@ use core::{ - any::Any, fmt::Debug, sync::atomic::{AtomicBool, Ordering}, }; @@ -9,7 +8,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use intertrait::CastFromSync; use system_error::SystemError; use crate::{ @@ -132,10 +130,6 @@ impl EPollItem { } } -pub trait KernelIoctlData: Send + Sync + Any + Debug + CastFromSync {} - -impl KernelIoctlData for EPollItem {} - /// ### Epoll文件的私有信息 #[derive(Debug, Clone)] pub struct EPollPrivateData { @@ -436,7 +430,6 @@ impl EventPoll { } // 判断epoll上有没有就绪事件 let mut available = epoll_guard.ep_events_available(); - drop(epoll_guard); loop { if available { @@ -573,7 +566,7 @@ impl EventPoll { // 记数加一 res += 1; - // crate::debug!("ep send {event:?}"); + // crate::kdebug!("ep send {event:?}"); if ep_events.contains(EPollEventType::EPOLLONESHOT) { let mut event_writer = epitem.event.write(); @@ -760,7 +753,6 @@ impl EventPoll { /// 与C兼容的Epoll事件结构体 #[derive(Copy, Clone, Default)] #[repr(packed)] -#[repr(C)] pub struct EPollEvent { /// 表示触发的事件 events: u32, @@ -872,8 +864,5 @@ bitflags! { /// 表示epoll已经被释放,但是在目前的设计中未用到 const POLLFREE = 0x4000; - - /// listen状态的socket可以接受连接 - const EPOLL_LISTEN_CAN_ACCEPT = Self::EPOLLIN.bits | Self::EPOLLRDNORM.bits; } } diff --git a/kernel/src/net/net_core.rs b/kernel/src/net/net_core.rs index da1486da5..eb7efe7b6 100644 --- a/kernel/src/net/net_core.rs +++ b/kernel/src/net/net_core.rs @@ -1,10 +1,10 @@ use alloc::{boxed::Box, collections::BTreeMap, sync::Arc}; -use log::{debug, info, warn}; use smoltcp::{socket::dhcpv4, wire}; use system_error::SystemError; use crate::{ driver::net::NetDevice, + kdebug, kinfo, kwarn, libs::rwlock::RwLockReadGuard, net::{socket::SocketPollMethod, NET_DEVICES}, time::timer::{next_n_ms_timer_jiffies, Timer, TimerFunction}, @@ -19,7 +19,6 @@ use super::{ /// /// The main purpose of this function is to poll all network interfaces. #[derive(Debug)] -#[allow(dead_code)] struct NetWorkPollFunc; impl TimerFunction for NetWorkPollFunc { @@ -44,9 +43,7 @@ pub fn net_init() -> Result<(), SystemError> { fn dhcp_query() -> Result<(), SystemError> { let binding = NET_DEVICES.write_irqsave(); - //由于现在os未实现在用户态为网卡动态分配内存,而lo网卡的id最先分配且ip固定不能被分配 - //所以特判取用id为1的网卡(也就是virto_net) - let net_face = binding.get(&1).ok_or(SystemError::ENODEV)?.clone(); + let net_face = binding.get(&0).ok_or(SystemError::ENODEV)?.clone(); drop(binding); @@ -63,7 +60,7 @@ fn dhcp_query() -> Result<(), SystemError> { const DHCP_TRY_ROUND: u8 = 10; for i in 0..DHCP_TRY_ROUND { - debug!("DHCP try round: {}", i); + kdebug!("DHCP try round: {}", i); net_face.poll(&mut SOCKET_SET.lock_irqsave()).ok(); let mut binding = SOCKET_SET.lock_irqsave(); let event = binding.get_mut::(dhcp_handle).poll(); @@ -72,9 +69,9 @@ fn dhcp_query() -> Result<(), SystemError> { None => {} Some(dhcpv4::Event::Configured(config)) => { - // debug!("Find Config!! {config:?}"); - // debug!("Find ip address: {}", config.address); - // debug!("iface.ip_addrs={:?}", net_face.inner_iface.ip_addrs()); + // kdebug!("Find Config!! {config:?}"); + // kdebug!("Find ip address: {}", config.address); + // kdebug!("iface.ip_addrs={:?}", net_face.inner_iface.ip_addrs()); net_face .update_ip_addrs(&[wire::IpCidr::Ipv4(config.address)]) @@ -89,7 +86,7 @@ fn dhcp_query() -> Result<(), SystemError> { .unwrap(); let cidr = net_face.inner_iface().lock().ip_addrs().first().cloned(); if let Some(cidr) = cidr { - info!("Successfully allocated ip by Dhcpv4! Ip:{}", cidr); + kinfo!("Successfully allocated ip by Dhcpv4! Ip:{}", cidr); return Ok(()); } } else { @@ -102,7 +99,7 @@ fn dhcp_query() -> Result<(), SystemError> { } Some(dhcpv4::Event::Deconfigured) => { - debug!("Dhcp v4 deconfigured"); + kdebug!("Dhcp v4 deconfigured"); net_face .update_ip_addrs(&[smoltcp::wire::IpCidr::Ipv4(wire::Ipv4Cidr::new( wire::Ipv4Address::UNSPECIFIED, @@ -124,7 +121,7 @@ fn dhcp_query() -> Result<(), SystemError> { pub fn poll_ifaces() { let guard: RwLockReadGuard>> = NET_DEVICES.read_irqsave(); if guard.len() == 0 { - warn!("poll_ifaces: No net driver found!"); + kwarn!("poll_ifaces: No net driver found!"); return; } let mut sockets = SOCKET_SET.lock_irqsave(); @@ -145,7 +142,7 @@ pub fn poll_ifaces_try_lock(times: u16) -> Result<(), SystemError> { let guard: RwLockReadGuard>> = NET_DEVICES.read_irqsave(); if guard.len() == 0 { - warn!("poll_ifaces: No net driver found!"); + kwarn!("poll_ifaces: No net driver found!"); // 没有网卡,返回错误 return Err(SystemError::ENODEV); } @@ -175,7 +172,7 @@ pub fn poll_ifaces_try_lock(times: u16) -> Result<(), SystemError> { pub fn poll_ifaces_try_lock_onetime() -> Result<(), SystemError> { let guard: RwLockReadGuard>> = NET_DEVICES.read_irqsave(); if guard.len() == 0 { - warn!("poll_ifaces: No net driver found!"); + kwarn!("poll_ifaces: No net driver found!"); // 没有网卡,返回错误 return Err(SystemError::ENODEV); } @@ -192,25 +189,25 @@ fn send_event(sockets: &smoltcp::iface::SocketSet) -> Result<(), SystemError> { for (handle, socket_type) in sockets.iter() { let handle_guard = HANDLE_MAP.read_irqsave(); let global_handle = GlobalSocketHandle::new_smoltcp_handle(handle); - let item: Option<&super::socket::SocketHandleItem> = handle_guard.get(&global_handle); + let item = handle_guard.get(&global_handle); if item.is_none() { continue; } let handle_item = item.unwrap(); - let posix_item = handle_item.posix_item(); - if posix_item.is_none() { - continue; - } - let posix_item = posix_item.unwrap(); // 获取socket上的事件 - let mut events = SocketPollMethod::poll(socket_type, handle_item).bits() as u64; + let mut events = + SocketPollMethod::poll(socket_type, handle_item.shutdown_type()).bits() as u64; // 分发到相应类型socket处理 match socket_type { smoltcp::socket::Socket::Raw(_) | smoltcp::socket::Socket::Udp(_) => { - posix_item.wakeup_any(events); + handle_guard + .get(&global_handle) + .unwrap() + .wait_queue + .wakeup_any(events); } smoltcp::socket::Socket::Icmp(_) => unimplemented!("Icmp socket hasn't unimplemented"), smoltcp::socket::Socket::Tcp(inner_socket) => { @@ -220,21 +217,23 @@ fn send_event(sockets: &smoltcp::iface::SocketSet) -> Result<(), SystemError> { if inner_socket.state() == smoltcp::socket::tcp::State::Established { events |= TcpSocket::CAN_CONNECT; } - if inner_socket.state() == smoltcp::socket::tcp::State::CloseWait { - events |= EPollEventType::EPOLLHUP.bits() as u64; - } - - posix_item.wakeup_any(events); + handle_guard + .get(&global_handle) + .unwrap() + .wait_queue + .wakeup_any(events); } smoltcp::socket::Socket::Dhcpv4(_) => {} smoltcp::socket::Socket::Dns(_) => unimplemented!("Dns socket hasn't unimplemented"), } + drop(handle_guard); + let mut handle_guard = HANDLE_MAP.write_irqsave(); + let handle_item = handle_guard.get_mut(&global_handle).unwrap(); EventPoll::wakeup_epoll( - &posix_item.epitems, + &handle_item.epitems, EPollEventType::from_bits_truncate(events as u32), )?; - drop(handle_guard); - // crate::debug!( + // crate::kdebug!( // "{} send_event {:?}", // handle, // EPollEventType::from_bits_truncate(events as u32) diff --git a/kernel/src/net/socket/inet.rs b/kernel/src/net/socket/inet.rs index a7cb975cf..6c4580a6a 100644 --- a/kernel/src/net/socket/inet.rs +++ b/kernel/src/net/socket/inet.rs @@ -1,13 +1,14 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec}; -use log::{error, warn}; use smoltcp::{ - socket::{raw, tcp, udp}, + socket::{raw, tcp, udp, AnySocket}, wire, }; use system_error::SystemError; use crate::{ + arch::rand::rand, driver::net::NetDevice, + kerror, kwarn, libs::rwlock::RwLock, net::{ event_poll::EPollEventType, net_core::poll_ifaces, Endpoint, Protocol, ShutdownType, @@ -16,8 +17,8 @@ use crate::{ }; use super::{ - handle::GlobalSocketHandle, PosixSocketHandleItem, Socket, SocketHandleItem, SocketMetadata, - SocketOptions, SocketPollMethod, SocketType, HANDLE_MAP, PORT_MANAGER, SOCKET_SET, + handle::GlobalSocketHandle, Socket, SocketHandleItem, SocketMetadata, SocketOptions, + SocketPollMethod, SocketType, HANDLE_MAP, PORT_MANAGER, SOCKET_SET, }; /// @brief 表示原始的socket。原始套接字绕过传输层协议(如 TCP 或 UDP)并提供对网络层协议(如 IP)的直接访问。 @@ -32,7 +33,6 @@ pub struct RawSocket { header_included: bool, /// socket的metadata metadata: SocketMetadata, - posix_item: Arc, } impl RawSocket { @@ -77,29 +77,18 @@ impl RawSocket { options, ); - let posix_item = Arc::new(PosixSocketHandleItem::new(None)); - return Self { handle, header_included: false, metadata, - posix_item, }; } } impl Socket for RawSocket { - fn posix_item(&self) -> Arc { - self.posix_item.clone() - } - fn close(&mut self) { let mut socket_set_guard = SOCKET_SET.lock_irqsave(); - if let smoltcp::socket::Socket::Udp(mut sock) = - socket_set_guard.remove(self.handle.smoltcp_handle().unwrap()) - { - sock.close(); - } + socket_set_guard.remove(self.handle.smoltcp_handle().unwrap()); // 删除的时候,会发送一条FINISH的信息? drop(socket_set_guard); poll_ifaces(); } @@ -131,7 +120,11 @@ impl Socket for RawSocket { } } drop(socket_set_guard); - self.posix_item.sleep(EPollEventType::EPOLLIN.bits() as u64); + SocketHandleItem::sleep( + self.socket_handle(), + EPollEventType::EPOLLIN.bits() as u64, + HANDLE_MAP.read_irqsave(), + ); } } @@ -201,7 +194,7 @@ impl Socket for RawSocket { drop(socket_set_guard); return Ok(len); } else { - warn!("Unsupport Ip protocol type!"); + kwarn!("Unsupport Ip protocol type!"); return Err(SystemError::EINVAL); } } else { @@ -244,7 +237,6 @@ pub struct UdpSocket { pub handle: GlobalSocketHandle, remote_endpoint: Option, // 记录远程endpoint提供给connect(), 应该使用IP地址。 metadata: SocketMetadata, - posix_item: Arc, } impl UdpSocket { @@ -283,13 +275,10 @@ impl UdpSocket { options, ); - let posix_item = Arc::new(PosixSocketHandleItem::new(None)); - return Self { handle, remote_endpoint: None, metadata, - posix_item, }; } @@ -300,7 +289,7 @@ impl UdpSocket { ip.port = PORT_MANAGER.get_ephemeral_port(self.metadata.socket_type)?; } // 检测端口是否已被占用 - PORT_MANAGER.bind_port(self.metadata.socket_type, ip.port)?; + PORT_MANAGER.bind_port(self.metadata.socket_type, ip.port, self.clone())?; let bind_res = if ip.addr.is_unspecified() { socket.bind(ip.port) @@ -319,17 +308,9 @@ impl UdpSocket { } impl Socket for UdpSocket { - fn posix_item(&self) -> Arc { - self.posix_item.clone() - } - fn close(&mut self) { let mut socket_set_guard = SOCKET_SET.lock_irqsave(); - if let smoltcp::socket::Socket::Udp(mut sock) = - socket_set_guard.remove(self.handle.smoltcp_handle().unwrap()) - { - sock.close(); - } + socket_set_guard.remove(self.handle.smoltcp_handle().unwrap()); // 删除的时候,会发送一条FINISH的信息? drop(socket_set_guard); poll_ifaces(); } @@ -337,13 +318,13 @@ impl Socket for UdpSocket { /// @brief 在read函数执行之前,请先bind到本地的指定端口 fn read(&self, buf: &mut [u8]) -> (Result, Endpoint) { loop { - // debug!("Wait22 to Read"); + // kdebug!("Wait22 to Read"); poll_ifaces(); let mut socket_set_guard = SOCKET_SET.lock_irqsave(); let socket = socket_set_guard.get_mut::(self.handle.smoltcp_handle().unwrap()); - // debug!("Wait to Read"); + // kdebug!("Wait to Read"); if socket.can_recv() { if let Ok((size, metadata)) = socket.recv_slice(buf) { @@ -356,12 +337,16 @@ impl Socket for UdpSocket { // return (Err(SystemError::ENOTCONN), Endpoint::Ip(None)); } drop(socket_set_guard); - self.posix_item.sleep(EPollEventType::EPOLLIN.bits() as u64); + SocketHandleItem::sleep( + self.socket_handle(), + EPollEventType::EPOLLIN.bits() as u64, + HANDLE_MAP.read_irqsave(), + ); } } fn write(&self, buf: &[u8], to: Option) -> Result { - // debug!("udp to send: {:?}, len={}", to, buf.len()); + // kdebug!("udp to send: {:?}, len={}", to, buf.len()); let remote_endpoint: &wire::IpEndpoint = { if let Some(Endpoint::Ip(Some(ref endpoint))) = to { endpoint @@ -371,28 +356,28 @@ impl Socket for UdpSocket { return Err(SystemError::ENOTCONN); } }; - // debug!("udp write: remote = {:?}", remote_endpoint); + // kdebug!("udp write: remote = {:?}", remote_endpoint); let mut socket_set_guard = SOCKET_SET.lock_irqsave(); let socket = socket_set_guard.get_mut::(self.handle.smoltcp_handle().unwrap()); - // debug!("is open()={}", socket.is_open()); - // debug!("socket endpoint={:?}", socket.endpoint()); + // kdebug!("is open()={}", socket.is_open()); + // kdebug!("socket endpoint={:?}", socket.endpoint()); if socket.can_send() { - // debug!("udp write: can send"); + // kdebug!("udp write: can send"); match socket.send_slice(buf, *remote_endpoint) { Ok(()) => { - // debug!("udp write: send ok"); + // kdebug!("udp write: send ok"); drop(socket_set_guard); poll_ifaces(); return Ok(buf.len()); } Err(_) => { - // debug!("udp write: send err"); + // kdebug!("udp write: send err"); return Err(SystemError::ENOBUFS); } } } else { - // debug!("udp write: can not send"); + // kdebug!("udp write: can not send"); return Err(SystemError::ENOBUFS); }; } @@ -400,7 +385,7 @@ impl Socket for UdpSocket { fn bind(&mut self, endpoint: Endpoint) -> Result<(), SystemError> { let mut sockets = SOCKET_SET.lock_irqsave(); let socket = sockets.get_mut::(self.handle.smoltcp_handle().unwrap()); - // debug!("UDP Bind to {:?}", endpoint); + // kdebug!("UDP Bind to {:?}", endpoint); return self.do_bind(socket, endpoint); } @@ -492,7 +477,6 @@ pub struct TcpSocket { local_endpoint: Option, // save local endpoint for bind() is_listening: bool, metadata: SocketMetadata, - posix_item: Arc, } impl TcpSocket { @@ -525,15 +509,13 @@ impl TcpSocket { Self::DEFAULT_METADATA_BUF_SIZE, options, ); - let posix_item = Arc::new(PosixSocketHandleItem::new(None)); - // debug!("when there's a new tcp socket,its'len: {}",handles.len()); + // kdebug!("when there's a new tcp socket,its'len: {}",handles.len()); return Self { handles, local_endpoint: None, is_listening: false, metadata, - posix_item, }; } @@ -543,13 +525,15 @@ impl TcpSocket { local_endpoint: wire::IpEndpoint, ) -> Result<(), SystemError> { let listen_result = if local_endpoint.addr.is_unspecified() { + // kdebug!("Tcp Socket Listen on port {}", local_endpoint.port); socket.listen(local_endpoint.port) } else { + // kdebug!("Tcp Socket Listen on {local_endpoint}"); socket.listen(local_endpoint) }; return match listen_result { Ok(()) => { - // debug!( + // kdebug!( // "Tcp Socket Listen on {local_endpoint}, open?:{}", // socket.is_open() // ); @@ -570,49 +554,16 @@ impl TcpSocket { let tx_buffer = tcp::SocketBuffer::new(vec![0; Self::DEFAULT_TX_BUF_SIZE]); tcp::Socket::new(rx_buffer, tx_buffer) } - - /// listening状态的posix socket是需要特殊处理的 - fn tcp_poll_listening(&self) -> EPollEventType { - let socketset_guard = SOCKET_SET.lock_irqsave(); - - let can_accept = self.handles.iter().any(|h| { - if let Some(sh) = h.smoltcp_handle() { - let socket = socketset_guard.get::(sh); - socket.is_active() - } else { - false - } - }); - - if can_accept { - return EPollEventType::EPOLL_LISTEN_CAN_ACCEPT; - } else { - return EPollEventType::empty(); - } - } } impl Socket for TcpSocket { - fn posix_item(&self) -> Arc { - self.posix_item.clone() - } - fn close(&mut self) { for handle in self.handles.iter() { - { - let mut socket_set_guard = SOCKET_SET.lock_irqsave(); - let smoltcp_handle = handle.smoltcp_handle().unwrap(); - socket_set_guard - .get_mut::(smoltcp_handle) - .close(); - drop(socket_set_guard); - } - poll_ifaces(); - SOCKET_SET - .lock_irqsave() - .remove(handle.smoltcp_handle().unwrap()); - // debug!("[Socket] [TCP] Close: {:?}", handle); + let mut socket_set_guard = SOCKET_SET.lock_irqsave(); + socket_set_guard.remove(handle.smoltcp_handle().unwrap()); // 删除的时候,会发送一条FINISH的信息? + drop(socket_set_guard); } + poll_ifaces(); } fn read(&self, buf: &mut [u8]) -> (Result, Endpoint) { @@ -625,18 +576,18 @@ impl Socket for TcpSocket { { return (Err(SystemError::ENOTCONN), Endpoint::Ip(None)); } - // debug!("tcp socket: read, buf len={}", buf.len()); - // debug!("tcp socket:read, socket'len={}",self.handle.len()); + // kdebug!("tcp socket: read, buf len={}", buf.len()); + // kdebug!("tcp socket:read, socket'len={}",self.handle.len()); loop { poll_ifaces(); let mut socket_set_guard = SOCKET_SET.lock_irqsave(); let socket = socket_set_guard - .get_mut::(self.handles.first().unwrap().smoltcp_handle().unwrap()); + .get_mut::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); // 如果socket已经关闭,返回错误 if !socket.is_active() { - // debug!("Tcp Socket Read Error, socket is closed"); + // kdebug!("Tcp Socket Read Error, socket is closed"); return (Err(SystemError::ENOTCONN), Endpoint::Ip(None)); } @@ -656,7 +607,7 @@ impl Socket for TcpSocket { } } Err(tcp::RecvError::InvalidState) => { - warn!("Tcp Socket Read Error, InvalidState"); + kwarn!("Tcp Socket Read Error, InvalidState"); return (Err(SystemError::ENOTCONN), Endpoint::Ip(None)); } Err(tcp::RecvError::Finished) => { @@ -674,8 +625,11 @@ impl Socket for TcpSocket { return (Err(SystemError::ENOTCONN), Endpoint::Ip(None)); } drop(socket_set_guard); - self.posix_item - .sleep((EPollEventType::EPOLLIN | EPollEventType::EPOLLHUP).bits() as u64); + SocketHandleItem::sleep( + self.socket_handle(), + EPollEventType::EPOLLIN.bits() as u64, + HANDLE_MAP.read_irqsave(), + ); } } @@ -689,12 +643,12 @@ impl Socket for TcpSocket { { return Err(SystemError::ENOTCONN); } - // debug!("tcp socket:write, socket'len={}",self.handle.len()); + // kdebug!("tcp socket:write, socket'len={}",self.handle.len()); let mut socket_set_guard = SOCKET_SET.lock_irqsave(); let socket = socket_set_guard - .get_mut::(self.handles.first().unwrap().smoltcp_handle().unwrap()); + .get_mut::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); if socket.is_open() { if socket.can_send() { @@ -705,7 +659,7 @@ impl Socket for TcpSocket { return Ok(size); } Err(e) => { - error!("Tcp Socket Write Error {e:?}"); + kerror!("Tcp Socket Write Error {e:?}"); return Err(SystemError::ENOBUFS); } } @@ -718,44 +672,37 @@ impl Socket for TcpSocket { } fn poll(&self) -> EPollEventType { - // 处理listen的快速路径 - if self.is_listening { - return self.tcp_poll_listening(); - } - // 由于上面处理了listening状态,所以这里只处理非listening状态,这种情况下只有一个handle - - assert!(self.handles.len() == 1); - let mut socket_set_guard = SOCKET_SET.lock_irqsave(); - // debug!("tcp socket:poll, socket'len={}",self.handle.len()); + // kdebug!("tcp socket:poll, socket'len={}",self.handle.len()); let socket = socket_set_guard - .get_mut::(self.handles.first().unwrap().smoltcp_handle().unwrap()); - let handle_map_guard = HANDLE_MAP.read_irqsave(); - let handle_item = handle_map_guard.get(&self.socket_handle()).unwrap(); - let shutdown_type = handle_item.shutdown_type(); - let is_posix_listen = handle_item.is_posix_listen; - drop(handle_map_guard); - - return SocketPollMethod::tcp_poll(socket, shutdown_type, is_posix_listen); + .get_mut::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); + return SocketPollMethod::tcp_poll( + socket, + HANDLE_MAP + .read_irqsave() + .get(&self.socket_handle()) + .unwrap() + .shutdown_type(), + ); } fn connect(&mut self, endpoint: Endpoint) -> Result<(), SystemError> { let mut sockets = SOCKET_SET.lock_irqsave(); - // debug!("tcp socket:connect, socket'len={}", self.handles.len()); + // kdebug!("tcp socket:connect, socket'len={}",self.handle.len()); let socket = - sockets.get_mut::(self.handles.first().unwrap().smoltcp_handle().unwrap()); + sockets.get_mut::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); if let Endpoint::Ip(Some(ip)) = endpoint { let temp_port = PORT_MANAGER.get_ephemeral_port(self.metadata.socket_type)?; // 检测端口是否被占用 - PORT_MANAGER.bind_port(self.metadata.socket_type, temp_port)?; + PORT_MANAGER.bind_port(self.metadata.socket_type, temp_port, self.clone())?; - // debug!("temp_port: {}", temp_port); + // kdebug!("temp_port: {}", temp_port); let iface: Arc = NET_DEVICES.write_irqsave().get(&0).unwrap().clone(); let mut inner_iface = iface.inner_iface().lock(); - // debug!("to connect: {ip:?}"); + // kdebug!("to connect: {ip:?}"); match socket.connect(inner_iface.context(), ip, temp_port) { Ok(()) => { @@ -767,7 +714,7 @@ impl Socket for TcpSocket { poll_ifaces(); let mut sockets = SOCKET_SET.lock_irqsave(); let socket = sockets.get_mut::( - self.handles.first().unwrap().smoltcp_handle().unwrap(), + self.handles.get(0).unwrap().smoltcp_handle().unwrap(), ); match socket.state() { @@ -776,7 +723,11 @@ impl Socket for TcpSocket { } tcp::State::SynSent => { drop(sockets); - self.posix_item.sleep(Self::CAN_CONNECT); + SocketHandleItem::sleep( + self.socket_handle(), + Self::CAN_CONNECT, + HANDLE_MAP.read_irqsave(), + ); } _ => { return Err(SystemError::ECONNREFUSED); @@ -785,7 +736,7 @@ impl Socket for TcpSocket { } } Err(e) => { - // error!("Tcp Socket Connect Error {e:?}"); + // kerror!("Tcp Socket Connect Error {e:?}"); match e { tcp::ConnectError::InvalidState => return Err(SystemError::EISCONN), tcp::ConnectError::Unaddressable => return Err(SystemError::EADDRNOTAVAIL), @@ -799,17 +750,12 @@ impl Socket for TcpSocket { /// @brief tcp socket 监听 local_endpoint 端口 /// - /// @param backlog 未处理的连接队列的最大长度 + /// @param backlog 未处理的连接队列的最大长度. 由于smoltcp不支持backlog,所以这个参数目前无效 fn listen(&mut self, backlog: usize) -> Result<(), SystemError> { if self.is_listening { return Ok(()); } - // debug!( - // "tcp socket:listen, socket'len={}, backlog = {backlog}", - // self.handles.len() - // ); - let local_endpoint = self.local_endpoint.ok_or(SystemError::EINVAL)?; let mut sockets = SOCKET_SET.lock_irqsave(); // 获取handle的数量 @@ -817,22 +763,17 @@ impl Socket for TcpSocket { let backlog = handlen.max(backlog); // 添加剩余需要构建的socket - // debug!("tcp socket:before listen, socket'len={}", self.handle_list.len()); + // kdebug!("tcp socket:before listen, socket'len={}",self.handle.len()); let mut handle_guard = HANDLE_MAP.write_irqsave(); - let socket_handle_item_0 = handle_guard.get_mut(&self.socket_handle()).unwrap(); - socket_handle_item_0.is_posix_listen = true; - self.handles.extend((handlen..backlog).map(|_| { let socket = Self::create_new_socket(); let handle = GlobalSocketHandle::new_smoltcp_handle(sockets.add(socket)); - let mut handle_item = SocketHandleItem::new(Arc::downgrade(&self.posix_item)); - handle_item.is_posix_listen = true; + let handle_item = SocketHandleItem::new(); handle_guard.insert(handle, handle_item); handle })); - - // debug!("tcp socket:listen, socket'len={}", self.handles.len()); - // debug!("tcp socket:listen, backlog={backlog}"); + // kdebug!("tcp socket:listen, socket'len={}",self.handle.len()); + // kdebug!("tcp socket:listen, backlog={backlog}"); // 监听所有的socket for i in 0..backlog { @@ -841,12 +782,11 @@ impl Socket for TcpSocket { let socket = sockets.get_mut::(handle.smoltcp_handle().unwrap()); if !socket.is_listening() { - // debug!("Tcp Socket is already listening on {local_endpoint}"); + // kdebug!("Tcp Socket is already listening on {local_endpoint}"); self.do_listen(socket, local_endpoint)?; } - // debug!("Tcp Socket before listen, open={}", socket.is_open()); + // kdebug!("Tcp Socket before listen, open={}", socket.is_open()); } - return Ok(()); } @@ -857,12 +797,11 @@ impl Socket for TcpSocket { } // 检测端口是否已被占用 - PORT_MANAGER.bind_port(self.metadata.socket_type, ip.port)?; - // debug!("tcp socket:bind, socket'len={}",self.handle.len()); + PORT_MANAGER.bind_port(self.metadata.socket_type, ip.port, self.clone())?; + // kdebug!("tcp socket:bind, socket'len={}",self.handle.len()); self.local_endpoint = Some(ip); self.is_listening = false; - return Ok(()); } return Err(SystemError::EINVAL); @@ -879,87 +818,101 @@ impl Socket for TcpSocket { } fn accept(&mut self) -> Result<(Box, Endpoint), SystemError> { - if !self.is_listening { - return Err(SystemError::EINVAL); - } let endpoint = self.local_endpoint.ok_or(SystemError::EINVAL)?; loop { - // debug!("tcp accept: poll_ifaces()"); + // kdebug!("tcp accept: poll_ifaces()"); poll_ifaces(); - // debug!("tcp socket:accept, socket'len={}", self.handle_list.len()); - - let mut sockset = SOCKET_SET.lock_irqsave(); - // Get the corresponding activated handler - let global_handle_index = self.handles.iter().position(|handle| { - let con_smol_sock = sockset.get::(handle.smoltcp_handle().unwrap()); - con_smol_sock.is_active() - }); - - if let Some(handle_index) = global_handle_index { - let con_smol_sock = sockset - .get::(self.handles[handle_index].smoltcp_handle().unwrap()); - - // debug!("[Socket] [TCP] Accept: {:?}", handle); - // handle is connected socket's handle - let remote_ep = con_smol_sock - .remote_endpoint() - .ok_or(SystemError::ENOTCONN)?; - - let tcp_socket = Self::create_new_socket(); - - let new_handle = GlobalSocketHandle::new_smoltcp_handle(sockset.add(tcp_socket)); - - // let handle in TcpSock be the new empty handle, and return the old connected handle - let old_handle = core::mem::replace(&mut self.handles[handle_index], new_handle); - - let metadata = SocketMetadata::new( - SocketType::Tcp, - Self::DEFAULT_TX_BUF_SIZE, - Self::DEFAULT_RX_BUF_SIZE, - Self::DEFAULT_METADATA_BUF_SIZE, - self.metadata.options, - ); - - let sock_ret = Box::new(TcpSocket { - handles: vec![old_handle], - local_endpoint: self.local_endpoint, - is_listening: false, - metadata, - posix_item: Arc::new(PosixSocketHandleItem::new(None)), - }); - - { - let mut handle_guard = HANDLE_MAP.write_irqsave(); - // 先删除原来的 - let item = handle_guard.remove(&old_handle).unwrap(); - item.reset_shutdown_type(); - assert!(item.is_posix_listen); - - // 按照smoltcp行为,将新的handle绑定到原来的item - let new_item = SocketHandleItem::new(Arc::downgrade(&sock_ret.posix_item)); - handle_guard.insert(old_handle, new_item); - // 插入新的item - handle_guard.insert(new_handle, item); - - let socket = sockset.get_mut::( - self.handles[handle_index].smoltcp_handle().unwrap(), - ); + // kdebug!("tcp socket:accept, socket'len={}",self.handle.len()); + + let mut sockets = SOCKET_SET.lock_irqsave(); + + // 随机获取访问的socket的handle + let index: usize = rand() % self.handles.len(); + let handle = self.handles.get(index).unwrap(); + + let socket = sockets + .iter_mut() + .find(|y| { + tcp::Socket::downcast(y.1) + .map(|y| y.is_active()) + .unwrap_or(false) + }) + .map(|y| tcp::Socket::downcast_mut(y.1).unwrap()); + if let Some(socket) = socket { + if socket.is_active() { + // kdebug!("tcp accept: socket.is_active()"); + let remote_ep = socket.remote_endpoint().ok_or(SystemError::ENOTCONN)?; + + let new_socket = { + // The new TCP socket used for sending and receiving data. + let mut tcp_socket = Self::create_new_socket(); + self.do_listen(&mut tcp_socket, endpoint) + .expect("do_listen failed"); + + // tcp_socket.listen(endpoint).unwrap(); + + // 之所以把old_handle存入new_socket, 是因为当前时刻,smoltcp已经把old_handle对应的socket与远程的endpoint关联起来了 + // 因此需要再为当前的socket分配一个新的handle + let new_handle = + GlobalSocketHandle::new_smoltcp_handle(sockets.add(tcp_socket)); + let old_handle = ::core::mem::replace( + &mut *self.handles.get_mut(index).unwrap(), + new_handle, + ); - if !socket.is_listening() { - self.do_listen(socket, endpoint)?; - } + let metadata = SocketMetadata::new( + SocketType::Tcp, + Self::DEFAULT_TX_BUF_SIZE, + Self::DEFAULT_RX_BUF_SIZE, + Self::DEFAULT_METADATA_BUF_SIZE, + self.metadata.options, + ); - drop(handle_guard); - } + let new_socket = Box::new(TcpSocket { + handles: vec![old_handle], + local_endpoint: self.local_endpoint, + is_listening: false, + metadata, + }); + // kdebug!("tcp socket:after accept, socket'len={}",new_socket.handle.len()); + + // 更新端口与 socket 的绑定 + if let Some(Endpoint::Ip(Some(ip))) = self.endpoint() { + PORT_MANAGER.unbind_port(self.metadata.socket_type, ip.port)?; + PORT_MANAGER.bind_port( + self.metadata.socket_type, + ip.port, + *new_socket.clone(), + )?; + } - return Ok((sock_ret, Endpoint::Ip(Some(remote_ep)))); - } + // 更新handle表 + let mut handle_guard = HANDLE_MAP.write_irqsave(); + // 先删除原来的 + + let item = handle_guard.remove(&old_handle).unwrap(); - drop(sockset); + // 按照smoltcp行为,将新的handle绑定到原来的item + handle_guard.insert(new_handle, item); + let new_item = SocketHandleItem::new(); + + // 插入新的item + handle_guard.insert(old_handle, new_item); + + new_socket + }; + // kdebug!("tcp accept: new socket: {:?}", new_socket); + drop(sockets); + poll_ifaces(); + + return Ok((new_socket, Endpoint::Ip(Some(remote_ep)))); + } + } + // kdebug!("tcp socket:before sleep, handle_guard'len={}",HANDLE_MAP.write_irqsave().len()); - // debug!("[TCP] [Accept] sleeping socket with handle: {:?}", self.handles.first().unwrap().smoltcp_handle().unwrap()); - self.posix_item.sleep(Self::CAN_ACCPET); - // debug!("tcp socket:after sleep, handle_guard'len={}",HANDLE_MAP.write_irqsave().len()); + drop(sockets); + SocketHandleItem::sleep(*handle, Self::CAN_ACCPET, HANDLE_MAP.read_irqsave()); + // kdebug!("tcp socket:after sleep, handle_guard'len={}",HANDLE_MAP.write_irqsave().len()); } } @@ -968,10 +921,10 @@ impl Socket for TcpSocket { if result.is_none() { let sockets = SOCKET_SET.lock_irqsave(); - // debug!("tcp socket:endpoint, socket'len={}",self.handle.len()); + // kdebug!("tcp socket:endpoint, socket'len={}",self.handle.len()); let socket = - sockets.get::(self.handles.first().unwrap().smoltcp_handle().unwrap()); + sockets.get::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); if let Some(ep) = socket.local_endpoint() { result = Some(Endpoint::Ip(Some(ep))); } @@ -981,10 +934,10 @@ impl Socket for TcpSocket { fn peer_endpoint(&self) -> Option { let sockets = SOCKET_SET.lock_irqsave(); - // debug!("tcp socket:peer_endpoint, socket'len={}",self.handle.len()); + // kdebug!("tcp socket:peer_endpoint, socket'len={}",self.handle.len()); let socket = - sockets.get::(self.handles.first().unwrap().smoltcp_handle().unwrap()); + sockets.get::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); return socket.remote_endpoint().map(|x| Endpoint::Ip(Some(x))); } @@ -997,9 +950,9 @@ impl Socket for TcpSocket { } fn socket_handle(&self) -> GlobalSocketHandle { - // debug!("tcp socket:socket_handle, socket'len={}",self.handle.len()); + // kdebug!("tcp socket:socket_handle, socket'len={}",self.handle.len()); - *self.handles.first().unwrap() + *self.handles.get(0).unwrap() } fn as_any_ref(&self) -> &dyn core::any::Any { diff --git a/kernel/src/net/socket/mod.rs b/kernel/src/net/socket/mod.rs index c055f30e7..70fb54142 100644 --- a/kernel/src/net/socket/mod.rs +++ b/kernel/src/net/socket/mod.rs @@ -8,7 +8,6 @@ use alloc::{ vec::Vec, }; use hashbrown::HashMap; -use log::warn; use smoltcp::{ iface::SocketSet, socket::{self, raw, tcp, udp}, @@ -22,11 +21,10 @@ use crate::{ Metadata, }, libs::{ - rwlock::{RwLock, RwLockWriteGuard}, + rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, spinlock::{SpinLock, SpinLockGuard}, wait_queue::EventWaitQueue, }, - process::{Pid, ProcessManager}, sched::{schedule, SchedMode}, }; @@ -87,7 +85,7 @@ pub(super) fn new_socket( } }; - let handle_item = SocketHandleItem::new(Arc::downgrade(&socket.posix_item())); + let handle_item = SocketHandleItem::new(); HANDLE_MAP .write_irqsave() .insert(socket.socket_handle(), handle_item); @@ -228,7 +226,7 @@ pub trait Socket: Sync + Send + Debug + Any { _optname: usize, _optval: &[u8], ) -> Result<(), SystemError> { - warn!("setsockopt is not implemented"); + kwarn!("setsockopt is not implemented"); Ok(()) } @@ -243,26 +241,36 @@ pub trait Socket: Sync + Send + Debug + Any { fn as_any_mut(&mut self) -> &mut dyn Any; fn add_epoll(&mut self, epitem: Arc) -> Result<(), SystemError> { - let posix_item = self.posix_item(); - posix_item.add_epoll(epitem); + HANDLE_MAP + .write_irqsave() + .get_mut(&self.socket_handle()) + .unwrap() + .add_epoll(epitem); Ok(()) } fn remove_epoll(&mut self, epoll: &Weak>) -> Result<(), SystemError> { - let posix_item = self.posix_item(); - posix_item.remove_epoll(epoll)?; + HANDLE_MAP + .write_irqsave() + .get_mut(&self.socket_handle()) + .unwrap() + .remove_epoll(epoll)?; Ok(()) } fn clear_epoll(&mut self) -> Result<(), SystemError> { - let posix_item = self.posix_item(); + let mut handle_map_guard = HANDLE_MAP.write_irqsave(); + let handle_item = handle_map_guard.get_mut(&self.socket_handle()).unwrap(); - for epitem in posix_item.epitems.lock_irqsave().iter() { + for epitem in handle_item.epitems.lock_irqsave().iter() { let epoll = epitem.epoll(); - - if let Some(epoll) = epoll.upgrade() { - EventPoll::ep_remove(&mut epoll.lock_irqsave(), epitem.fd(), None)?; + if epoll.upgrade().is_some() { + EventPoll::ep_remove( + &mut epoll.upgrade().unwrap().lock_irqsave(), + epitem.fd(), + None, + )?; } } @@ -270,8 +278,6 @@ pub trait Socket: Sync + Send + Debug + Any { } fn close(&mut self); - - fn posix_item(&self) -> Arc; } impl Clone for Box { @@ -297,8 +303,19 @@ impl SocketInode { pub unsafe fn inner_no_preempt(&self) -> SpinLockGuard> { self.0.lock_no_preempt() } +} - fn do_close(&self) -> Result<(), SystemError> { +impl IndexNode for SocketInode { + fn open( + &self, + _data: SpinLockGuard, + _mode: &FileMode, + ) -> Result<(), SystemError> { + self.1.fetch_add(1, core::sync::atomic::Ordering::SeqCst); + Ok(()) + } + + fn close(&self, _data: SpinLockGuard) -> Result<(), SystemError> { let prev_ref_count = self.1.fetch_sub(1, core::sync::atomic::Ordering::SeqCst); if prev_ref_count == 1 { // 最后一次关闭,需要释放 @@ -309,7 +326,7 @@ impl SocketInode { } if let Some(Endpoint::Ip(Some(ip))) = socket.endpoint() { - PORT_MANAGER.unbind_port(socket.metadata().socket_type, ip.port); + PORT_MANAGER.unbind_port(socket.metadata().socket_type, ip.port)?; } socket.clear_epoll()?; @@ -323,29 +340,6 @@ impl SocketInode { Ok(()) } -} - -impl Drop for SocketInode { - fn drop(&mut self) { - for _ in 0..self.1.load(core::sync::atomic::Ordering::SeqCst) { - let _ = self.do_close(); - } - } -} - -impl IndexNode for SocketInode { - fn open( - &self, - _data: SpinLockGuard, - _mode: &FileMode, - ) -> Result<(), SystemError> { - self.1.fetch_add(1, core::sync::atomic::Ordering::SeqCst); - Ok(()) - } - - fn close(&self, _data: SpinLockGuard) -> Result<(), SystemError> { - self.do_close() - } fn read_at( &self, @@ -402,35 +396,54 @@ impl IndexNode for SocketInode { } #[derive(Debug)] -pub struct PosixSocketHandleItem { +pub struct SocketHandleItem { + /// shutdown状态 + pub shutdown_type: RwLock, /// socket的waitqueue - wait_queue: Arc, - + pub wait_queue: EventWaitQueue, + /// epitems,考虑写在这是否是最优解? pub epitems: SpinLock>>, } -impl PosixSocketHandleItem { - pub fn new(wait_queue: Option>) -> Self { +impl SocketHandleItem { + pub fn new() -> Self { Self { - wait_queue: wait_queue.unwrap_or(Arc::new(EventWaitQueue::new())), + shutdown_type: RwLock::new(ShutdownType::empty()), + wait_queue: EventWaitQueue::new(), epitems: SpinLock::new(LinkedList::new()), } } + /// ## 在socket的等待队列上睡眠 - pub fn sleep(&self, events: u64) { + pub fn sleep( + socket_handle: GlobalSocketHandle, + events: u64, + handle_map_guard: RwLockReadGuard<'_, HashMap>, + ) { unsafe { - ProcessManager::preempt_disable(); - self.wait_queue.sleep_without_schedule(events); - ProcessManager::preempt_enable(); - } + handle_map_guard + .get(&socket_handle) + .unwrap() + .wait_queue + .sleep_without_schedule(events) + }; + drop(handle_map_guard); schedule(SchedMode::SM_NONE); } - pub fn add_epoll(&self, epitem: Arc) { + pub fn shutdown_type(&self) -> ShutdownType { + *self.shutdown_type.read() + } + + pub fn shutdown_type_writer(&mut self) -> RwLockWriteGuard { + self.shutdown_type.write_irqsave() + } + + pub fn add_epoll(&mut self, epitem: Arc) { self.epitems.lock_irqsave().push_back(epitem) } - pub fn remove_epoll(&self, epoll: &Weak>) -> Result<(), SystemError> { + pub fn remove_epoll(&mut self, epoll: &Weak>) -> Result<(), SystemError> { let is_remove = !self .epitems .lock_irqsave() @@ -444,59 +457,15 @@ impl PosixSocketHandleItem { Err(SystemError::ENOENT) } - - /// ### 唤醒该队列上等待events的进程 - /// - /// ### 参数 - /// - events: 发生的事件 - /// - /// 需要注意的是,只要触发了events中的任意一件事件,进程都会被唤醒 - pub fn wakeup_any(&self, events: u64) { - self.wait_queue.wakeup_any(events); - } -} -#[derive(Debug)] -pub struct SocketHandleItem { - /// 对应的posix socket是否为listen的 - pub is_posix_listen: bool, - /// shutdown状态 - pub shutdown_type: RwLock, - pub posix_item: Weak, -} - -impl SocketHandleItem { - pub fn new(posix_item: Weak) -> Self { - Self { - is_posix_listen: false, - shutdown_type: RwLock::new(ShutdownType::empty()), - posix_item, - } - } - - pub fn shutdown_type(&self) -> ShutdownType { - *self.shutdown_type.read() - } - - pub fn shutdown_type_writer(&mut self) -> RwLockWriteGuard { - self.shutdown_type.write_irqsave() - } - - pub fn reset_shutdown_type(&self) { - *self.shutdown_type.write() = ShutdownType::empty(); - } - - pub fn posix_item(&self) -> Option> { - self.posix_item.upgrade() - } } /// # TCP 和 UDP 的端口管理器。 /// 如果 TCP/UDP 的 socket 绑定了某个端口,它会在对应的表中记录,以检测端口冲突。 pub struct PortManager { // TCP 端口记录表 - tcp_port_table: SpinLock>, + tcp_port_table: SpinLock>>, // UDP 端口记录表 - udp_port_table: SpinLock>, + udp_port_table: SpinLock>>, } impl PortManager { @@ -548,7 +517,12 @@ impl PortManager { /// @brief 检测给定端口是否已被占用,如果未被占用则在 TCP/UDP 对应的表中记录 /// /// TODO: 增加支持端口复用的逻辑 - pub fn bind_port(&self, socket_type: SocketType, port: u16) -> Result<(), SystemError> { + pub fn bind_port( + &self, + socket_type: SocketType, + port: u16, + socket: impl Socket, + ) -> Result<(), SystemError> { if port > 0 { let mut listen_table_guard = match socket_type { SocketType::Udp => self.udp_port_table.lock(), @@ -557,7 +531,7 @@ impl PortManager { }; match listen_table_guard.get(&port) { Some(_) => return Err(SystemError::EADDRINUSE), - None => listen_table_guard.insert(port, ProcessManager::current_pid()), + None => listen_table_guard.insert(port, Arc::new(socket)), }; drop(listen_table_guard); } @@ -565,17 +539,15 @@ impl PortManager { } /// @brief 在对应的端口记录表中将端口和 socket 解绑 - /// should call this function when socket is closed or aborted - pub fn unbind_port(&self, socket_type: SocketType, port: u16) { + pub fn unbind_port(&self, socket_type: SocketType, port: u16) -> Result<(), SystemError> { let mut listen_table_guard = match socket_type { SocketType::Udp => self.udp_port_table.lock(), SocketType::Tcp => self.tcp_port_table.lock(), - _ => { - return; - } + _ => return Ok(()), }; listen_table_guard.remove(&port); drop(listen_table_guard); + return Ok(()); } } @@ -780,47 +752,33 @@ impl TryFrom for PosixSocketType { pub struct SocketPollMethod; impl SocketPollMethod { - pub fn poll(socket: &socket::Socket, handle_item: &SocketHandleItem) -> EPollEventType { - let shutdown = handle_item.shutdown_type(); + pub fn poll(socket: &socket::Socket, shutdown: ShutdownType) -> EPollEventType { match socket { socket::Socket::Udp(udp) => Self::udp_poll(udp, shutdown), - socket::Socket::Tcp(tcp) => Self::tcp_poll(tcp, shutdown, handle_item.is_posix_listen), + socket::Socket::Tcp(tcp) => Self::tcp_poll(tcp, shutdown), socket::Socket::Raw(raw) => Self::raw_poll(raw, shutdown), _ => todo!(), } } - pub fn tcp_poll( - socket: &tcp::Socket, - shutdown: ShutdownType, - is_posix_listen: bool, - ) -> EPollEventType { + pub fn tcp_poll(socket: &tcp::Socket, shutdown: ShutdownType) -> EPollEventType { let mut events = EPollEventType::empty(); - // debug!("enter tcp_poll! is_posix_listen:{}", is_posix_listen); - // 处理listen的socket - if is_posix_listen { - // 如果是listen的socket,那么只有EPOLLIN和EPOLLRDNORM - if socket.is_active() { - events.insert(EPollEventType::EPOLL_LISTEN_CAN_ACCEPT); - } - - // debug!("tcp_poll listen socket! events:{:?}", events); + if socket.is_listening() && socket.is_active() { + events.insert(EPollEventType::EPOLLIN | EPollEventType::EPOLLRDNORM); return events; } - let state = socket.state(); - - if shutdown == ShutdownType::SHUTDOWN_MASK || state == tcp::State::Closed { - events.insert(EPollEventType::EPOLLHUP); + // socket已经关闭 + if !socket.is_open() { + events.insert(EPollEventType::EPOLLHUP) } - if shutdown.contains(ShutdownType::RCV_SHUTDOWN) { events.insert( EPollEventType::EPOLLIN | EPollEventType::EPOLLRDNORM | EPollEventType::EPOLLRDHUP, ); } - // Connected or passive Fast Open socket? + let state = socket.state(); if state != tcp::State::SynSent && state != tcp::State::SynReceived { // socket有可读数据 if socket.can_recv() { @@ -828,12 +786,12 @@ impl SocketPollMethod { } if !(shutdown.contains(ShutdownType::SEND_SHUTDOWN)) { - // 缓冲区可写(这里判断可写的逻辑好像跟linux不太一样) + // 缓冲区可写 if socket.send_queue() < socket.send_capacity() { events.insert(EPollEventType::EPOLLOUT | EPollEventType::EPOLLWRNORM); } else { - // TODO:触发缓冲区已满的信号SIGIO - todo!("A signal SIGIO that the buffer is full needs to be sent"); + // TODO:触发缓冲区已满的信号 + todo!("A signal that the buffer is full needs to be sent"); } } else { // 如果我们的socket关闭了SEND_SHUTDOWN,epoll事件就是EPOLLOUT @@ -844,7 +802,6 @@ impl SocketPollMethod { } // socket发生错误 - // TODO: 这里的逻辑可能有问题,需要进一步验证是否is_active()==false就代表socket发生错误 if !socket.is_active() { events.insert(EPollEventType::EPOLLERR); } @@ -883,7 +840,7 @@ impl SocketPollMethod { } pub fn raw_poll(socket: &raw::Socket, shutdown: ShutdownType) -> EPollEventType { - //debug!("enter raw_poll!"); + //kdebug!("enter raw_poll!"); let mut event = EPollEventType::empty(); if shutdown.contains(ShutdownType::RCV_SHUTDOWN) { @@ -896,21 +853,21 @@ impl SocketPollMethod { } if socket.can_recv() { - //debug!("poll can recv!"); + //kdebug!("poll can recv!"); event.insert(EPollEventType::EPOLLIN | EPollEventType::EPOLLRDNORM); } else { - //debug!("poll can not recv!"); + //kdebug!("poll can not recv!"); } if socket.can_send() { - //debug!("poll can send!"); + //kdebug!("poll can send!"); event.insert( EPollEventType::EPOLLOUT | EPollEventType::EPOLLWRNORM | EPollEventType::EPOLLWRBAND, ); } else { - //debug!("poll can not send!"); + //kdebug!("poll can not send!"); // TODO: 缓冲区空间不够,需要使用信号处理 todo!() } diff --git a/kernel/src/net/socket/unix.rs b/kernel/src/net/socket/unix.rs index f15037775..83cd0c5d2 100644 --- a/kernel/src/net/socket/unix.rs +++ b/kernel/src/net/socket/unix.rs @@ -4,8 +4,7 @@ use system_error::SystemError; use crate::{libs::spinlock::SpinLock, net::Endpoint}; use super::{ - handle::GlobalSocketHandle, PosixSocketHandleItem, Socket, SocketInode, SocketMetadata, - SocketOptions, SocketType, + handle::GlobalSocketHandle, Socket, SocketInode, SocketMetadata, SocketOptions, SocketType, }; #[derive(Debug, Clone)] @@ -14,7 +13,6 @@ pub struct StreamSocket { buffer: Arc>>, peer_inode: Option>, handle: GlobalSocketHandle, - posix_item: Arc, } impl StreamSocket { @@ -38,22 +36,16 @@ impl StreamSocket { options, ); - let posix_item = Arc::new(PosixSocketHandleItem::new(None)); - Self { metadata, buffer, peer_inode: None, handle: GlobalSocketHandle::new_kernel_handle(), - posix_item, } } } impl Socket for StreamSocket { - fn posix_item(&self) -> Arc { - self.posix_item.clone() - } fn socket_handle(&self) -> GlobalSocketHandle { self.handle } @@ -129,7 +121,6 @@ pub struct SeqpacketSocket { buffer: Arc>>, peer_inode: Option>, handle: GlobalSocketHandle, - posix_item: Arc, } impl SeqpacketSocket { @@ -153,22 +144,16 @@ impl SeqpacketSocket { options, ); - let posix_item = Arc::new(PosixSocketHandleItem::new(None)); - Self { metadata, buffer, peer_inode: None, handle: GlobalSocketHandle::new_kernel_handle(), - posix_item, } } } impl Socket for SeqpacketSocket { - fn posix_item(&self) -> Arc { - self.posix_item.clone() - } fn close(&mut self) {} fn read(&self, buf: &mut [u8]) -> (Result, Endpoint) { diff --git a/kernel/src/net/syscall.rs b/kernel/src/net/syscall.rs index 91aff6ebb..cc9bf5696 100644 --- a/kernel/src/net/syscall.rs +++ b/kernel/src/net/syscall.rs @@ -345,7 +345,7 @@ impl Syscall { .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; let mut socket = unsafe { socket.inner_no_preempt() }; - socket.shutdown(ShutdownType::from_bits_truncate((how + 1) as u8))?; + socket.shutdown(ShutdownType::from_bits_truncate(how as u8))?; return Ok(0); } @@ -404,13 +404,13 @@ impl Syscall { let socket: Arc = ProcessManager::current_pcb() .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; - // debug!("accept: socket={:?}", socket); + // kdebug!("accept: socket={:?}", socket); let mut socket = unsafe { socket.inner_no_preempt() }; // 从socket中接收连接 let (new_socket, remote_endpoint) = socket.accept()?; drop(socket); - // debug!("accept: new_socket={:?}", new_socket); + // kdebug!("accept: new_socket={:?}", new_socket); // Insert the new socket into the file descriptor vector let new_socket: Arc = SocketInode::new(new_socket); @@ -426,9 +426,9 @@ impl Syscall { .fd_table() .write() .alloc_fd(File::new(new_socket, file_mode)?, None)?; - // debug!("accept: new_fd={}", new_fd); + // kdebug!("accept: new_fd={}", new_fd); if !addr.is_null() { - // debug!("accept: write remote_endpoint to user"); + // kdebug!("accept: write remote_endpoint to user"); // 将对端地址写入用户空间 let sockaddr_in = SockAddr::from(remote_endpoint); unsafe { diff --git a/kernel/src/process/abi.rs b/kernel/src/process/abi.rs index a39f4ff3a..cbaab9d0a 100644 --- a/kernel/src/process/abi.rs +++ b/kernel/src/process/abi.rs @@ -38,7 +38,7 @@ pub enum AtType { /// Frequency at which times() increments. ClkTck, /// Secure mode boolean. - Secure = 23, + Secure, /// String identifying real platform, may differ from AT_PLATFORM. BasePlatform, /// Address of 16 random bytes. @@ -46,7 +46,7 @@ pub enum AtType { /// Extension of AT_HWCAP. HwCap2, /// Filename of program. - ExecFn = 31, + ExecFn, /// Minimal stack size for signal delivery. MinSigStackSize, } diff --git a/kernel/src/process/cred.rs b/kernel/src/process/cred.rs deleted file mode 100644 index 952cfbfd5..000000000 --- a/kernel/src/process/cred.rs +++ /dev/null @@ -1,170 +0,0 @@ -use core::sync::atomic::AtomicUsize; - -use alloc::vec::Vec; - -const GLOBAL_ROOT_UID: Kuid = Kuid(0); -const GLOBAL_ROOT_GID: Kgid = Kgid(0); -pub static INIT_CRED: Cred = Cred::init(); - -int_like!(Kuid, AtomicKuid, usize, AtomicUsize); -int_like!(Kgid, AtomicKgid, usize, AtomicUsize); - -bitflags! { - pub struct CAPFlags:u64{ - const CAP_EMPTY_SET = 0; - const CAP_FULL_SET = (1 << 41) - 1; - } -} - -pub enum CredFsCmp { - Equal, - Less, - Greater, -} - -/// 凭证集 -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Cred { - /// 进程实际uid - pub uid: Kuid, - /// 进程实际gid - pub gid: Kgid, - /// 进程保存的uid - pub suid: Kuid, - /// 进程保存的gid - pub sgid: Kgid, - /// 进程有效的uid - pub euid: Kuid, - /// 进程有效的gid - pub egid: Kgid, - /// UID for VFS ops - pub fsuid: Kuid, - /// GID for VFS ops - pub fsgid: Kgid, - /// 子进程可以继承的权限 - pub cap_inheritable: CAPFlags, - /// 当前进程被赋予的权限 - pub cap_permitted: CAPFlags, - /// 当前进程实际使用的权限 - pub cap_effective: CAPFlags, - /// capability bounding set - pub cap_bset: CAPFlags, - /// Ambient capability set - pub cap_ambient: CAPFlags, - /// supplementary groups for euid/fsgid - pub group_info: Option, -} - -impl Cred { - pub const fn init() -> Self { - Self { - uid: GLOBAL_ROOT_UID, - gid: GLOBAL_ROOT_GID, - suid: GLOBAL_ROOT_UID, - sgid: GLOBAL_ROOT_GID, - euid: GLOBAL_ROOT_UID, - egid: GLOBAL_ROOT_GID, - fsuid: GLOBAL_ROOT_UID, - fsgid: GLOBAL_ROOT_GID, - cap_inheritable: CAPFlags::CAP_EMPTY_SET, - cap_permitted: CAPFlags::CAP_FULL_SET, - cap_effective: CAPFlags::CAP_FULL_SET, - cap_bset: CAPFlags::CAP_FULL_SET, - cap_ambient: CAPFlags::CAP_FULL_SET, - group_info: None, - } - } - - #[allow(dead_code)] - /// Compare two credentials with respect to filesystem access. - pub fn fscmp(&self, other: Cred) -> CredFsCmp { - if *self == other { - return CredFsCmp::Equal; - } - - if self.fsuid < other.fsuid { - return CredFsCmp::Less; - } - if self.fsuid > other.fsuid { - return CredFsCmp::Greater; - } - - if self.fsgid < other.fsgid { - return CredFsCmp::Less; - } - if self.fsgid > other.fsgid { - return CredFsCmp::Greater; - } - - if self.group_info == other.group_info { - return CredFsCmp::Equal; - } - - if let (Some(ga), Some(gb)) = (&self.group_info, &other.group_info) { - let ga_count = ga.gids.len(); - let gb_count = gb.gids.len(); - - if ga_count < gb_count { - return CredFsCmp::Less; - } - if ga_count > gb_count { - return CredFsCmp::Greater; - } - - for i in 0..ga_count { - if ga.gids[i] < gb.gids[i] { - return CredFsCmp::Less; - } - if ga.gids[i] > gb.gids[i] { - return CredFsCmp::Greater; - } - } - } else { - if self.group_info.is_none() { - return CredFsCmp::Less; - } - if other.group_info.is_none() { - return CredFsCmp::Greater; - } - } - - return CredFsCmp::Equal; - } - - pub fn setuid(&mut self, uid: usize) { - self.uid.0 = uid; - } - - pub fn seteuid(&mut self, euid: usize) { - self.euid.0 = euid; - } - - pub fn setsuid(&mut self, suid: usize) { - self.suid.0 = suid; - } - - pub fn setfsuid(&mut self, fsuid: usize) { - self.fsuid.0 = fsuid; - } - - pub fn setgid(&mut self, gid: usize) { - self.gid.0 = gid; - } - - pub fn setegid(&mut self, egid: usize) { - self.egid.0 = egid; - } - - pub fn setsgid(&mut self, sgid: usize) { - self.sgid.0 = sgid; - } - - pub fn setfsgid(&mut self, fsgid: usize) { - self.fsgid.0 = fsgid; - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct GroupInfo { - pub gids: Vec, -} diff --git a/kernel/src/process/exec.rs b/kernel/src/process/exec.rs index 9d0cba351..3233b06fe 100644 --- a/kernel/src/process/exec.rs +++ b/kernel/src/process/exec.rs @@ -1,6 +1,6 @@ use core::{fmt::Debug, ptr::null}; -use alloc::{collections::BTreeMap, ffi::CString, string::String, sync::Arc, vec::Vec}; +use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec}; use system_error::SystemError; use crate::{ @@ -16,8 +16,6 @@ use crate::{ }, }; -use super::ProcessManager; - /// 系统支持的所有二进制文件加载器的列表 const BINARY_LOADERS: [&'static dyn BinaryLoader; 1] = [&ELF_LOADER]; @@ -127,7 +125,7 @@ impl ExecParam { file, vm, flags, - init_info: ProcInitInfo::new(ProcessManager::current_pcb().basic().name()), + init_info: ProcInitInfo::new(), }) } @@ -172,7 +170,7 @@ pub fn load_binary_file(param: &mut ExecParam) -> Result Result, - pub envs: Vec, + pub proc_name: String, + pub args: Vec, + pub envs: Vec, pub auxv: BTreeMap, - pub rand_num: [u8; 16], } impl ProcInitInfo { - pub fn new(proc_name: &str) -> Self { + pub fn new() -> Self { Self { - proc_name: CString::new(proc_name).unwrap_or(CString::new("").unwrap()), + proc_name: String::new(), args: Vec::new(), envs: Vec::new(), auxv: BTreeMap::new(), - rand_num: [0u8; 16], } } @@ -227,7 +223,7 @@ impl ProcInitInfo { /// /// 返回值是一个元组,第一个元素是最终的用户栈顶地址,第二个元素是环境变量pointer数组的起始地址 pub unsafe fn push_at( - &mut self, + &self, ustack: &mut UserStack, ) -> Result<(VirtAddr, VirtAddr), SystemError> { // 先把程序的名称压入栈中 @@ -238,7 +234,7 @@ impl ProcInitInfo { .envs .iter() .map(|s| { - self.push_str(ustack, s).expect("push_str failed"); + self.push_str(ustack, s.as_str()).expect("push_str failed"); ustack.sp() }) .collect::>(); @@ -248,25 +244,11 @@ impl ProcInitInfo { .args .iter() .map(|s| { - self.push_str(ustack, s).expect("push_str failed"); + self.push_str(ustack, s.as_str()).expect("push_str failed"); ustack.sp() }) .collect::>(); - // 压入随机数,把指针放入auxv - self.push_slice(ustack, &[self.rand_num])?; - self.auxv - .insert(super::abi::AtType::Random as u8, ustack.sp().data()); - - // 实现栈的16字节对齐 - // 用当前栈顶地址减去后续要压栈的长度,得到的压栈后的栈顶地址与0xF按位与操作得到对齐要填充的字节数 - let length_to_push = (self.auxv.len() + envps.len() + 1 + argps.len() + 1 + 1) - * core::mem::align_of::(); - self.push_slice( - ustack, - &vec![0u8; (ustack.sp().data() - length_to_push) & 0xF], - )?; - // 压入auxv self.push_slice(ustack, &[null::(), null::()])?; for (&k, &v) in self.auxv.iter() { @@ -280,6 +262,7 @@ impl ProcInitInfo { // 把参数指针压入栈中 self.push_slice(ustack, &[null::()])?; self.push_slice(ustack, argps.as_slice())?; + let argv_ptr = ustack.sp(); // 把argc压入栈中 @@ -302,9 +285,9 @@ impl ProcInitInfo { return Ok(()); } - fn push_str(&self, ustack: &mut UserStack, s: &CString) -> Result<(), SystemError> { - let bytes = s.as_bytes_with_nul(); - self.push_slice(ustack, bytes)?; + fn push_str(&self, ustack: &mut UserStack, s: &str) -> Result<(), SystemError> { + self.push_slice(ustack, &[b'\0'])?; + self.push_slice(ustack, s.as_bytes())?; return Ok(()); } } diff --git a/kernel/src/process/exit.rs b/kernel/src/process/exit.rs index c88a9493b..fd8817006 100644 --- a/kernel/src/process/exit.rs +++ b/kernel/src/process/exit.rs @@ -1,7 +1,6 @@ use core::intrinsics::likely; use alloc::sync::Arc; -use log::warn; use system_error::SystemError; use crate::{ @@ -32,7 +31,6 @@ pub struct KernelWaitOption<'a> { } #[derive(Debug, Clone)] -#[allow(dead_code)] pub struct WaitIdInfo { pub pid: Pid, pub status: i32, @@ -71,11 +69,11 @@ pub fn kernel_wait4( pidtype = PidType::MAX; } else if pid < 0 { pidtype = PidType::PGID; - warn!("kernel_wait4: currently not support pgid, default to wait for pid\n"); + kwarn!("kernel_wait4: currently not support pgid, default to wait for pid\n"); pid = -pid; } else if pid == 0 { pidtype = PidType::PGID; - warn!("kernel_wait4: currently not support pgid, default to wait for pid\n"); + kwarn!("kernel_wait4: currently not support pgid, default to wait for pid\n"); pid = ProcessManager::current_pcb().pid().data() as i64; } else { pidtype = PidType::PID; @@ -169,7 +167,7 @@ fn do_wait(kwo: &mut KernelWaitOption) -> Result { schedule(SchedMode::SM_NONE); } else { // todo: 对于pgid的处理 - warn!("kernel_wait4: currently not support {:?}", kwo.pid_type); + kwarn!("kernel_wait4: currently not support {:?}", kwo.pid_type); return Err(SystemError::EINVAL); } } @@ -227,7 +225,7 @@ fn do_waitpid( } ProcessState::Exited(status) => { let pid = child_pcb.pid(); - // debug!("wait4: child exited, pid: {:?}, status: {status}\n", pid); + // kdebug!("wait4: child exited, pid: {:?}, status: {status}\n", pid); if likely(!kwo.options.contains(WaitOption::WEXITED)) { return None; @@ -246,7 +244,7 @@ fn do_waitpid( kwo.ret_status = status as i32; drop(child_pcb); - // debug!("wait4: to release {pid:?}"); + // kdebug!("wait4: to release {pid:?}"); unsafe { ProcessManager::release(pid) }; return Some(Ok(pid.into())); } diff --git a/kernel/src/process/fork.rs b/kernel/src/process/fork.rs index 2ad49fe91..ee0c3e61b 100644 --- a/kernel/src/process/fork.rs +++ b/kernel/src/process/fork.rs @@ -1,7 +1,6 @@ use core::{intrinsics::unlikely, sync::atomic::Ordering}; use alloc::{string::ToString, sync::Arc}; -use log::error; use system_error::SystemError; use crate::{ @@ -85,7 +84,6 @@ bitflags! { /// /// 仅仅作为参数传递 #[derive(Debug, Clone, Copy)] -#[allow(dead_code)] pub struct KernelCloneArgs { pub flags: CloneFlags, @@ -170,7 +168,7 @@ impl ProcessManager { args.flags = clone_flags; args.exit_signal = Signal::SIGCHLD; Self::copy_process(¤t_pcb, &pcb, args, current_trapframe).map_err(|e| { - error!( + kerror!( "fork: Failed to copy process, current pid: [{:?}], new pid: [{:?}]. Error: {:?}", current_pcb.pid(), pcb.pid(), diff --git a/kernel/src/process/kthread.rs b/kernel/src/process/kthread.rs index 6f4d113da..aa6c91719 100644 --- a/kernel/src/process/kthread.rs +++ b/kernel/src/process/kthread.rs @@ -10,13 +10,13 @@ use alloc::{ sync::{Arc, Weak}, }; use atomic_enum::atomic_enum; -use log::info; use system_error::SystemError; use crate::{ arch::CurrentIrqArch, exception::{irqdesc::IrqAction, InterruptArch}, init::initial_kthread::initial_kernel_thread, + kinfo, libs::{once::Once, spinlock::SpinLock}, process::{ProcessManager, ProcessState}, sched::{schedule, SchedMode}, @@ -80,12 +80,6 @@ impl KernelThreadPcbPrivate { } } -impl Default for KernelThreadPcbPrivate { - fn default() -> Self { - Self::new() - } -} - /// 内核线程的闭包,参数必须与闭包的参数一致,返回值必须是i32 /// /// 元组的第一个元素是闭包,第二个元素是闭包的参数对象 @@ -264,7 +258,7 @@ pub struct KernelThreadMechanism; impl KernelThreadMechanism { pub fn init_stage1() { assert!(ProcessManager::current_pcb().pid() == Pid::new(0)); - info!("Initializing kernel thread mechanism stage1..."); + kinfo!("Initializing kernel thread mechanism stage1..."); // 初始化第一个内核线程 @@ -296,7 +290,7 @@ impl KernelThreadMechanism { .remove(ProcessFlags::KTHREAD); drop(irq_guard); - info!("Initializing kernel thread mechanism stage1 complete"); + kinfo!("Initializing kernel thread mechanism stage1 complete"); } pub fn init_stage2() { @@ -305,7 +299,7 @@ impl KernelThreadMechanism { .contains(ProcessFlags::KTHREAD)); static INIT: Once = Once::new(); INIT.call_once(|| { - info!("Initializing kernel thread mechanism stage2..."); + kinfo!("Initializing kernel thread mechanism stage2..."); // 初始化kthreadd let closure = KernelThreadClosure::EmptyClosure((Box::new(Self::kthread_daemon), ())); let info = KernelThreadCreateInfo::new(closure, "kthreadd".to_string()); @@ -321,7 +315,7 @@ impl KernelThreadMechanism { unsafe { KTHREAD_DAEMON_PCB.replace(pcb); } - info!("Initialize kernel thread mechanism stage2 complete"); + kinfo!("Initialize kernel thread mechanism stage2 complete"); }); } diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index f48d4491b..c22c17dde 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -1,5 +1,4 @@ use core::{ - fmt, hash::Hash, hint::spin_loop, intrinsics::{likely, unlikely}, @@ -8,14 +7,11 @@ use core::{ }; use alloc::{ - ffi::CString, string::{String, ToString}, sync::{Arc, Weak}, vec::Vec, }; -use cred::INIT_CRED; use hashbrown::HashMap; -use log::{debug, error, info, warn}; use system_error::SystemError; use crate::{ @@ -32,6 +28,7 @@ use crate::{ vfs::{file::FileDescriptorVec, FileType}, }, ipc::signal_types::{SigInfo, SigPending, SignalStruct}, + kdebug, kinfo, libs::{ align::AlignedBox, casting::DowncastArc, @@ -65,11 +62,10 @@ use crate::{ }; use timer::AlarmTimer; -use self::{cred::Cred, kthread::WorkerPrivate}; +use self::kthread::WorkerPrivate; pub mod abi; pub mod c_adapter; -pub mod cred; pub mod exec; pub mod exit; pub mod fork; @@ -120,24 +116,24 @@ impl ProcessManager { unsafe { compiler_fence(Ordering::SeqCst); - debug!("To create address space for INIT process."); + kdebug!("To create address space for INIT process."); // test_buddy(); set_IDLE_PROCESS_ADDRESS_SPACE( AddressSpace::new(true).expect("Failed to create address space for INIT process."), ); - debug!("INIT process address space created."); + kdebug!("INIT process address space created."); compiler_fence(Ordering::SeqCst); }; ALL_PROCESS.lock_irqsave().replace(HashMap::new()); Self::init_switch_result(); Self::arch_init(); - debug!("process arch init done."); + kdebug!("process arch init done."); Self::init_idle(); - debug!("process idle init done."); + kdebug!("process idle init done."); unsafe { __PROCESS_MANAGEMENT_INIT_DONE = true }; - info!("Process Manager initialized."); + kinfo!("Process Manager initialized."); } fn init_switch_result() { @@ -151,7 +147,6 @@ impl ProcessManager { } /// 判断进程管理器是否已经初始化完成 - #[allow(dead_code)] pub fn initialized() -> bool { unsafe { __PROCESS_MANAGEMENT_INIT_DONE } } @@ -159,7 +154,7 @@ impl ProcessManager { /// 获取当前进程的pcb pub fn current_pcb() -> Arc { if unlikely(unsafe { !__PROCESS_MANAGEMENT_INIT_DONE }) { - error!("unsafe__PROCESS_MANAGEMENT_INIT_DONE == false"); + kerror!("unsafe__PROCESS_MANAGEMENT_INIT_DONE == false"); loop { spin_loop(); } @@ -366,7 +361,7 @@ impl ProcessManager { let parent_pcb = r.unwrap(); let r = Syscall::kill(parent_pcb.pid(), Signal::SIGCHLD as i32); if r.is_err() { - warn!( + kwarn!( "failed to send kill signal to {:?}'s parent pcb {:?}", current.pid(), parent_pcb.pid() @@ -426,7 +421,7 @@ impl ProcessManager { ProcessManager::exit_notify(); // unsafe { CurrentIrqArch::interrupt_enable() }; __schedule(SchedMode::SM_NONE); - error!("pid {pid:?} exited but sched again!"); + kerror!("pid {pid:?} exited but sched again!"); #[allow(clippy::empty_loop)] loop { spin_loop(); @@ -446,7 +441,7 @@ impl ProcessManager { // } else { // // 如果不为1就panic // let msg = format!("pcb '{:?}' is still referenced, strong count={}",pcb.pid(), Arc::strong_count(&pcb)); - // error!("{}", msg); + // kerror!("{}", msg); // panic!() // } @@ -456,7 +451,7 @@ impl ProcessManager { /// 上下文切换完成后的钩子函数 unsafe fn switch_finish_hook() { - // debug!("switch_finish_hook"); + // kdebug!("switch_finish_hook"); let prev_pcb = PROCESS_SWITCH_RESULT .as_mut() .unwrap() @@ -516,9 +511,9 @@ pub unsafe fn switch_finish_hook() { int_like!(Pid, AtomicPid, usize, AtomicUsize); -impl fmt::Display for Pid { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.0) +impl ToString for Pid { + fn to_string(&self) -> String { + self.0.to_string() } } @@ -651,9 +646,6 @@ pub struct ProcessControlBlock { /// 进程的robust lock列表 robust_list: RwLock>, - - /// 进程作为主体的凭证集 - cred: SpinLock, } impl ProcessControlBlock { @@ -679,29 +671,14 @@ impl ProcessControlBlock { return Self::do_create_pcb(name, kstack, true); } - /// # 函数的功能 - /// - /// 返回此函数是否是内核进程 - /// - /// # 返回值 - /// - /// 若进程是内核进程则返回true 否则返回false - pub fn is_kthread(&self) -> bool { - return matches!(self.flags(), &mut ProcessFlags::KTHREAD); - } - #[inline(never)] fn do_create_pcb(name: String, kstack: KernelStack, is_idle: bool) -> Arc { - let (pid, ppid, cwd, cred) = if is_idle { - let cred = INIT_CRED.clone(); - (Pid(0), Pid(0), "/".to_string(), cred) + let (pid, ppid, cwd) = if is_idle { + (Pid(0), Pid(0), "/".to_string()) } else { let ppid = ProcessManager::current_pcb().pid(); - let mut cred = ProcessManager::current_pcb().cred(); - cred.cap_permitted = cred.cap_ambient; - cred.cap_effective = cred.cap_ambient; let cwd = ProcessManager::current_pcb().basic().cwd(); - (Self::generate_pid(), ppid, cwd, cred) + (Self::generate_pid(), ppid, cwd) }; let basic_info = ProcessBasicInfo::new(Pid(0), ppid, name, cwd, None); @@ -736,7 +713,6 @@ impl ProcessControlBlock { thread: RwLock::new(ThreadInfo::new()), alarm_timer: SpinLock::new(None), robust_list: RwLock::new(None), - cred: SpinLock::new(cred), }; // 初始化系统调用栈 @@ -889,11 +865,6 @@ impl ProcessControlBlock { return self.basic.read().fd_table().unwrap(); } - #[inline(always)] - pub fn cred(&self) -> Cred { - self.cred.lock().clone() - } - /// 根据文件描述符序号,获取socket对象的Arc指针 /// /// ## 参数 @@ -938,11 +909,11 @@ impl ProcessControlBlock { } /// 生成进程的名字 - pub fn generate_name(program_path: &str, args: &Vec) -> String { + pub fn generate_name(program_path: &str, args: &Vec) -> String { let mut name = program_path.to_string(); for arg in args { name.push(' '); - name.push_str(arg.to_string_lossy().as_ref()); + name.push_str(arg); } return name; } @@ -975,14 +946,6 @@ impl ProcessControlBlock { return None; } - /// 判断当前进程是否有未处理的信号 - pub fn has_pending_signal(&self) -> bool { - let sig_info = self.sig_info_irqsave(); - let has_pending = sig_info.sig_pending().has_pending(); - drop(sig_info); - return has_pending; - } - pub fn sig_struct(&self) -> SpinLockGuard { self.sig_struct.lock_irqsave() } @@ -1167,7 +1130,6 @@ pub struct ProcessSchedulerInfo { } #[derive(Debug, Default)] -#[allow(dead_code)] pub struct SchedInfo { /// 记录任务在特定 CPU 上运行的次数 pub pcount: usize, @@ -1180,7 +1142,6 @@ pub struct SchedInfo { } #[derive(Debug)] -#[allow(dead_code)] pub struct PrioData { pub prio: i32, pub static_prio: i32, @@ -1406,7 +1367,7 @@ impl KernelStack { // 如果内核栈的最低地址处已经有了一个pcb,那么,这里就不再设置,直接返回错误 if unlikely(unsafe { !(*stack_bottom_ptr).is_null() }) { - error!("kernel stack bottom is not null: {:p}", *stack_bottom_ptr); + kerror!("kernel stack bottom is not null: {:p}", *stack_bottom_ptr); return Err(SystemError::EPERM); } // 将pcb的地址放到内核栈的最低地址处 diff --git a/kernel/src/process/syscall.rs b/kernel/src/process/syscall.rs index 1a2fec460..61dfea100 100644 --- a/kernel/src/process/syscall.rs +++ b/kernel/src/process/syscall.rs @@ -1,12 +1,14 @@ use core::ffi::c_void; -use alloc::{ffi::CString, string::ToString, sync::Arc, vec::Vec}; -use log::error; +use alloc::{ + string::{String, ToString}, + sync::Arc, + vec::Vec, +}; use system_error::SystemError; use super::{ abi::WaitOption, - cred::{Kgid, Kuid}, exit::kernel_wait4, fork::{CloneFlags, KernelCloneArgs}, resource::{RLimit64, RLimitID, RUsage, RUsageWho}, @@ -96,13 +98,13 @@ impl Syscall { envp: *const *const u8, frame: &mut TrapFrame, ) -> Result<(), SystemError> { - // debug!( + // kdebug!( // "execve path: {:?}, argv: {:?}, envp: {:?}\n", // path, // argv, // envp // ); - // debug!( + // kdebug!( // "before execve: strong count: {}", // Arc::strong_count(&ProcessManager::current_pcb()) // ); @@ -112,16 +114,16 @@ impl Syscall { } let x = || { - let path: CString = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; - let argv: Vec = check_and_clone_cstr_array(argv)?; - let envp: Vec = check_and_clone_cstr_array(envp)?; + let path: String = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let argv: Vec = check_and_clone_cstr_array(argv)?; + let envp: Vec = check_and_clone_cstr_array(envp)?; Ok((path, argv, envp)) }; - let (path, argv, envp) = x().inspect_err(|e: &SystemError| { - error!("Failed to execve: {:?}", e); - })?; - - let path = path.into_string().map_err(|_| SystemError::EINVAL)?; + let r: Result<(String, Vec, Vec), SystemError> = x(); + if let Err(e) = r { + panic!("Failed to execve: {:?}", e); + } + let (path, argv, envp) = r.unwrap(); ProcessManager::current_pcb() .basic_mut() .set_name(ProcessControlBlock::generate_name(&path, &argv)); @@ -131,7 +133,7 @@ impl Syscall { // 关闭设置了O_CLOEXEC的文件描述符 let fd_table = ProcessManager::current_pcb().fd_table(); fd_table.write().close_on_exec(); - // debug!( + // kdebug!( // "after execve: strong count: {}", // Arc::strong_count(&ProcessManager::current_pcb()) // ); @@ -287,125 +289,25 @@ impl Syscall { } pub fn getuid() -> Result { - let pcb = ProcessManager::current_pcb(); - return Ok(pcb.cred.lock().uid.data()); - } - - pub fn getgid() -> Result { - let pcb = ProcessManager::current_pcb(); - return Ok(pcb.cred.lock().gid.data()); - } - - pub fn geteuid() -> Result { - let pcb = ProcessManager::current_pcb(); - return Ok(pcb.cred.lock().euid.data()); - } - - pub fn getegid() -> Result { - let pcb = ProcessManager::current_pcb(); - return Ok(pcb.cred.lock().egid.data()); - } - - pub fn setuid(uid: usize) -> Result { - let pcb = ProcessManager::current_pcb(); - let mut guard = pcb.cred.lock(); - - if guard.uid.data() == 0 { - guard.setuid(uid); - guard.seteuid(uid); - guard.setsuid(uid); - } else if uid == guard.uid.data() || uid == guard.suid.data() { - guard.seteuid(uid); - } else { - return Err(SystemError::EPERM); - } - + // todo: 增加credit功能之后,需要修改 return Ok(0); } - pub fn setgid(gid: usize) -> Result { - let pcb = ProcessManager::current_pcb(); - let mut guard = pcb.cred.lock(); - - if guard.egid.data() == 0 { - guard.setgid(gid); - guard.setegid(gid); - guard.setsgid(gid); - guard.setfsgid(gid); - } else if guard.gid.data() == gid || guard.sgid.data() == gid { - guard.setegid(gid); - guard.setfsgid(gid); - } else { - return Err(SystemError::EPERM); - } - + pub fn getgid() -> Result { + // todo: 增加credit功能之后,需要修改 return Ok(0); } - pub fn seteuid(euid: usize) -> Result { - let pcb = ProcessManager::current_pcb(); - let mut guard = pcb.cred.lock(); - - if euid == usize::MAX || (euid == guard.euid.data() && euid == guard.fsuid.data()) { - return Ok(0); - } - - if euid != usize::MAX { - guard.seteuid(euid); - } - - let euid = guard.euid.data(); - guard.setfsuid(euid); - + pub fn geteuid() -> Result { + // todo: 增加credit功能之后,需要修改 return Ok(0); } - pub fn setegid(egid: usize) -> Result { - let pcb = ProcessManager::current_pcb(); - let mut guard = pcb.cred.lock(); - - if egid == usize::MAX || (egid == guard.egid.data() && egid == guard.fsgid.data()) { - return Ok(0); - } - - if egid != usize::MAX { - guard.setegid(egid); - } - - let egid = guard.egid.data(); - guard.setfsgid(egid); - + pub fn getegid() -> Result { + // todo: 增加credit功能之后,需要修改 return Ok(0); } - pub fn setfsuid(fsuid: usize) -> Result { - let fsuid = Kuid::new(fsuid); - - let pcb = ProcessManager::current_pcb(); - let mut guard = pcb.cred.lock(); - let old_fsuid = guard.fsuid; - - if fsuid == guard.uid || fsuid == guard.euid || fsuid == guard.suid { - guard.setfsuid(fsuid.data()); - } - - Ok(old_fsuid.data()) - } - - pub fn setfsgid(fsgid: usize) -> Result { - let fsgid = Kgid::new(fsgid); - - let pcb = ProcessManager::current_pcb(); - let mut guard = pcb.cred.lock(); - let old_fsgid = guard.fsgid; - - if fsgid == guard.gid || fsgid == guard.egid || fsgid == guard.sgid { - guard.setfsgid(fsgid.data()); - } - - Ok(old_fsgid.data()) - } - pub fn get_rusage(who: i32, rusage: *mut RUsage) -> Result { let who = RUsageWho::try_from(who)?; let mut writer = UserBufferWriter::new(rusage, core::mem::size_of::(), true)?; diff --git a/kernel/src/process/timer.rs b/kernel/src/process/timer.rs index d206fbd23..7099d0748 100644 --- a/kernel/src/process/timer.rs +++ b/kernel/src/process/timer.rs @@ -84,7 +84,7 @@ impl AlarmTimer { >::from(Duration::from_secs(self.expired_second)) .timer_jiffies(); let remain_second = Duration::from(Jiffies::new(end_jiffies - now_jiffies)); - // debug!( + // kdebug!( // "end: {} - now: {} = remain: {}", // end_jiffies, // now_jiffies, diff --git a/kernel/src/sched/completion.rs b/kernel/src/sched/completion.rs index 61488c25f..2d3450f2d 100644 --- a/kernel/src/sched/completion.rs +++ b/kernel/src/sched/completion.rs @@ -7,8 +7,8 @@ use crate::{ time::timer::schedule_timeout, }; -const COMPLETE_ALL: u32 = u32::MAX; -const MAX_TIMEOUT: i64 = i64::MAX; +const COMPLETE_ALL: u32 = core::u32::MAX; +const MAX_TIMEOUT: i64 = core::i64::MAX; #[derive(Debug)] pub struct Completion { diff --git a/kernel/src/sched/fair.rs b/kernel/src/sched/fair.rs index eb8de762d..0428b1b93 100644 --- a/kernel/src/sched/fair.rs +++ b/kernel/src/sched/fair.rs @@ -144,10 +144,7 @@ impl FairSchedEntity { #[allow(clippy::mut_from_ref)] pub fn force_mut(&self) -> &mut Self { - unsafe { - let p = self as *const Self as usize; - (p as *mut Self).as_mut().unwrap() - } + unsafe { &mut *(self as *const Self as usize as *mut Self) } } /// 判断是否是进程持有的调度实体 @@ -419,11 +416,7 @@ impl CfsRunQueue { #[inline] #[allow(clippy::mut_from_ref)] pub fn force_mut(&self) -> &mut Self { - unsafe { - (self as *const Self as usize as *mut Self) - .as_mut() - .unwrap() - } + unsafe { &mut *(self as *const Self as usize as *mut Self) } } #[inline] @@ -574,7 +567,7 @@ impl CfsRunQueue { fence(Ordering::SeqCst); if unlikely(now <= curr.exec_start) { - // warn!( + // kwarn!( // "update_current return now <= curr.exec_start now {now} execstart {}", // curr.exec_start // ); @@ -603,11 +596,11 @@ impl CfsRunQueue { fn account_cfs_rq_runtime(&mut self, delta_exec: u64) { if likely(self.runtime_remaining > delta_exec) { self.runtime_remaining -= delta_exec; - // error!("runtime_remaining {}", self.runtime_remaining); + // kerror!("runtime_remaining {}", self.runtime_remaining); return; } - // warn!( + // kwarn!( // "runtime_remaining {} delta exec {delta_exec} nr_running {}", // self.runtime_remaining, // self.nr_running @@ -616,14 +609,14 @@ impl CfsRunQueue { self.runtime_remaining = 5000 * NSEC_PER_MSEC as u64; if likely(self.current().is_some()) && self.nr_running > 1 { - // error!("account_cfs_rq_runtime"); + // kerror!("account_cfs_rq_runtime"); self.rq().resched_current(); } } /// 计算deadline,如果vruntime到期会重调度 pub fn update_deadline(&mut self, se: &Arc) { - // error!("vruntime {} deadline {}", se.vruntime, se.deadline); + // kerror!("vruntime {} deadline {}", se.vruntime, se.deadline); if se.vruntime < se.deadline { return; } @@ -1138,7 +1131,7 @@ impl CfsRunQueue { self.avg_vruntime_add(se); se.force_mut().min_deadline = se.deadline; self.entities.insert(se.vruntime, se.clone()); - // warn!( + // kwarn!( // "enqueue pcb {:?} cfsrq {:?}", // se.pcb().pid(), // self.entities @@ -1160,7 +1153,7 @@ impl CfsRunQueue { } fn inner_dequeue_entity(&mut self, se: &Arc) { - // warn!( + // kwarn!( // "before dequeue pcb {:?} cfsrq {:?}", // se.pcb().pid(), // self.entities @@ -1203,7 +1196,7 @@ impl CfsRunQueue { // ) // .as_bytes(), // ); - // warn!( + // kwarn!( // "after dequeue pcb {:?}(real: {:?}) cfsrq {:?}", // se.pcb().pid(), // remove.pcb().pid(), @@ -1373,11 +1366,6 @@ impl CfsRunQueue { } } -impl Default for CfsRunQueue { - fn default() -> Self { - Self::new() - } -} pub struct CompletelyFairScheduler; impl CompletelyFairScheduler { diff --git a/kernel/src/sched/mod.rs b/kernel/src/sched/mod.rs index 9a0f2ff7e..c50782e82 100644 --- a/kernel/src/sched/mod.rs +++ b/kernel/src/sched/mod.rs @@ -111,9 +111,7 @@ pub trait Scheduler { flags: WakeupFlags, ); - #[allow(dead_code)] /// ## 选择接下来最适合运行的任务 - #[allow(dead_code)] fn pick_task(rq: &mut CpuRunQueue) -> Option>; /// ## 选择接下来最适合运行的任务 @@ -274,7 +272,6 @@ pub trait SchedArch { /// 开启当前核心的调度 fn enable_sched_local(); /// 关闭当前核心的调度 - #[allow(dead_code)] fn disable_sched_local(); /// 在第一次开启调度之前,进行初始化工作。 @@ -366,22 +363,14 @@ impl CpuRunQueue { { // 在本cpu已上锁则可以直接拿 ( - unsafe { - (self as *const Self as usize as *mut Self) - .as_mut() - .unwrap() - }, + unsafe { &mut *(self as *const Self as usize as *mut Self) }, None, ) } else { // 否则先上锁再拿 let guard = self.lock(); ( - unsafe { - (self as *const Self as usize as *mut Self) - .as_mut() - .unwrap() - }, + unsafe { &mut *(self as *const Self as usize as *mut Self) }, Some(guard), ) } @@ -521,7 +510,7 @@ impl CpuRunQueue { let delta = clock - self.clock; self.clock += delta; - // error!("clock {}", self.clock); + // kerror!("clock {}", self.clock); self.update_rq_clock_task(delta); } @@ -529,7 +518,7 @@ impl CpuRunQueue { pub fn update_rq_clock_task(&mut self, mut delta: u64) { let mut irq_delta = irq_time_read(self.cpu) - self.prev_irq_time; // if self.cpu == 0 { - // error!( + // kerror!( // "cpu 0 delta {delta} irq_delta {} irq_time_read(self.cpu) {} self.prev_irq_time {}", // irq_delta, // irq_time_read(self.cpu), @@ -553,7 +542,7 @@ impl CpuRunQueue { self.clock_task += delta; compiler_fence(Ordering::SeqCst); // if self.cpu == 0 { - // error!("cpu {} clock_task {}", self.cpu, self.clock_task); + // kerror!("cpu {} clock_task {}", self.cpu, self.clock_task); // } // todo: pelt? } @@ -667,7 +656,7 @@ impl CpuRunQueue { if let Some(pcb) = p.as_ref() { return pcb.clone(); } else { - // error!( + // kerror!( // "pick idle cfs rq {:?}", // self.cfs_rq() // .entities @@ -860,7 +849,7 @@ pub fn __schedule(sched_mod: SchedMode) { // .map(|x| { x.1.vruntime }) // .collect::>(), // ); - // warn!( + // kwarn!( // "before cfs rq {:?} prev {:?}", // rq.cfs // .entities @@ -870,12 +859,12 @@ pub fn __schedule(sched_mod: SchedMode) { // prev.pid() // ); - // error!("prev pid {:?} {:?}", prev.pid(), prev.sched_info().policy()); + // kerror!("prev pid {:?} {:?}", prev.pid(), prev.sched_info().policy()); if !sched_mod.contains(SchedMode::SM_MASK_PREEMPT) && prev.sched_info().policy() != SchedPolicy::IDLE && prev.sched_info().inner_lock_read_irqsave().is_mark_sleep() { - // warn!("deactivate_task prev {:?}", prev.pid()); + // kwarn!("deactivate_task prev {:?}", prev.pid()); // TODO: 这里需要处理信号 // https://code.dragonos.org.cn/xref/linux-6.6.21/kernel/sched/core.c?r=&mo=172979&fi=6578#6630 rq.deactivate_task( @@ -900,13 +889,13 @@ pub fn __schedule(sched_mod: SchedMode) { // .collect::>(), // ); - // error!("next {:?}", next.pid()); + // kerror!("next {:?}", next.pid()); prev.flags().remove(ProcessFlags::NEED_SCHEDULE); fence(Ordering::SeqCst); if likely(!Arc::ptr_eq(&prev, &next)) { rq.set_current(Arc::downgrade(&next)); - // warn!( + // kwarn!( // "switch_process prev {:?} next {:?} sched_mode {sched_mod:?}", // prev.pid(), // next.pid() diff --git a/kernel/src/smp/cpu/mod.rs b/kernel/src/smp/cpu/mod.rs index 186184ef5..62c0b4d14 100644 --- a/kernel/src/smp/cpu/mod.rs +++ b/kernel/src/smp/cpu/mod.rs @@ -1,7 +1,6 @@ use core::sync::atomic::AtomicU32; use alloc::{sync::Arc, vec::Vec}; -use log::{debug, error, info}; use system_error::SystemError; use crate::{ @@ -203,14 +202,14 @@ impl SmpCpuManager { continue; } - debug!("Bring up CPU {}", cpu_id.data()); + kdebug!("Bring up CPU {}", cpu_id.data()); if let Err(e) = self.cpu_up(cpu_id, CpuHpState::Online) { - error!("Failed to bring up CPU {}: {:?}", cpu_id.data(), e); + kerror!("Failed to bring up CPU {}: {:?}", cpu_id.data(), e); } } - info!("All non-boot CPUs have been brought up"); + kinfo!("All non-boot CPUs have been brought up"); } fn cpu_up(&self, cpu_id: ProcessorId, target_state: CpuHpState) -> Result<(), SystemError> { @@ -219,7 +218,7 @@ impl SmpCpuManager { } let cpu_state = self.cpuhp_state(cpu_id).state; - debug!( + kdebug!( "cpu_up: cpu_id: {}, cpu_state: {:?}, target_state: {:?}", cpu_id.data(), cpu_state, diff --git a/kernel/src/smp/init.rs b/kernel/src/smp/init.rs index 27d622093..689116124 100644 --- a/kernel/src/smp/init.rs +++ b/kernel/src/smp/init.rs @@ -1,5 +1,3 @@ -use log::info; - use crate::{ arch::{syscall::arch_syscall_init, CurrentIrqArch, CurrentSchedArch}, exception::InterruptArch, @@ -24,6 +22,6 @@ pub fn smp_ap_start_stage2() -> ! { #[inline(never)] fn do_ap_start_stage2() { - info!("Successfully started AP {}", smp_get_processor_id().data()); + kinfo!("Successfully started AP {}", smp_get_processor_id().data()); arch_syscall_init().expect("AP core failed to initialize syscall"); } diff --git a/kernel/src/syscall/misc.rs b/kernel/src/syscall/misc.rs index f78029291..7398e85a8 100644 --- a/kernel/src/syscall/misc.rs +++ b/kernel/src/syscall/misc.rs @@ -5,7 +5,6 @@ use crate::{ }; use alloc::vec::Vec; use core::cmp; -use log::warn; use system_error::SystemError; use super::{user_access::UserBufferWriter, Syscall}; @@ -60,7 +59,7 @@ impl Syscall { } pub fn umask(_mask: u32) -> Result { - warn!("SYS_UMASK has not yet been implemented\n"); + kwarn!("SYS_UMASK has not yet been implemented\n"); return Ok(0o777); } diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 574e34a8b..e6d03a67e 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -20,7 +20,6 @@ use crate::{ syscall::user_access::check_and_clone_cstr, }; -use log::{info, warn}; use num_traits::FromPrimitive; use system_error::SystemError; @@ -29,9 +28,10 @@ use crate::{ filesystem::vfs::{ fcntl::{AtFlags, FcntlCommand}, file::FileMode, - syscall::{ModeType, PosixKstat, UtimensFlags}, + syscall::{ModeType, PosixKstat}, MAX_PATHLEN, }, + kinfo, libs::align::page_align_up, mm::{verify_area, MemoryManagementArch, VirtAddr}, net::syscall::SockAddr, @@ -69,9 +69,9 @@ impl Syscall { if prev { panic!("Cannot initialize syscall more than once!"); } - info!("Initializing syscall..."); + kinfo!("Initializing syscall..."); let r = crate::arch::syscall::arch_syscall_init(); - info!("Syscall init successfully!"); + kinfo!("Syscall init successfully!"); return r; } @@ -369,7 +369,7 @@ impl Syscall { SYS_KILL => { let pid = Pid::new(args[0]); let sig = args[1] as c_int; - // debug!("KILL SYSCALL RECEIVED"); + // kdebug!("KILL SYSCALL RECEIVED"); Self::kill(pid, sig) } @@ -383,7 +383,7 @@ impl Syscall { SYS_GETPID => Self::getpid().map(|pid| pid.into()), SYS_SCHED => { - warn!("syscall sched"); + kwarn!("syscall sched"); schedule(SchedMode::SM_NONE); Ok(0) } @@ -650,7 +650,7 @@ impl Syscall { Err(SystemError::EINVAL) }; - // debug!("FCNTL: fd: {}, cmd: {:?}, arg: {}, res: {:?}", fd, cmd, arg, res); + // kdebug!("FCNTL: fd: {}, cmd: {:?}, arg: {}, res: {:?}", fd, cmd, arg, res); res } @@ -658,7 +658,7 @@ impl Syscall { let fd = args[0] as i32; let len = args[1]; let res = Self::ftruncate(fd, len); - // debug!("FTRUNCATE: fd: {}, len: {}, res: {:?}", fd, len, res); + // kdebug!("FTRUNCATE: fd: {}, len: {}, res: {:?}", fd, len, res); res } @@ -835,35 +835,33 @@ impl Syscall { #[cfg(target_arch = "x86_64")] SYS_POLL => { - warn!("SYS_POLL has not yet been implemented"); + kwarn!("SYS_POLL has not yet been implemented"); Ok(0) } SYS_SETPGID => { - warn!("SYS_SETPGID has not yet been implemented"); + kwarn!("SYS_SETPGID has not yet been implemented"); Ok(0) } SYS_RT_SIGPROCMASK => { - warn!("SYS_RT_SIGPROCMASK has not yet been implemented"); + kwarn!("SYS_RT_SIGPROCMASK has not yet been implemented"); Ok(0) } SYS_TKILL => { - warn!("SYS_TKILL has not yet been implemented"); + kwarn!("SYS_TKILL has not yet been implemented"); Ok(0) } SYS_SIGALTSTACK => { - warn!("SYS_SIGALTSTACK has not yet been implemented"); + kwarn!("SYS_SIGALTSTACK has not yet been implemented"); Ok(0) } SYS_EXIT_GROUP => { - let exit_code = args[0]; - Self::exit(exit_code) - // warn!("SYS_EXIT_GROUP has not yet been implemented"); - // Ok(0) + kwarn!("SYS_EXIT_GROUP has not yet been implemented"); + Ok(0) } SYS_MADVISE => { @@ -877,6 +875,7 @@ impl Syscall { } SYS_GETTID => Self::gettid().map(|tid| tid.into()), + SYS_GETUID => Self::getuid(), SYS_SYSLOG => { let syslog_action_type = args[0]; @@ -890,29 +889,27 @@ impl Syscall { Self::do_syslog(syslog_action_type, user_buf, len) } - SYS_GETUID => Self::getuid(), SYS_GETGID => Self::getgid(), - SYS_SETUID => Self::setuid(args[0]), - SYS_SETGID => Self::setgid(args[0]), - - SYS_GETEUID => Self::geteuid(), - SYS_GETEGID => Self::getegid(), - SYS_SETRESUID => Self::seteuid(args[1]), - SYS_SETRESGID => Self::setegid(args[1]), - - SYS_SETFSUID => Self::setfsuid(args[0]), - SYS_SETFSGID => Self::setfsgid(args[0]), - + SYS_SETUID => { + kwarn!("SYS_SETUID has not yet been implemented"); + Ok(0) + } + SYS_SETGID => { + kwarn!("SYS_SETGID has not yet been implemented"); + Ok(0) + } SYS_SETSID => { - warn!("SYS_SETSID has not yet been implemented"); + kwarn!("SYS_SETSID has not yet been implemented"); Ok(0) } - + SYS_GETEUID => Self::geteuid(), + SYS_GETEGID => Self::getegid(), SYS_GETRUSAGE => { let who = args[0] as c_int; let rusage = args[1] as *mut RUsage; Self::get_rusage(who, rusage) } + #[cfg(target_arch = "x86_64")] SYS_READLINK => { let path = args[0] as *const u8; @@ -978,17 +975,17 @@ impl Syscall { } SYS_FCHOWN => { - warn!("SYS_FCHOWN has not yet been implemented"); + kwarn!("SYS_FCHOWN has not yet been implemented"); Ok(0) } SYS_FSYNC => { - warn!("SYS_FSYNC has not yet been implemented"); + kwarn!("SYS_FSYNC has not yet been implemented"); Ok(0) } SYS_RSEQ => { - warn!("SYS_RSEQ has not yet been implemented"); + kwarn!("SYS_RSEQ has not yet been implemented"); Ok(0) } @@ -1106,40 +1103,7 @@ impl Syscall { Self::shmctl(id, cmd, user_buf, from_user) } - SYS_MSYNC => { - let start = page_align_up(args[0]); - let len = page_align_up(args[1]); - let flags = args[2]; - Self::msync(VirtAddr::new(start), len, flags) - } - SYS_UTIMENSAT => Self::sys_utimensat( - args[0] as i32, - args[1] as *const u8, - args[2] as *const PosixTimeSpec, - args[3] as u32, - ), - #[cfg(target_arch = "x86_64")] - SYS_FUTIMESAT => { - let flags = UtimensFlags::empty(); - Self::sys_utimensat( - args[0] as i32, - args[1] as *const u8, - args[2] as *const PosixTimeSpec, - flags.bits(), - ) - } - #[cfg(target_arch = "x86_64")] - SYS_UTIMES => Self::sys_utimes(args[0] as *const u8, args[1] as *const PosixTimeval), - #[cfg(target_arch = "x86_64")] - SYS_EVENTFD => { - let initval = args[0] as u32; - Self::sys_eventfd(initval, 0) - } - SYS_EVENTFD2 => { - let initval = args[0] as u32; - let flags = args[1] as u32; - Self::sys_eventfd(initval, flags) - } + _ => panic!("Unsupported syscall ID: {}", syscall_num), }; @@ -1159,9 +1123,7 @@ impl Syscall { back_color: u32, ) -> Result { // todo: 删除这个系统调用 - let s = check_and_clone_cstr(s, Some(4096))? - .into_string() - .map_err(|_| SystemError::EINVAL)?; + let s = check_and_clone_cstr(s, Some(4096))?; let fr = (front_color & 0x00ff0000) >> 16; let fg = (front_color & 0x0000ff00) >> 8; let fb = front_color & 0x000000ff; diff --git a/kernel/src/syscall/user_access.rs b/kernel/src/syscall/user_access.rs index 18a74764e..664931561 100644 --- a/kernel/src/syscall/user_access.rs +++ b/kernel/src/syscall/user_access.rs @@ -2,11 +2,10 @@ use core::{ mem::size_of, - num::NonZero, slice::{from_raw_parts, from_raw_parts_mut}, }; -use alloc::{ffi::CString, vec::Vec}; +use alloc::{string::String, vec::Vec}; use crate::mm::{verify_area, VirtAddr}; @@ -71,11 +70,10 @@ pub unsafe fn copy_from_user(dst: &mut [u8], src: VirtAddr) -> Result, -) -> Result { +) -> Result { if user.is_null() { return Err(SystemError::EFAULT); } @@ -95,12 +93,9 @@ pub fn check_and_clone_cstr( if c[0] == 0 { break; } - buffer.push(NonZero::new(c[0]).ok_or(SystemError::EINVAL)?); + buffer.push(c[0]); } - - let cstr = CString::from(buffer); - - return Ok(cstr); + String::from_utf8(buffer).map_err(|_| SystemError::EFAULT) } /// 检查并从用户态拷贝一个 C 字符串数组 @@ -117,11 +112,11 @@ pub fn check_and_clone_cstr( /// ## 错误 /// /// - `EFAULT`:用户态地址不合法 -pub fn check_and_clone_cstr_array(user: *const *const u8) -> Result, SystemError> { +pub fn check_and_clone_cstr_array(user: *const *const u8) -> Result, SystemError> { if user.is_null() { Ok(Vec::new()) } else { - // debug!("check_and_clone_cstr_array: {:p}\n", user); + // kdebug!("check_and_clone_cstr_array: {:p}\n", user); let mut buffer = Vec::new(); for i in 0.. { let addr = unsafe { user.add(i) }; @@ -134,7 +129,7 @@ pub fn check_and_clone_cstr_array(user: *const *const u8) -> Result let dst = core::mem::transmute::<[u8; size_of::()], [usize; 1]>(dst); str_ptr = dst[0] as *const u8; - // debug!("str_ptr: {:p}, addr:{addr:?}\n", str_ptr); + // kdebug!("str_ptr: {:p}, addr:{addr:?}\n", str_ptr); } if str_ptr.is_null() { diff --git a/kernel/src/time/clocksource.rs b/kernel/src/time/clocksource.rs index 6565038ce..df0ed81af 100644 --- a/kernel/src/time/clocksource.rs +++ b/kernel/src/time/clocksource.rs @@ -11,7 +11,6 @@ use alloc::{ vec::Vec, }; use lazy_static::__Deref; -use log::{debug, info}; use system_error::SystemError; use unified_init::macros::unified_init; @@ -19,6 +18,7 @@ use crate::{ arch::CurrentIrqArch, exception::InterruptArch, init::initcall::INITCALL_LATE, + kdebug, kinfo, libs::spinlock::SpinLock, process::{ kthread::{KernelThreadClosure, KernelThreadMechanism}, @@ -41,7 +41,7 @@ lazy_static! { pub static ref WATCHDOG_LIST: SpinLock>> = SpinLock::new(LinkedList::new()); - pub static ref CLOCKSOURCE_WATCHDOG:SpinLock = SpinLock::new(ClocksouceWatchdog::new()); + pub static ref CLOCKSOUCE_WATCHDOG:SpinLock = SpinLock::new(ClocksouceWatchdog::new()); pub static ref OVERRIDE_NAME: SpinLock = SpinLock::new(String::from("")); @@ -53,7 +53,7 @@ static mut WATCHDOG_KTHREAD: Option> = None; /// 正在被使用时钟源 pub static CUR_CLOCKSOURCE: SpinLock>> = SpinLock::new(None); /// 是否完成加载 -pub static FINISHED_BOOTING: AtomicBool = AtomicBool::new(false); +pub static mut FINISHED_BOOTING: AtomicBool = AtomicBool::new(false); /// Interval: 0.5sec Threshold: 0.0625s /// 系统节拍率 @@ -138,6 +138,8 @@ pub struct ClocksouceWatchdog { watchdog: Option>, /// 检查器是否在工作的标志 is_running: bool, + /// 上一次检查的时刻 + last_check: CycleNum, /// 定时监视器的过期时间 timer_expires: u64, } @@ -146,6 +148,7 @@ impl ClocksouceWatchdog { Self { watchdog: None, is_running: false, + last_check: CycleNum(0), timer_expires: 0, } } @@ -165,13 +168,7 @@ impl ClocksouceWatchdog { // 生成一个定时器 let wd_timer_func: Box = Box::new(WatchdogTimerFunc {}); self.timer_expires += clock() + WATCHDOG_INTERVAL; - let mut wd_data = self.watchdog.as_ref().unwrap().clone().clocksource_data(); - wd_data.watchdog_last = self.watchdog.as_ref().unwrap().clone().read(); - self.watchdog - .as_ref() - .unwrap() - .update_clocksource_data(wd_data) - .expect("clocksource_start_watchdog: failed to update watchdog data"); + self.last_check = self.watchdog.as_ref().unwrap().clone().read(); let wd_timer = Timer::new(wd_timer_func, self.timer_expires); wd_timer.activate(); self.is_running = true; @@ -207,12 +204,10 @@ pub trait Clocksource: Send + Sync + Debug { return Err(SystemError::ENOSYS); } /// optional function to disable the clocksource - #[allow(dead_code)] fn disable(&self) -> Result<(), SystemError> { return Err(SystemError::ENOSYS); } /// vsyscall based read - #[allow(dead_code)] fn vread(&self) -> Result { return Err(SystemError::ENOSYS); } @@ -276,14 +271,15 @@ impl dyn Clocksource { let cs_data_guard = self.clocksource_data(); let mut max_cycles: u64; - max_cycles = (1 << (63 - (log2(cs_data_guard.mult + cs_data_guard.maxadj) + 1))) as u64; + // 这里我有问题,不知道要不要修改,暂时不修改它 + max_cycles = (1 << (63 - (log2(cs_data_guard.mult) + 1))) as u64; max_cycles = max_cycles.min(cs_data_guard.mask.bits); let max_nsecs = clocksource_cyc2ns( CycleNum(max_cycles), - cs_data_guard.mult - cs_data_guard.maxadj, + cs_data_guard.mult, cs_data_guard.shift, ); - return max_nsecs - (max_nsecs >> 3); + return max_nsecs - (max_nsecs >> 5); } /// # 计算时钟源的mult和shift,以便将一个时钟源的频率转换为另一个时钟源的频率 @@ -322,8 +318,9 @@ impl dyn Clocksource { /// # 更新时钟源频率,初始化mult/shift 和 max_idle_ns fn clocksource_update_freq_scale(&self, scale: u32, freq: u32) -> Result<(), SystemError> { + let mut cs_data = self.clocksource_data(); + if freq != 0 { - let mut cs_data = self.clocksource_data(); let mut sec: u64 = cs_data.mask.bits(); sec /= freq as u64; @@ -338,10 +335,8 @@ impl dyn Clocksource { self.clocks_calc_mult_shift(freq, NSEC_PER_SEC / scale, sec as u32 * scale); cs_data.set_mult(mult); cs_data.set_shift(shift); - self.update_clocksource_data(cs_data)?; } - let mut cs_data = self.clocksource_data(); if scale != 0 && freq != 0 && cs_data.uncertainty_margin == 0 { cs_data.set_uncertainty_margin(NSEC_PER_SEC / (scale * freq)); if cs_data.uncertainty_margin < 2 * WATCHDOG_MAX_SKEW { @@ -353,25 +348,10 @@ impl dyn Clocksource { // 确保时钟源没有太大的mult值造成溢出 cs_data.set_maxadj(self.clocksource_max_adjustment()); - self.update_clocksource_data(cs_data)?; - while freq != 0 - && (self.clocksource_data().mult + self.clocksource_data().maxadj - < self.clocksource_data().mult - || self.clocksource_data().mult - self.clocksource_data().maxadj - > self.clocksource_data().mult) - { - let mut cs_data = self.clocksource_data(); - cs_data.set_mult(cs_data.mult >> 1); - cs_data.set_shift(cs_data.shift - 1); - self.update_clocksource_data(cs_data)?; - let mut cs_data = self.clocksource_data(); - cs_data.set_maxadj(self.clocksource_max_adjustment()); - self.update_clocksource_data(cs_data)?; - } - let mut cs_data = self.clocksource_data(); let ns = self.clocksource_max_deferment(); cs_data.set_max_idle_ns(ns as u32); + self.update_clocksource_data(cs_data)?; return Ok(()); @@ -398,7 +378,7 @@ impl dyn Clocksource { .expect("register: failed to enqueue watchdog list"); // 选择一个最好的时钟源 clocksource_select(); - debug!("clocksource_register successfully"); + kdebug!("clocksource_register successfully"); return Ok(()); } @@ -407,7 +387,7 @@ impl dyn Clocksource { // 根据rating由大到小排序 let cs_data = self.clocksource_data(); let mut list_guard = CLOCKSOURCE_LIST.lock(); - let mut spilt_pos: usize = list_guard.len(); + let mut spilt_pos: usize = 0; for (pos, ele) in list_guard.iter().enumerate() { if ele.clocksource_data().rating < cs_data.rating { spilt_pos = pos; @@ -418,7 +398,7 @@ impl dyn Clocksource { let cs = self.clocksource(); list_guard.push_back(cs); list_guard.append(&mut temp_list); - // debug!( + // kdebug!( // "CLOCKSOURCE_LIST len = {:?},clocksource_enqueue sccessfully", // list_guard.len() // ); @@ -465,7 +445,7 @@ impl dyn Clocksource { drop(list_guard); // 对比当前注册的时间源的精度和监视器的精度 - let mut cs_watchdog = CLOCKSOURCE_WATCHDOG.lock_irqsave(); + let mut cs_watchdog = CLOCKSOUCE_WATCHDOG.lock_irqsave(); if cs_watchdog.watchdog.is_none() || cs_data.rating > cs_watchdog @@ -493,9 +473,10 @@ impl dyn Clocksource { pub fn set_unstable(&self, delta: i64) -> Result { let mut cs_data = self.clocksource_data(); // 打印出unstable的时钟源信息 - debug!( + kdebug!( "clocksource :{:?} is unstable, its delta is {:?}", - cs_data.name, delta + cs_data.name, + delta ); cs_data.flags.remove( ClocksourceFlags::CLOCK_SOURCE_VALID_FOR_HRES | ClocksourceFlags::CLOCK_SOURCE_WATCHDOG, @@ -506,7 +487,7 @@ impl dyn Clocksource { self.update_clocksource_data(cs_data)?; // 启动watchdog线程 进行后续处理 - if FINISHED_BOOTING.load(Ordering::Relaxed) { + if unsafe { FINISHED_BOOTING.load(Ordering::Relaxed) } { // TODO 在实现了工作队列后,将启动线程换成schedule work run_watchdog_kthread(); } @@ -516,7 +497,7 @@ impl dyn Clocksource { /// # 将时间源从监视链表中弹出 fn clocksource_dequeue_watchdog(&self) { let data = self.clocksource_data(); - let mut locked_watchdog = CLOCKSOURCE_WATCHDOG.lock_irqsave(); + let mut locked_watchdog = CLOCKSOUCE_WATCHDOG.lock_irqsave(); let watchdog = locked_watchdog .get_watchdog() .clone() @@ -672,14 +653,10 @@ pub struct ClocksourceData { pub max_idle_ns: u32, pub flags: ClocksourceFlags, pub watchdog_last: CycleNum, - /// 用于watchdog机制中的字段,记录主时钟源上一次被读取的周期数 - pub cs_last: CycleNum, // 用于描述时钟源的不确定性边界,时钟源读取的时间可能存在的不确定性和误差范围 pub uncertainty_margin: u32, // 最大的时间调整量 pub maxadj: u32, - /// 上一次读取时钟源时的周期数 - pub cycle_last: CycleNum, } impl ClocksourceData { @@ -705,10 +682,8 @@ impl ClocksourceData { max_idle_ns, flags, watchdog_last: CycleNum(0), - cs_last: CycleNum(0), uncertainty_margin, maxadj, - cycle_last: CycleNum(0), }; return csd; } @@ -753,9 +728,6 @@ impl ClocksourceData { /// converts clocksource cycles to nanoseconds /// pub fn clocksource_cyc2ns(cycles: CycleNum, mult: u32, shift: u32) -> u64 { - // info!(""); - // info!("cycles = {:?}, mult = {:?}, shift = {:?}", cycles, mult, shift); - // info!("ret = {:?}", (cycles.data() * mult as u64) >> shift); return (cycles.data() * mult as u64) >> shift; } @@ -768,7 +740,7 @@ pub fn clocksource_resume() { match ele.resume() { Ok(_) => continue, Err(_) => { - debug!("clocksource {:?} resume failed", data.name); + kdebug!("clocksource {:?} resume failed", data.name); } } } @@ -784,7 +756,7 @@ pub fn clocksource_suspend() { match ele.suspend() { Ok(_) => continue, Err(_) => { - debug!("clocksource {:?} suspend failed", data.name); + kdebug!("clocksource {:?} suspend failed", data.name); } } } @@ -797,15 +769,25 @@ pub fn clocksource_suspend() { /// * `Ok()` - 检查完成 /// * `Err(SystemError)` - 错误码 pub fn clocksource_watchdog() -> Result<(), SystemError> { - let cs_watchdog = CLOCKSOURCE_WATCHDOG.lock_irqsave(); - // debug!("clocksource_watchdog start"); + let mut cs_watchdog = CLOCKSOUCE_WATCHDOG.lock_irqsave(); + // kdebug!("clocksource_watchdog start"); // watchdog没有在运行的话直接退出 if !cs_watchdog.is_running || cs_watchdog.watchdog.is_none() { - // debug!("is_running = {:?},watchdog = {:?}", cs_watchdog.is_running, cs_watchdog.watchdog); + // kdebug!("is_running = {:?},watchdog = {:?}", cs_watchdog.is_running, cs_watchdog.watchdog); return Ok(()); } - + let cur_watchdog = cs_watchdog.watchdog.as_ref().unwrap().clone(); + let cur_wd_data = cur_watchdog.as_ref().clocksource_data(); + let cur_wd_nowclock = cur_watchdog.as_ref().read().data(); + + let wd_last = cs_watchdog.last_check.data(); + let wd_dev_nsec = clocksource_cyc2ns( + CycleNum((cur_wd_nowclock - wd_last) & cur_wd_data.mask.bits), + cur_wd_data.mult, + cur_wd_data.shift, + ); + cs_watchdog.last_check = CycleNum(cur_wd_nowclock); drop(cs_watchdog); let watchdog_list = WATCHDOG_LIST.lock_irqsave(); for cs in watchdog_list.iter() { @@ -815,71 +797,47 @@ pub fn clocksource_watchdog() -> Result<(), SystemError> { .flags .contains(ClocksourceFlags::CLOCK_SOURCE_UNSTABLE) { - // debug!("clocksource_watchdog unstable"); + // kdebug!("clocksource_watchdog unstable"); // 启动watchdog_kthread - if FINISHED_BOOTING.load(Ordering::Relaxed) { - // TODO 在实现了工作队列后,将启动线程换成schedule work - run_watchdog_kthread(); - } + run_watchdog_kthread(); continue; } - // 读取时钟源现在的时间 let cs_now_clock = cs.read(); - // 读取watchdog现在的时间 - let wd = CLOCKSOURCE_WATCHDOG.lock_irqsave(); - let wd_now = wd.watchdog.as_ref().unwrap().clone(); - let wd_now_data = wd_now.as_ref().clocksource_data(); - let wd_now_clock = wd_now.as_ref().read().data(); - - // info!("cs_name = {:?}", cs_data.name); - // info!("cs_last = {:?}", cs_data.cs_last); - // info!("cs_now_clock = {:?}", cs_now_clock); - // info!("wd_name"); - // info!("wd_last = {:?}", cs_data.watchdog_last); - // info!("wd_now_clock = {:?}", wd_now_clock); // 如果时钟源没有被监视,则开始监视他 if !cs_data .flags .contains(ClocksourceFlags::CLOCK_SOURCE_WATCHDOG) { - // debug!("clocksource_watchdog start watch"); + // kdebug!("clocksource_watchdog start watch"); cs_data .flags .insert(ClocksourceFlags::CLOCK_SOURCE_WATCHDOG); // 记录此次检查的时刻 - cs_data.watchdog_last = CycleNum::new(wd_now_clock); - cs_data.cs_last = cs_now_clock; + cs_data.watchdog_last = cs_now_clock; cs.update_clocksource_data(cs_data.clone())?; continue; } - - let wd_dev_nsec = clocksource_cyc2ns( - CycleNum((wd_now_clock - cs_data.watchdog_last.data()) & wd_now_data.mask.bits), - wd_now_data.mult, - wd_now_data.shift, - ); - + // kdebug!("cs_data.watchdog_last = {:?},cs_now_clock = {:?}", cs_data.watchdog_last, cs_now_clock); + // 计算时钟源的误差 let cs_dev_nsec = clocksource_cyc2ns( - CycleNum(cs_now_clock.div(cs_data.cs_last).data() & cs_data.mask.bits), - cs_data.mult, // 2343484437 - cs_data.shift, // 23 + CycleNum(cs_now_clock.div(cs_data.watchdog_last).data() & cs_data.mask.bits), + cs_data.mult, + cs_data.shift, ); // 记录此次检查的时刻 - cs_data.watchdog_last = CycleNum::new(wd_now_clock); - cs_data.cs_last = cs_now_clock; + cs_data.watchdog_last = cs_now_clock; cs.update_clocksource_data(cs_data.clone())?; - - // 判断是否有误差 if cs_dev_nsec.abs_diff(wd_dev_nsec) > WATCHDOG_THRESHOLD.into() { - // debug!("set_unstable"); + // kdebug!("set_unstable"); // 误差过大,标记为unstable - info!("cs_dev_nsec = {}", cs_dev_nsec); - info!("wd_dev_nsec = {}", wd_dev_nsec); - cs.set_unstable(cs_dev_nsec.abs_diff(wd_dev_nsec).try_into().unwrap())?; + kinfo!("cs_dev_nsec = {}", cs_dev_nsec); + kinfo!("wd_dev_nsec = {}", wd_dev_nsec); + cs.set_unstable((cs_dev_nsec - wd_dev_nsec).try_into().unwrap())?; continue; } + // kdebug!("clocksource_watchdog aaa"); // 判断是否要切换为高精度模式 if !cs_data @@ -888,7 +846,7 @@ pub fn clocksource_watchdog() -> Result<(), SystemError> { && cs_data .flags .contains(ClocksourceFlags::CLOCK_SOURCE_IS_CONTINUOUS) - && wd_now_data + && cur_wd_data .flags .contains(ClocksourceFlags::CLOCK_SOURCE_IS_CONTINUOUS) { @@ -904,7 +862,7 @@ pub fn clocksource_watchdog() -> Result<(), SystemError> { } fn create_new_watchdog_timer_function() { - let mut cs_watchdog = CLOCKSOURCE_WATCHDOG.lock_irqsave(); + let mut cs_watchdog = CLOCKSOUCE_WATCHDOG.lock_irqsave(); cs_watchdog.timer_expires += WATCHDOG_INTERVAL; //创建定时器执行watchdog @@ -933,13 +891,25 @@ fn __clocksource_watchdog_kthread() { } // 检查是否需要停止watchdog - CLOCKSOURCE_WATCHDOG + CLOCKSOUCE_WATCHDOG .lock_irqsave() .clocksource_stop_watchdog(wd_list.len()); drop(wd_list); // 将不稳定的时钟源精度都设置为最低,然后删除unstable标记 for clock in del_clocks.iter() { clock.clocksource_change_rating(0); + let mut data = clock.clocksource_data(); + data.watchdog_last = clock.read(); + kdebug!("kthread: watchdog_last = {:?}", data.watchdog_last); + data.flags.remove(ClocksourceFlags::CLOCK_SOURCE_UNSTABLE); + clock + .update_clocksource_data(data) + .expect("clocksource_watchdog_kthread: failed to update clocksource data"); + + // 重新插入监视链表 + clock + .clocksource_enqueue_watchdog() + .expect("clocksource_watchdog_kthread: failed to enqueue watchdog list"); } } @@ -947,7 +917,7 @@ fn __clocksource_watchdog_kthread() { pub fn clocksource_watchdog_kthread() -> i32 { // return 0; loop { - // debug!("clocksource_watchdog_kthread start"); + // kdebug!("clocksource_watchdog_kthread start"); __clocksource_watchdog_kthread(); if KernelThreadMechanism::should_stop(&ProcessManager::current_pcb()) { break; @@ -978,7 +948,7 @@ pub fn clocksource_resume_watchdog() { /// # 根据精度选择最优的时钟源,或者接受用户指定的时间源 pub fn clocksource_select() { let list_guard = CLOCKSOURCE_LIST.lock(); - if !FINISHED_BOOTING.load(Ordering::Relaxed) || list_guard.is_empty() { + if unsafe { FINISHED_BOOTING.load(Ordering::Relaxed) } || list_guard.is_empty() { return; } let mut best = list_guard.front().unwrap().clone(); @@ -999,26 +969,26 @@ pub fn clocksource_select() { let cur_clocksource = CUR_CLOCKSOURCE.lock().as_ref().unwrap().clone(); let best_name = &best.clocksource_data().name; if cur_clocksource.clocksource_data().name.ne(best_name) { - info!("Switching to the clocksource {:?}\n", best_name); + kinfo!("Switching to the clocksource {:?}\n", best_name); drop(cur_clocksource); - CUR_CLOCKSOURCE.lock().replace(best.clone()); + CUR_CLOCKSOURCE.lock().replace(best); // TODO 通知timerkeeping 切换了时间源 } } else { // 当前时钟源为空 - CUR_CLOCKSOURCE.lock().replace(best.clone()); + CUR_CLOCKSOURCE.lock().replace(best); } - debug!("clocksource_select finish, CUR_CLOCKSOURCE = {best:?}"); + kdebug!(" clocksource_select finish"); } /// # clocksource模块加载完成 pub fn clocksource_boot_finish() { let mut cur_clocksource = CUR_CLOCKSOURCE.lock(); cur_clocksource.replace(clocksource_default_clock()); - FINISHED_BOOTING.store(true, Ordering::Relaxed); + unsafe { FINISHED_BOOTING.store(true, Ordering::Relaxed) }; // 清除不稳定的时钟源 __clocksource_watchdog_kthread(); - debug!("clocksource_boot_finish"); + kdebug!("clocksource_boot_finish"); } fn run_watchdog_kthread() { diff --git a/kernel/src/time/jiffies.rs b/kernel/src/time/jiffies.rs index 4025295f1..e01451fa4 100644 --- a/kernel/src/time/jiffies.rs +++ b/kernel/src/time/jiffies.rs @@ -2,10 +2,9 @@ use alloc::{ string::ToString, sync::{Arc, Weak}, }; -use log::{error, info}; use system_error::SystemError; -use crate::{arch::time::CLOCK_TICK_RATE, libs::spinlock::SpinLock}; +use crate::{arch::time::CLOCK_TICK_RATE, kerror, kinfo, libs::spinlock::SpinLock}; use super::{ clocksource::{Clocksource, ClocksourceData, ClocksourceFlags, ClocksourceMask, CycleNum, HZ}, @@ -48,20 +47,16 @@ impl Clocksource for ClocksourceJiffies { fn clocksource(&self) -> Arc { self.0.lock_irqsave().self_ref.upgrade().unwrap() } - fn update_clocksource_data(&self, data: ClocksourceData) -> Result<(), SystemError> { + fn update_clocksource_data(&self, _data: ClocksourceData) -> Result<(), SystemError> { let d = &mut self.0.lock_irqsave().data; - d.set_name(data.name); - d.set_rating(data.rating); - d.set_mask(data.mask); - d.set_mult(data.mult); - d.set_shift(data.shift); - d.set_max_idle_ns(data.max_idle_ns); - d.set_flags(data.flags); - d.watchdog_last = data.watchdog_last; - d.cs_last = data.cs_last; - d.set_uncertainty_margin(data.uncertainty_margin); - d.set_maxadj(data.maxadj); - d.cycle_last = data.cycle_last; + d.set_flags(_data.flags); + d.set_mask(_data.mask); + d.set_max_idle_ns(_data.max_idle_ns); + d.set_mult(_data.mult); + d.set_name(_data.name); + d.set_rating(_data.rating); + d.set_shift(_data.shift); + d.watchdog_last = _data.watchdog_last; return Ok(()); } @@ -80,10 +75,8 @@ impl ClocksourceJiffies { max_idle_ns: Default::default(), flags: ClocksourceFlags::new(0), watchdog_last: CycleNum::new(0), - cs_last: CycleNum::new(0), uncertainty_margin: 0, maxadj: 0, - cycle_last: CycleNum::new(0), }; let jiffies = Arc::new(ClocksourceJiffies(SpinLock::new(InnerJiffies { data, @@ -103,10 +96,10 @@ pub fn jiffies_init() { let jiffies = clocksource_default_clock() as Arc; match jiffies.register(1, 0) { Ok(_) => { - info!("jiffies_init sccessfully"); + kinfo!("jiffies_init sccessfully"); } Err(_) => { - error!("jiffies_init failed, no default clock running"); + kerror!("jiffies_init failed, no default clock running"); } }; } diff --git a/kernel/src/time/mod.rs b/kernel/src/time/mod.rs index 9a0c829f5..d42a6d4af 100644 --- a/kernel/src/time/mod.rs +++ b/kernel/src/time/mod.rs @@ -5,7 +5,6 @@ use core::{ }; use crate::arch::CurrentTimeArch; -use crate::time::syscall::PosixTimeval; use self::timekeeping::getnstimeofday; @@ -13,7 +12,6 @@ pub mod clocksource; pub mod jiffies; pub mod sleep; pub mod syscall; -pub mod tick_common; pub mod timeconv; pub mod timekeep; pub mod timekeeping; @@ -116,15 +114,6 @@ impl From for PosixTimeSpec { } } -impl From for PosixTimeSpec { - fn from(value: PosixTimeval) -> Self { - PosixTimeSpec { - tv_sec: value.tv_sec, - tv_nsec: value.tv_usec as i64 * 1000, - } - } -} - impl From for Duration { fn from(val: PosixTimeSpec) -> Self { Duration::from_micros(val.tv_sec as u64 * 1000000 + val.tv_nsec as u64 / 1000) @@ -141,6 +130,7 @@ impl From for Duration { /// * A value less than `0` indicates a time before the starting /// point. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Instant { micros: i64, } @@ -316,6 +306,7 @@ impl ops::Sub for Instant { /// A relative amount of time. #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Duration { micros: u64, } diff --git a/kernel/src/time/syscall.rs b/kernel/src/time/syscall.rs index 76f9349eb..77fd13348 100644 --- a/kernel/src/time/syscall.rs +++ b/kernel/src/time/syscall.rs @@ -3,7 +3,6 @@ use core::{ time::Duration, }; -use log::warn; use num_traits::FromPrimitive; use system_error::SystemError; @@ -139,7 +138,7 @@ impl Syscall { pub fn clock_gettime(clock_id: c_int, tp: *mut PosixTimeSpec) -> Result { let clock_id = PosixClockID::try_from(clock_id)?; if clock_id != PosixClockID::Realtime { - warn!("clock_gettime: currently only support Realtime clock, but got {:?}. Defaultly return realtime!!!\n", clock_id); + kwarn!("clock_gettime: currently only support Realtime clock, but got {:?}. Defaultly return realtime!!!\n", clock_id); } if tp.is_null() { return Err(SystemError::EFAULT); diff --git a/kernel/src/time/tick_common.rs b/kernel/src/time/tick_common.rs deleted file mode 100644 index 24e89c682..000000000 --- a/kernel/src/time/tick_common.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::{ - arch::interrupt::TrapFrame, - process::ProcessManager, - smp::{core::smp_get_processor_id, cpu::ProcessorId}, - time::timer::run_local_timer, -}; - -use super::timer::update_timer_jiffies; - -/// # 函数的功能 -/// 用于周期滴答的事件处理 -pub fn tick_handle_periodic(trap_frame: &TrapFrame) { - let cpu_id = smp_get_processor_id(); - - tick_periodic(cpu_id, trap_frame); -} - -fn tick_periodic(cpu_id: ProcessorId, trap_frame: &TrapFrame) { - if cpu_id.data() == 0 { - update_timer_jiffies(1); - run_local_timer(); - } - - ProcessManager::update_process_times(trap_frame.is_from_user()); -} diff --git a/kernel/src/time/timekeep.rs b/kernel/src/time/timekeep.rs index b003b2075..0d81d6fa2 100644 --- a/kernel/src/time/timekeep.rs +++ b/kernel/src/time/timekeep.rs @@ -1,16 +1,10 @@ #![allow(dead_code)] -use core::intrinsics::unlikely; - use system_error::SystemError; use crate::driver::rtc::interface::rtc_read_time_default; -use super::{PosixTimeSpec, NSEC_PER_SEC}; - -// 参考:https://code.dragonos.org.cn/xref/linux-3.4.99/include/linux/time.h#110 -const KTIME_MAX: i64 = !(1u64 << 63) as i64; -const KTIME_SEC_MAX: i64 = KTIME_MAX / NSEC_PER_SEC as i64; +use super::PosixTimeSpec; #[allow(non_camel_case_types)] pub type ktime_t = i64; @@ -37,25 +31,3 @@ pub fn ktime_get_real_ns() -> i64 { let kt: ktime_t = ktime_get_real().unwrap_or(0); return ktime_to_ns(kt); } - -// # 用于将两个ktime_t类型的变量相加 -// #[inline(always)] -// pub(super) fn ktime_add(add1: ktime_t, add2: ktime_t) -> ktime_t { -// let res = add1 + add2; -// } - -/// # 通过sec和nsec构造一个ktime_t -#[inline(always)] -fn ktime_set(secs: i64, nsecs: u64) -> ktime_t { - if unlikely(secs >= KTIME_SEC_MAX) { - return KTIME_MAX; - } - - return secs * NSEC_PER_SEC as i64 + nsecs as i64; -} - -/// # 将PosixTimeSpec转换成ktime_t -#[inline(always)] -pub fn timespec_to_ktime(ts: PosixTimeSpec) -> ktime_t { - return ktime_set(ts.tv_sec, ts.tv_nsec as u64); -} diff --git a/kernel/src/time/timekeeping.rs b/kernel/src/time/timekeeping.rs index 09bcf5ccb..136805751 100644 --- a/kernel/src/time/timekeeping.rs +++ b/kernel/src/time/timekeeping.rs @@ -1,13 +1,12 @@ use alloc::sync::Arc; -use core::intrinsics::{likely, unlikely}; -use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; -use log::{debug, info, warn}; +use core::sync::atomic::{compiler_fence, AtomicBool, AtomicI64, AtomicUsize, Ordering}; use system_error::SystemError; use crate::{ - arch::CurrentIrqArch, + arch::{CurrentIrqArch, CurrentTimeArch}, exception::InterruptArch, - libs::rwlock::RwLock, + kdebug, kinfo, + libs::rwlock::{RwLock, RwLockReadGuard}, time::{ jiffies::{clocksource_default_clock, jiffies_init}, timekeep::ktime_get_real_ns, @@ -15,11 +14,10 @@ use crate::{ }, }; -use super::timekeep::{ktime_t, timespec_to_ktime}; use super::{ clocksource::{clocksource_cyc2ns, Clocksource, CycleNum, HZ}, syscall::PosixTimeval, - NSEC_PER_SEC, + TimeArch, NSEC_PER_SEC, }; /// NTP周期频率 pub const NTP_INTERVAL_FREQ: u64 = HZ; @@ -30,12 +28,17 @@ pub const NTP_SCALE_SHIFT: u32 = 32; /// timekeeping休眠标志,false为未休眠 pub static TIMEKEEPING_SUSPENDED: AtomicBool = AtomicBool::new(false); +/// 已经递增的微秒数 +static __ADDED_USEC: AtomicI64 = AtomicI64::new(0); /// timekeeper全局变量,用于管理timekeeper模块 static mut __TIMEKEEPER: Option = None; #[derive(Debug)] pub struct Timekeeper { inner: RwLock, + + /// 上一次更新墙上时间时的CPU周期数 + last_update_cpu_cycle: AtomicUsize, } #[allow(dead_code)] @@ -49,6 +52,7 @@ pub struct TimekeeperData { cycle_interval: CycleNum, /// 一个NTP间隔中时钟移位的纳秒数。 xtime_interval: u64, + /// xtime_remainder: i64, /// 每个NTP间隔累积的原始纳米秒 raw_interval: i64, @@ -64,8 +68,6 @@ pub struct TimekeeperData { wall_to_monotonic: PosixTimeSpec, total_sleep_time: PosixTimeSpec, xtime: PosixTimeSpec, - /// 单调时间和实时时间的偏移量 - real_time_offset: ktime_t, } impl TimekeeperData { pub fn new() -> Self { @@ -96,7 +98,6 @@ impl TimekeeperData { tv_nsec: 0, tv_sec: 0, }, - real_time_offset: 0, } } } @@ -104,6 +105,7 @@ impl Timekeeper { fn new() -> Self { Self { inner: RwLock::new(TimekeeperData::new()), + last_update_cpu_cycle: AtomicUsize::new(0), } } @@ -116,9 +118,9 @@ impl Timekeeper { let mut timekeeper = self.inner.write_irqsave(); // 更新clock let mut clock_data = clock.clocksource_data(); - clock_data.cycle_last = clock.read(); + clock_data.watchdog_last = clock.read(); if clock.update_clocksource_data(clock_data).is_err() { - debug!("timekeeper_setup_internals:update_clocksource_data run failed"); + kdebug!("timekeeper_setup_internals:update_clocksource_data run failed"); } timekeeper.clock.replace(clock.clone()); @@ -142,150 +144,35 @@ impl Timekeeper { timekeeper.mult = clock_data.mult; } - pub fn timekeeping_get_ns(&self) -> i64 { - let timekeeper = self.inner.read_irqsave(); + /// # 获取当前时钟源距离上次watchdog检测走过的纳秒数 + #[allow(dead_code)] + pub fn tk_get_ns(&self) -> u64 { + let timekeeper: RwLockReadGuard<'_, TimekeeperData> = self.inner.read_irqsave(); let clock = timekeeper.clock.clone().unwrap(); + drop(timekeeper); - let cycle_now = clock.read(); + let clock_now = clock.read(); let clock_data = clock.clocksource_data(); - let cycle_delta = (cycle_now.div(clock_data.cycle_last)).data() & clock_data.mask.bits(); + let clock_delta = clock_now.div(clock_data.watchdog_last).data() & clock_data.mask.bits(); return clocksource_cyc2ns( - CycleNum::new(cycle_delta), - timekeeper.mult, - timekeeper.shift as u32, - ) as i64; + CycleNum::new(clock_delta), + clock_data.mult, + clock_data.shift, + ); } - /// # 处理大幅度调整 - pub fn timekeeping_bigadjust(&self, error: i64, interval: i64, offset: i64) -> (i64, i64, i32) { - let mut error = error; - let mut interval = interval; - let mut offset = offset; - - // TODO: 计算look_head并调整ntp误差 - - let tmp = interval; - let mut mult = 1; - let mut adj = 0; - if error < 0 { - error = -error; - interval = -interval; - offset = -offset; - mult = -1; - } - while error > tmp { - adj += 1; - error >>= 1; - } - - interval <<= adj; - offset <<= adj; - mult <<= adj; - - return (interval, offset, mult); + #[inline] + fn do_read_cpu_cycle_ns(&self) -> usize { + CurrentTimeArch::cycles2ns( + CurrentTimeArch::get_cycles() + .wrapping_sub(self.last_update_cpu_cycle.load(Ordering::SeqCst)), + ) } - /// # 调整时钟的mult减少ntp_error - pub fn timekeeping_adjust(&self, offset: i64) -> i64 { - let mut timekeeper = self.inner.write_irqsave(); - let mut interval = timekeeper.cycle_interval.data() as i64; - let mut offset = offset; - let adj: i32; - - // 计算误差 - let mut error = timekeeper.ntp_error >> (timekeeper.ntp_error_shift - 1); - - // 误差超过一个interval,就要进行调整 - if error >= 0 { - if error > interval { - error >>= 2; - if likely(error <= interval) { - adj = 1; - } else { - (interval, offset, adj) = self.timekeeping_bigadjust(error, interval, offset); - } - } else { - // 不需要校准 - return offset; - } - } else if -error > interval { - if likely(-error <= interval) { - adj = -1; - interval = -interval; - offset = -offset; - } else { - (interval, offset, adj) = self.timekeeping_bigadjust(error, interval, offset); - } - } else { - // 不需要校准 - return offset; - } - - // 检查最大调整值,确保调整值不会超过时钟源允许的最大值 - let clock_data = timekeeper.clock.clone().unwrap().clocksource_data(); - if unlikely( - clock_data.maxadj != 0 - && (timekeeper.mult as i32 + adj - > clock_data.mult as i32 + clock_data.maxadj as i32), - ) { - warn!( - "Adjusting {:?} more than ({} vs {})", - clock_data.name, - timekeeper.mult as i32 + adj, - clock_data.mult as i32 + clock_data.maxadj as i32 - ); - } - - if error > 0 { - timekeeper.mult += adj as u32; - timekeeper.xtime_interval += interval as u64; - timekeeper.xtime_nsec -= offset as u64; - } else { - timekeeper.mult -= adj as u32; - timekeeper.xtime_interval -= interval as u64; - timekeeper.xtime_nsec += offset as u64; - } - timekeeper.ntp_error -= (interval - offset) << timekeeper.ntp_error_shift; - - return offset; - } - /// # 用于累积时间间隔,并将其转换为纳秒时间 - pub fn logarithmic_accumulation(&self, offset: u64, shift: i32) -> u64 { - let mut timekeeper = self.inner.write_irqsave(); - let clock = timekeeper.clock.clone().unwrap(); - let clock_data = clock.clocksource_data(); - let nsecps = (NSEC_PER_SEC as u64) << timekeeper.shift; - let mut offset = offset; - - // 检查offset是否小于一个NTP周期间隔 - if offset < timekeeper.cycle_interval.data() << shift { - return offset; - } - - // 累积一个移位的interval - offset -= timekeeper.cycle_interval.data() << shift; - clock_data - .cycle_last - .add(CycleNum::new(timekeeper.cycle_interval.data() << shift)); - if clock.update_clocksource_data(clock_data).is_err() { - debug!("logarithmic_accumulation:update_clocksource_data run failed"); - } - timekeeper.clock.replace(clock.clone()); - - // 更新xime_nsec - timekeeper.xtime_nsec += timekeeper.xtime_interval << shift; - while timekeeper.xtime_nsec >= nsecps { - timekeeper.xtime_nsec -= nsecps; - timekeeper.xtime.tv_sec += 1; - // TODO: 处理闰秒 - } - - // TODO:更新raw_time - - // TODO:计算ntp_error - - return offset; + fn mark_update_wall_time_ok(&self) { + self.last_update_cpu_cycle + .store(CurrentTimeArch::get_cycles(), Ordering::SeqCst); } } @@ -306,7 +193,7 @@ pub fn timekeeper_init() { /// /// * 'TimeSpec' - 时间戳 pub fn getnstimeofday() -> PosixTimeSpec { - // debug!("enter getnstimeofday"); + // kdebug!("enter getnstimeofday"); let nsecs; let mut xtime: PosixTimeSpec; @@ -316,18 +203,22 @@ pub fn getnstimeofday() -> PosixTimeSpec { Some(tk) => { xtime = tk.xtime; drop(tk); + // 提供基于cpu周期数的ns时间,以便在两次update_wall_time之间提供更好的精度 + let cpu_delta_ns = timekeeper().do_read_cpu_cycle_ns() as u64; - nsecs = timekeeper().timekeeping_get_ns(); + // 尚未同步到xtime的时间 + let tmp_delta_ns = __ADDED_USEC.load(Ordering::SeqCst) as u64 * 1000; + nsecs = cpu_delta_ns + tmp_delta_ns; // TODO 不同架构可能需要加上不同的偏移量 break; } } } - xtime.tv_nsec += nsecs; + xtime.tv_nsec += nsecs as i64; xtime.tv_sec += xtime.tv_nsec / NSEC_PER_SEC as i64; xtime.tv_nsec %= NSEC_PER_SEC as i64; - // debug!("getnstimeofday: xtime = {:?}, nsecs = {:}", xtime, nsecs); + // kdebug!("getnstimeofday: xtime = {:?}, nsecs = {:}", xtime, nsecs); // TODO 将xtime和当前时间源的时间相加 @@ -357,7 +248,7 @@ pub fn do_settimeofday64(time: PosixTimeSpec) -> Result<(), SystemError> { /// # 初始化timekeeping模块 #[inline(never)] pub fn timekeeping_init() { - info!("Initializing timekeeping module..."); + kinfo!("Initializing timekeeping module..."); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; timekeeper_init(); @@ -378,16 +269,17 @@ pub fn timekeeping_init() { timekeeper.wall_to_monotonic.tv_sec, ) = (-timekeeper.xtime.tv_nsec, -timekeeper.xtime.tv_sec); + __ADDED_USEC.store(0, Ordering::SeqCst); + drop(irq_guard); drop(timekeeper); jiffies_init(); - info!("timekeeping_init successfully"); + kinfo!("timekeeping_init successfully"); } /// # 使用当前时钟源增加wall time -/// 参考:https://code.dragonos.org.cn/xref/linux-3.4.99/kernel/time/timekeeping.c#1041 -pub fn update_wall_time() { - // debug!("enter update_wall_time, stack_use = {:}",stack_use); +pub fn update_wall_time(delta_us: i64) { + // kdebug!("enter update_wall_time, stack_use = {:}",stack_use); compiler_fence(Ordering::SeqCst); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; // 如果在休眠那就不更新 @@ -395,74 +287,60 @@ pub fn update_wall_time() { return; } - let mut tk = timekeeper().inner.write_irqsave(); - // 获取当前时钟源 - let clock = tk.clock.clone().unwrap(); - let clock_data = clock.clocksource_data(); - // 计算从上一次更新周期以来经过的时钟周期数 - let mut offset = (clock.read().div(clock_data.cycle_last).data()) & clock_data.mask.bits(); - // 检查offset是否达到了一个NTP周期间隔 - if offset < tk.cycle_interval.data() { - return; - } - - // 将纳秒部分转换为更高精度的格式 - tk.xtime_nsec = (tk.xtime.tv_nsec as u64) << tk.shift; - - let mut shift = (offset.ilog2() - tk.cycle_interval.data().ilog2()) as i32; - shift = shift.max(0); - // let max_shift = (64 - (ntp_tick_length().ilog2()+1)) - 1; - // shift = min(shift, max_shift) - while offset >= tk.cycle_interval.data() { - offset = timekeeper().logarithmic_accumulation(offset, shift); - if offset < tk.cycle_interval.data() << shift { - shift -= 1; - } - } - - timekeeper().timekeeping_adjust(offset as i64); + // ===== 请不要删除这些注释 ===== + // let clock = timekeeper.clock.clone().unwrap(); + // let clock_data = clock.clocksource_data(); + // let offset = (clock.read().div(clock_data.watchdog_last).data()) & clock_data.mask.bits(); + + // timekeeper.xtime_nsec = (timekeeper.xtime.tv_nsec as u64) << timekeeper.shift; + // // TODO 当有ntp模块之后 需要将timekeep与ntp进行同步并检查 + // timekeeper.xtime.tv_nsec = ((timekeeper.xtime_nsec as i64) >> timekeeper.shift) + 1; + // timekeeper.xtime_nsec -= (timekeeper.xtime.tv_nsec as u64) << timekeeper.shift; + + // timekeeper.xtime.tv_nsec += offset as i64; + // while unlikely(timekeeper.xtime.tv_nsec >= NSEC_PER_SEC.into()) { + // timekeeper.xtime.tv_nsec -= NSEC_PER_SEC as i64; + // timekeeper.xtime.tv_sec += 1; + // // TODO 需要处理闰秒 + // } + // ================ + compiler_fence(Ordering::SeqCst); - // 处理xtime_nsec下溢问题,并对NTP误差进行调整 - if unlikely((tk.xtime_nsec as i64) < 0) { - let neg = -(tk.xtime_nsec as i64); - tk.xtime_nsec = 0; - tk.ntp_error += neg << tk.ntp_error_shift; - } + __ADDED_USEC.fetch_add(delta_us, Ordering::SeqCst); + compiler_fence(Ordering::SeqCst); + let mut retry = 10; - // 将纳秒部分舍入后存储在xtime.tv_nsec中 - tk.xtime.tv_nsec = ((tk.xtime_nsec as i64) >> tk.shift) + 1; - tk.xtime_nsec -= (tk.xtime.tv_nsec as u64) << tk.shift; + let usec = __ADDED_USEC.load(Ordering::SeqCst); - // 确保经过舍入后的xtime.tv_nsec不会大于NSEC_PER_SEC,并在超过1秒的情况下进行适当的调整 - if unlikely(tk.xtime.tv_nsec >= NSEC_PER_SEC.into()) { - tk.xtime.tv_nsec -= NSEC_PER_SEC as i64; - tk.xtime.tv_sec += 1; - // TODO: 处理闰秒 + // 一分钟同步一次 + loop { + if (usec & !((1 << 26) - 1)) != 0 { + if __ADDED_USEC + .compare_exchange(usec, 0, Ordering::SeqCst, Ordering::SeqCst) + .is_ok() + || retry == 0 + { + // 同步时间 + // 我感觉这里会出问题:多个读者不退出的话,写者就无法写入 + // 然后这里会超时,导致在中断返回之后,会不断的进入这个中断,最终爆栈。 + let mut timekeeper = timekeeper().inner.write_irqsave(); + timekeeper.xtime.tv_nsec = ktime_get_real_ns(); + timekeeper.xtime.tv_sec = 0; + __ADDED_USEC.store(0, Ordering::SeqCst); + + drop(timekeeper); + break; + } + retry -= 1; + } else { + break; + } } - - // 更新时间的相关信息 - timekeeping_update(); - + timekeeper().mark_update_wall_time_ok(); + // TODO 需要检查是否更新时间源 compiler_fence(Ordering::SeqCst); drop(irq_guard); compiler_fence(Ordering::SeqCst); } +// TODO timekeeping_adjust // TODO wall_to_monotic - -/// 参考:https://code.dragonos.org.cn/xref/linux-3.4.99/kernel/time/timekeeping.c#190 -pub fn timekeeping_update() { - // TODO:如果clearntp为true,则会清除NTP错误并调用ntp_clear() - - // 更新实时时钟偏移量,用于跟踪硬件时钟与系统时间的差异,以便进行时间校正 - update_rt_offset(); -} - -/// # 更新实时偏移量(墙上之间与单调时间的差值) -pub fn update_rt_offset() { - let mut timekeeper = timekeeper().inner.write_irqsave(); - let ts = PosixTimeSpec::new( - -timekeeper.wall_to_monotonic.tv_sec, - -timekeeper.wall_to_monotonic.tv_nsec, - ); - timekeeper.real_time_offset = timespec_to_ktime(ts); -} diff --git a/kernel/src/time/timer.rs b/kernel/src/time/timer.rs index 917502ca6..1dce3bb27 100644 --- a/kernel/src/time/timer.rs +++ b/kernel/src/time/timer.rs @@ -10,7 +10,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::{error, info, warn}; use system_error::SystemError; use crate::{ @@ -19,6 +18,7 @@ use crate::{ softirq::{softirq_vectors, SoftirqNumber, SoftirqVec}, InterruptArch, }, + kerror, kinfo, libs::spinlock::{SpinLock, SpinLockGuard}, process::{ProcessControlBlock, ProcessManager}, sched::{schedule, SchedMode}, @@ -160,7 +160,7 @@ impl Timer { let mut split_pos: usize = 0; for (pos, elt) in timer_list.iter().enumerate() { if Arc::ptr_eq(&self_arc, &elt.1) { - warn!("Timer already in list"); + kwarn!("Timer already in list"); } if elt.0 > expire_jiffies { split_pos = pos; @@ -180,7 +180,7 @@ impl Timer { drop(timer); let r = func.map(|mut f| f.run()).unwrap_or(Ok(())); if unlikely(r.is_err()) { - error!( + kerror!( "Failed to run timer function: {self:?} {:?}", r.as_ref().err().unwrap() ); @@ -246,7 +246,7 @@ impl SoftirqVec for DoTimerSoftirq { } // 最多只处理TIMER_RUN_CYCLE_THRESHOLD个计时器 for _ in 0..TIMER_RUN_CYCLE_THRESHOLD { - // debug!("DoTimerSoftirq run"); + // kdebug!("DoTimerSoftirq run"); let timer_list = TIMER_LIST.try_lock_irqsave(); if timer_list.is_err() { continue; @@ -258,7 +258,7 @@ impl SoftirqVec for DoTimerSoftirq { } let (front_jiffies, timer_list_front) = timer_list.first().unwrap().clone(); - // debug!("to lock timer_list_front"); + // kdebug!("to lock timer_list_front"); if front_jiffies >= TIMER_JIFFIES.load(Ordering::SeqCst) { break; @@ -280,7 +280,7 @@ pub fn timer_init() { softirq_vectors() .register_softirq(SoftirqNumber::TIMER, do_timer_softirq) .expect("Failed to register timer softirq"); - info!("timer initialized successfully"); + kinfo!("timer initialized successfully"); } /// 计算接下来n毫秒对应的定时器时间片 @@ -300,7 +300,7 @@ pub fn next_n_us_timer_jiffies(expire_us: u64) -> u64 { /// /// @return Err(SystemError) 错误码 pub fn schedule_timeout(mut timeout: i64) -> Result { - // debug!("schedule_timeout"); + // kdebug!("schedule_timeout"); if timeout == MAX_TIMEOUT { let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_sleep(true).ok(); @@ -308,7 +308,7 @@ pub fn schedule_timeout(mut timeout: i64) -> Result { schedule(SchedMode::SM_NONE); return Ok(MAX_TIMEOUT); } else if timeout < 0 { - error!("timeout can't less than 0"); + kerror!("timeout can't less than 0"); return Err(SystemError::EINVAL); } else { // 禁用中断,防止在这段期间发生调度,造成死锁 @@ -337,16 +337,16 @@ pub fn schedule_timeout(mut timeout: i64) -> Result { pub fn timer_get_first_expire() -> Result { // FIXME - // debug!("rs_timer_get_first_expire,timer_jif = {:?}", TIMER_JIFFIES); + // kdebug!("rs_timer_get_first_expire,timer_jif = {:?}", TIMER_JIFFIES); for _ in 0..10 { match TIMER_LIST.try_lock_irqsave() { Ok(timer_list) => { - // debug!("rs_timer_get_first_expire TIMER_LIST lock successfully"); + // kdebug!("rs_timer_get_first_expire TIMER_LIST lock successfully"); if timer_list.is_empty() { - // debug!("timer_list is empty"); + // kdebug!("timer_list is empty"); return Ok(0); } else { - // debug!("timer_list not empty"); + // kdebug!("timer_list not empty"); return Ok(timer_list.first().unwrap().0); } } @@ -366,17 +366,11 @@ pub fn try_raise_timer_softirq() { } } -/// 处理本地定时器中断 -pub fn run_local_timer() { - assert!(!CurrentIrqArch::is_irq_enabled()); - try_raise_timer_softirq(); -} - /// 更新系统时间片 -pub fn update_timer_jiffies(add_jiffies: u64) -> u64 { +pub fn update_timer_jiffies(add_jiffies: u64, time_us: i64) -> u64 { let prev = TIMER_JIFFIES.fetch_add(add_jiffies, Ordering::SeqCst); compiler_fence(Ordering::SeqCst); - update_wall_time(); + update_wall_time(time_us); compiler_fence(Ordering::SeqCst); return prev + add_jiffies; diff --git a/kernel/src/virt/kvm/host_mem.rs b/kernel/src/virt/kvm/host_mem.rs index 95291b146..75780f0a5 100644 --- a/kernel/src/virt/kvm/host_mem.rs +++ b/kernel/src/virt/kvm/host_mem.rs @@ -1,8 +1,10 @@ -use log::debug; use system_error::SystemError; use super::{vcpu::Vcpu, vm}; -use crate::mm::{kernel_mapper::KernelMapper, page::EntryFlags, VirtAddr}; +use crate::{ + kdebug, + mm::{kernel_mapper::KernelMapper, page::PageFlags, VirtAddr}, +}; /* * Address types: @@ -85,7 +87,7 @@ pub fn kvm_vcpu_memslots(_vcpu: &mut dyn Vcpu) -> KvmMemorySlots { } fn __gfn_to_memslot(slots: KvmMemorySlots, gfn: u64) -> Option { - debug!("__gfn_to_memslot"); + kdebug!("__gfn_to_memslot"); // TODO: 使用二分查找的方式优化 for i in 0..slots.used_slots { let memslot = slots.memslots[i as usize]; @@ -105,7 +107,7 @@ fn __gfn_to_hva_many( nr_pages: Option<&mut u64>, write: bool, ) -> Result { - debug!("__gfn_to_hva_many"); + kdebug!("__gfn_to_hva_many"); if slot.is_none() { return Err(SystemError::KVM_HVA_ERR_BAD); } @@ -139,10 +141,10 @@ fn __gfn_to_hva_many( // host端虚拟地址到物理地址的转换,有两种方式,hva_to_pfn_fast、hva_to_pfn_slow // 正确性待验证 fn hva_to_pfn(addr: u64, _atomic: bool, _writable: &mut bool) -> Result { - debug!("hva_to_pfn"); + kdebug!("hva_to_pfn"); unsafe { let raw = addr as *const i32; - debug!("raw={:x}", *raw); + kdebug!("raw={:x}", *raw); } // let hpa = MMArch::virt_2_phys(VirtAddr::new(addr)).unwrap().data() as u64; let hva = VirtAddr::new(addr as usize); @@ -152,7 +154,7 @@ fn hva_to_pfn(addr: u64, _atomic: bool, _writable: &mut bool) -> Result> PAGE_SHIFT); } unsafe { - mapper.map(hva, EntryFlags::mmio_flags()); + mapper.map(hva, PageFlags::mmio_flags()); } let (hpa, _) = mapper.translate(hva).unwrap(); return Ok(hpa.data() as u64 >> PAGE_SHIFT); @@ -165,11 +167,11 @@ pub fn __gfn_to_pfn( write: bool, writable: &mut bool, ) -> Result { - debug!("__gfn_to_pfn"); + kdebug!("__gfn_to_pfn"); let mut nr_pages = 0; let addr = __gfn_to_hva_many(slot, gfn, Some(&mut nr_pages), write)?; let pfn = hva_to_pfn(addr, atomic, writable)?; - debug!("hva={}, pfn={}", addr, pfn); + kdebug!("hva={}, pfn={}", addr, pfn); return Ok(pfn); } diff --git a/kernel/src/virt/kvm/kvm_dev.rs b/kernel/src/virt/kvm/kvm_dev.rs index 065c85395..3a900f6ad 100644 --- a/kernel/src/virt/kvm/kvm_dev.rs +++ b/kernel/src/virt/kvm/kvm_dev.rs @@ -1,5 +1,4 @@ use crate::driver::base::device::device_number::DeviceNumber; -use crate::filesystem; use crate::filesystem::devfs::{DevFS, DeviceINode}; use crate::filesystem::vfs::{ core::generate_inode_id, @@ -9,6 +8,7 @@ use crate::filesystem::vfs::{ use crate::libs::spinlock::SpinLockGuard; use crate::process::ProcessManager; use crate::{arch::KVMArch, libs::spinlock::SpinLock, time::PosixTimeSpec}; +use crate::{filesystem, kdebug}; // use crate::virt::kvm::{host_stack}; use super::push_vm; use crate::virt::kvm::vm_dev::LockedVmInode; @@ -17,7 +17,6 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use log::debug; use system_error::SystemError; pub const KVM_API_VERSION: u32 = 12; @@ -95,7 +94,7 @@ impl IndexNode for LockedKvmInode { _data: SpinLockGuard, _mode: &FileMode, ) -> Result<(), SystemError> { - debug!("file private data:{:?}", _data); + kdebug!("file private data:{:?}", _data); return Ok(()); } @@ -142,12 +141,12 @@ impl IndexNode for LockedKvmInode { ) -> Result { match cmd { 0xdeadbeef => { - debug!("kvm ioctl"); + kdebug!("kvm ioctl"); Ok(0) } KVM_GET_API_VERSION => Ok(KVM_API_VERSION as usize), KVM_CREATE_VM => { - debug!("kvm KVM_CREATE_VM"); + kdebug!("kvm KVM_CREATE_VM"); kvm_dev_ioctl_create_vm(data) } KVM_CHECK_EXTENSION diff --git a/kernel/src/virt/kvm/mod.rs b/kernel/src/virt/kvm/mod.rs index 123984cb8..deb4b2fee 100644 --- a/kernel/src/virt/kvm/mod.rs +++ b/kernel/src/virt/kvm/mod.rs @@ -1,10 +1,9 @@ use self::kvm_dev::LockedKvmInode; use crate::arch::KVMArch; use crate::filesystem::devfs::devfs_register; - +use crate::kdebug; use crate::libs::mutex::Mutex; use alloc::vec::Vec; -use log::debug; use vm::Vm; pub mod host_mem; @@ -24,7 +23,7 @@ pub static VM_LIST: Mutex> = Mutex::new(Vec::new()); pub fn push_vm(id: usize) -> Result<(), ()> { let mut vm_list = VM_LIST.lock(); if vm_list.iter().any(|x| x.id == id) { - debug!("push_vm: vm {} already exists", id); + kdebug!("push_vm: vm {} already exists", id); Err(()) } else { vm_list.push(Vm::new(id).unwrap()); @@ -55,14 +54,14 @@ pub fn vm(id: usize) -> Option { #[inline(never)] pub fn kvm_init() { - debug!("kvm init"); + kdebug!("kvm init"); match KVMArch::kvm_arch_cpu_supports_vm() { Ok(_) => { - debug!("[+] CPU supports Intel VMX"); + kdebug!("[+] CPU supports Intel VMX"); } Err(e) => { - debug!("[-] CPU does not support Intel VMX: {:?}", e); + kdebug!("[-] CPU does not support Intel VMX: {:?}", e); } }; @@ -77,9 +76,9 @@ pub fn kvm_init() { // let host_stack = vec![0xCC; HOST_STACK_SIZE]; // let guest_rsp = guest_stack.as_ptr() as u64 + GUEST_STACK_SIZE as u64; // let host_rsp = (host_stack.as_ptr() as u64) + HOST_STACK_SIZE as u64; - // debug!("guest rsp: {:x}", guest_rsp); - // debug!("guest rip: {:x}", guest_code as *const () as u64); - // debug!("host rsp: {:x}", host_rsp); + // kdebug!("guest rsp: {:x}", guest_rsp); + // kdebug!("guest rip: {:x}", guest_code as *const () as u64); + // kdebug!("host rsp: {:x}", host_rsp); // let hypervisor = Hypervisor::new(1, host_rsp, 0).expect("Cannot create hypervisor"); // let vcpu = VmxVcpu::new(1, Arc::new(Mutex::new(hypervisor)), host_rsp, guest_rsp, guest_code as *const () as u64).expect("Cannot create VcpuData"); // vcpu.virtualize_cpu().expect("Cannot virtualize cpu"); diff --git a/kernel/src/virt/kvm/vcpu.rs b/kernel/src/virt/kvm/vcpu.rs index ac87d50c6..3d4b84e52 100644 --- a/kernel/src/virt/kvm/vcpu.rs +++ b/kernel/src/virt/kvm/vcpu.rs @@ -1,6 +1,5 @@ use system_error::SystemError; -#[allow(dead_code)] pub trait Vcpu: Send + Sync { /// Virtualize the CPU fn virtualize_cpu(&mut self) -> Result<(), SystemError>; diff --git a/kernel/src/virt/kvm/vcpu_dev.rs b/kernel/src/virt/kvm/vcpu_dev.rs index ee8719e11..53e56e8cd 100644 --- a/kernel/src/virt/kvm/vcpu_dev.rs +++ b/kernel/src/virt/kvm/vcpu_dev.rs @@ -1,7 +1,6 @@ use crate::arch::kvm::vmx::vcpu::VcpuContextFrame; use crate::arch::KVMArch; use crate::driver::base::device::device_number::DeviceNumber; -use crate::filesystem; use crate::filesystem::devfs::DevFS; use crate::filesystem::vfs::{ core::generate_inode_id, file::FileMode, FilePrivateData, FileSystem, FileType, IndexNode, @@ -12,13 +11,13 @@ use crate::mm::VirtAddr; use crate::syscall::user_access::copy_from_user; use crate::virt::kvm::vcpu::Vcpu; use crate::virt::kvm::vm; +use crate::{filesystem, kdebug}; use crate::{libs::spinlock::SpinLock, time::PosixTimeSpec}; use alloc::{ string::String, sync::{Arc, Weak}, vec::Vec, }; -use log::debug; use system_error::SystemError; // pub const KVM_API_VERSION:u32 = 12; @@ -103,7 +102,7 @@ impl IndexNode for LockedVcpuInode { _data: SpinLockGuard, _mode: &FileMode, ) -> Result<(), SystemError> { - debug!("file private data:{:?}", _data); + kdebug!("file private data:{:?}", _data); return Ok(()); } @@ -150,11 +149,11 @@ impl IndexNode for LockedVcpuInode { ) -> Result { match cmd { 0xdeadbeef => { - debug!("kvm_cpu ioctl"); + kdebug!("kvm_cpu ioctl"); Ok(0) } KVM_RUN => { - debug!("kvm_cpu ioctl"); + kdebug!("kvm_cpu ioctl"); // let guest_stack = vec![0xCC; GUEST_STACK_SIZE]; // let host_stack = vec![0xCC; HOST_STACK_SIZE]; // let guest_rsp = guest_stack.as_ptr() as u64 + GUEST_STACK_SIZE as u64; @@ -178,9 +177,12 @@ impl IndexNode for LockedVcpuInode { VirtAddr::new(data), )?; } - debug!( + kdebug!( "rip={:x}, rflags={:x}, rsp={:x}, rax={:x}", - kvm_regs.rip, kvm_regs.rflags, kvm_regs.regs[6], kvm_regs.regs[0], + kvm_regs.rip, + kvm_regs.rflags, + kvm_regs.regs[6], + kvm_regs.regs[0], ); let vcpu = vm(0).unwrap().vcpu[0].clone(); @@ -189,7 +191,7 @@ impl IndexNode for LockedVcpuInode { Ok(0) } _ => { - debug!("kvm_cpu ioctl"); + kdebug!("kvm_cpu ioctl"); Ok(usize::MAX) } } diff --git a/kernel/src/virt/kvm/vm.rs b/kernel/src/virt/kvm/vm.rs index 68444dfe4..5b2bbb272 100644 --- a/kernel/src/virt/kvm/vm.rs +++ b/kernel/src/virt/kvm/vm.rs @@ -1,11 +1,10 @@ use crate::arch::kvm::vmx::vcpu::VmxVcpu; -use crate::arch::KVMArch; use crate::arch::MMArch; use crate::libs::mutex::Mutex; use crate::mm::MemoryManagementArch; +use crate::{arch::KVMArch, kdebug}; use alloc::sync::Arc; use alloc::vec::Vec; -use log::debug; use system_error::SystemError; // use super::HOST_STACK_SIZE; @@ -14,6 +13,7 @@ use super::host_mem::{ KVM_ADDRESS_SPACE_NUM, KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_MAX_NR_PAGES, KVM_MEM_READONLY, KVM_MEM_SLOTS_NUM, KVM_USER_MEM_SLOTS, PAGE_SHIFT, }; +// use crate::kdebug; #[derive(Debug, Clone)] pub struct Vm { @@ -24,7 +24,6 @@ pub struct Vm { // memory config pub nr_mem_slots: u32, /* Number of memory slots in each address space */ pub memslots: [KvmMemorySlots; KVM_ADDRESS_SPACE_NUM], - #[allow(dead_code)] // arch related config pub arch: KVMArch, } @@ -49,10 +48,10 @@ impl Vm { &mut self, mem: &KvmUserspaceMemoryRegion, ) -> Result<(), SystemError> { - debug!("set_user_memory_region"); + kdebug!("set_user_memory_region"); let id: u16 = mem.slot as u16; // slot id let as_id = mem.slot >> 16; // address space id - debug!("id={}, as_id={}", id, as_id); + kdebug!("id={}, as_id={}", id, as_id); // 检查slot是否合法 if mem.slot as usize >= self.nr_mem_slots as usize { diff --git a/kernel/src/virt/kvm/vm_dev.rs b/kernel/src/virt/kvm/vm_dev.rs index 9e7fd26cc..4b2f1facf 100644 --- a/kernel/src/virt/kvm/vm_dev.rs +++ b/kernel/src/virt/kvm/vm_dev.rs @@ -1,5 +1,4 @@ use crate::driver::base::device::device_number::DeviceNumber; -use crate::filesystem; use crate::filesystem::devfs::DevFS; use crate::filesystem::vfs::{ core::generate_inode_id, @@ -15,12 +14,12 @@ use crate::virt::kvm::update_vm; use crate::virt::kvm::vcpu_dev::LockedVcpuInode; use crate::virt::kvm::vm; use crate::{arch::KVMArch, libs::spinlock::SpinLock, time::PosixTimeSpec}; +use crate::{filesystem, kdebug}; use alloc::{ string::String, sync::{Arc, Weak}, vec::Vec, }; -use log::debug; use system_error::SystemError; // pub const KVM_API_VERSION:u32 = 12; @@ -101,7 +100,7 @@ impl IndexNode for LockedVmInode { _data: SpinLockGuard, _mode: &FileMode, ) -> Result<(), SystemError> { - debug!("file private data:{:?}", _data); + kdebug!("file private data:{:?}", _data); return Ok(()); } @@ -148,15 +147,15 @@ impl IndexNode for LockedVmInode { ) -> Result { match cmd { 0xdeadbeef => { - debug!("kvm_vm ioctl"); + kdebug!("kvm_vm ioctl"); Ok(0) } KVM_CREATE_VCPU => { - debug!("kvm_vcpu ioctl KVM_CREATE_VCPU"); + kdebug!("kvm_vcpu ioctl KVM_CREATE_VCPU"); kvm_vm_ioctl_create_vcpu(data as u32) } KVM_SET_USER_MEMORY_REGION => { - debug!("kvm_vcpu ioctl KVM_SET_USER_MEMORY_REGION data={:x}", data); + kdebug!("kvm_vcpu ioctl KVM_SET_USER_MEMORY_REGION data={:x}", data); let mut kvm_userspace_mem = KvmUserspaceMemoryRegion::default(); // = unsafe { (data as *const KvmUserspaceMemoryRegion).as_ref().unwrap() }; unsafe { copy_from_user( @@ -167,7 +166,7 @@ impl IndexNode for LockedVmInode { VirtAddr::new(data), )?; } - debug!( + kdebug!( "slot={}, flag={}, memory_size={:x}, guest_phys_addr={}, userspace_addr={}", kvm_userspace_mem.slot, kvm_userspace_mem.flags, @@ -185,7 +184,7 @@ impl IndexNode for LockedVmInode { Err(SystemError::ENOSYS) } _ => { - debug!("kvm_vm ioctl"); + kdebug!("kvm_vm ioctl"); Ok(usize::MAX) } } diff --git a/tools/Makefile b/tools/Makefile index c4faeea5f..93bb1ab24 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -6,4 +6,4 @@ clean: @cargo clean check: - @cargo +nightly-2024-07-23 check --workspace --message-format=json + @cargo check --all \ No newline at end of file diff --git a/tools/bootstrap.sh b/tools/bootstrap.sh index d3d583f87..02555caeb 100644 --- a/tools/bootstrap.sh +++ b/tools/bootstrap.sh @@ -94,20 +94,6 @@ install_ubuntu_debian_pkg() } - -#################################### -# 当检测到gentoo时,执行此函数 # -#################################### -gentoo() -{ - pkgman="emerge" - echo "检测到Gentoo发行版" - echo "正在更新包管理器的列表..." - sudo "${pkgman}" --sync - echo "正在安装所需的包..." - sudo "${pkgman}" net-misc/curl net-misc/wget net-misc/bridge-utils net-dns/dnsmasq sys-apps/diffutils dev-util/pkgconf sys-apps/which app-arch/unzip sys-apps/util-linux sys-fs/dosfstools sys-devel/gcc dev-build/make sys-devel/flex sys-apps/texinfo dev-libs/gmp dev-libs/mpfr app-emulation/qemu dev-libs/mpc dev-libs/openssl -} - install_archlinux_pkg() { pkgman="pacman" @@ -228,26 +214,25 @@ rustInstall() { fi echo "正在安装DragonOS所需的rust组件...首次安装需要一些时间来更新索引,请耐心等待..." cargo install cargo-binutils + rustup toolchain install nightly-2023-01-21-x86_64-unknown-linux-gnu rustup toolchain install nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup toolchain install nightly-2024-07-23-x86_64-unknown-linux-gnu - rustup component add rust-src --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu + rustup component add rust-src --toolchain nightly-2023-01-21-x86_64-unknown-linux-gnu rustup component add rust-src --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup target add x86_64-unknown-none --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu + rustup target add x86_64-unknown-none --toolchain nightly-2023-01-21-x86_64-unknown-linux-gnu rustup target add x86_64-unknown-none --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu rustup target add x86_64-unknown-linux-musl --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup target add x86_64-unknown-linux-musl --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu - rustup toolchain install nightly-2024-07-23-riscv64gc-unknown-linux-gnu --force-non-host + rustup toolchain install nightly-2023-01-21-riscv64gc-unknown-linux-gnu --force-non-host rustup toolchain install nightly-2023-08-15-riscv64gc-unknown-linux-gnu --force-non-host - rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2024-07-23-riscv64gc-unknown-linux-gnu - rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2024-07-23-riscv64gc-unknown-linux-gnu + rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2023-01-21-riscv64gc-unknown-linux-gnu + rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2023-01-21-riscv64gc-unknown-linux-gnu rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2023-08-15-riscv64gc-unknown-linux-gnu rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2023-08-15-riscv64gc-unknown-linux-gnu rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu rustup component add rust-src rustup component add llvm-tools-preview - rustup default nightly-2024-07-23 + rustup default nightly echo "Rust已经成功的在您的计算机上安装!请运行 source ~/.cargo/env 以使rust在当前窗口生效!" fi diff --git a/tools/change_rust_src.sh b/tools/change_rust_src.sh index c62270e08..7e78a72ef 100644 --- a/tools/change_rust_src.sh +++ b/tools/change_rust_src.sh @@ -1,33 +1,8 @@ -echo "正在为rust换源" -echo "bash change_rust_src.sh --sparse以使用稀疏索引" -sparse="false" -while true; do - if [ -z "$1" ]; then - break; - fi - case "$1" in - "--sparse") - echo "使用稀疏索引" - sparse="" - ;; - esac - shift 1 - done -if [ -z ${sparse} ]; then - echo -e "[source.crates-io] \n \ -registry = \"https://github.com/rust-lang/crates.io-index\" \n \ -\n \ -replace-with = 'tuna' \n \ -[source.tuna] \n \ -registry = \"sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/\" \n \ -" > ~/.cargo/config.toml -else - echo -e "[source.crates-io] \n \ +# 更换Rust镜像源 +echo -e "[source.crates-io] \n \ registry = \"https://github.com/rust-lang/crates.io-index\" \n \ \n \ replace-with = 'tuna' \n \ [source.tuna] \n \ registry = \"https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git\" \n \ -" > ~/.cargo/config.toml - -fi +" > ~/.cargo/config \ No newline at end of file diff --git a/tools/debugging/logmonitor/src/backend/monitor/mm.rs b/tools/debugging/logmonitor/src/backend/monitor/mm.rs index 3c0de957a..7da9b8930 100644 --- a/tools/debugging/logmonitor/src/backend/monitor/mm.rs +++ b/tools/debugging/logmonitor/src/backend/monitor/mm.rs @@ -154,7 +154,7 @@ impl MMMonitorThread { info!("MMMonitorThread::run(): kmem_path: {:?}", self.kmem_path); let mut kmem_file = { - let file: File; + let mut file: File; loop { let f = self.open_kmem_file(); if f.is_ok() { diff --git a/tools/grub_auto_install.sh b/tools/grub_auto_install.sh index 09b41fd3e..1837b9e8f 100644 --- a/tools/grub_auto_install.sh +++ b/tools/grub_auto_install.sh @@ -38,25 +38,14 @@ export OBJCOPY=objcopy if [ -d ${grub_dir_i386_efi}/bin ] && [ -d ${grub_dir_i386_legacy}/bin ] && [ -d ${grub_dir_x86_64_efi}/bin ] ; then exit 0 fi -#仅支持Ubuntu/Debain, Arch, Centos/RHEL8/Fedora gentoo下的自动安装 -supported_package_manager="apt-get pacman dnf yum emerge" +#仅支持Ubuntu/Debain, Arch, Centos/RHEL8/Fedora下的自动安装 +supported_package_manager="apt-get pacman dnf" packages=("make binutils bison gcc gettext flex bison automake autoconf wget gawk" \ - "make binutils bison gcc gettext flex bison automake autoconf wget gawk" \ - "make binutils bison gcc gettext flex bison automake autoconf wget gawk" \ - "make binutils bison gcc gettext flex bison automake autoconf wget gawk" \ - "dev-build/make sys-devel/binutils sys-devel/bison sys-devel/gcc sys-devel/gettext sys-devel/flex dev-build/automake dev-build/autoconf net-misc/wget sys-apps/gawk") + "make binutils bison gcc gettext flex bison automake autoconf wget gawk") update_options=("update" \ - "-Sy" \ - "update" \ - "update" \ - "--sync" - ) + "-Sy") install_options=("install -y" \ - "-S --needed --noconfirm" \ - "install -y" \ - "install -y" \ - "" - ) + "-S --needed --noconfirm") found_pm=0 pm_index=0 for pm in ${supported_package_manager}; do diff --git a/triagebot.toml b/triagebot.toml index 887cec0c3..e408f4432 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -72,7 +72,7 @@ trigger_files = ["kernel/src/arch/x86_64"] [autolabel."O-riscv64"] trigger_files = ["kernel/src/arch/riscv64"] -[autolabel."A-driver"] +[autolabel."T-driver"] trigger_files = [ "kernel/src/driver", "kernel/src/arch/x86_64/driver", @@ -116,36 +116,29 @@ users_on_vacation = [] [assign.adhoc_groups] # 驱动程序 -driver = ["@dragonos/main"] - -# SIG-MM -sig-mm = ["@dragonos/mm"] +driver = ["@fslongjin", "@YJwu2023", "@GnoCiYeH"] # 虚拟化 -virtulization = ["@dragonos/virtualization"] +virtulization = ["@fslongjin", "@ZXXYy"] -main = [ "@dragonos/main" ] -network = [ "@dragonos/network" ] +filesystem = ["@fslongjin"] riscv64 = ["@fslongjin"] x86_64 = ["@fslongjin", "@GnoCiYeH", "@Chiichen"] # CI/CD -infra = ["@dragonos/infra"] +infra-ci = ["@fslongjin", "@Chiichen"] +bootstrap = ["@fslongjin"] [assign.owners] -"/.github/workflows" = ["infra"] -"/.github/actions" = ["infra"] -"/triagebot.toml" = ["infra"] -"/kernel/src/driver" = ["main"] -"/kernel/src/filesystem" = ["main"] -"/kernel/src/sched" = ["main"] -"/kernel/src/process" = ["main"] -"/kernel/src/net" = ["network"] +"/.github/workflows" = ["infra-ci"] +"/triagebot.toml" = ["infra-ci"] +"/kernel/src/driver" = ["driver"] +"/kernel/src/filesystem" = ["filesystem"] "/kernel/src/virt" = ["virtulization"] "/kernel/src/arch/x86_64/kvm" = ["virtulization"] "/kernel/src/arch/x86_64" = ["x86_64"] "/kernel/src/arch/riscv64" = ["riscv64"] -"/tools" = ["infra"] +"/tools" = ["bootstrap"] diff --git a/user/apps/about/about.c b/user/apps/about/about.c index 059618eac..f559e2dcd 100644 --- a/user/apps/about/about.c +++ b/user/apps/about/about.c @@ -17,7 +17,7 @@ void print_copyright() printf(" DragonOS - An opensource operating system.\n"); printf(" Copyright: DragonOS Community. 2022-2024, All rights reserved.\n"); printf(" Version: "); - printf("\033[1;32m%s\033[0m", "V0.1.10\n"); + printf("\033[1;32m%s\033[0m", "V0.1.9\n"); printf(" Git commit SHA1: %s\n", DRAGONOS_GIT_COMMIT_SHA1); printf(" Build time: %s %s\n", __DATE__, __TIME__); printf(" \nYou can visit the project via:\n"); diff --git a/user/apps/clear/Makefile b/user/apps/clear/Makefile index 127c6ccb3..0239a0625 100644 --- a/user/apps/clear/Makefile +++ b/user/apps/clear/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" # RUSTFLAGS+="-C target-feature=+crt-static -C link-arg=-no-pie" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/http_server/main.c b/user/apps/http_server/main.c index 76fb33546..95dbb2d1c 100644 --- a/user/apps/http_server/main.c +++ b/user/apps/http_server/main.c @@ -233,8 +233,6 @@ int main(int argc, char const *argv[]) // 关闭客户端连接 close(new_socket); } - // 关闭tcp socket - close(server_fd); return 0; } \ No newline at end of file diff --git a/user/apps/libgcc/Makefile b/user/apps/libgcc/Makefile deleted file mode 100644 index 1a15d2447..000000000 --- a/user/apps/libgcc/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -install: - cp -r lib $(DADK_CURRENT_BUILD_DIR) \ No newline at end of file diff --git a/user/apps/libgcc/lib/libgcc_s.so.1 b/user/apps/libgcc/lib/libgcc_s.so.1 deleted file mode 100644 index f758425aee23c11880dd691b64a869bdc4d00d7c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 732952 zcmeFadw3K@_BY-G2_zEefT*CTL4zi&L;`rpDw0ToJvvd8fJ9L)xd@7Y$%KpgYG5+p zh-h?m*UMg5*HzS2(RE$KMP?Ep7Z5^FOo%|ZPZNP8U=l#c`}tH=PZ|W@@B4e+=lT7! zou|6%oH}*t)TvXaE?v`kHqAAvlf$7|S7+@utpn1uWR>vxfNmBEa5rlg;C_uZK+uMT zQhphkb%VrNPc2%)EF+xJWZrNki+kw##6P4I*3+I(l-H=FJnQ*-lH_AO?fJSO>GeUK z1i|aW&wNI!N;C?gK%(R%74xl7`BtcW)>D&gH0v25xN92OF-(n@1ZO`7D?00G+C z7>zIy!Mg58YmVcsb=myARL5AUvqxnRxygf$bKLP=xRV!s7^* zj=YHf9)VZ6e2Dw!D$X{TtKzHh{2M}NgohFOAv}+ejc_{xuK+?R!kq|x5f&ow`n!B+ zFRACh@XS?lKBgjEf$$;$TqOt-L`>8Ep`O;`DHZutJwL;9wv~wIKox(+hIREBGrJ%_62ZR*L#0)z^L{s_N8h(#zw_+JP&Bk;;YxK+e7ZHam&s^?93 z&OsQ7uv+2x&{h0xJTFE_KuAUS7~x3-`}M9J_?7rLq~#;TA%r7jAfzMk`T`*eVL8GZ z2oE5f@A?o4|3O$|BlrX2|5R`{Jb!0PV|1{B=~7-*aqCg3BE8gen0j_s&nfEpTRb0A z@l5sPqlYd2h%L_OEQA#Ze}C!a@KRvS+E6OZAJbso^L8zAD&Ckk?#hDo2Q;n;~9f6 z31JYz`L5#gB`EyMw)AUlal;nB4Dsh|Fr(9K@GXc_m(*!A!UYHy_UqgyELL+k=4%OE6;{OrwBiMBM`7>ht}D6SaQr5wx^t(v3vURUG`)Io z|Ep(BO0HgDJgOx-k_$U)$-QE==X!REa8@~DeHX>W#6*nj_U?5>;Bn0*g z%LmdojX)TQkcMyv0`=rX;23Zhg4O3O`y34%FWT4`1ls=n2;&j#{p>-&4pZ*(R1;R=M`{f5via`Il4xt?3{)XNwn%^6;H@UIrlmC3> z-qoj9_wF@&*^s?|c;xknpEg$&@18gLblAn4i+BBV?5Fv&8sA@7-uBZz;PxYwJlOwO zATj5X>c8|q_TZkQ%O1P-*s>=NOupovS)-liWAg*Aw{J{3II?8*!k6FcH|zaTAJxWX zFF&!UWc5je)Y>=ZJqP^bj~(@ocwo)TcmG+2-#okJy<1ARy!TjbMBCCE4d;tLk9O|; zi}zFS?j!vQcmCn!$gjTaSA6yBFJF7hH<_n`O*cDUS$of)3Yt#dH2j;)&${fIKl!C8 zAJ5t{<>N7W_VT}H^<8l9+yDM!WAU($K3ujgGveCK#g6M5-;YK;dLnrK`enaGANF4H z`y;(qZ2xj$J zpJce;xgO2h8(;Q2^-A3FS1uc}R~vH4f}YcREI2x`{yoR%&lPWM@7-(9Gx?vc?YX&l z^)uPU+qSMBeW=&QPj~N%-&>8gfBCKc#~%G;^}NRr-dvsjRN9jF`VAeKy=*IVw9-?u zdgP+)Wz{`DXdLk7u$|rCT=R0$br;q;XI)tPRl&dKm3_P+v;2!M`~7RD_tS?V=Of3z z`E*X-@ak9o^w_p#1AAP!b@_(OPFD~2PK4gJ{o&P|3m+No?K$|81;bx?)N|9}8=m~} zhS??m+%tJ+3}ikWf9aDY*G}2_ZrX??7Z(1QnmKDSFG2gRf^IiH*7*J& z=yy5v`^dXP_P+Y3=ml@Cpa0OVahFUq#6I z{Mkv5M4lV}{(0zs>T>RK{}<%E>TKqKb?`rzoKKyH{x|2LcYs&SIr#tSJo>3VkNoeP zhyD%FpG(dm=aJ{b^YGz#b1pfH&r{z~pg))14xC5+G3RNoXeg+c)?F*)AcV`R%kVIX zkKQZsKX;PPe}zlDeRCzmx=hx(DSQkE8eHAvLwnuAb5OyRCLh{I zoh2V_i$v5ZK6hF21SS4jh5sGu)l+L~SJ^|4Ohun;N>qfB^J391-L<6$C4(Zx$EV6o zeqYk(DnVZa$a*z$pvHBjd}wc-r`(UugXg@Pe2T|O1{W$m99xN>rTE{W@H-T~O3`1U z@UKE%(#K4Yh~FywQiY$S@W~3#@m|oM#eb8f=kF!qQbivp?6bR8zg#Nj0;RWU3P0;} ziEmW&TU5EplO$r5qW{-<^bn27;JMmsvXUpJv*iCB`~|Na%HBrjN&GU%@oV_CihlVN zNxxXhv-&*pJPc;k&+-h(mvcm3U#NODDt%h>xC4s6_N3%b``~pxe1_sv^?;;{k`L`6 zg|AomD-=FV>0y>fA!IRPvx;(RbrOF-CBKXM5kH9&He90>{SjsVORtcKrAlun&!dL| zMXw!_d~Q;Fn$AP-vHI6xNpJP9qe^~%t;7#ed?pM34?X`ciLmtWy24i}{M9m>mZI>5 z3g1uRV^z6+b`V@&DgL8{AMURCYb3&&bKVY+`YBX;w))Xs3cpn0uafMvt5y3(-y->Z zqwH|L((~ZilF(WMlqos2XC%E)^8dXh8|_ksU#jp^R6pWeoAM+pzjU#pPg43{ zs`PK!^E=8O3Rg=)w{m=cQT3hFEb%|cYG`v5{b<#(PAL5|o#k2^O{YP}>ouhZ?Lo<) zT=98J@rhRDPL<@^N#)04l$=VfnqToxR*Np>4z$lyyDxuIGO+sBWL2*J9$DbqN}l=3 z9{gw7`B$pG(LYH(EvjCL3SU?+@y{tfU#oJprzQRd`Otn=^vT!Aa!VASFI2fpYbE|^ zHGYk;@Mp=hNXcJ6Uefng?K@P_&pL~~htlV)A}PqjGMhF^$vL<{;`5Z80~CF7nnZYd z$ma^B|4H9V1}d9IKh5@CK1b5MulUbUd>R)?{5VzLNF{&tAW67c;adR;JDDlz!$HSu zx6<44OCD&qv&atg9}|@PgOy*h%6(VK6QlZ_2kuKy^da&fFQ~F=}y=1UZ@mZnp_1{Xw^(c?mqpBSnRXg4+(PE#2{QYAk zB39vFR(3U7O^70qUtEe$v|EA@TU>*c9_mwMfh9`NQA(cZ1Cs71MgNiFpL|Rr0t)}L zYL~$uNf*MXWzUCzT(g~9AE=7N_l4r1L-*|=Zuke!; zK1JdGejdGDr|6d}`k{*czm zEBQnzyZtSmJ-I`BI~d~PegrR?u_#yh;j0B!(@a(0!8H=`ypsQ0C4aqYU#lJeq{=;e zp3q0}sq#ubFDX8;c(UAR<%cc#FID>Y{~+nx75ys3|Lk?~&5F-x#pgFl{u#>NCaHb_ z^@=M=>9bMUgEfwvkN*k9r)s|xQ!#VRywxn>g{2$7jx%nD3sP=bkuGo9N6)pO~GKm-EQ9e0NS>M*7I< zGiT&vOrA15N2Z-SWn$Lc$rE#@&6qrW+LIuh_;|r|_q3si%*c7fJ#7dg)AOh1Nz~;0 zsVWgMh0A(;mLw|(#j>X7OwOB_H92c48BU&(KRX+YCQq4}=T>mOV9XSmcXD<%Xjm#^ z`JtFBHostsN@Yx@GA2_Yuu5f2rZN_)g(zLr2dS(OYNpD~&CRE16DJ}fCCQrpq{JYi zFm4OuR+w2cXJySSnBkU$79^=>P0pLCQ0)e9vi3<^A%_^8UZASLAtjX?~d5BG;4H5~8oau0c0xxOs85+BO|a?cP6wlHerLN*wyT|t9h;+fJQ_n)c`gCRqse~=SrCbnLBq%N6K6~kZlL@?Z8w8 zphHZx>&pV9zNXq*#67J8a!;FbMv8!Po{+XPBS$_T$&vMc#4VrG6+B(RIZsIZ427pH zS!O09bQns9Gw97>u{l`0rc4*|Nln^-P>_q42P#Q8i zM>}Wa&J1BdeP+6A@{C6cCO?uhW>!wtwA^V~sgvE4L)6HB9|oP7GsbWv3ZX|qsI(bb z(`V*SoAJmf^eQ(<&Pbg&Dm5*H02gw-FK7Dglk+idbkr&XMrNV&-Fee z+T5%hj6rthGxM@?vL%NQr~7j93m)%aEWwHJ3HRl=r{>L^6CzB_K^vWe>+R6CJ8yCp z2enzqk~2Ab;$u1UCT1b#&e1H?%o(6~Jm>MOS@X2VCr_U~GmFsUGiT>$xp_IvYz@Q{ zbMq!Yo&%qhJ9FX`R676MX}`e!0wGzrg>n1}qltpzm$@x;$K>H$lASXZ%+AJZ6SJpt zLXkaj=9EWsvfLrXWX%L_&SW@XOH~3hC2!{B?5xT89q7kl+;b-u%z)}13(-oXKw;vO zl{IlzPG0^@PFvjbCe9vA9p~rJx95Ozdj7;ma@?Fu4cLm7l?Z#o;?ujp)j3s2CNhQy5h+gsXF5T zOc$Iz>!C#yGGABVBJhkT?4Y>-E09h&D<=7N#wj`GQSoNlICo#f_47q11&J;7&R#olnXy^IZVx1pmTLI9bbDMuoH=UbTqg z%Aga{Mqe|po0Z1*guIy68L4AbS}b6`%fm8Cv(vEb$rG=MWPZ|czk)QRw7f{u1vFD0?%>1sVx%E0FVe8S*)|%!t0E0)d69;CpY*`BA$hnDMjF!cA`P`P zX;KFb_xwpiT3)1~)+bHupy56wX-Lb9G~Gxurh|ri5u_n4FVaMj=DrRZz9%9LX?c-` zwma#8fgzds?uj&{dufRZ&g(l3xqE82D!AZM!qJ)mnAcKuuAJra zD$J12WoO}&6@K|y_-M7DUYa5;*y3Zs)_HfqWxAr)QueaeZBEq_or94=MpK*pc!iHxZtSj1vf6YST*~f;r`ZSYc zZ20Lm`Zyc@4jX>34WDnr-)zHU%O-Rs+wdQTusFA8!(&S#bd9#*Zwz5^UeAVS+gR5m z8~!T`iRV-sp1QNHSvGuyg~W5N4R7r^GP%%(SFfZ*uIFs{-wBMo72EKZ%`xS58yA4d2a%ud?CawBc)P_@{07dK=!_ z`(tv@hQHcI-)O^MZo{|P@aol-OjZ4tZK&JmBW(D`Z1`v!{%zy3&$u@j1BGhHr@Bs^n=V%+=dZ)|e@iu&^jee31|DFv$ z)rP09x2{<>{7?&t=Uf|pkPTmG!(VK}KWD>Fu;Ghs_)Bc~*KPRGHvE61|JA^MHSk{z z{8t12)xdu>@Lvu5R|Egm!2h2bFg!m-7>mQ#;+%ou^}9Q@R~eo)5gjw`_8TgZ)IMMX z?)sIw16KV%q5wr3HUz3T#A{Er-2x7Ct)tB%z6RePMAw0vr53fCCsId=@;-2 z!rb~WmkW3xVJ=b3r2@W+Ft8)S$CJ##0zOEX+nHvJfOiw-($tI=@HWC+ikX^#s|oXG31;Id*1wW) z4B>hKmk{nxxJtk)313CnFW}DzUrl(qfIlG2El+c)fZrxOfN-&Z-z0nu;X(nwLik$3 zvjqG+;W)yR1iXN7JmJv-ev8Bm5h}g9ZF3;p+&;2zUx%E&a2p*=5cfDDuIB9u-um*^8#nrglnkfu zXT#Szc8uXW8r<=OLtFeoRP zzOs=~bQ{hk8L<)0#i_9ohHqq_MnnJf(t@81-_8dgN||6jJ{!u*P0+8Ddq=s&V-s5p z->EUejV-##@U|4h8;c)`FuZl{zLWz$N+G^tXN6Q+y>rym-4pF!4sjSI~#*Z>RVIfnb<#eTwgU zPraVTNVzXHwk_3Ho)Tz?2n3^2JbUy3jUWd4=lOdOiO{`&fd@@XJd~E$l9sqPrL{~e z#BXoIJaybA`_x={+ zN{Z)17k%Maj8Hr~t|eBm4a!?eq12P0Z7BuYE=Xx9jn=)*5Td0FN!t-&LGdl6F|v>_ zSqL5|gnU)iyUxESOiN}qD3U*KYbizK736O%rRH>RrHV%&{!bOBbPs|HX$pZ)RdKe) zAaaL_b?>Dz2En>AMwL)iO<+PmG~^Eo5N5I_1R&2pLI4VTH3W3hy-yQp@dKSJV}-hR zG-565l6CJbh_sgQ)rs!CTE$t7C>4)Jd>QLm5~F*6he(?Dko#KZpzx)NN<%Rps(3PO z09ry#)4VNuQ4pl8xI@`SA#G!xZ24%Bq9rRsh!J2XMTpunpQhR!`~(=Lh_y}vjIcll zdryp&SBtP2u@(lvDnu)!HZkYWy)h7(fUu=*3P^^9OX;-amXc6u(JdvRqGDRsq4G4o z`MX(9(6ho?GP{2vBEtB-M2o=(yf2%vH`p&L)V-bY6q-Mb?8{($+k{g0x(6&nEAp4D z$Uq}G2@zXLfQtoAsJ|B?P^mj&8QS6$lGzr$K^_D0U4RSsL#(6Vhi(ux`@R*N2q`I+ zAtjq7qNPTwl5dri7tW4b!6^(oOW__AxCV6rhd#5TS8yt#zb%ZMhcZ_HEM%AR4;Qh8 zf5U^m4#<{3vhIDp6J!mbtcNLmb-jMuP!^KGj4qsKER-^rAw2Q8mO{e98P% zcjU|{(2F_&ntv*#z&%wjx&iFwpBkAn!>zv>Nb#KPqA%hvs8*n&T2X~wvLj&PPDf# z9v6jnbo-@^6t=8Mc7IO8`UGU?EX!a_?!PU*C&IVUv(e$36XDz1y0vwk7Us|jUZ57x zABsSVSMEjgFlZI-Pu7cOA<|j}tky;MW&wb%%RBq-i10N6xm44=cN5u6`y363vu965 zSKmDm<_(ClvIP$;cEz?~X#P1e;5mNf?0z0|!2IJMfaRQI*OKsYxMBo-iq@HbDkf$OjG+c{qtU%pc)}>=#zEkq$Y=%4d4a!>3Y${OioaELblj zO{$1OP|X~qqFRi3F{02#nCY-^oyorM$S4XlKUUae&3s)ElVY;c zXN+d1t7w>JX0gKegb5)oQ3{R5k$8!Q8Z`58n=Az?`ASAn4fA$TK(=IakjkYMnx@JX zgY?ZRPTDV3oZ5az#ZjX9BICLSO_}p#6#dnltfE?k>9nHJW+I{<-C_3Q^PaG1vx|&4 z%tiE&da}74Q$%EmFoz<7_Uft^?bpy=a5tKHt);YN zO$tEo9iu4;I+;BLlL+&WkSI);q(_f2dq}Oa@-MKxm<_62K{Vsg_B&P_Hd`#>JdB~4 zm#R3G)dg|SJrT*~G4K}|wN7R|;$>aHMDvulo3B9X!aZS&@4$Rz5M-t&j}SARLd^$c zQ66{@83xGAQOuFsbRdpS-Gv*UuvbVlzGL{{$rVTg_ZVa37@v|z9`|3E%+G;|K5UUoR$8t zgmbO*y8%N^s?)q(raf<^4UuUWN;LBt8K((xU@q!|hJ^;ja{dCDCblL{qY1!IY#TJo zcu(fvk2v%fDtD`d@3zWX2blhtemNyz_Kpms=wAdpL5HU;QeX5kN`PM;_IKfz3#DI1 zG4u|e4>PW_0WFndqqUo7cE5u8WP&u zEQzo<0@0PTFZ3M0e$Jehb$ZuZQ z%3dCAo&*krG7*tza|hx zT0APMWldPY)RsHCL=@bK@eRZ9ik3AP?}noPnB7F~Ftog8Ud%2#ia%n}%%d_+6_^JQ zFWepG?t?_J7jy{z1aYCON=e9mX?`d1REhZ+@QMD?j^(g^rG{nP=0oTpOTsJpNNRIm z1pC?8fH)RUk?r;Rm4(Qwm8vJ`|0?Kjc$1I#(}u6n2pk?^1ll?qj&;W7)9&72@R4E= z(O#dcUzysGpX=Yk;RVR675rwb;TirsDB%b8xucB5!*2sV73jIKIDgN$;+uV!hqEgXyg`hOgQ1v|}+| z-EP%Kr294--fH)-loiRSsWZMR-RpnmViF_QmY}iaV5%=*Y}s!_HkLBxYfP+etwl;{ zZm}r1xfGQwqMGFT9?Wh!gIQmcKwkSXwGkYejV(uv$P=Z-fD-*=cEIqRI3K^W<%4Jx z@j}7#1Oz!-J|l2q1evy#o>RW06Nazc=-a6O(QhPuZ}i=$f8p2vRHA=T?Wi(dw;4!o zvD8nwfh?eZrGws6(+*ORZp)zP+h7hOjMoiE*{m=lv8{A;F!rF_?;$^5TcGVqBy|=k z7a*m>BxQMM&t|f4W%*HobYH+3I6eaI$knq_FFgJuyi}s!l~@UVN%}U!(KyS2Vpf@8 z*1=7t``T3MUzi%&3=(!J^H&+ZO2g4+^la7(mxBFS?TMy~m;4_(H-D8n;(Yv3pVLks zN-1M^@tttR`;ES+%5OzkHi@MDpgdTysL+qWGb$wNi)|s5x{;~2>~eo8<$l9gg-+r0 zRi})j8dJtPeb^G)sy5p;xqQ3Fr2F>JN?b`L1(z8~0j#TFy)Iw9(R%p7&=Lgp&7=cJ zN~}w)X0rYXy2UnZlR5QIzD0oQw>n#QJAL0f9j%6MoeM>`)*Fr!u+VvthVOe<{P(DF zy0>~>7gv0}tM5S<6z!=-pAQHtNvhJlH#cC|qGl=^7D#`Qme`(HosRN+jp)%OE^zQ2 zN8wumRX80eW3SWqUAkkf)8}{jYK+!hl)$$x-Lc8=R?LfZ`Hs8dYn{GL>DUSA;*4)` z_H9ph9Ckq~p6@kbTS;xYcQuU9MGnno7dVyNVn{vr7I4s?TR?q8qV{irCb2p#u|ief z<*Q`fjU`bRqvD36QukG2a%1L$u2^JUgsfOI0d{)Y+I4RxV#uFZjoK)+HOcI##AejM z=>YQzSNvw(Hwd(l2&ypM;A(h|gFzo4AsH6$|5Hx8=B_k%!yH}x1Cqse0I1B!dcCLu zaV1Za6sbbg$gx?dH6&3v8Vr_?meQYl4S7s11Al36^f9e9!d!>%Kf_$D{-;u}WU9?v zF)pzGIel%A#_&Ow6VBEeXWu46UwM3l5m~7hUClbMfhys#6@8*VvHGZ?|EJ93cNpGw zJ+D!m2-t4eBtI&R|zN*aAI06O8{v$FlcQ|r`nZ63m z&^p4=ru*c=5R;qapjW#=Ab+JsGjKu@)(}wn}Y?7-u8Jqa)ieq z$5H*^0EqRtKu7!e+M2;{h@D?9X=sn+*R1&AY4HOtI$T4JX7D310yzqMubUnl;cK&G z8(!;g=oXqk)K-AwrL`3`<&dy>bMS|Xf@9lj8ZNNr7u#zgZCGt^N0XGVHrTMSqf5#D zma5135h@U>j;89FC5UWeWIb}{Y}4~a7cf%B&JF) z!jb{fMh_o|TIg|&;IT{3Xw?&LIQ2V+ma1nQ(0^Y7=m;Qv;S$c@n;p4n`jHl7x*;5y zkmInPu};sx^r9Mt4s1@WPSq0ztP=U-_NVD%59@y@Nzuox)f1|j^$@8J0>2N)4f^xX zf~%fTr$5iHZ|HH&;J8Q1Fz*Z*9s@L|fV?L&_E-@-<>&Chkg^Qe@Vkf|0B94S?!yUP z0_b}{7hMlXU$~DRo9(G5`1R-c?G-(tLVuoLKY?ue^A|};o|>#@Tps_bLu(kX=AW!I z1W)v%jufE&71~hr4PhWUipuD5`}K^idd3d@4}S6p40sBL>cOvcbn~^pI)5;I|VKo^xY!~uM5H9$5E8|k%HMjbJ31|-Zt}AgJ#VvVQe=#$u2@$ zc&p=WS1#-`*bGBG$k=+s#&5_KM(W3-LfPuqXdYC5_@7Y}m^6~xkH{b0z363PGFm}= z!(7IpfFrP6a7$=9!*1Kb;D*PkvM;KUb)XQl1$oycRwGx!pd^_+?l5rsVXk9aVZGop zZXK8l`)y&qW+ojbexLq4zg~b;n6O90;+iR1gC)`a;#3_^kIA_vWhE|N^V6HxHvm_yIT^#}(MN)dK9>k~|R(_{FsMYBGB4RC-n=^xk9KZ@$>i>AU{V6^M;kbax>J;20W zv%fVj5e8a_YO`(kH@sr=fHdHFXQXd6pA^Pt(eDrwE7gCI|Dw65t+`9kkl%o*gv&y3 z_;|w7fMI1#ut{c3Eu+;G2J_sFm=F+O<0>RlK~mwB{Q@Ra3iu z$L<{^b-`MguD>ow`z_In#$mBvyQX$m-JqYLm)eRtvv#fidw=b&+KQbA=UiB4Zbvge z0iuRTCHMAPPoo3NHoVh4R)1!)H@NH58R$(#aQguBHy|}xe(8IO$Xt0}WE6vZ4D)VjM4hd?7 zJhJ={9MbbL=y_RfrQ%j0H0SOFUW4v<5jw6+^zS@~b`n|^v{aR%W}66FQ7(l)O2H}` zbk&~rda>U(T&m;>=?TPLa*H8;Wo@8leQki;cU0^!Yr|)~;Lv`k5b%cD${h@)w7rHt zps%Bz8&D_dzb!qZuN9>+w6_lbuYa173%HTNG~5*RDs^s?8Q)(LYg24O?-0&Fs&u z7Bn+E`j<`DIKzN`DD`a`qD!Kkq?NKQ^rEhz^x6uu69BZYJ>88mRsLzB_%rg`+tR%M zm+5*@Ye)Vc&y`*lN{20!0644smpalb%;!bA7{4%8@pF{HvjS6=2jCHp_!(KxK?;NS z110chZNTn@Z^Q8Xz%KZ&xNk9f!P}+jnVaGIPLVc<^dK<-MAmS0JBZr^Kc64lyx-kdw1!G#%N0%{J5%e$d}4(bKl-3E%2(1@yFyF_pS^H&QTV81U#~`0}{Z zD8EwA_*qXFJ{;dBf;ZiuFFZmLjFRC4fdcLD7eULhMqltUl8|LR2_*(w<6?>j$FFDX z5)&R|3BL?k#6%}yz+N9qYU2zBlbx}r#QZO)X9V&YzI5oYPO>%v^*8gi4!J<7rlgP z=;JOb7q!W3MwZQ#A+Drht!2-sQvh{ZgHbMg804LOg-}2|qO4|~?kh3Fgsn>Z2VM6l z)@$Lbma2&gaI zgfvvf$!--|bBp~W*e0i1n1#eOqV~^(KLViu9EU|_)boatuK!VA_z9|v*r0pBFt0zC zH0g-JQfm+z5wI}ItP-r4i3GzyfGlhEg}Y%d#0->KLgX@gboix$Qb>Z?!NYri{}HS< zuzXAhuIH3s9r{UwzVKQ&dL(nQZ~z$eiV6FXWi0}x({UTjkAu>yf2Aw(USlE z5dV63M$v}Q>rrV2-G_GY?}Cir|Bb%zXZTd|?{Y5wJCO+f57U?Km->4c^GD7j$HOqF z$4pp_(CIhkhhU2F33#l4sOyJ0?<|q#B52WM6VM(He`7u?XbaKe)X#uP-VXg#+Vzw0 zNYb6=SSr@rwz`#BbsKPi)U4M! zteRB4Hpn^&uYh_`cEqjv#Wg`@EDW(m(8rPn4WEgQHx9Tsx)wBH=1~OHlZ%e%0sj;# z6Lq9t*`U_V@R*mM(ZuG}1Vaw=?D@Z#;{`BLN2tSs@{h2o?X`7-vcVqn_@?c(dA%`( z39nj!IogIF*8fns1M36OhnE1e6FK-rb5eX^qiBe+><4qV-fBLKT_Dc?q~0(GY~tFZ zrbLuHC`^>Mvk9Egsrfa?n#!8BpsB3+re^<+CQPv2f@sKJH>ez1waYflRx-KOhF_y( zVA~IaA!k@C%m6xZNp@JLtXHxi)z=`1)scQ`wtKl%Lz<-yTT6$P|hX8 zN6&X?oPS5KDq{e!5gO8k{ecb{q2=48UJ>6$MhyuHw$iT%QKCIV@dztcTcP&ju!@r_ zxjg8suGwl|+QclO)$KLRoc-DdMI;4SOR^k!h!K=*4vxdGPEtUMz6ZS7i=&-H9j zNbz(m>}4uchJ~m(`*tz=M+|jXup2CE!;fK)PEeO{k$4*@vBLA*2$>1{O(mA{l{*5phq2J#uwy*}mS8(7 zOpavi543Q7;oWd@b%P4QR>Gs`^zMRK#S&g8(kF$|35#Q2=%441{LIyQL4IwClzS7D z|4X^ot2I=mSfLgS7t6&pLhA4|Xc&@LLh|)kC|2y)!-b-G8>E-^4*!=3IRVJ|Z*6_U zYSs%+UoG0$9Kh62^H}dA%dPb!{Ui75bz)#o5+>@)5%52J<;ZscN#DD>;k}CAzZc<= z2F!p+6Fe;aF@J6NHhg^)RvS^*l%IfIhNoj&^na(uwiIrN%H@_!?ey4VJK5!?$F>+a zo`Zu0R({g%*ws$7-yi-Esuf%YEKf)9r7V8w zV6<-QD4+5(Kk8mS=K^!p2IWVXKNk#cC4={XJ&S=Qzi5BZqW00Xhj%v3=>cyM&0@@y zO8UEcaEh+GH8wZ|Uvl)|R}FlZ3#XiR6KFKT>B%tkTHUtgJ@K%ED0SY_@_loQav za*XHQ>jcpR%w?`G$xk6smGMd`=93dDWe$BMjws+AHC{a8?G)&aR2Z8NdUiSR-jQ55 zQ>bY8TqR@EOPm_OE9$wA=tcaLG8|)zr)9+I6vSz2+-fMmSZTz!`i}cf8~R68 z#+DPwEoI4HCJXYklKA7!EjV6LVt6VYI9p)oX(e!h15xqzWZnA?sJO*o*|~u;RQjro zhNo!+4o7(Zgycj&4t1a~zy6vZN28e8k1XM*<~VpxP&U4- z(L_&j!4Kp_ zULjnrSkD&&C-tc$K1-j5&mG&ib9!uJZFo zba+$;frcX%o&)5EF3_e58;>)Vmi>$Ik0)FHU)5JM7KJ@`eLEWc-1TL;7};cfX#$_R z#c)b9vDKG^s4oc$I|$CKuPW@9^;Px(2Q{xtP56)b9AWm0dp+*LV0Khm1``E?v7K-D zrM`Qhoh&a*ORRIYZZpct;AGpKeUG|)J6yi?={WZljq^j8IXXR?HD_xr zkoaL|!PTy$b@?-d3$03Qaklb66Ri)&T5wLN?}Ml~PQLEQzdEsn$B@dIh8g7nYc|rl zjU>{ax%5@*op6F_-im^eM$*x|NM=e;D#=@y*dl%i)v$_sghwstF1#B~eYGB?x8|`? zQMf!KM=2U!c4loB6W3HziRPrYu&xC0eAS?X4!b&u1x27;l0ysia3ok{-uC?mec)pr|^ zNb(&|$9cBChtU$^9PVZ(4)@|j(U!xmNE|ZaabI(kPz?J)oxS};jnRInA1f+qX>y+w z6@(t-dB#%ASy7T+bO384SL81x<~+2MKkr)?ghB25 zZf&Tr>`kW!|?N=c5t>fi3S$^2;%ZEo3oX2n9t~vGky!|=kk?0ea)z@ zAv_cMA>)xea_$t)3;k3mqZ*1uM-^@gM_{F!s?O^t+}Q!D66Rlx9{|up($!MFC8KcY z_CaxepUz*58HR6@Yw-k3GcW;Zqdxjl9}{@~*VinnP$e0l|3WVpaxD$w<_9~*z*Pg1)of4ZFTxeF{Zh@IepR2vSj|`1-^lW$@X%gAuc3A*K}Wm(>E)E zKUJ-3_(6Wsz^Sj=W_XT{fKz?gnbfQ=IECyS#gmTfo+iXaPNxsOe>8GR#?pHzyXHu3 z8LQ9{dL^ty8Y_ivZ89_IOtVT?C4-@_HXVb1{95Oh-LOZW2_3@&M9NlaR4@k;6N+OB zMv!%I6|!h&D+U8}X`C*HQ$HZQj%tgGoJoQFXd}MO)weo*$wN3(jX`S-qUDPMx5F<`u$INxxg3@)g%Vl9}0tzAxs-vNKcja(u?J&Edvw3t#>| z><84zt=GC^YjQoV*dIF8X2;gN1!R~zn$ucNQ15?~GuVd%z(&|eBP`Nr8!}oTJqKcV zE;c&6pUfJ5E1qasIX}1`Z*!j6$+3r#?@}`OSTlA^uKvt=Y%1KG>zfdJRc@>jMzSqZY|iQ#`eZt5M`#a zmOOhOQ z*AvS11zBK$q~TAI993@O%EjR_lkm?r7Itp!(5s^Y^tsn;N+< zxDmKuEpD=5)`WjMoz*TAm>Ea$f-s?xEr54{e6JaNhRuXss;jJq%pIT==FZ>K{%mAv z@MYw(S>gf#LU(;|99uL3?F7e6U_h`CX&d8t-Xi?UN1%h-CA~~^#YUkBX->?RN`st0 zHsQ^=a=94nDWrr!`KHHS$1Z_aNHbDnufs0V&9<449H(jhYl4UWMG8X&p~3V2kHRJ&I|-!kXh~WvT}i7J`$)U^C9~E z&<4IxL~peFNY?po5Ctt-d z`hqva6bdP@qYsb*`8|*}_@XUa`0IkxVue~KHU6~XbDD)NxEFkE_PWn*uUEq{K%=)J4gx=2#WffYto9s6JVybxSps~97VIxksy$oqN{iMI z2NlbF;o&4RQ&^JC{;>YE#uqZ&DrO86+8SOAj4bF1VtnZn{OYrime7UP1wRCAQxG~r z4C)yOdVm*#bLIo#Zw}74az@h>%7RY_7)~8}gh^u%$;7C$Ht3Z241viI9bZ)cw>VW0 zyiO*{Qlxf8;;lHY71WVvDGTqD*Wj?H@Us}NC_)TS6JmeMvk}ki5S6+n_~WOR9z|`o zOK=8$n=+vhr!<29Awpl{j=hzf;QYY!Ho;*n*`Vs{$U07FXJGJ8AeXg~bzCCg@f>9G zBx|%7N?IGt2gnf*z#1{+O_2y|$U6*IE}R30s%tQG1QW?XnL@wdP{pncB83lv^U#7Z z5;fxhhC-2*V_(A*d?ZT$eOa)1xu^-$%9`N(WC;b23LXR=)ZI5u7Vbu0_=ar|=@>qK z2i^LD$hlVdc)Eos*%2ex2Hz!bs6z;|6g^Z6{!XH19AGD`w|7FeudfE2N<*A_gOeOI zCLkM+WKg*;@+^Au=Pp8|oLln1zK@(}$2HO)nu$xI`({z}hgJ z2;M?92R;$3{LnyfJ>p2in61L;>CYr%hXpgE6DD{9fbORV%5 zE%<_DokP~M5Dz|OWtfv8c)&MLl{6`&agpZ!5Y1pg6Q>1JCC%lei6hNWi$-4p{ zygXafOnRd}=HEeKdG8KGd6ez)Z@5j-Ak=a+T6Z_*1p_cGsG>G|2MbUNn@G5?nPeNB zB2hDr{bH8E_9RC*RZfX!tUZfepWroCO~wiVQQ_Ve=4y#q8|(y(=$f+WS^tj|20j{D zc3B`Kmv*;FU^oPzgJg>lJ{wa74hPmipvU1}pF5JHoqD5)JVX~a=eZYbTh8NvQe~+ET z?RZ+_#6T?=1v>PSeJrW*AF!K~xCuL+{gSx}LxI|7LL2Aem%5^vccDw6AdE_-Vw~rG zfJi?i(*r7ft@&~&otDzO!&3gNNQ7_hZ4QM{YTk9GMa{O;^)X}4ptJG2+fLWpyhl!P zEV-22@rtO_y!6a;C3mDsUuy2Il^5cp84w9l)0yd4xK=(aqWfHtt=NdiT=zBXeQ)9h(?Nb| zqHh&A)eqG@*TW4PE2TcVYW!&|m)}p|D;ao4iOtyN5uSrluB>go&0=2}n|;`>@*R)I zhOdd`dL%#Qke*bdzmx#!Qn6LzYfN2oy8}N@^aWC}0gJr?>@V-f7OVametNzU?}@~g z-hL`Hax2oEzE)>sSvr1v?0po%r1}n|@f|x}1sP>%Fx3cw13KFw!oVV!7YMokb8D<2 zj<#v|T#nHnzo^Bx4h{@PAx8g7d=$dKiKU2KeP&oJHc9c#L00R`B?S6cy6-2w=pLba z{8H>RX|Q=5*{T<9L+PHQ?RrrFA3AZ)KPemJ1$^cvWhI(OG|P*JkRh>RHS(jQ$^AfV zu^mqLVK@6nq3d$~~t@h0C$g=xMd<{UC7SN8KR~AA7BeL0utn%#cDz+-i@fOaEL1uoBLyGh&-88XU zXu*67ap={##Bj6*PSc3J(NGe;DuAM$k@#9i6E}Ihf1^QT>%Qndlt%+7!jK3du^-iH z#2+#GZiaxnV5Fm<6eIp8qi>1eA=P^foTLEeYWj%ckZ3%IE!6Z<5*t%>8DP+7S}C^J z)fT(>htuNmQUu*)S1Px!&}q;^_N7NQS)IliS%DPv8uL$L_6#QRsc6}TcQC9u1@Q-7y*IDaE<@&mT~{sJ_* zh&tw;FAVaSv`?utwr?}OoNyy@j4+n;zP%;T#XW5C)3MiyY8+sN=C{kr3;`3wMP>Ls zPBOk5m9gx-$+N2~q}n}9_s{~+rtV9r7IH#omfBH23ruDYhv+F{2c0_5S2hoo^<6Lo zJsZ3K_=$RFTF$G8C$%{_j&l&1&e8BQ?^!qdHb3sRi&Rc6WxD$Me1 z?N=Ju-ryx%F?5D61ekxsN0Fd!ZVyKu)xG>OHe74U3U+gTrJ~(@5)`Opk;Y3edJ@ec zwkMm=q9K3q2lh?RsTcGYPC%BF*6pZFkWN}!+^e)W;iWOqIDIW?@pw}r_pqhK@kL~M zWFsvOP7)oYTv#05r=cUT6B@A1%+qlL|ZqJIJgkb;k67IbS#jU9xqX*MB&^pT{u4#h0FcNhj7&xsNG ziwlqA$i5i`1!3S3!^|CM0t_=8V^TbAAbA8jUvejgp~HZa-4}mZh^E5u6CXgQ#tS+O zI~Zp$$e3?{8DH^6;$tN*qRnVeMp;-a+gA1oVOheM@clsiB2>6ZOmgKAlIr`O6N=#H z_>P7cQ_dPnXnrx4DquV}qp{@s>%Hzq3okw7^R_ow_y9qmJT(p@lJzSfTn3V&LSzA5aqC(1D#hdYLaaWF>qd*MPv4^%#UcUNnyVtbB_^7liy_$?>bvj;%mg0lMbl42j>N9?m?qga}V0I z`2#8=-rxSh(Jx38)e=oq^o@%9PD2r9PAfC9ZRtN~RJFodQ_=c&p<8i2;yIb@?k?It z9$tL65&0btIUGvXUn|20cOo~Loxr3+zu+VFpV%VZ5L$rW`?mVK(7zmF&83i8>RT_0 zWz{`D!3TPv=a3s%f{q}SDaM``PeYxF71GU#S+nvpMQZp%+-xSUHCvsVE1q%5^sVUb?lG5Mw z6Z9h+LuyHUk51W-)edW5OV(-NFnWH&TZQj{mZW$}I+>pcos0S_KJkY!Yc|&&3)vyv zpSPWc;R(R~p`n@qbl8`0k2`^C*by4PIy_9W{;K#Ii*i6`x*BsFDnJ+Ps}ebM??iM# z4t|�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

3HXhTSg@`lh6LPtU;Z0JO2 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 diff --git a/user/apps/test-backlog/Makefile b/user/apps/test-backlog/Makefile index f65b0d520..e7c1704af 100644 --- a/user/apps/test-backlog/Makefile +++ b/user/apps/test-backlog/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" RUSTFLAGS+="" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test-blockcache/Makefile b/user/apps/test-blockcache/Makefile index 86a122015..a4c3cfe71 100644 --- a/user/apps/test-blockcache/Makefile +++ b/user/apps/test-blockcache/Makefile @@ -1,6 +1,6 @@ # The toolchain we use. # You can get it by running DragonOS' `tools/bootstrap.sh` -TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" RUSTFLAGS+="-C target-feature=+crt-static -C link-arg=-no-pie" # 如果是在dadk中编译,那么安装到dadk的安装目录中 diff --git a/user/apps/test-for-robustfutex/Makefile b/user/apps/test-for-robustfutex/Makefile index d9eb6cd1e..1b0274d20 100644 --- a/user/apps/test-for-robustfutex/Makefile +++ b/user/apps/test-for-robustfutex/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" RUSTFLAGS+="" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test-mount/Makefile b/user/apps/test-mount/Makefile index d9eb6cd1e..1b0274d20 100644 --- a/user/apps/test-mount/Makefile +++ b/user/apps/test-mount/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" RUSTFLAGS+="" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test_alarm/Makefile b/user/apps/test_alarm/Makefile index d9eb6cd1e..1b0274d20 100644 --- a/user/apps/test_alarm/Makefile +++ b/user/apps/test_alarm/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" RUSTFLAGS+="" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test_cred/.gitignore b/user/apps/test_cred/.gitignore deleted file mode 100644 index 91773eb0b..000000000 --- a/user/apps/test_cred/.gitignore +++ /dev/null @@ -1 +0,0 @@ -test_cred \ No newline at end of file diff --git a/user/apps/test_cred/Makefile b/user/apps/test_cred/Makefile deleted file mode 100644 index 18ce21000..000000000 --- a/user/apps/test_cred/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -ifeq ($(ARCH), x86_64) - CROSS_COMPILE=x86_64-linux-musl- -else ifeq ($(ARCH), riscv64) - CROSS_COMPILE=riscv64-linux-musl- -endif - -CC=$(CROSS_COMPILE)gcc - -.PHONY: all -all: main.c - $(CC) -static -o test_cred main.c - -.PHONY: install clean -install: all - mv test_cred $(DADK_CURRENT_BUILD_DIR)/test_cred - -clean: - rm test_cred *.o - -fmt: diff --git a/user/apps/test_cred/main.c b/user/apps/test_cred/main.c deleted file mode 100644 index 8e4784d20..000000000 --- a/user/apps/test_cred/main.c +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include -#include -#include - -int main() -{ - printf("Current uid: %d, euid: %d, gid: %d, egid: %d\n\n", getuid(), geteuid(), getgid(), getegid()); - - // 测试uid - printf("Set uid 1000\n"); - setuid(1000); - int uid = getuid(); - assert(uid == 1000); - printf("Current uid:%d\n\n", uid); - - // 测试gid - printf("Set gid 1000\n"); - setgid(1000); - int gid = getgid(); - assert(gid == 1000); - printf("Current gid:%d\n\n", gid); - - // 测试euid - printf("Setg euid 1000\n"); - seteuid(1000); - int euid = geteuid(); - assert(euid == 1000); - printf("Current euid:%d\n\n", euid); - - // 测试egid - printf("Set egid 1000\n"); - setegid(1000); - int egid = getegid(); - assert(egid == 1000); - printf("Current egid:%d\n\n", egid); - - // 测试uid在非root用户下无法修改 - printf("Try to setuid for non_root.\n"); - assert(setuid(0) < 0); // 非root用户无法修改uid - printf("Current uid: %d, euid: %d, gid: %d, egid: %d\n", getuid(), geteuid(), getgid(), getegid()); -} \ No newline at end of file diff --git a/user/apps/test_eventfd/.gitignore b/user/apps/test_eventfd/.gitignore deleted file mode 100644 index fdf3c0f82..000000000 --- a/user/apps/test_eventfd/.gitignore +++ /dev/null @@ -1 +0,0 @@ -test_eventfd \ No newline at end of file diff --git a/user/apps/test_eventfd/Makefile b/user/apps/test_eventfd/Makefile deleted file mode 100644 index fc61e3624..000000000 --- a/user/apps/test_eventfd/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -ifeq ($(ARCH), x86_64) - CROSS_COMPILE=x86_64-linux-musl- -else ifeq ($(ARCH), riscv64) - CROSS_COMPILE=riscv64-linux-musl- -endif - -CC=$(CROSS_COMPILE)gcc - -.PHONY: all -all: main.c - $(CC) -static -o test_eventfd main.c - -.PHONY: install clean -install: all - mv test_eventfd $(DADK_CURRENT_BUILD_DIR)/test_eventfd - -clean: - rm test_eventfd *.o - -fmt: diff --git a/user/apps/test_eventfd/main.c b/user/apps/test_eventfd/main.c deleted file mode 100644 index cea0dc0a0..000000000 --- a/user/apps/test_eventfd/main.c +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -int -main(int argc, char *argv[]) -{ - int efd; - uint64_t u; - ssize_t s; - - if (argc < 2) { - fprintf(stderr, "Usage: %s ...\n", argv[0]); - exit(EXIT_FAILURE); - } - - efd = eventfd(0, 0); - if (efd == -1) - err(EXIT_FAILURE, "eventfd"); - - switch (fork()) { - case 0: - for (size_t j = 1; j < argc; j++) { - printf("Child writing %s to efd\n", argv[j]); - u = strtoull(argv[j], NULL, 0); - /* strtoull() allows various bases */ - s = write(efd, &u, sizeof(uint64_t)); - if (s != sizeof(uint64_t)) - err(EXIT_FAILURE, "write"); - } - printf("Child completed write loop\n"); - - exit(EXIT_SUCCESS); - - default: - sleep(2); - - printf("Parent about to read\n"); - s = read(efd, &u, sizeof(uint64_t)); - if (s != sizeof(uint64_t)) - err(EXIT_FAILURE, "read"); - printf("Parent read %"PRIu64" (%#"PRIx64") from efd\n", u, u); - exit(EXIT_SUCCESS); - - case -1: - err(EXIT_FAILURE, "fork"); - } -} \ No newline at end of file diff --git a/user/apps/test_lo/.gitignore b/user/apps/test_lo/.gitignore deleted file mode 100644 index 1ac354611..000000000 --- a/user/apps/test_lo/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -Cargo.lock -/install/ \ No newline at end of file diff --git a/user/apps/test_lo/Cargo.toml b/user/apps/test_lo/Cargo.toml deleted file mode 100644 index cb9613c17..000000000 --- a/user/apps/test_lo/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "test_lo" -version = "0.1.0" -edition = "2021" -description = "测试lo网卡功能" -authors = [ "smallc <2628035541@qq.com>" ] - diff --git a/user/apps/test_lo/Makefile b/user/apps/test_lo/Makefile deleted file mode 100644 index 7522ea16c..000000000 --- a/user/apps/test_lo/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -TOOLCHAIN= -RUSTFLAGS= - -ifdef DADK_CURRENT_BUILD_DIR -# 如果是在dadk中编译,那么安装到dadk的安装目录中 - INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) -else -# 如果是在本地编译,那么安装到当前目录下的install目录中 - INSTALL_DIR = ./install -endif - -ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl -else ifeq ($(ARCH), riscv64) - export RUST_TARGET=riscv64gc-unknown-linux-gnu -else -# 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl -endif - -run: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) - -build: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) - -clean: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) - -test: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) - -doc: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET) - -fmt: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt - -fmt-check: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check - -run-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release - -build-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release - -clean-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release - -test-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release - -.PHONY: install -install: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test_lo/README.md b/user/apps/test_lo/README.md deleted file mode 100644 index cd96a78e1..000000000 --- a/user/apps/test_lo/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# test-lo - -lo网卡功能测试程序 - -## 测试过程: - -通过创建一个UDP套接字,然后发送一条消息到本地回环地址127.0.0.1(lo网卡),再接收并验证这条消息,以此来测试lo网卡的功能。期望发送的消息和接收到的消息是完全一样的。通过日志输出查看测试是否成功。 \ No newline at end of file diff --git a/user/apps/test_lo/src/main.rs b/user/apps/test_lo/src/main.rs deleted file mode 100644 index a92493735..000000000 --- a/user/apps/test_lo/src/main.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::net::UdpSocket; -use std::str; - -fn main() -> std::io::Result<()> { - let socket = UdpSocket::bind("127.0.0.1:34254")?; - socket.connect("127.0.0.1:34254")?; - - let msg = "Hello, loopback!"; - socket.send(msg.as_bytes())?; - - let mut buf = [0; 1024]; - let (amt, _src) = socket.recv_from(&mut buf)?; - - let received_msg = str::from_utf8(&buf[..amt]).expect("Could not read buffer as UTF-8"); - - println!("Sent: {}", msg); - println!("Received: {}", received_msg); - - assert_eq!( - msg, received_msg, - "The sent and received messages do not match!" - ); - - Ok(()) -} diff --git a/user/apps/test_socket/Makefile b/user/apps/test_socket/Makefile index d9eb6cd1e..1b0274d20 100644 --- a/user/apps/test_socket/Makefile +++ b/user/apps/test_socket/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" RUSTFLAGS+="" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test_statx/Makefile b/user/apps/test_statx/Makefile index 127c6ccb3..0239a0625 100644 --- a/user/apps/test_statx/Makefile +++ b/user/apps/test_statx/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" # RUSTFLAGS+="-C target-feature=+crt-static -C link-arg=-no-pie" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/test_tokio/.gitignore b/user/apps/test_tokio/.gitignore deleted file mode 100644 index 1ac354611..000000000 --- a/user/apps/test_tokio/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -Cargo.lock -/install/ \ No newline at end of file diff --git a/user/apps/test_tokio/Cargo.toml b/user/apps/test_tokio/Cargo.toml deleted file mode 100644 index c559f8d2c..000000000 --- a/user/apps/test_tokio/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "test_tokio" -version = "0.1.0" -edition = "2021" - -[dependencies] -tokio = { version = "1.25", features = [ - "macros", - "rt", - "rt-multi-thread", - "net", - "signal", -] } \ No newline at end of file diff --git a/user/apps/test_tokio/Makefile b/user/apps/test_tokio/Makefile deleted file mode 100644 index d9eb6cd1e..000000000 --- a/user/apps/test_tokio/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" -RUSTFLAGS+="" - -ifdef DADK_CURRENT_BUILD_DIR -# 如果是在dadk中编译,那么安装到dadk的安装目录中 - INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) -else -# 如果是在本地编译,那么安装到当前目录下的install目录中 - INSTALL_DIR = ./install -endif - -ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl -else ifeq ($(ARCH), riscv64) - export RUST_TARGET=riscv64gc-unknown-linux-gnu -else -# 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl -endif - -run: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) - -build: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) - -clean: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) - -test: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) - -doc: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET) - -fmt: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt - -fmt-check: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check - -run-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release - -build-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release - -clean-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release - -test-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release - -.PHONY: install -install: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test_tokio/src/main.rs b/user/apps/test_tokio/src/main.rs deleted file mode 100644 index 91b06613b..000000000 --- a/user/apps/test_tokio/src/main.rs +++ /dev/null @@ -1,17 +0,0 @@ -use tokio::signal; - -async fn say_world() { - println!("world"); -} - -#[tokio::main(flavor = "current_thread")] -async fn main() { - // Calling `say_world()` does not execute the body of `say_world()`. - let op = say_world(); - - // This println! comes first - println!("hello"); - - // Calling `.await` on `op` starts executing `say_world`. - op.await; -} diff --git a/user/apps/test_utimensat/.gitignore b/user/apps/test_utimensat/.gitignore deleted file mode 100644 index f49103416..000000000 --- a/user/apps/test_utimensat/.gitignore +++ /dev/null @@ -1 +0,0 @@ -test_utimensat \ No newline at end of file diff --git a/user/apps/test_utimensat/Makefile b/user/apps/test_utimensat/Makefile deleted file mode 100644 index 985606c7b..000000000 --- a/user/apps/test_utimensat/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -ifeq ($(ARCH), x86_64) - CROSS_COMPILE=x86_64-linux-musl- -else ifeq ($(ARCH), riscv64) - CROSS_COMPILE=riscv64-linux-musl- -endif - -CC=$(CROSS_COMPILE)gcc - -.PHONY: all -all: main.c - $(CC) -static -o test_utimensat main.c - -.PHONY: install clean -install: all - mv test_utimensat $(DADK_CURRENT_BUILD_DIR)/test_utimensat - -clean: - rm test_utimensat *.o - -fmt: diff --git a/user/apps/test_utimensat/main.c b/user/apps/test_utimensat/main.c deleted file mode 100644 index 441fdef63..000000000 --- a/user/apps/test_utimensat/main.c +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include -#include -#include -#include - - -int main(){ - int res = utimensat(AT_FDCWD, "/bin/about.elf", NULL, 0); - printf("utimensat res = %d\n", res); -} \ No newline at end of file diff --git a/user/apps/user-manage/.gitignore b/user/apps/user-manage/.gitignore deleted file mode 100644 index afe59cbbe..000000000 --- a/user/apps/user-manage/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -/Cargo.lock -/install \ No newline at end of file diff --git a/user/apps/user-manage/Cargo.toml b/user/apps/user-manage/Cargo.toml deleted file mode 100644 index c3e73f109..000000000 --- a/user/apps/user-manage/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "user_manage_tool" -version = "0.1.0" -edition = "2021" - -[[bin]] -name = "useradd" -path = "src/cmd/useradd.rs" - -[[bin]] -name = "userdel" -path = "src/cmd/userdel.rs" - -[[bin]] -name = "usermod" -path = "src/cmd/usermod.rs" - -[[bin]] -name = "passwd" -path = "src/cmd/passwd.rs" - -[[bin]] -name = "groupadd" -path = "src/cmd/groupadd.rs" - -[[bin]] -name = "groupdel" -path = "src/cmd/groupdel.rs" - -[[bin]] -name = "groupmod" -path = "src/cmd/groupmod.rs" - -[dependencies] -libc = "0.2.153" -lazy_static = "1.4.0" diff --git a/user/apps/user-manage/Makefile b/user/apps/user-manage/Makefile deleted file mode 100644 index 82cc81f92..000000000 --- a/user/apps/user-manage/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# The toolchain we use. -# You can get it by running DragonOS' `tools/bootstrap.sh` -TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" -RUSTFLAGS+="" - -ifdef DADK_CURRENT_BUILD_DIR -# 如果是在dadk中编译,那么安装到dadk的安装目录中 - INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) -else -# 如果是在本地编译,那么安装到当前目录下的install目录中 - INSTALL_DIR = ./install -endif - - -ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl -else ifeq ($(ARCH), riscv64) - export RUST_TARGET=riscv64gc-unknown-linux-gnu -else -# 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl -endif - -build: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) - -run-dragonreach: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --bin DragonReach - -clean: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean - -build-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release - -clean-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release - -fmt: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt - -fmt-check: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check - -.PHONY: install -install: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force \ No newline at end of file diff --git a/user/apps/user-manage/README.md b/user/apps/user-manage/README.md deleted file mode 100644 index 510189013..000000000 --- a/user/apps/user-manage/README.md +++ /dev/null @@ -1,142 +0,0 @@ -## useradd - -- usage:添加用户 - - > useradd [options] username - - useradd -c \ -d \ -G \ -g \ -s \ -u \ username - -- 参数说明: - - - 选项: - -c comment 指定一段注释性描述 - -d 目录 指定用户主目录,如果不存在,则创建该目录 - -G 用户组 指定用户所属的用户组 - -g 组id - -s Shell 文件 指定用户的登录 Shell - -u 用户号 指定用户的用户号 - - - 用户名: - 指定新账号的登录名。 - -- 更新文件: - > /etc/passwd - > /etc/shadow - > /etc/group - > /etc/gshadow - -## userdel - -- usage:删除用户 - - > userdel [options] username - - userdel -r username - -- 选项: - -r 连同用户主目录一起删除。 - -- 更新文件: - > /etc/passwd - > /etc/shadow - > /etc/group - -## usermod - -- usage:修改用户 - - > usermod [options] username - - usermod -a -G<组 1,组 2,...> -c<备注> -d<登入目录> -G<组名> -l<名称> -s<登入终端> -u<用户 id> username - -- 选项: - -a -G<组 1,组 2,...> 将用户添加到其它组中 - -c<备注>  修改用户帐号的备注文字。 - -d 登入目录>  修改用户登入时的目录。 - -G<组名>  修改用户所属的群组。 - -l<名称>  修改用户名称。 - -s\  修改用户登入后所使用的 shell。 - -u\  修改用户 ID。 - -- 更新文件: - > /etc/passwd - > /etc/shadow - > /etc/group - > /etc/gshadow - -## passwd - -- usage:设置密码 - - > 普通用户: passwd - > root 用户: passwd username - - 普通用户只能修改自己的密码,因此不需要指定用户名。 - -- 更新文件 - > /etc/shadow - > /etc/passwd - -## groupadd - -- usage:添加用户组 - - > groupadd [options] groupname - - groupadd -g\ -p\ groupname - -- 选项: - -g\ 指定组 id - -p 设置密码 - -- 更新文件 - > /etc/group - > /etc/gshadow - -## groupdel - -- usage:删除用户组 - - > groupdel groupname - - groupdel \ - -- 注意事项: - 只有当用户组的组成员为空时才可以删除该组 - -- 更新文件 - > /etc/group - > /etc/gshadow - -## groupmod - -- usage:修改用户组信息 - - > groupmod [options] groupname - - groupadd -g\ -n\ groupname - -- 选项: - -g 设置新 gid - -n 设置新组名 - -- 更新文件 - > /etc/group - > /etc/gshadow - > /etc/passwd - -_/etc/passwd 文件格式:_ - -> 用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录 Shell - -_/etc/shadow 文件格式:_ - -> 登录名:加密口令:最后一次修改时间:最小时间间隔:最大时间间隔:警告时间:不活动时间:失效时间:标志 - -_/etc/group 文件格式:_ - -> 组名:口令:组标识号:组内用户列表 - -_/etc/gshadow 文件格式:_ - -> 组名:组密码:组管理员名称:组成员 diff --git a/user/apps/user-manage/src/check/check.rs b/user/apps/user-manage/src/check/check.rs deleted file mode 100644 index 36754c928..000000000 --- a/user/apps/user-manage/src/check/check.rs +++ /dev/null @@ -1,908 +0,0 @@ -use super::info::{GAddInfo, GDelInfo, GModInfo, PasswdInfo, UAddInfo, UDelInfo, UModInfo}; -use crate::{ - error::error::{ErrorHandler, ExitStatus}, - parser::cmd::{CmdOption, GroupCommand, PasswdCommand, UserCommand}, -}; -use std::{ - collections::{HashMap, HashSet}, - fs, - io::Write, -}; - -/// useradd命令检查器 -#[derive(Debug)] -pub struct UAddCheck; - -impl UAddCheck { - /// **校验解析后的useradd命令** - /// - /// ## 参数 - /// - `cmd`: 解析后的useradd命令 - /// - /// ## 返回 - /// - `UAddInfo`: 校验后的信息 - pub fn check(cmd: UserCommand) -> UAddInfo { - let mut info = UAddInfo::default(); - info.username = cmd.username; - - // 填充信息 - for (option, arg) in cmd.options.iter() { - match option { - CmdOption::Shell => { - info.shell = arg.clone(); - } - CmdOption::Comment => { - info.comment = arg.clone(); - } - CmdOption::Uid => { - info.uid = arg.clone(); - } - CmdOption::Group => { - info.group = arg.clone(); - } - CmdOption::Gid => { - info.gid = arg.clone(); - } - CmdOption::Dir => { - info.home_dir = arg.clone(); - } - _ => { - let op: &str = option.clone().into(); - ErrorHandler::error_handle( - format!("Unimplemented option: {}", op), - ExitStatus::InvalidCmdSyntax, - ); - } - } - } - - // 完善用户信息 - if info.username.is_empty() { - ErrorHandler::error_handle("Invalid username".to_string(), ExitStatus::InvalidArg); - } - - if info.uid.is_empty() { - ErrorHandler::error_handle("Uid is required".to_string(), ExitStatus::InvalidCmdSyntax); - } - - if info.comment.is_empty() { - info.comment = info.username.clone() + ",,,"; - } - if info.home_dir.is_empty() { - let home_dir = format!("/home/{}", info.username.clone()); - info.home_dir = home_dir; - } - if info.shell.is_empty() { - info.shell = "/bin/NovaShell".to_string(); - } - - // 校验终端是否有效 - check_shell(&info.shell); - - // 校验是否有重复用户名和用户id - scan_passwd( - PasswdField { - username: Some(info.username.clone()), - uid: Some(info.uid.clone()), - }, - false, - ); - - // 判断group和gid是否有效 - Self::check_group_gid(&mut info); - - info - } - - /// 检查组名、组id是否有效,如果组名不存在,则创建新的用户组 - fn check_group_gid(info: &mut UAddInfo) { - if info.group.is_empty() && info.gid.is_empty() { - ErrorHandler::error_handle( - "user must belong to a group".to_string(), - ExitStatus::InvalidCmdSyntax, - ); - } - - let r = fs::read_to_string("/etc/group"); - let mut max_gid: u32 = 0; - match r { - Ok(content) => { - for line in content.lines() { - let data: Vec<&str> = line.split(":").collect(); - let (groupname, gid) = (data[0].to_string(), data[2].to_string()); - if !info.group.is_empty() && info.group == groupname { - if !info.gid.is_empty() && info.gid != gid { - ErrorHandler::error_handle( - format!("The gid of the group [{}] isn't {}", info.group, info.gid), - ExitStatus::InvalidArg, - ) - } else if info.gid.is_empty() || info.gid == gid { - info.gid = gid; - return; - } - } - - if !info.gid.is_empty() && info.gid == gid { - if !info.group.is_empty() && info.group != groupname { - ErrorHandler::error_handle( - format!("The gid of the group [{}] isn't {}", info.group, info.gid), - ExitStatus::InvalidArg, - ) - } else if info.group.is_empty() || info.group == groupname { - info.group = groupname; - return; - } - } - - max_gid = max_gid.max(u32::from_str_radix(data[2], 10).unwrap()); - } - } - Err(_) => { - ErrorHandler::error_handle( - "Can't read file: /etc/group".to_string(), - ExitStatus::GroupFile, - ); - } - } - - // 没有对应的用户组,默认创建新的用户组 - let mut groupname = info.username.clone(); - let mut gid = (max_gid + 1).to_string(); - if !info.group.is_empty() { - groupname = info.group.clone(); - } else { - info.group = groupname.clone(); - } - - if !info.gid.is_empty() { - gid = info.gid.clone(); - } else { - info.gid = gid.clone(); - } - let mut success = true; - let r = std::process::Command::new("/bin/groupadd") - .arg("-g") - .arg(gid.clone()) - .arg(groupname) - .status(); - if let Ok(exit_status) = r { - if exit_status.code() != Some(0) { - success = false; - } - } else { - success = false; - } - - if !success { - ErrorHandler::error_handle("groupadd failed".to_string(), ExitStatus::GroupaddFail); - } - } -} - -/// userdel命令检查器 -#[derive(Debug)] -pub struct UDelCheck; - -impl UDelCheck { - /// **校验userdel命令** - /// - /// ## 参数 - /// - `cmd`: userdel命令 - /// - /// ## 返回 - /// - `UDelInfo`: 校验后的用户信息 - pub fn check(cmd: UserCommand) -> UDelInfo { - let mut info = UDelInfo::default(); - info.username = cmd.username; - - // 检查用户是否存在 - scan_passwd( - PasswdField { - username: Some(info.username.clone()), - uid: None, - }, - true, - ); - - if let Some(_) = cmd.options.get(&CmdOption::Remove) { - info.home = Some(Self::home(&info.username)); - } - - info - } - - /// 获取用户家目录 - fn home(username: &String) -> String { - let mut home = String::new(); - match std::fs::read_to_string("/etc/passwd") { - Ok(data) => { - for line in data.lines() { - let data = line.split(':').collect::>(); - if data[0] == username { - home = data[5].to_string(); - break; - } - } - } - Err(_) => { - ErrorHandler::error_handle( - "Can't read file: /etc/passwd".to_string(), - ExitStatus::PasswdFile, - ); - } - } - home - } -} - -/// usermod命令检查器 -#[derive(Debug)] -pub struct UModCheck; - -impl UModCheck { - /// **校验usermod命令** - /// - /// ## 参数 - /// - `cmd`: usermod命令 - /// - /// ## 返回 - /// - `UModInfo`: 校验后的用户信息 - pub fn check(cmd: UserCommand) -> UModInfo { - let mut info = Self::parse_options(&cmd.options); - info.username = cmd.username; - - // 校验shell是否有效 - if let Some(shell) = &info.new_shell { - check_shell(shell); - } - - // 校验new_home是否有效 - if let Some(new_home) = &info.new_home { - Self::check_home(new_home); - } - - // 校验用户是否存在 - scan_passwd( - PasswdField { - username: Some(info.username.clone()), - uid: None, - }, - true, - ); - - // 校验new_name、new_uid是否有效 - scan_passwd( - PasswdField { - username: info.new_name.clone(), - uid: info.new_uid.clone(), - }, - false, - ); - - // 校验groups、new_gid是否有效 - scan_group( - GroupField { - groups: info.groups.clone(), - gid: info.new_gid.clone(), - }, - true, - ); - - info - } - - /// **校验home目录是否有效** - /// - /// ## 参数 - /// - `home`: home目录路径 - fn check_home(home: &String) { - if fs::File::open(home).is_ok() { - ErrorHandler::error_handle(format!("{} already exists", home), ExitStatus::InvalidArg); - } - } - - /// **解析options** - /// - /// ## 参数 - /// - `options`: 命令选项 - /// - /// ## 返回 - /// - `UModInfo`: 用户信息 - fn parse_options(options: &HashMap) -> UModInfo { - let mut info = UModInfo::default(); - for (option, arg) in options { - match option { - CmdOption::Append => { - info.groups = Some(arg.split(",").map(|s| s.to_string()).collect()); - } - CmdOption::Comment => { - info.new_comment = Some(arg.clone()); - } - CmdOption::Dir => { - info.new_home = Some(arg.clone()); - } - CmdOption::Gid => { - info.new_gid = Some(arg.clone()); - } - CmdOption::Login => { - info.new_name = Some(arg.clone()); - } - CmdOption::Shell => { - info.new_shell = Some(arg.clone()); - } - CmdOption::Uid => { - info.new_uid = Some(arg.clone()); - } - _ => ErrorHandler::error_handle( - "Invalid option".to_string(), - ExitStatus::InvalidCmdSyntax, - ), - } - } - info - } -} - -/// passwd命令检查器 -#[derive(Debug)] -pub struct PasswdCheck; - -impl PasswdCheck { - /// **校验passwd命令** - /// - /// ## 参数 - /// - `cmd`: passwd命令 - /// - /// ## 返回 - /// - `PasswdInfo`: 校验后的信息 - pub fn check(cmd: PasswdCommand) -> PasswdInfo { - let uid = unsafe { libc::geteuid().to_string() }; - let cur_username = Self::cur_username(uid.clone()); - let mut to_change_username = String::new(); - - if let Some(username) = cmd.username { - to_change_username = username.clone(); - - // 不是root用户不能修改别人的密码 - if uid != "0" && cur_username != username { - ErrorHandler::error_handle( - "You can't change password for other users".to_string(), - ExitStatus::PermissionDenied, - ); - } - - // 检验待修改用户是否存在 - scan_passwd( - PasswdField { - username: Some(username.clone()), - uid: None, - }, - true, - ); - } - - let mut new_password = String::new(); - match uid.as_str() { - "0" => { - if to_change_username.is_empty() { - to_change_username = cur_username; - } - print!("New password: "); - std::io::stdout().flush().unwrap(); - std::io::stdin().read_line(&mut new_password).unwrap(); - new_password = new_password.trim().to_string(); - let mut check_password = String::new(); - print!("\nRe-enter new password: "); - std::io::stdout().flush().unwrap(); - std::io::stdin().read_line(&mut check_password).unwrap(); - check_password = check_password.trim().to_string(); - if new_password != check_password { - ErrorHandler::error_handle( - "\nThe two passwords that you entered do not match.".to_string(), - ExitStatus::InvalidArg, - ) - } - } - _ => { - to_change_username = cur_username.clone(); - print!("Old password: "); - std::io::stdout().flush().unwrap(); - let mut old_password = String::new(); - std::io::stdin().read_line(&mut old_password).unwrap(); - old_password = old_password.trim().to_string(); - Self::check_password(cur_username, old_password); - print!("\nNew password: "); - std::io::stdout().flush().unwrap(); - std::io::stdin().read_line(&mut new_password).unwrap(); - new_password = new_password.trim().to_string(); - print!("\nRe-enter new password: "); - std::io::stdout().flush().unwrap(); - let mut check_password = String::new(); - std::io::stdin().read_line(&mut check_password).unwrap(); - check_password = check_password.trim().to_string(); - if new_password != check_password { - println!("{}", new_password); - ErrorHandler::error_handle( - "\nThe two passwords that you entered do not match.".to_string(), - ExitStatus::InvalidArg, - ) - } - } - }; - - PasswdInfo { - username: to_change_username, - new_password, - } - } - - /// **获取uid对应的用户名** - /// - /// ## 参数 - /// - `uid`: 用户id - /// - /// ## 返回 - /// 用户名 - fn cur_username(uid: String) -> String { - let r = fs::read_to_string("/etc/passwd"); - let mut cur_username = String::new(); - - match r { - Ok(content) => { - for line in content.lines() { - let field = line.split(":").collect::>(); - if uid == field[2] { - cur_username = field[0].to_string(); - } - } - } - Err(_) => { - ErrorHandler::error_handle( - "Can't read /etc/passwd".to_string(), - ExitStatus::PasswdFile, - ); - } - } - - cur_username - } - - /// **校验密码** - /// - /// ## 参数 - /// - `username`: 用户名 - /// - `password`: 密码 - fn check_password(username: String, password: String) { - let r = fs::read_to_string("/etc/shadow"); - match r { - Ok(content) => { - for line in content.lines() { - let field = line.split(":").collect::>(); - if username == field[0] { - if password != field[1] { - ErrorHandler::error_handle( - "Password error".to_string(), - ExitStatus::InvalidArg, - ); - } else { - return; - } - } - } - } - Err(_) => { - ErrorHandler::error_handle( - "Can't read /etc/shadow".to_string(), - ExitStatus::ShadowFile, - ); - } - } - } -} - -/// groupadd命令检查器 -#[derive(Debug)] -pub struct GAddCheck; - -impl GAddCheck { - /// **校验groupadd命令** - /// - /// ## 参数 - /// - `cmd`: groupadd命令 - /// - /// ## 返回 - /// - `GAddInfo`: 校验后的组信息 - pub fn check(cmd: GroupCommand) -> GAddInfo { - let mut info = GAddInfo { - groupname: cmd.groupname.clone(), - gid: String::new(), - passwd: None, - }; - - if info.groupname.is_empty() { - ErrorHandler::error_handle("groupname is required".to_string(), ExitStatus::InvalidArg); - } - - if let Some(gid) = cmd.options.get(&CmdOption::Gid) { - info.gid = gid.clone(); - } else { - ErrorHandler::error_handle("gid is required".to_string(), ExitStatus::InvalidArg); - } - - if let Some(passwd) = cmd.options.get(&CmdOption::Passwd) { - info.passwd = Some(passwd.clone()); - } - - // 检查组名或组id是否已存在 - scan_group( - GroupField { - groups: Some(vec![info.groupname.clone()]), - gid: Some(info.gid.clone()), - }, - false, - ); - - info - } -} - -/// groupdel命令检查器 -#[derive(Debug)] -pub struct GDelCheck; - -impl GDelCheck { - /// **校验groupdel命令** - /// - /// ## 参数 - /// - `cmd`: groupdel命令 - /// - /// ## 返回 - /// - `GDelInfo`: 校验后的组信息 - pub fn check(cmd: GroupCommand) -> GDelInfo { - if let Some(gid) = check_groupname(cmd.groupname.clone()) { - // 检查group是不是某个用户的主组,如果是的话则不能删除 - Self::is_main_group(gid); - } else { - // 用户组不存在 - ErrorHandler::error_handle( - format!("group:[{}] doesn't exist", cmd.groupname), - ExitStatus::GroupNotExist, - ); - } - GDelInfo { - groupname: cmd.groupname, - } - } - - /// **检查该组是否为某个用户的主用户组** - /// - /// ## 参数 - /// - `gid`: 组id - /// - /// ## 返回 - /// Some(gid): 组id - /// None - fn is_main_group(gid: String) { - // 读取/etc/passwd文件 - let r = fs::read_to_string("/etc/passwd"); - match r { - Ok(content) => { - for line in content.lines() { - let field = line.split(":").collect::>(); - if field[3] == gid { - ErrorHandler::error_handle( - format!( - "groupdel failed: group is main group of user:[{}]", - field[0] - ), - ExitStatus::InvalidArg, - ) - } - } - } - Err(_) => { - ErrorHandler::error_handle( - "Can't read file: /etc/passwd".to_string(), - ExitStatus::PasswdFile, - ); - } - } - } -} - -/// groupmod命令检查器 -#[derive(Debug)] -pub struct GModCheck; - -impl GModCheck { - /// **校验groupmod命令** - /// - /// ## 参数 - /// - `cmd`: groupmod命令 - /// - /// ## 返回 - /// - `GModInfo`: 校验后的组信息 - pub fn check(cmd: GroupCommand) -> GModInfo { - let mut info = GModInfo::default(); - info.groupname = cmd.groupname; - - if let Some(new_groupname) = cmd.options.get(&CmdOption::NewGroupName) { - info.new_groupname = Some(new_groupname.clone()); - } - - if let Some(new_gid) = cmd.options.get(&CmdOption::Gid) { - info.new_gid = Some(new_gid.clone()); - } - - Self::check_group_file(&mut info); - - info - } - - /// 查看groupname是否存在,同时检测new_gid、new_groupname是否重复 - fn check_group_file(info: &mut GModInfo) { - let mut is_group_exist = false; - let r = fs::read_to_string("/etc/group"); - match r { - Ok(content) => { - for line in content.lines() { - let field = line.split(':').collect::>(); - if field[0] == info.groupname { - is_group_exist = true; - info.gid = field[2].to_string(); - } - - if let Some(new_gid) = &info.new_gid { - if new_gid == field[2] { - ErrorHandler::error_handle( - format!("gid:[{}] is already used", new_gid), - ExitStatus::InvalidArg, - ); - } - } - - if let Some(new_groupname) = &info.new_groupname { - if new_groupname == field[0] { - ErrorHandler::error_handle( - format!("groupname:[{}] is already used", new_groupname), - ExitStatus::InvalidArg, - ); - } - } - } - } - Err(_) => ErrorHandler::error_handle( - "Can't read file: /etc/group".to_string(), - ExitStatus::GroupFile, - ), - } - - if !is_group_exist { - ErrorHandler::error_handle( - format!("groupname:[{}] doesn't exist", info.groupname), - ExitStatus::GroupNotExist, - ); - } - } -} - -/// passwd文件待校验字段 -pub struct PasswdField { - username: Option, - uid: Option, -} - -/// group文件待校验字段 -pub struct GroupField { - groups: Option>, - gid: Option, -} - -/// **校验uid** -/// -/// ## 参数 -/// - `passwd_field`: passwd文件字段 -/// - `should_exist`: 是否应该存在 -fn scan_passwd(passwd_field: PasswdField, should_exist: bool) { - let mut username_check = false; - let mut uid_check = false; - match fs::read_to_string("/etc/passwd") { - Ok(content) => { - for line in content.lines() { - let field = line.split(':').collect::>(); - if let Some(uid) = &passwd_field.uid { - // uid必须是有效的数字 - let r = uid.parse::(); - if r.is_err() { - ErrorHandler::error_handle( - format!("Uid {} is invalid", uid), - ExitStatus::InvalidArg, - ); - } - if field[2] == uid { - uid_check = true; - // username如果不用校验或者被校验过了,才可以return - if should_exist && (passwd_field.username.is_none() || username_check) { - return; - } else { - ErrorHandler::error_handle( - format!("UID {} already exists", uid), - ExitStatus::UidInUse, - ); - } - } - } - - if let Some(username) = &passwd_field.username { - if field[0] == username { - username_check = true; - // uid如果不用校验或者被校验过了,才可以return - if should_exist && (passwd_field.uid.is_none() || uid_check) { - return; - } else { - ErrorHandler::error_handle( - format!("Username {} already exists", username), - ExitStatus::UsernameInUse, - ); - } - } - } - } - - if should_exist { - if let Some(uid) = &passwd_field.uid { - if !uid_check { - ErrorHandler::error_handle( - format!("UID {} doesn't exist", uid), - ExitStatus::InvalidArg, - ); - } - } - if let Some(username) = &passwd_field.username { - if !username_check { - ErrorHandler::error_handle( - format!("User {} doesn't exist", username), - ExitStatus::InvalidArg, - ); - } - } - } - } - Err(_) => ErrorHandler::error_handle( - "Can't read file: /etc/passwd".to_string(), - ExitStatus::PasswdFile, - ), - } -} - -/// **校验gid** -/// -/// ## 参数 -/// - `group_field`: group文件字段 -/// - `should_exist`: 是否应该存在 -fn scan_group(group_field: GroupField, should_exist: bool) { - let mut gid_check = false; - let mut set1 = HashSet::new(); - let mut set2 = HashSet::new(); - if let Some(groups) = group_field.groups.clone() { - set2.extend(groups.into_iter()); - } - match fs::read_to_string("/etc/group") { - Ok(content) => { - for line in content.lines() { - let field = line.split(':').collect::>(); - if let Some(gid) = &group_field.gid { - // gid必须是有效的数字 - let r = gid.parse::(); - if r.is_err() { - ErrorHandler::error_handle( - format!("Gid {} is invalid", gid), - ExitStatus::InvalidArg, - ); - } - if field[2] == gid { - gid_check = true; - if should_exist && group_field.groups.is_none() { - return; - } else { - ErrorHandler::error_handle( - format!("GID {} already exists", gid), - ExitStatus::InvalidArg, - ); - } - } - } - - // 统计所有组 - set1.insert(field[0].to_string()); - } - - if should_exist { - if let Some(gid) = &group_field.gid { - if !gid_check { - ErrorHandler::error_handle( - format!("GID {} doesn't exist", gid), - ExitStatus::InvalidArg, - ); - } - } - if group_field.groups.is_some() { - let mut non_exist_group = Vec::new(); - for group in set2.iter() { - if !set1.contains(group) { - non_exist_group.push(group.clone()); - } - } - - if non_exist_group.len() > 0 { - ErrorHandler::error_handle( - format!("group: {} doesn't exist", non_exist_group.join(",")), - ExitStatus::GroupNotExist, - ); - } - } - } - } - - Err(_) => ErrorHandler::error_handle( - "Can't read file: /etc/group".to_string(), - ExitStatus::GroupFile, - ), - } -} - -/// **校验shell是否有效** -/// -/// ## 参数 -/// - `shell`: shell路径 -fn check_shell(shell: &String) { - if let Ok(file) = fs::File::open(shell.clone()) { - if !file.metadata().unwrap().is_file() { - ErrorHandler::error_handle(format!("{} is not a file", shell), ExitStatus::InvalidArg); - } - } else { - ErrorHandler::error_handle(format!("{} doesn't exist", shell), ExitStatus::InvalidArg); - } -} - -/// **校验组名,判断该用户组是否存在,以及成员是否为空** -/// -/// ## 参数 -/// - `groupname`: 组名 -/// -/// ## 返回 -/// Some(gid): 组id -/// None -fn check_groupname(groupname: String) -> Option { - let r = fs::read_to_string("/etc/group"); - match r { - Ok(content) => { - for line in content.lines() { - let field = line.split(":").collect::>(); - let users = field[3].split(",").collect::>(); - let filter_users = users - .iter() - .filter(|&x| !x.is_empty()) - .collect::>(); - if field[0] == groupname { - if filter_users.is_empty() { - return Some(field[2].to_string()); - } else { - ErrorHandler::error_handle( - format!("group:[{}] is not empty, unable to delete", groupname), - ExitStatus::InvalidArg, - ) - } - } - } - } - Err(_) => { - ErrorHandler::error_handle( - "Can't read file: /etc/group".to_string(), - ExitStatus::GroupFile, - ); - } - } - - None -} diff --git a/user/apps/user-manage/src/check/info.rs b/user/apps/user-manage/src/check/info.rs deleted file mode 100644 index b56fb952e..000000000 --- a/user/apps/user-manage/src/check/info.rs +++ /dev/null @@ -1,95 +0,0 @@ -#[derive(Debug, Default, Clone)] -/// useradd的信息 -pub struct UAddInfo { - /// 用户名 - pub username: String, - pub uid: String, - pub gid: String, - /// 所在组的组名 - pub group: String, - /// 用户描述信息 - pub comment: String, - /// 主目录 - pub home_dir: String, - /// 终端程序名 - pub shell: String, -} - -impl From for String { - fn from(info: UAddInfo) -> Self { - format!( - "{}::{}:{}:{}:{}:{}\n", - info.username, info.uid, info.gid, info.comment, info.home_dir, info.shell - ) - } -} - -#[derive(Debug, Default, Clone)] -/// userdel的信息 -pub struct UDelInfo { - pub username: String, - pub home: Option, -} - -#[derive(Debug, Default, Clone)] -/// usermod的信息 -pub struct UModInfo { - pub username: String, - pub groups: Option>, - pub new_comment: Option, - pub new_home: Option, - pub new_gid: Option, - pub new_group: Option, - pub new_name: Option, - pub new_shell: Option, - pub new_uid: Option, -} - -#[derive(Debug, Default, Clone)] -/// passwd的信息 -pub struct PasswdInfo { - pub username: String, - pub new_password: String, -} - -#[derive(Debug, Default, Clone)] -/// groupadd的信息 -pub struct GAddInfo { - pub groupname: String, - pub gid: String, - pub passwd: Option, -} - -impl GAddInfo { - pub fn to_string_group(&self) -> String { - let mut passwd = String::from(""); - if self.passwd.is_some() { - passwd = "x".to_string(); - } - format!("{}:{}:{}:\n", self.groupname, passwd, self.gid) - } - - pub fn to_string_gshadow(&self) -> String { - let mut passwd = String::from("!"); - if let Some(gpasswd) = &self.passwd { - passwd = gpasswd.clone(); - } - - format!("{}:{}::\n", self.groupname, passwd) - } -} - -#[derive(Debug, Default, Clone)] -/// groupdel的信息 -pub struct GDelInfo { - pub groupname: String, -} - -#[derive(Debug, Default, Clone)] -/// groupmod的信息 -pub struct GModInfo { - pub groupname: String, - pub gid: String, - pub new_groupname: Option, - pub new_gid: Option, -} diff --git a/user/apps/user-manage/src/check/mod.rs b/user/apps/user-manage/src/check/mod.rs deleted file mode 100644 index 8bea63796..000000000 --- a/user/apps/user-manage/src/check/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![allow(dead_code)] -pub mod check; -pub mod info; diff --git a/user/apps/user-manage/src/cmd/groupadd.rs b/user/apps/user-manage/src/cmd/groupadd.rs deleted file mode 100644 index 78e3ac7a5..000000000 --- a/user/apps/user-manage/src/cmd/groupadd.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::{ - check::check::GAddCheck, - error::error::{ErrorHandler, ExitStatus}, - executor::executor::GAddExecutor, - parser::parser::GroupParser, -}; -use libc::geteuid; -use std::process::exit; - -#[path = "../check/mod.rs"] -mod check; -#[path = "../error/mod.rs"] -mod error; -#[path = "../executor/mod.rs"] -mod executor; -#[path = "../parser/mod.rs"] -mod parser; - -#[allow(dead_code)] -fn main() { - let args = std::env::args().collect::>(); - - if unsafe { geteuid() } != 0 { - ErrorHandler::error_handle( - "permission denied (are you root?)".to_string(), - ExitStatus::PermissionDenied, - ) - } - - if args.len() < 2 { - ErrorHandler::error_handle( - format!("usage: {} [options] groupname", args[0]), - ExitStatus::InvalidCmdSyntax, - ); - } - - let cmd = GroupParser::parse(args); - let info = GAddCheck::check(cmd); - let groupname = info.groupname.clone(); - GAddExecutor::execute(info); - - println!("Add group [{}] successfully!", groupname); - - exit(ExitStatus::Success as i32); -} diff --git a/user/apps/user-manage/src/cmd/groupdel.rs b/user/apps/user-manage/src/cmd/groupdel.rs deleted file mode 100644 index ecb1a1739..000000000 --- a/user/apps/user-manage/src/cmd/groupdel.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::{ - check::check::GDelCheck, - error::error::{ErrorHandler, ExitStatus}, - executor::executor::GDelExecutor, - parser::parser::GroupParser, -}; -use libc::geteuid; -use std::process::exit; - -#[path = "../check/mod.rs"] -mod check; -#[path = "../error/mod.rs"] -mod error; -#[path = "../executor/mod.rs"] -mod executor; -#[path = "../parser/mod.rs"] -mod parser; - -#[allow(dead_code)] -fn main() { - let args = std::env::args().collect::>(); - - if unsafe { geteuid() } != 0 { - ErrorHandler::error_handle( - "permission denied (are you root?)".to_string(), - ExitStatus::PermissionDenied, - ) - } - - if args.len() < 2 { - ErrorHandler::error_handle( - format!("usage: {} [options] groupname", args[0]), - ExitStatus::InvalidCmdSyntax, - ); - } - - let cmd = GroupParser::parse(args); - let info = GDelCheck::check(cmd); - let groupname = info.groupname.clone(); - GDelExecutor::execute(info); - - println!("Delete group [{}] successfully!", groupname); - - exit(ExitStatus::Success as i32); -} diff --git a/user/apps/user-manage/src/cmd/groupmod.rs b/user/apps/user-manage/src/cmd/groupmod.rs deleted file mode 100644 index 3d374132b..000000000 --- a/user/apps/user-manage/src/cmd/groupmod.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::{ - check::check::GModCheck, - error::error::{ErrorHandler, ExitStatus}, - executor::executor::GModExecutor, - parser::parser::GroupParser, -}; -use libc::geteuid; -use std::process::exit; - -#[path = "../check/mod.rs"] -mod check; -#[path = "../error/mod.rs"] -mod error; -#[path = "../executor/mod.rs"] -mod executor; -#[path = "../parser/mod.rs"] -mod parser; - -#[allow(dead_code)] -fn main() { - let args = std::env::args().collect::>(); - - if unsafe { geteuid() } != 0 { - ErrorHandler::error_handle( - "permission denied (are you root?)".to_string(), - ExitStatus::PermissionDenied, - ) - } - - if args.len() < 2 { - ErrorHandler::error_handle( - format!("usage: {} [options] groupname", args[0]), - ExitStatus::InvalidCmdSyntax, - ); - } - - let cmd = GroupParser::parse(args); - if !cmd.options.is_empty() { - let info = GModCheck::check(cmd); - let groupname = info.groupname.clone(); - GModExecutor::execute(info); - println!("Modify group [{}] successfully!", groupname); - } - - exit(ExitStatus::Success as i32); -} diff --git a/user/apps/user-manage/src/cmd/mod.rs b/user/apps/user-manage/src/cmd/mod.rs deleted file mode 100644 index 93886dea6..000000000 --- a/user/apps/user-manage/src/cmd/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -mod groupadd; -mod groupdel; -mod groupmod; -mod passwd; -mod useradd; -mod userdel; -mod usermod; diff --git a/user/apps/user-manage/src/cmd/passwd.rs b/user/apps/user-manage/src/cmd/passwd.rs deleted file mode 100644 index 204be906f..000000000 --- a/user/apps/user-manage/src/cmd/passwd.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::{ - check::check::PasswdCheck, error::error::ExitStatus, executor::executor::PasswdExecutor, - parser::parser::PasswdParser, -}; -use std::process::exit; - -#[path = "../check/mod.rs"] -mod check; -#[path = "../error/mod.rs"] -mod error; -#[path = "../executor/mod.rs"] -mod executor; -#[path = "../parser/mod.rs"] -mod parser; - -#[allow(dead_code)] -fn main() { - let args = std::env::args().collect::>(); - - let cmd = PasswdParser::parse(args); - let info = PasswdCheck::check(cmd); - PasswdExecutor::execute(info); - - exit(ExitStatus::Success as i32); -} diff --git a/user/apps/user-manage/src/cmd/useradd.rs b/user/apps/user-manage/src/cmd/useradd.rs deleted file mode 100644 index 4ad109a24..000000000 --- a/user/apps/user-manage/src/cmd/useradd.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::{ - check::check::UAddCheck, - error::error::{ErrorHandler, ExitStatus}, - executor::executor::UAddExecutor, - parser::parser::UserParser, -}; -use libc::geteuid; -use std::process::exit; - -#[path = "../check/mod.rs"] -mod check; -#[path = "../error/mod.rs"] -mod error; -#[path = "../executor/mod.rs"] -mod executor; -#[path = "../parser/mod.rs"] -mod parser; - -#[allow(dead_code)] -fn main() { - let args = std::env::args().collect::>(); - - if unsafe { geteuid() } != 0 { - ErrorHandler::error_handle( - "permission denied (are you root?)".to_string(), - ExitStatus::PermissionDenied, - ) - } - - if args.len() < 2 { - ErrorHandler::error_handle( - format!("usage: {} [options] username", args[0]), - ExitStatus::InvalidCmdSyntax, - ); - } - - let cmd = UserParser::parse(args); - let info = UAddCheck::check(cmd); - let username = info.username.clone(); - UAddExecutor::execute(info); - println!("Add user[{}] successfully!", username); - - exit(ExitStatus::Success as i32); -} diff --git a/user/apps/user-manage/src/cmd/userdel.rs b/user/apps/user-manage/src/cmd/userdel.rs deleted file mode 100644 index 8d1a675fe..000000000 --- a/user/apps/user-manage/src/cmd/userdel.rs +++ /dev/null @@ -1,44 +0,0 @@ -use crate::{ - check::check::UDelCheck, - error::error::{ErrorHandler, ExitStatus}, - executor::executor::UDelExecutor, - parser::parser::UserParser, -}; -use libc::geteuid; -use std::process::exit; - -#[path = "../check/mod.rs"] -mod check; -#[path = "../error/mod.rs"] -mod error; -#[path = "../executor/mod.rs"] -mod executor; -#[path = "../parser/mod.rs"] -mod parser; - -#[allow(dead_code)] -fn main() { - let args = std::env::args().collect::>(); - - if unsafe { geteuid() } != 0 { - ErrorHandler::error_handle( - "permission denied (are you root?)".to_string(), - ExitStatus::PermissionDenied, - ) - } - - if args.len() < 2 { - ErrorHandler::error_handle( - format!("usage: {} [options] username", args[0]), - ExitStatus::InvalidCmdSyntax, - ); - } - - let cmd = UserParser::parse(args); - let info = UDelCheck::check(cmd); - let username = info.username.clone(); - UDelExecutor::execute(info); - println!("Delete user[{}] successfully!", username); - - exit(ExitStatus::Success as i32); -} diff --git a/user/apps/user-manage/src/cmd/usermod.rs b/user/apps/user-manage/src/cmd/usermod.rs deleted file mode 100644 index 37d8ba585..000000000 --- a/user/apps/user-manage/src/cmd/usermod.rs +++ /dev/null @@ -1,46 +0,0 @@ -use crate::{ - check::check::UModCheck, - error::error::{ErrorHandler, ExitStatus}, - executor::executor::UModExecutor, - parser::parser::UserParser, -}; -use libc::geteuid; -use std::process::exit; - -#[path = "../check/mod.rs"] -mod check; -#[path = "../error/mod.rs"] -mod error; -#[path = "../executor/mod.rs"] -mod executor; -#[path = "../parser/mod.rs"] -mod parser; - -#[allow(dead_code)] -fn main() { - let args = std::env::args().collect::>(); - - if unsafe { geteuid() } != 0 { - ErrorHandler::error_handle( - "permission denied (are you root?)".to_string(), - ExitStatus::PermissionDenied, - ) - } - - if args.len() < 2 { - ErrorHandler::error_handle( - format!("usage: {} [options] username", args[0]), - ExitStatus::InvalidCmdSyntax, - ); - } - - let cmd = UserParser::parse(args); - if !cmd.options.is_empty() { - let info = UModCheck::check(cmd); - let username = info.username.clone(); - UModExecutor::execute(info); - println!("Modify user[{}] successfully!", username); - } - - exit(ExitStatus::Success as i32); -} diff --git a/user/apps/user-manage/src/error/error.rs b/user/apps/user-manage/src/error/error.rs deleted file mode 100644 index cb299d5f6..000000000 --- a/user/apps/user-manage/src/error/error.rs +++ /dev/null @@ -1,33 +0,0 @@ -use std::process::exit; - -#[derive(Debug)] -pub enum ExitStatus { - Success = 0, - PasswdFile = 1, - InvalidCmdSyntax = 2, - InvalidArg = 3, - UidInUse = 4, - GroupNotExist = 6, - UsernameInUse = 9, - GroupFile = 10, - CreateHomeFail = 12, - PermissionDenied = -1, - ShadowFile = -2, - GshadowFile = -3, - GroupaddFail = -4, -} - -pub struct ErrorHandler; - -impl ErrorHandler { - /// **错误处理函数** - /// - /// ## 参数 - /// - /// - `error`错误信息 - /// - `exit_status` - 退出状态码 - pub fn error_handle(error: String, exit_status: ExitStatus) { - eprintln!("{error}"); - exit(exit_status as i32); - } -} diff --git a/user/apps/user-manage/src/error/mod.rs b/user/apps/user-manage/src/error/mod.rs deleted file mode 100644 index 7c82bf8fb..000000000 --- a/user/apps/user-manage/src/error/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#![allow(dead_code)] -pub mod error; diff --git a/user/apps/user-manage/src/executor/executor.rs b/user/apps/user-manage/src/executor/executor.rs deleted file mode 100644 index fdfa50e0c..000000000 --- a/user/apps/user-manage/src/executor/executor.rs +++ /dev/null @@ -1,729 +0,0 @@ -use crate::{ - check::info::{GAddInfo, GDelInfo, GModInfo, PasswdInfo, UAddInfo, UDelInfo, UModInfo}, - error::error::{ErrorHandler, ExitStatus}, -}; -use lazy_static::lazy_static; -use std::{ - fs::{self, File, OpenOptions}, - io::{Read, Seek, Write}, - sync::Mutex, -}; - -lazy_static! { - static ref GLOBAL_FILE: Mutex = Mutex::new(GlobalFile::new()); -} - -#[derive(Debug)] -pub struct GlobalFile { - passwd_file: File, - shadow_file: File, - group_file: File, - gshadow_file: File, -} - -impl GlobalFile { - pub fn new() -> Self { - let passwd = open_file("/etc/passwd"); - let shadow = open_file("/etc/shadow"); - let group = open_file("/etc/group"); - let gshadow = open_file("/etc/gshadow"); - Self { - passwd_file: passwd, - shadow_file: shadow, - group_file: group, - gshadow_file: gshadow, - } - } -} - -fn open_file(file_path: &str) -> File { - let r = OpenOptions::new() - .read(true) - .write(true) - .append(true) - .open(file_path); - - let exit_status = match file_path { - "/etc/group" => ExitStatus::GroupFile, - "/etc/gshadow" => ExitStatus::GshadowFile, - "/etc/passwd" => ExitStatus::PasswdFile, - "/etc/shadow" => ExitStatus::ShadowFile, - _ => ExitStatus::InvalidArg, - }; - - if r.is_err() { - ErrorHandler::error_handle(format!("Can't open file: {}", file_path), exit_status); - } - - r.unwrap() -} - -/// useradd执行器 -pub struct UAddExecutor; - -impl UAddExecutor { - /// **执行useradd** - /// - /// ## 参数 - /// - `info`: 用户信息 - pub fn execute(info: UAddInfo) { - // 创建用户home目录 - let home = info.home_dir.clone(); - let dir_builder = fs::DirBuilder::new(); - if dir_builder.create(home.clone()).is_err() { - ErrorHandler::error_handle( - format!("unable to create {}", home), - ExitStatus::CreateHomeFail, - ); - } - - Self::write_passwd_file(&info); - Self::write_shadow_file(&info); - Self::write_group_file(&info); - Self::write_gshadow_file(&info); - } - - /// 写入/etc/passwd文件:添加用户信息 - fn write_passwd_file(info: &UAddInfo) { - let userinfo: String = info.clone().into(); - GLOBAL_FILE - .lock() - .unwrap() - .passwd_file - .write_all(userinfo.as_bytes()) - .unwrap(); - } - - /// 写入/etc/group文件:将用户添加到对应用户组中 - fn write_group_file(info: &UAddInfo) { - if info.group == info.username { - return; - } - - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.group_file); - let mut new_content = String::new(); - for line in content.lines() { - let mut field = line.split(":").collect::>(); - let mut users = field.last().unwrap().split(",").collect::>(); - users = users - .into_iter() - .filter(|username| !username.is_empty()) - .collect::>(); - if field[0].eq(info.group.as_str()) && !users.contains(&info.username.as_str()) { - users.push(info.username.as_str()); - } - - let new_users = users.join(","); - field[3] = new_users.as_str(); - new_content.push_str(format!("{}\n", field.join(":")).as_str()); - } - - guard.group_file.set_len(0).unwrap(); - guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.group_file.write_all(new_content.as_bytes()).unwrap(); - guard.group_file.flush().unwrap(); - } - - /// 写入/etc/shadow文件:添加用户口令相关信息 - fn write_shadow_file(info: &UAddInfo) { - let data = format!("{}::::::::\n", info.username,); - GLOBAL_FILE - .lock() - .unwrap() - .shadow_file - .write_all(data.as_bytes()) - .unwrap(); - } - - /// 写入/etc/gshadow文件:将用户添加到对应用户组中 - fn write_gshadow_file(info: &UAddInfo) { - if info.group == info.username { - return; - } - - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.gshadow_file); - let mut new_content = String::new(); - for line in content.lines() { - let mut field = line.split(":").collect::>(); - let mut users = field.last().unwrap().split(",").collect::>(); - users = users - .into_iter() - .filter(|username| !username.is_empty()) - .collect::>(); - if field[0].eq(info.group.as_str()) && !users.contains(&info.username.as_str()) { - users.push(info.username.as_str()); - } - - let new_users = users.join(","); - field[3] = new_users.as_str(); - new_content.push_str(format!("{}\n", field.join(":")).as_str()); - } - guard.gshadow_file.set_len(0).unwrap(); - guard - .gshadow_file - .seek(std::io::SeekFrom::Start(0)) - .unwrap(); - guard - .gshadow_file - .write_all(new_content.as_bytes()) - .unwrap(); - guard.gshadow_file.flush().unwrap(); - } -} - -/// userdel执行器 -pub struct UDelExecutor; - -impl UDelExecutor { - /// **执行userdel** - /// - /// ## 参数 - /// - `info`: 用户信息 - pub fn execute(info: UDelInfo) { - // 移除home目录 - if let Some(home) = info.home.clone() { - std::fs::remove_dir_all(home).unwrap(); - } - - Self::update_passwd_file(&info); - Self::update_shadow_file(&info); - Self::update_group_file(&info); - Self::update_gshadow_file(&info); - } - - /// 更新/etc/passwd文件: 删除用户信息 - fn update_passwd_file(info: &UDelInfo) { - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.passwd_file); - let lines: Vec<&str> = content.lines().collect(); - let new_content = lines - .into_iter() - .filter(|&line| { - let field = line.split(':').collect::>(); - field[0] != info.username.as_str() - }) - .collect::>() - .join("\n"); - - guard.passwd_file.set_len(0).unwrap(); - guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.passwd_file.write_all(new_content.as_bytes()).unwrap(); - guard.passwd_file.flush().unwrap(); - } - - /// 更新/etc/group文件: 将用户从组中移除 - fn update_group_file(info: &UDelInfo) { - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.group_file); - let mut new_content = String::new(); - for line in content.lines() { - let mut field = line.split(':').collect::>(); - let mut users = field.last().unwrap().split(",").collect::>(); - if users.contains(&info.username.as_str()) { - field.remove(field.len() - 1); - users.remove( - users - .iter() - .position(|&x| x == info.username.as_str()) - .unwrap(), - ); - let users = users.join(","); - field.push(&users.as_str()); - new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str()); - } else { - new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str()); - } - - guard.group_file.set_len(0).unwrap(); - guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.group_file.write_all(new_content.as_bytes()).unwrap(); - guard.group_file.flush().unwrap(); - } - } - - /// 更新/etc/shadow文件: 将用户信息删去 - fn update_shadow_file(info: &UDelInfo) { - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.shadow_file); - let lines: Vec<&str> = content.lines().collect(); - let new_content = lines - .into_iter() - .filter(|&line| !line.contains(&info.username)) - .collect::>() - .join("\n"); - - guard.shadow_file.set_len(0).unwrap(); - guard.shadow_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.shadow_file.write_all(new_content.as_bytes()).unwrap(); - guard.shadow_file.flush().unwrap(); - } - - /// 更新/etc/gshadow文件: 将用户从组中移除 - fn update_gshadow_file(info: &UDelInfo) { - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.gshadow_file); - let mut new_content = String::new(); - for line in content.lines() { - let mut field = line.split(':').collect::>(); - let mut users = field.last().unwrap().split(",").collect::>(); - if users.contains(&info.username.as_str()) { - field.remove(field.len() - 1); - users.remove( - users - .iter() - .position(|&x| x == info.username.as_str()) - .unwrap(), - ); - let users = users.join(","); - field.push(&users.as_str()); - new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str()); - } else { - new_content.push_str(format!("{}\n", field.join(":").as_str()).as_str()); - } - - guard.gshadow_file.set_len(0).unwrap(); - guard - .gshadow_file - .seek(std::io::SeekFrom::Start(0)) - .unwrap(); - guard - .gshadow_file - .write_all(new_content.as_bytes()) - .unwrap(); - guard.gshadow_file.flush().unwrap(); - } - } -} - -/// usermod执行器 -pub struct UModExecutor; - -impl UModExecutor { - /// **执行usermod** - /// - /// ## 参数 - /// - `info`: 用户信息 - pub fn execute(mut info: UModInfo) { - // 创建new_home - if let Some(new_home) = &info.new_home { - let dir_builder = fs::DirBuilder::new(); - if dir_builder.create(new_home.clone()).is_err() { - ErrorHandler::error_handle( - format!("unable to create {}", new_home), - ExitStatus::CreateHomeFail, - ); - } - } - - Self::update_passwd_file(&info); - Self::update_shadow_file(&info); - Self::update_group_file(&mut info); - Self::update_gshadow_file(&info); - } - - /// 更新/etc/passwd文件的username、uid、comment、home、shell - fn update_passwd_file(info: &UModInfo) { - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.passwd_file); - for line in content.lines() { - let mut fields = line.split(':').collect::>(); - if fields[0] == info.username { - if let Some(new_username) = &info.new_name { - fields[0] = new_username; - } - if let Some(new_uid) = &info.new_uid { - fields[2] = new_uid; - } - if let Some(new_gid) = &info.new_gid { - fields[3] = new_gid; - } - if let Some(new_comment) = &info.new_comment { - fields[4] = new_comment; - } - if let Some(new_home) = &info.new_home { - fields[5] = new_home; - } - if let Some(new_shell) = &info.new_shell { - fields[6] = new_shell; - } - new_content.push_str(format!("{}\n", fields.join(":")).as_str()); - } else { - new_content.push_str(format!("{}\n", line).as_str()); - } - - guard.passwd_file.set_len(0).unwrap(); - guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.passwd_file.write_all(new_content.as_bytes()).unwrap(); - guard.passwd_file.flush().unwrap(); - } - } - - /// 更新/etc/group文件中各用户组中的用户 - fn update_group_file(info: &mut UModInfo) { - let mut name = info.username.clone(); - if let Some(new_name) = &info.new_name { - name = new_name.clone(); - } - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.group_file); - for line in content.lines() { - let mut fields = line.split(':').collect::>(); - let mut users = fields[3].split(",").collect::>(); - users = users - .into_iter() - .filter(|username| !username.is_empty()) - .collect::>(); - if let Some(idx) = users.iter().position(|&r| r == info.username) { - if let Some(gid) = &info.new_gid { - // 换组,将用户从当前组删去 - if gid != fields[2] { - users.remove(idx); - } else { - info.new_group = Some(fields[0].to_string()) - } - } else { - // 不换组但是要更新名字 - users[idx] = &name; - } - } - - if let Some(groups) = &info.groups { - if groups.contains(&fields[0].to_string()) && !users.contains(&name.as_str()) { - users.push(&name); - } - } - - let new_users = users.join(","); - fields[3] = new_users.as_str(); - new_content.push_str(format!("{}\n", fields.join(":")).as_str()); - } - - guard.group_file.set_len(0).unwrap(); - guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.group_file.write_all(new_content.as_bytes()).unwrap(); - guard.group_file.flush().unwrap(); - } - - /// 更新/etc/shadow文件的username - fn update_shadow_file(info: &UModInfo) { - if let Some(new_name) = &info.new_name { - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.shadow_file); - for line in content.lines() { - let mut fields = line.split(':').collect::>(); - if fields[0] == info.username { - fields[0] = new_name; - new_content.push_str(format!("{}\n", fields.join(":")).as_str()); - } else { - new_content.push_str(format!("{}\n", line).as_str()); - } - } - - guard.shadow_file.set_len(0).unwrap(); - guard.shadow_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.shadow_file.write_all(new_content.as_bytes()).unwrap(); - guard.shadow_file.flush().unwrap(); - } - } - - /// 更新/etc/gshadow文件中各用户组中的用户 - fn update_gshadow_file(info: &UModInfo) { - let mut name = info.username.clone(); - if let Some(new_name) = &info.new_name { - name = new_name.clone(); - } - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.gshadow_file); - for line in content.lines() { - let mut fields = line.split(':').collect::>(); - let mut users = fields[3].split(",").collect::>(); - users = users - .into_iter() - .filter(|username| !username.is_empty()) - .collect::>(); - if let Some(idx) = users.iter().position(|&r| r == info.username) { - if let Some(group) = &info.new_group { - // 换组,将用户从当前组删去 - if group != fields[0] { - users.remove(idx); - } - } else { - // 不换组但是要更新名字 - users[idx] = &name; - } - } - - let tmp = format!(",{}", name); - if let Some(groups) = &info.groups { - if groups.contains(&fields[0].to_string()) && !users.contains(&name.as_str()) { - if users.is_empty() { - users.push(&name); - } else { - users.push(tmp.as_str()); - } - } - } - - let new_users = users.join(","); - fields[3] = new_users.as_str(); - new_content.push_str(format!("{}\n", fields.join(":")).as_str()); - } - - guard.gshadow_file.set_len(0).unwrap(); - guard - .gshadow_file - .seek(std::io::SeekFrom::Start(0)) - .unwrap(); - guard - .gshadow_file - .write_all(new_content.as_bytes()) - .unwrap(); - guard.gshadow_file.flush().unwrap(); - } -} - -/// passwd执行器 -pub struct PasswdExecutor; - -impl PasswdExecutor { - /// **执行passwd** - /// - /// ## 参数 - /// - `info`: 用户密码信息 - pub fn execute(info: PasswdInfo) { - Self::update_passwd_file(&info); - Self::update_shadow_file(&info); - } - - /// 更新/etc/passwd文件: 修改用户密码 - fn update_passwd_file(info: &PasswdInfo) { - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.passwd_file); - for line in content.lines() { - let mut field = line.split(':').collect::>(); - if field[0] == info.username { - if info.new_password.is_empty() { - field[1] = ""; - } else { - field[1] = "x"; - } - } - new_content.push_str(format!("{}\n", field.join(":")).as_str()); - } - - guard.passwd_file.set_len(0).unwrap(); - guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.passwd_file.write_all(new_content.as_bytes()).unwrap(); - guard.passwd_file.flush().unwrap(); - } - - /// 更新/etc/shadow文件: 修改用户密码 - fn update_shadow_file(info: &PasswdInfo) { - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.shadow_file); - for line in content.lines() { - let mut field = line.split(':').collect::>(); - if field[0] == info.username { - field[1] = info.new_password.as_str(); - } - new_content.push_str(format!("{}\n", field.join(":")).as_str()); - } - - guard.shadow_file.set_len(0).unwrap(); - guard.shadow_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.shadow_file.write_all(new_content.as_bytes()).unwrap(); - guard.shadow_file.flush().unwrap(); - } -} - -/// groupadd执行器 -pub struct GAddExecutor; - -impl GAddExecutor { - /// **执行groupadd** - /// - /// ## 参数 - /// - `info`: 组信息 - pub fn execute(info: GAddInfo) { - Self::write_group_file(&info); - Self::write_gshadow_file(&info); - } - - /// 写入/etc/group文件: 添加用户组信息 - fn write_group_file(info: &GAddInfo) { - GLOBAL_FILE - .lock() - .unwrap() - .group_file - .write_all(info.to_string_group().as_bytes()) - .unwrap() - } - - /// 写入/etc/gshadow文件: 添加用户组密码信息 - fn write_gshadow_file(info: &GAddInfo) { - GLOBAL_FILE - .lock() - .unwrap() - .gshadow_file - .write_all(info.to_string_gshadow().as_bytes()) - .unwrap(); - } -} - -/// groupdel执行器 -pub struct GDelExecutor; - -impl GDelExecutor { - /// **执行groupdel** - /// - /// ## 参数 - /// - `info`: 组信息 - pub fn execute(info: GDelInfo) { - Self::update_group_file(&info); - Self::update_gshadow_file(&info); - } - - /// 更新/etc/group文件:删除用户组 - pub fn update_group_file(info: &GDelInfo) { - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.group_file); - for line in content.lines() { - let field = line.split(':').collect::>(); - if field[0] != info.groupname { - new_content.push_str(format!("{}\n", line).as_str()); - } - } - - guard.group_file.set_len(0).unwrap(); - guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.group_file.write_all(new_content.as_bytes()).unwrap(); - guard.group_file.flush().unwrap(); - } - - /// 更新/etc/gshadow文件:移除用户组 - pub fn update_gshadow_file(info: &GDelInfo) { - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.gshadow_file); - for line in content.lines() { - let field = line.split(':').collect::>(); - if field[0] != info.groupname { - new_content.push_str(format!("{}\n", line).as_str()); - } - } - - guard.gshadow_file.set_len(0).unwrap(); - guard - .gshadow_file - .seek(std::io::SeekFrom::Start(0)) - .unwrap(); - guard - .gshadow_file - .write_all(new_content.as_bytes()) - .unwrap(); - guard.gshadow_file.flush().unwrap(); - } -} - -/// groupmod执行器 -pub struct GModExecutor; - -impl GModExecutor { - /// **执行groupmod** - /// - /// ## 参数 - /// - `info`: 组信息 - pub fn execute(info: GModInfo) { - Self::update_passwd_file(&info); - Self::update_group_file(&info); - Self::update_gshadow_file(&info); - } - - /// 更新/etc/group文件: 更新用户组信息 - fn update_group_file(info: &GModInfo) { - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.group_file); - for line in content.lines() { - let mut field = line.split(':').collect::>(); - if field[0] == info.groupname { - if let Some(new_groupname) = &info.new_groupname { - field[0] = new_groupname; - } - if let Some(new_gid) = &info.new_gid { - field[2] = new_gid; - } - } - new_content.push_str(format!("{}\n", field.join(":")).as_str()); - } - - guard.group_file.set_len(0).unwrap(); - guard.group_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.group_file.write_all(new_content.as_bytes()).unwrap(); - guard.group_file.flush().unwrap(); - } - - /// 更新/etc/gshadow文件: 更新用户组密码信息 - fn update_gshadow_file(info: &GModInfo) { - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.gshadow_file); - for line in content.lines() { - let mut field = line.split(':').collect::>(); - if field[0] == info.groupname { - if let Some(new_groupname) = &info.new_groupname { - field[0] = new_groupname; - } - } - new_content.push_str(format!("{}\n", field.join(":")).as_str()); - } - - guard.gshadow_file.set_len(0).unwrap(); - guard - .gshadow_file - .seek(std::io::SeekFrom::Start(0)) - .unwrap(); - guard - .gshadow_file - .write_all(new_content.as_bytes()) - .unwrap(); - guard.gshadow_file.flush().unwrap(); - } - - /// 更新/etc/passwd文件: 更新用户组ID信息,因为用户组ID可能会被修改 - fn update_passwd_file(info: &GModInfo) { - let mut new_content = String::new(); - let mut guard = GLOBAL_FILE.lock().unwrap(); - let content = read_to_string(&guard.passwd_file); - for line in content.lines() { - let mut field = line.split(':').collect::>(); - if field[3] == info.gid { - if let Some(new_gid) = &info.new_gid { - field[3] = new_gid; - } - } - new_content.push_str(format!("{}\n", field.join(":")).as_str()); - } - - guard.passwd_file.set_len(0).unwrap(); - guard.passwd_file.seek(std::io::SeekFrom::Start(0)).unwrap(); - guard.passwd_file.write_all(new_content.as_bytes()).unwrap(); - guard.passwd_file.flush().unwrap(); - } -} - -fn read_to_string(mut file: &File) -> String { - file.seek(std::io::SeekFrom::Start(0)).unwrap(); - let mut content = String::new(); - file.read_to_string(&mut content).unwrap(); - content -} diff --git a/user/apps/user-manage/src/executor/mod.rs b/user/apps/user-manage/src/executor/mod.rs deleted file mode 100644 index f989249a2..000000000 --- a/user/apps/user-manage/src/executor/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -#![allow(dead_code)] -pub mod executor; diff --git a/user/apps/user-manage/src/lib.rs b/user/apps/user-manage/src/lib.rs deleted file mode 100644 index b85c394e7..000000000 --- a/user/apps/user-manage/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod check; -pub mod cmd; -pub mod error; -pub mod executor; -pub mod parser; diff --git a/user/apps/user-manage/src/parser/cmd.rs b/user/apps/user-manage/src/parser/cmd.rs deleted file mode 100644 index a8a8a3231..000000000 --- a/user/apps/user-manage/src/parser/cmd.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::collections::HashMap; - -/// 命令类型 -pub enum CmdType { - User, - Passwd, - Group, -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub enum CmdOption { - /// 用户描述 - Comment, - /// 用户主目录 - Dir, - /// 组名 - Group, - /// 组id - Gid, - /// 终端程序 - Shell, - /// 用户id - Uid, - /// 删除用户的home目录 - Remove, - /// 添加到其它用户组中 - Append, - /// 修改用户名 - Login, - /// 设置组密码 - Passwd, - /// 修改组名 - NewGroupName, - /// 无效选项 - Invalid, -} - -impl From for CmdOption { - fn from(s: String) -> Self { - match s.as_str() { - "-c" => CmdOption::Comment, - "-d" => CmdOption::Dir, - "-G" => CmdOption::Group, - "-g" => CmdOption::Gid, - "-s" => CmdOption::Shell, - "-u" => CmdOption::Uid, - "-r" => CmdOption::Remove, - "-a" => CmdOption::Append, - "-l" => CmdOption::Login, - "-p" => CmdOption::Passwd, - "-n" => CmdOption::NewGroupName, - _ => CmdOption::Invalid, - } - } -} - -impl From for &str { - fn from(option: CmdOption) -> Self { - match option { - CmdOption::Comment => "-c", - CmdOption::Dir => "-d", - CmdOption::Group => "-G", - CmdOption::Shell => "-s", - CmdOption::Uid => "-u", - CmdOption::Login => "-l", - CmdOption::Append => "-a", - CmdOption::Gid => "-g", - CmdOption::NewGroupName => "-n", - CmdOption::Passwd => "-p", - CmdOption::Remove => "-r", - CmdOption::Invalid => "Invalid option", - } - } -} - -/// useradd/userdel/usermod命令 -#[derive(Debug)] -pub struct UserCommand { - /// 用户名 - pub username: String, - /// 选项 - pub options: HashMap, -} - -/// passwd命令 -#[derive(Debug)] -pub struct PasswdCommand { - pub username: Option, -} - -/// groupadd/groupdel/groupmod命令 -#[derive(Debug)] -pub struct GroupCommand { - pub groupname: String, - pub options: HashMap, -} diff --git a/user/apps/user-manage/src/parser/mod.rs b/user/apps/user-manage/src/parser/mod.rs deleted file mode 100644 index 2913a06f1..000000000 --- a/user/apps/user-manage/src/parser/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -#![allow(dead_code)] -pub mod cmd; -pub mod parser; diff --git a/user/apps/user-manage/src/parser/parser.rs b/user/apps/user-manage/src/parser/parser.rs deleted file mode 100644 index 5f13d4ae9..000000000 --- a/user/apps/user-manage/src/parser/parser.rs +++ /dev/null @@ -1,137 +0,0 @@ -use super::cmd::{CmdOption, GroupCommand, PasswdCommand, UserCommand}; -use crate::error::error::{ErrorHandler, ExitStatus}; -use std::collections::HashMap; - -/// 用户命令(useradd/userdel/usermod)解析器 -pub struct UserParser; - -impl UserParser { - /// **解析用户命令** - /// - /// ## 参数 - /// - `args`: 用户命令参数 - /// - /// ## 返回 - /// - `UserCommand`: 用户命令 - pub fn parse(args: Vec) -> UserCommand { - let username = args.last().unwrap().clone(); - let args = &args[1..args.len() - 1]; - let mut options = HashMap::new(); - - let mut idx = 0; - loop { - if idx >= args.len() { - break; - } - let option: CmdOption = args[idx].clone().into(); - match option { - CmdOption::Invalid => invalid_handle(), - CmdOption::Remove => { - if idx + 1 < args.len() { - let op: &str = option.clone().into(); - ErrorHandler::error_handle( - format!("Invalid arg {} of option: {}", args[idx + 1], op), - ExitStatus::InvalidCmdSyntax, - ) - } - options.insert(option, "".to_string()); - } - CmdOption::Append => { - if idx + 1 >= args.len() || idx + 2 >= args.len() || args[idx + 1] != "-G" { - ErrorHandler::error_handle( - "Invalid option: -a -G ".to_string(), - ExitStatus::InvalidCmdSyntax, - ); - } - idx += 2; - let groups = &args[idx]; - options.insert(option, groups.clone()); - } - _ => { - if idx + 1 >= args.len() { - let op: &str = option.clone().into(); - ErrorHandler::error_handle( - format!("Invalid arg of option: {}", op), - ExitStatus::InvalidCmdSyntax, - ); - } - idx += 1; - let value = args[idx].clone(); - options.insert(option, value); - } - } - idx += 1; - } - - UserCommand { username, options } - } -} - -/// passwd命令解析器 -pub struct PasswdParser; - -impl PasswdParser { - /// **解析passwd命令** - /// - /// ## 参数 - /// - `args`: passwd命令参数 - /// - /// ## 返回 - /// - `PasswdCommand`: passwd命令 - pub fn parse(args: Vec) -> PasswdCommand { - let mut username = None; - if args.len() > 1 { - username = Some(args.last().unwrap().clone()); - } - PasswdCommand { username } - } -} - -/// 组命令(groupadd/groupdel/groupmod)解析器 -pub struct GroupParser; - -impl GroupParser { - /// **解析组命令** - /// - /// ## 参数 - /// - `args`: 组命令参数 - /// - /// ## 返回 - /// - `GroupCommand`: 组命令 - pub fn parse(args: Vec) -> GroupCommand { - let groupname = args.last().unwrap().clone(); - let args = &args[1..args.len() - 1]; - let mut options = HashMap::new(); - - let mut idx = 0; - loop { - if idx >= args.len() { - break; - } - let option: CmdOption = args[idx].clone().into(); - match option { - CmdOption::Invalid => invalid_handle(), - _ => { - if idx + 1 >= args.len() { - let op: &str = option.clone().into(); - ErrorHandler::error_handle( - format!("Invalid arg of option: {}", op), - ExitStatus::InvalidCmdSyntax, - ); - } - idx += 1; - let value = args[idx].clone(); - options.insert(option, value); - } - } - idx += 1; - } - - GroupCommand { groupname, options } - } -} - -#[inline] -fn invalid_handle() { - ErrorHandler::error_handle("Invalid option".to_string(), ExitStatus::InvalidCmdSyntax); -} diff --git a/user/dadk/config/held-0.1.0.dadk b/user/dadk/config/held-0.1.0.dadk index 60bb04010..eb9124a5e 100644 --- a/user/dadk/config/held-0.1.0.dadk +++ b/user/dadk/config/held-0.1.0.dadk @@ -6,7 +6,7 @@ "BuildFromSource": { "Git": { "url" : "https://git.mirrors.dragonos.org.cn/DragonOS-Community/Held.git", - "revision": "f192df4" + "revision": "76304e995f" } } }, diff --git a/user/dadk/config/libgcc.dadk b/user/dadk/config/libgcc.dadk deleted file mode 100644 index a350285cd..000000000 --- a/user/dadk/config/libgcc.dadk +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "libgcc", - "version": "0.1.0", - "description": "gcc lib", - "rust_target": null, - "task_type": { - "BuildFromSource": { - "Local": { - "path": "apps/libgcc" - } - } - }, - "depends": [], - "build": { - "build_command": "make install" - }, - "install": { - "in_dragonos_path": "/" - }, - "clean": { - "clean_command": "cd build & make clean" - }, - "envs": [] -} \ No newline at end of file diff --git a/user/dadk/config/test_cred-0.1.0.dadk b/user/dadk/config/test_cred-0.1.0.dadk deleted file mode 100644 index 8efce9aab..000000000 --- a/user/dadk/config/test_cred-0.1.0.dadk +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "test_cred", - "version": "0.1.0", - "description": "测试cred", - "task_type": { - "BuildFromSource": { - "Local": { - "path": "apps/test_cred" - } - } - }, - "depends": [], - "build": { - "build_command": "make install" - }, - "clean": { - "clean_command": "make clean" - }, - "install": { - "in_dragonos_path": "/bin" - }, - "target_arch": ["x86_64"] -} diff --git a/user/dadk/config/test_eventfd_0_1_0.dadk b/user/dadk/config/test_eventfd_0_1_0.dadk deleted file mode 100644 index ddd8f1f5e..000000000 --- a/user/dadk/config/test_eventfd_0_1_0.dadk +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "test_eventfd", - "version": "0.1.0", - "description": "test_eventfd", - "task_type": { - "BuildFromSource": { - "Local": { - "path": "apps/test_eventfd" - } - } - }, - "depends": [], - "build": { - "build_command": "make install" - }, - "install": { - "in_dragonos_path": "/bin" - }, - "clean": { - "clean_command": "make clean" - }, - "target_arch": ["x86_64"] -} \ No newline at end of file diff --git a/user/dadk/config/test_glibc-0.1.0.dadk b/user/dadk/config/test_glibc-0.1.0.dadk index ee9f94dcf..58be8fff3 100644 --- a/user/dadk/config/test_glibc-0.1.0.dadk +++ b/user/dadk/config/test_glibc-0.1.0.dadk @@ -14,7 +14,7 @@ "build_command": "make install" }, "install": { - "in_dragonos_path": "/" + "in_dragonos_path": "/bin" }, "clean": { "clean_command": "make clean" diff --git a/user/dadk/config/test_lo_0_1_0.dadk b/user/dadk/config/test_lo_0_1_0.dadk deleted file mode 100644 index 2eed4571c..000000000 --- a/user/dadk/config/test_lo_0_1_0.dadk +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "test_lo", - "version": "0.1.0", - "description": "test for lo interface", - "rust_target": null, - "task_type": { - "BuildFromSource": { - "Local": { - "path": "apps/test_lo" - } - } - }, - "depends": [], - "build": { - "build_command": "make install" - }, - "install": { - "in_dragonos_path": "/" - }, - "clean": { - "clean_command": "make clean" - }, - "envs": [], - "build_once": false, - "install_once": false, - "target_arch": ["x86_64"] -} \ No newline at end of file diff --git a/user/dadk/config/test_tokio-0.1.0.dadk b/user/dadk/config/test_tokio-0.1.0.dadk deleted file mode 100644 index ea7c2e443..000000000 --- a/user/dadk/config/test_tokio-0.1.0.dadk +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "test_tokio", - "version": "0.1.0", - "description": "测试tokio", - "task_type": { - "BuildFromSource": { - "Local": { - "path": "apps/test_tokio" - } - } - }, - "depends": [], - "build": { - "build_command": "make install" - }, - "clean": { - "clean_command": "make clean" - }, - "install": { - "in_dragonos_path": "/" - }, - "target_arch": ["x86_64"] -} diff --git a/user/dadk/config/test_utimensat_0_1_0.dadk b/user/dadk/config/test_utimensat_0_1_0.dadk deleted file mode 100644 index 098a36701..000000000 --- a/user/dadk/config/test_utimensat_0_1_0.dadk +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "test_utimensat", - "version": "0.1.0", - "description": "test_utimensat", - "task_type": { - "BuildFromSource": { - "Local": { - "path": "apps/test_utimensat" - } - } - }, - "depends": [], - "build": { - "build_command": "make install" - }, - "install": { - "in_dragonos_path": "/bin" - }, - "clean": { - "clean_command": "make clean" - }, - "target_arch": ["x86_64"] -} \ No newline at end of file diff --git a/user/dadk/config/user_manage-0.1.0.dadk b/user/dadk/config/user_manage-0.1.0.dadk deleted file mode 100644 index 03892a482..000000000 --- a/user/dadk/config/user_manage-0.1.0.dadk +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "user_manage_tool", - "version": "0.1.0", - "description": "用户管理工具", - "task_type": { - "BuildFromSource": { - "Local": { - "path": "apps/user-manage" - } - } - }, - "depends": [], - "build": { - "build_command": "make install" - }, - "install": { - "in_dragonos_path": "/" - }, - "clean": { - "clean_command": "make clean" - }, - "envs": [], - "target_arch": ["x86_64"] -} \ No newline at end of file diff --git a/user/sysconfig/etc/group b/user/sysconfig/etc/group deleted file mode 100644 index ce813f1aa..000000000 --- a/user/sysconfig/etc/group +++ /dev/null @@ -1 +0,0 @@ -root::0: \ No newline at end of file diff --git a/user/sysconfig/etc/gshadow b/user/sysconfig/etc/gshadow deleted file mode 100644 index 653b2e409..000000000 --- a/user/sysconfig/etc/gshadow +++ /dev/null @@ -1 +0,0 @@ -root::: \ No newline at end of file diff --git a/user/sysconfig/etc/passwd b/user/sysconfig/etc/passwd deleted file mode 100644 index 3891904b9..000000000 --- a/user/sysconfig/etc/passwd +++ /dev/null @@ -1 +0,0 @@ -root::0:0:root:/root:/bin/NovaShell \ No newline at end of file diff --git a/user/sysconfig/etc/shadow b/user/sysconfig/etc/shadow deleted file mode 100644 index 9c6022a5a..000000000 --- a/user/sysconfig/etc/shadow +++ /dev/null @@ -1 +0,0 @@ -root:::0:99999:7::: \ No newline at end of file diff --git a/user/sysconfig/home/reach/system/shell.service b/user/sysconfig/home/reach/system/shell.service deleted file mode 100644 index 75bd9a405..000000000 --- a/user/sysconfig/home/reach/system/shell.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=Shell - -[Service] -Type=simple -ExecStart=/bin/NovaShell -Restart=always -ExecStartPre=-/bin/about.elf From 08146c9d58b15fa06232fbc2e85fb39f3cd32c86 Mon Sep 17 00:00:00 2001 From: chiichen Date: Sun, 13 Oct 2024 21:52:23 +0800 Subject: [PATCH 46/60] chore: format & sync master --- kernel/Cargo.toml | 3 +-- kernel/src/driver/video/mod.rs | 4 +-- kernel/src/filesystem/vfs/file.rs | 1 - kernel/src/mm/c_adapter.rs | 44 +------------------------------ kernel/src/mm/ucontext.rs | 2 +- 5 files changed, 5 insertions(+), 49 deletions(-) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index de7e61cf3..0c3149cd8 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -10,7 +10,7 @@ edition = "2021" crate-type = ["staticlib"] [workspace] -members = [ +members = [ "crates/*", ] @@ -57,7 +57,6 @@ wait_queue_macros = { path = "crates/wait_queue_macros" } paste = "=1.0.14" slabmalloc = { path = "crates/rust-slabmalloc" } log = "0.4.21" -xarray = "0.1.0" lru = "0.12.3" # target为x86_64时,使用下面的依赖 diff --git a/kernel/src/driver/video/mod.rs b/kernel/src/driver/video/mod.rs index f86ddc3d2..a36a5be82 100644 --- a/kernel/src/driver/video/mod.rs +++ b/kernel/src/driver/video/mod.rs @@ -10,8 +10,8 @@ use crate::{ spinlock::SpinLock, }, mm::{ - allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::EntryFlags, mmio_buddy::mmio_pool - MemoryManagementArch, + allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, mmio_buddy::mmio_pool, + page::EntryFlags, MemoryManagementArch, }, time::timer::{Timer, TimerFunction}, }; diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index c825343ef..d15167bad 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -8,7 +8,6 @@ use alloc::{ use kdepends::xarray::XArray; use log::error; use system_error::SystemError; -use xarray::XArray; use super::{Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; use crate::filesystem::eventfd::EventFdInode; diff --git a/kernel/src/mm/c_adapter.rs b/kernel/src/mm/c_adapter.rs index 6fa293889..4f33b91b3 100644 --- a/kernel/src/mm/c_adapter.rs +++ b/kernel/src/mm/c_adapter.rs @@ -9,11 +9,7 @@ use system_error::SystemError; use crate::libs::spinlock::SpinLock; - -use super::{ - allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, mmio_buddy::mmio_pool, - no_init::pseudo_map_phys, page::EntryFlags, MemoryManagementArch, PhysAddr, VirtAddr, -}; +use super::{mmio_buddy::mmio_pool, VirtAddr}; lazy_static! { // 用于记录内核分配给C的空间信息 @@ -21,44 +17,6 @@ lazy_static! { } #[no_mangle] -pub unsafe extern "C" fn rs_pseudo_map_phys(vaddr: usize, paddr: usize, size: usize) { - let vaddr = VirtAddr::new(vaddr); - let paddr = PhysAddr::new(paddr); - let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); - pseudo_map_phys(vaddr, paddr, count); -} - -/// [EXTERN TO C] Use kernel mapper to map physical memory to virtual memory. -#[no_mangle] -pub unsafe extern "C" fn rs_map_phys(vaddr: usize, paddr: usize, size: usize, flags: usize) { - let mut vaddr = VirtAddr::new(vaddr); - let mut paddr = PhysAddr::new(paddr); - let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); - // debug!("rs_map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); - - let mut page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); - if flags & PAGE_U_S as usize != 0 { - page_flags = page_flags.set_user(true); - } - - let mut kernel_mapper = KernelMapper::lock(); - let mut kernel_mapper = kernel_mapper.as_mut(); - assert!(kernel_mapper.is_some()); - for _i in 0..count.data() { - let flusher = kernel_mapper - .as_mut() - .unwrap() - .map_phys(vaddr, paddr, page_flags) - .unwrap(); - - flusher.flush(); - - vaddr += MMArch::PAGE_SIZE; - paddr += MMArch::PAGE_SIZE; - } -} - - pub unsafe extern "C" fn kzalloc(size: usize, _gfp: u64) -> usize { // debug!("kzalloc: size: {size}"); return do_kmalloc(size, true); diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 17f469d34..a3d3865ec 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -360,7 +360,7 @@ impl InnerAddressSpace { | VmFlags::VM_MAYWRITE | VmFlags::VM_MAYEXEC; // debug!("map_anonymous: len = {}", len); - + let binding = ProcessManager::current_pcb().fd_table(); let fd_table_guard = binding.read(); From dee37984fe6f4a45bd2e1ee59fbd8962cf45b659 Mon Sep 17 00:00:00 2001 From: chiichen Date: Sun, 13 Oct 2024 22:02:44 +0800 Subject: [PATCH 47/60] chore: remove dead code --- kernel/src/sched/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/src/sched/mod.rs b/kernel/src/sched/mod.rs index 9a0f2ff7e..f453673e6 100644 --- a/kernel/src/sched/mod.rs +++ b/kernel/src/sched/mod.rs @@ -111,7 +111,6 @@ pub trait Scheduler { flags: WakeupFlags, ); - #[allow(dead_code)] /// ## 选择接下来最适合运行的任务 #[allow(dead_code)] fn pick_task(rq: &mut CpuRunQueue) -> Option>; From 2edd19b2960c42023194e3ec7630725919c270b7 Mon Sep 17 00:00:00 2001 From: chiichen Date: Sun, 13 Oct 2024 22:20:34 +0800 Subject: [PATCH 48/60] chore: sync Cargo.toml --- kernel/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 0c3149cd8..ce5cf489c 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -89,4 +89,4 @@ debug = true # Controls whether the compiler passes `-g` # The release profile, used for `cargo build --release` [profile.release] -debug = true +debug = false From aad25d70b703f0c4666f2b428bc18676191618b3 Mon Sep 17 00:00:00 2001 From: MemoryShore <1353318529@qq.com> Date: Tue, 15 Oct 2024 01:45:07 +0800 Subject: [PATCH 49/60] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E6=97=B6panic=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/driver/video/mod.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/kernel/src/driver/video/mod.rs b/kernel/src/driver/video/mod.rs index a36a5be82..42c4249ad 100644 --- a/kernel/src/driver/video/mod.rs +++ b/kernel/src/driver/video/mod.rs @@ -9,10 +9,7 @@ use crate::{ rwlock::{RwLock, RwLockReadGuard}, spinlock::SpinLock, }, - mm::{ - allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, mmio_buddy::mmio_pool, - page::EntryFlags, MemoryManagementArch, - }, + mm::{mmio_buddy::mmio_pool, page::EntryFlags}, time::timer::{Timer, TimerFunction}, }; use alloc::{boxed::Box, sync::Arc}; @@ -102,15 +99,8 @@ impl VideoRefreshManager { } // 地址映射 let paddr = bp.screen_info.lfb_base; - let count = PageFrameCount::new( - page_align_up(frame_buffer_info_guard.buf_size()) / MMArch::PAGE_SIZE, - ); let page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); - let mut kernel_mapper = KernelMapper::lock(); - let mut kernel_mapper = kernel_mapper.as_mut(); - assert!(kernel_mapper.is_some()); - let mut vaddr = buf_vaddr; unsafe { mmio_guard .map_phys_with_flags(paddr, page_align_up(buf_size), page_flags) From 25e31a430cfd73cf6a2b31bd865017a0967587db Mon Sep 17 00:00:00 2001 From: chiichen Date: Sat, 19 Oct 2024 17:40:16 +0800 Subject: [PATCH 50/60] chore: cp /lib to /lib64 --- user/dadk/config/glibc-2.39.9.dadk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user/dadk/config/glibc-2.39.9.dadk b/user/dadk/config/glibc-2.39.9.dadk index 456be2f84..c8334129a 100644 --- a/user/dadk/config/glibc-2.39.9.dadk +++ b/user/dadk/config/glibc-2.39.9.dadk @@ -12,7 +12,7 @@ }, "depends": [], "build": { - "build_command": "bash dragonos.sh" + "build_command": "bash dragonos.sh && mkdir -p $DADK_CURRENT_BUILD_DIR/lib64 && cp $DADK_CURRENT_BUILD_DIR/lib/* $DADK_CURRENT_BUILD_DIR/lib64" }, "install": { "in_dragonos_path": "/" From a756e6d64bcfac502c4776b79aa7e137bd7e0b1f Mon Sep 17 00:00:00 2001 From: chiichen Date: Sat, 19 Oct 2024 17:40:50 +0800 Subject: [PATCH 51/60] refactor(user/app): switch from musl to glibc --- user/apps/clear/Makefile | 6 +++--- user/apps/test-backlog/Makefile | 6 +++--- user/apps/test-for-robustfutex/Makefile | 6 +++--- user/apps/test-mount/Makefile | 6 +++--- user/apps/test_alarm/Makefile | 6 +++--- user/apps/test_glibc/Makefile | 4 ++-- user/apps/test_lo/Makefile | 6 +++--- user/apps/test_socket/Makefile | 6 +++--- user/apps/test_statx/Makefile | 6 +++--- user/apps/test_tokio/Makefile | 6 +++--- user/apps/user-manage/Makefile | 6 +++--- 11 files changed, 32 insertions(+), 32 deletions(-) diff --git a/user/apps/clear/Makefile b/user/apps/clear/Makefile index 127c6ccb3..d0e401330 100644 --- a/user/apps/clear/Makefile +++ b/user/apps/clear/Makefile @@ -10,12 +10,12 @@ else endif ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif run: diff --git a/user/apps/test-backlog/Makefile b/user/apps/test-backlog/Makefile index f65b0d520..cb4bfb56a 100644 --- a/user/apps/test-backlog/Makefile +++ b/user/apps/test-backlog/Makefile @@ -10,13 +10,13 @@ else endif ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu export CC=x86_64-linux-musl-gcc else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif run: diff --git a/user/apps/test-for-robustfutex/Makefile b/user/apps/test-for-robustfutex/Makefile index d9eb6cd1e..9643dfe60 100644 --- a/user/apps/test-for-robustfutex/Makefile +++ b/user/apps/test-for-robustfutex/Makefile @@ -10,12 +10,12 @@ else endif ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif run: diff --git a/user/apps/test-mount/Makefile b/user/apps/test-mount/Makefile index d9eb6cd1e..9643dfe60 100644 --- a/user/apps/test-mount/Makefile +++ b/user/apps/test-mount/Makefile @@ -10,12 +10,12 @@ else endif ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif run: diff --git a/user/apps/test_alarm/Makefile b/user/apps/test_alarm/Makefile index d9eb6cd1e..9643dfe60 100644 --- a/user/apps/test_alarm/Makefile +++ b/user/apps/test_alarm/Makefile @@ -10,12 +10,12 @@ else endif ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif run: diff --git a/user/apps/test_glibc/Makefile b/user/apps/test_glibc/Makefile index cd7ea39fb..a388e345d 100644 --- a/user/apps/test_glibc/Makefile +++ b/user/apps/test_glibc/Makefile @@ -13,9 +13,9 @@ ifeq ($(ARCH), x86_64) export RUST_TARGET=x86_64-unknown-linux-gnu else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif run: diff --git a/user/apps/test_lo/Makefile b/user/apps/test_lo/Makefile index 7522ea16c..775904d9d 100644 --- a/user/apps/test_lo/Makefile +++ b/user/apps/test_lo/Makefile @@ -10,12 +10,12 @@ else endif ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif run: diff --git a/user/apps/test_socket/Makefile b/user/apps/test_socket/Makefile index d9eb6cd1e..9643dfe60 100644 --- a/user/apps/test_socket/Makefile +++ b/user/apps/test_socket/Makefile @@ -10,12 +10,12 @@ else endif ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif run: diff --git a/user/apps/test_statx/Makefile b/user/apps/test_statx/Makefile index 127c6ccb3..d0e401330 100644 --- a/user/apps/test_statx/Makefile +++ b/user/apps/test_statx/Makefile @@ -10,12 +10,12 @@ else endif ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif run: diff --git a/user/apps/test_tokio/Makefile b/user/apps/test_tokio/Makefile index d9eb6cd1e..9643dfe60 100644 --- a/user/apps/test_tokio/Makefile +++ b/user/apps/test_tokio/Makefile @@ -10,12 +10,12 @@ else endif ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif run: diff --git a/user/apps/user-manage/Makefile b/user/apps/user-manage/Makefile index 82cc81f92..ffce73646 100644 --- a/user/apps/user-manage/Makefile +++ b/user/apps/user-manage/Makefile @@ -13,12 +13,12 @@ endif ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu -else +else # 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-musl + export RUST_TARGET=x86_64-unknown-linux-gnu endif build: From 5786e87b34601d3bc6cc8acd7e1a84d605a6c2b3 Mon Sep 17 00:00:00 2001 From: chiichen Date: Sun, 20 Oct 2024 11:54:16 +0800 Subject: [PATCH 52/60] chore: remove test_glibc --- user/apps/test_glibc/.gitignore | 3 - user/apps/test_glibc/Cargo.toml | 10 -- user/apps/test_glibc/LICENSE | 201 ------------------------- user/apps/test_glibc/Makefile | 56 ------- user/apps/test_glibc/README.md | 14 -- user/apps/test_glibc/src/main.rs | 3 - user/dadk/config/test_glibc-0.1.0.dadk | 23 --- 7 files changed, 310 deletions(-) delete mode 100644 user/apps/test_glibc/.gitignore delete mode 100644 user/apps/test_glibc/Cargo.toml delete mode 100644 user/apps/test_glibc/LICENSE delete mode 100644 user/apps/test_glibc/Makefile delete mode 100644 user/apps/test_glibc/README.md delete mode 100644 user/apps/test_glibc/src/main.rs delete mode 100644 user/dadk/config/test_glibc-0.1.0.dadk diff --git a/user/apps/test_glibc/.gitignore b/user/apps/test_glibc/.gitignore deleted file mode 100644 index 1ac354611..000000000 --- a/user/apps/test_glibc/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -Cargo.lock -/install/ \ No newline at end of file diff --git a/user/apps/test_glibc/Cargo.toml b/user/apps/test_glibc/Cargo.toml deleted file mode 100644 index f59f35209..000000000 --- a/user/apps/test_glibc/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "glibc-hello-world" -version = "0.1.0" -edition = "2021" -description = "glibc dynamic link test program" -authors = [ "chiichen " ] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] \ No newline at end of file diff --git a/user/apps/test_glibc/LICENSE b/user/apps/test_glibc/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/user/apps/test_glibc/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/user/apps/test_glibc/Makefile b/user/apps/test_glibc/Makefile deleted file mode 100644 index a388e345d..000000000 --- a/user/apps/test_glibc/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" -RUSTFLAGS+="" - -ifdef DADK_CURRENT_BUILD_DIR -# 如果是在dadk中编译,那么安装到dadk的安装目录中 - INSTALL_DIR = $(DADK_CURRENT_BUILD_DIR) -else -# 如果是在本地编译,那么安装到当前目录下的install目录中 - INSTALL_DIR = ./install -endif - -ifeq ($(ARCH), x86_64) - export RUST_TARGET=x86_64-unknown-linux-gnu -else ifeq ($(ARCH), riscv64) - export RUST_TARGET=riscv64gc-unknown-linux-gnu -else -# 默认为x86_86,用于本地编译 - export RUST_TARGET=x86_64-unknown-linux-gnu -endif - -run: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) - -build: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) - -clean: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) - -test: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) - -doc: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) doc --target $(RUST_TARGET) - -fmt: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt - -fmt-check: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) fmt --check - -run-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) run --target $(RUST_TARGET) --release - -build-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) build --target $(RUST_TARGET) --release - -clean-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) clean --target $(RUST_TARGET) --release - -test-release: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) test --target $(RUST_TARGET) --release - -.PHONY: install -install: - RUSTFLAGS=$(RUSTFLAGS) cargo $(TOOLCHAIN) install --target $(RUST_TARGET) --path . --no-track --root $(INSTALL_DIR) --force diff --git a/user/apps/test_glibc/README.md b/user/apps/test_glibc/README.md deleted file mode 100644 index 74b00a908..000000000 --- a/user/apps/test_glibc/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# DragonOS Rust-Application Template - -您可以使用此模板来创建DragonOS应用程序。 - -## 使用方法 - -1. 使用DragonOS的tools目录下的`bootstrap.sh`脚本初始化环境 -2. 在终端输入`cargo install cargo-generate` -3. 在终端输入`cargo generate --git https://github.com/DragonOS-Community/Rust-App-Template`即可创建项目 -如果您的网络较慢,请使用镜像站`cargo generate --git https://git.mirrors.dragonos.org/DragonOS-Community/Rust-App-Template` -4. 使用`cargo run`来运行项目 -5. 在DragonOS的`user/dadk/config`目录下,使用`dadk new`命令,创建编译配置,安装到DragonOS的`/`目录下。 -(在dadk的编译命令选项处,请使用Makefile里面的`make install`配置进行编译、安装) -6. 编译DragonOS即可安装 diff --git a/user/apps/test_glibc/src/main.rs b/user/apps/test_glibc/src/main.rs deleted file mode 100644 index aec1f5653..000000000 --- a/user/apps/test_glibc/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world DragonOS-musl"); -} diff --git a/user/dadk/config/test_glibc-0.1.0.dadk b/user/dadk/config/test_glibc-0.1.0.dadk deleted file mode 100644 index ee9f94dcf..000000000 --- a/user/dadk/config/test_glibc-0.1.0.dadk +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "glibc-hello-world", - "version": "0.1.0", - "description": "用于测试glibc功能的测试程序", - "task_type": { - "BuildFromSource": { - "Local": { - "path": "apps/test_glibc" - } - } - }, - "depends": [], - "build": { - "build_command": "make install" - }, - "install": { - "in_dragonos_path": "/" - }, - "clean": { - "clean_command": "make clean" - }, - "envs": [] -} From 71ec314f97032b0c47c61c293a9d39cbeb87ad32 Mon Sep 17 00:00:00 2001 From: chiichen Date: Sun, 20 Oct 2024 12:01:22 +0800 Subject: [PATCH 53/60] chore(user/app): musl to glibc --- user/apps/test-backlog/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user/apps/test-backlog/Makefile b/user/apps/test-backlog/Makefile index cb4bfb56a..9a1a40d7e 100644 --- a/user/apps/test-backlog/Makefile +++ b/user/apps/test-backlog/Makefile @@ -11,7 +11,7 @@ endif ifeq ($(ARCH), x86_64) export RUST_TARGET=x86_64-unknown-linux-gnu - export CC=x86_64-linux-musl-gcc + export CC=x86_64-linux-gnu-gcc else ifeq ($(ARCH), riscv64) export RUST_TARGET=riscv64gc-unknown-linux-gnu else From 25bc04293a9f8fa987e92c55a7f158c86abea430 Mon Sep 17 00:00:00 2001 From: chiichen Date: Sun, 20 Oct 2024 14:01:41 +0800 Subject: [PATCH 54/60] feat: use prebuilt glibc --- user/dadk/config/glibc-2.39.9.dadk | 27 --------------------------- user/dadk/config/glibc-2.40.9.dadk | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 27 deletions(-) delete mode 100644 user/dadk/config/glibc-2.39.9.dadk create mode 100644 user/dadk/config/glibc-2.40.9.dadk diff --git a/user/dadk/config/glibc-2.39.9.dadk b/user/dadk/config/glibc-2.39.9.dadk deleted file mode 100644 index c8334129a..000000000 --- a/user/dadk/config/glibc-2.39.9.dadk +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "glibc", - "version": "2.39.9", - "description": "glibc libc", - "rust_target": null, - "task_type": { - "BuildFromSource": { - "Archive": { - "url": "https://github.com/DragonOS-Community/glibc-mirror/archive/refs/tags/v2.39.9-beta.3.tar.gz" - } - } - }, - "depends": [], - "build": { - "build_command": "bash dragonos.sh && mkdir -p $DADK_CURRENT_BUILD_DIR/lib64 && cp $DADK_CURRENT_BUILD_DIR/lib/* $DADK_CURRENT_BUILD_DIR/lib64" - }, - "install": { - "in_dragonos_path": "/" - }, - "clean": { - "clean_command": "cd build & make clean" - }, - "envs": [], - "build_once": true, - - "install_once": true -} \ No newline at end of file diff --git a/user/dadk/config/glibc-2.40.9.dadk b/user/dadk/config/glibc-2.40.9.dadk new file mode 100644 index 000000000..cc1893f5e --- /dev/null +++ b/user/dadk/config/glibc-2.40.9.dadk @@ -0,0 +1,25 @@ +{ + "name": "glibc", + "version": "2.40.9", + "description": "glibc", + "rust_target": null, + "task_type": { + "InstallFromPrebuilt": { + "Archive": { + "url": "https://github.com/DragonOS-Community/glibc-cross/releases/download/v0.3.1/glibc-2.40.9000-x86_64-linux-gnu.tar.gz" + } + } + }, + "depends": [], + "build": { + + }, + "install": { + "in_dragonos_path": "/" + }, + "clean": { + "clean_command": null + }, + "envs": [], + "target_arch": ["x86_64"] +} From 8ac8cf6022051941d90718cfd35cdc8f7e9cd390 Mon Sep 17 00:00:00 2001 From: chiichen Date: Wed, 30 Oct 2024 00:15:49 +0800 Subject: [PATCH 55/60] feat: build glibc from source using latest stable glibc source archive from official gnu ftp server --- user/apps/glibc/.gitignore | 2 ++ user/apps/glibc/Makefile | 2 ++ user/apps/glibc/default_configure.sh | 19 +++++++++++++++++++ user/apps/glibc/exec.sh | 17 +++++++++++++++++ user/apps/glibc/install_deps.sh | 17 +++++++++++++++++ user/dadk/config/glibc-2.40.9.dadk | 13 +++++++------ 6 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 user/apps/glibc/.gitignore create mode 100644 user/apps/glibc/Makefile create mode 100644 user/apps/glibc/default_configure.sh create mode 100644 user/apps/glibc/exec.sh create mode 100644 user/apps/glibc/install_deps.sh diff --git a/user/apps/glibc/.gitignore b/user/apps/glibc/.gitignore new file mode 100644 index 000000000..c72011707 --- /dev/null +++ b/user/apps/glibc/.gitignore @@ -0,0 +1,2 @@ +glibc-2.40 +glibc-2.40.tar.gz \ No newline at end of file diff --git a/user/apps/glibc/Makefile b/user/apps/glibc/Makefile new file mode 100644 index 000000000..8146a8b91 --- /dev/null +++ b/user/apps/glibc/Makefile @@ -0,0 +1,2 @@ +install: + bash exec.sh \ No newline at end of file diff --git a/user/apps/glibc/default_configure.sh b/user/apps/glibc/default_configure.sh new file mode 100644 index 000000000..8535927b5 --- /dev/null +++ b/user/apps/glibc/default_configure.sh @@ -0,0 +1,19 @@ +mkdir -p build +mkdir -p install +if [ -z "$ARCH" ] || [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "riscv64" ] && [ "$ARCH" != "aarch64" ]; then + echo "No ARCH specified, use x86_64 as default" + export ARCH="x86_64" +fi +if [ "$ARCH" == "x86_64" ] || [ "$ARCH" == "riscv64" ] || [ "$ARCH" == "aarch64" ]; then + export TRIPLET=${ARCH}-linux-gnu +fi +export BUILD=`uname -m` +cd build +../configure \ + --prefix=/ \ + --host=${TRIPLET} \ + --build=${BUILD}-linux-gnu \ + CC="${TRIPLET}-gcc -m64" \ + CXX="${TRIPLET}-g++ -m64" \ + CFLAGS="-O2" \ + CXXFLAGS="-O2" \ No newline at end of file diff --git a/user/apps/glibc/exec.sh b/user/apps/glibc/exec.sh new file mode 100644 index 000000000..915e6d6f6 --- /dev/null +++ b/user/apps/glibc/exec.sh @@ -0,0 +1,17 @@ +if [ ! -f "glibc-2.40.tar.gz" ]; then + wget https://ftp.gnu.org/gnu/glibc/glibc-2.40.tar.gz +fi +if [ ! -d "glibc-2.40" ]; then + tar -xvf glibc-2.40.tar.gz + cp ./install_deps.sh ./glibc-2.40/ + cp ./default_configure.sh ./glibc-2.40/ +fi +cd glibc-2.40 +bash install_deps.sh +bash default_configure.sh +cd build +make -j $(nproc) +DESTDIR=$DADK_CURRENT_BUILD_DIR make install -j $(nproc) + +mkdir -p $DADK_CURRENT_BUILD_DIR/lib64 +cp -r $DADK_CURRENT_BUILD_DIR/lib/* $DADK_CURRENT_BUILD_DIR/lib64 \ No newline at end of file diff --git a/user/apps/glibc/install_deps.sh b/user/apps/glibc/install_deps.sh new file mode 100644 index 000000000..97d09956b --- /dev/null +++ b/user/apps/glibc/install_deps.sh @@ -0,0 +1,17 @@ +sudo apt-get install -y make gdb +sudo apt-get install -y texinfo gawk bison sed +sudo apt-get install -y python3-dev python-is-python3 +if [ -z "$ARCH" ] || [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "riscv" ] && [ "$ARCH" != "aarch64" ]; then + echo "No ARCH specified, use x86_64 as default" + export ARCH="x86_64" +fi + +if [ "$ARCH" == "riscv" ] || [ "$ARCH" == "aarch64" ]; then + export TRIPLET=${ARCH}-linux-gnu +fi + +if [ "$ARCH" == "x86_64" ] ; then + export TRIPLET=x86-64-linux-gnu +fi + +sudo apt-get install -y gcc-${TRIPLET} \ No newline at end of file diff --git a/user/dadk/config/glibc-2.40.9.dadk b/user/dadk/config/glibc-2.40.9.dadk index cc1893f5e..f366d5331 100644 --- a/user/dadk/config/glibc-2.40.9.dadk +++ b/user/dadk/config/glibc-2.40.9.dadk @@ -4,22 +4,23 @@ "description": "glibc", "rust_target": null, "task_type": { - "InstallFromPrebuilt": { - "Archive": { - "url": "https://github.com/DragonOS-Community/glibc-cross/releases/download/v0.3.1/glibc-2.40.9000-x86_64-linux-gnu.tar.gz" + "BuildFromSource": { + "Local": { + "path": "apps/glibc" } } }, "depends": [], "build": { - + "build_command": "make install" }, "install": { "in_dragonos_path": "/" }, "clean": { - "clean_command": null + "clean_command": "cd build & make clean" }, "envs": [], - "target_arch": ["x86_64"] + "build_once": true, + "install_once": true } From be0407ddb933772e0fc6f4cb6360ec411c507c7b Mon Sep 17 00:00:00 2001 From: chiichen Date: Thu, 31 Oct 2024 22:43:10 +0800 Subject: [PATCH 56/60] chore: remove redundant log in elf.rs --- kernel/src/libs/elf.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/kernel/src/libs/elf.rs b/kernel/src/libs/elf.rs index 6c74dec1a..9c35ecf4e 100644 --- a/kernel/src/libs/elf.rs +++ b/kernel/src/libs/elf.rs @@ -329,7 +329,7 @@ impl ElfLoader { interp_elf_ex: &mut ExecParam, load_bias: usize, ) -> Result { - log::debug!("loading elf interp"); + // log::debug!("loading elf interp"); let mut head_buf = [0u8; 512]; interp_elf_ex .file_mut() @@ -362,7 +362,7 @@ impl ElfLoader { let mut bss_prot: Option = None; for section in phdr_table { if section.p_type == PT_LOAD { - log::debug!("loading {:?}", section); + // log::debug!("loading {:?}", section); let mut elf_type = MapFlags::MAP_PRIVATE; let elf_prot = Self::make_prot(section.p_flags, true, true); let vaddr = TryInto::::try_into(section.p_vaddr).unwrap(); @@ -457,7 +457,7 @@ impl ElfLoader { load_addr + TryInto::::try_into(interp_hdr.e_entry).unwrap(), ))); } - log::debug!("sucessfully load elf interp"); + // log::debug!("sucessfully load elf interp"); return Ok(BinaryLoaderResult::new(load_addr)); } @@ -736,7 +736,7 @@ impl BinaryLoader for ElfLoader { if seg.p_filesz > 4096 || seg.p_filesz < 2 { return Err(ExecError::NotExecutable); } - log::debug!("seg:{:?}", seg); + // log::debug!("seg:{:?}", seg); let mut buffer = vec![0; seg.p_filesz.try_into().unwrap()]; let r = param .file_mut() @@ -762,7 +762,7 @@ impl BinaryLoader for ElfLoader { e )) })?; - log::debug!("opening interpreter at :{}", interpreter_path); + // log::debug!("opening interpreter at :{}", interpreter_path); interpreter = Some( ExecParam::new(interpreter_path, param.vm().clone(), ExecParamFlags::EXEC) .map_err(|e| { @@ -801,7 +801,7 @@ impl BinaryLoader for ElfLoader { .filter(|seg| seg.p_type == elf::abi::PT_LOAD); for seg_to_load in loadable_sections { - log::debug!("seg_to_load = {:?}", seg_to_load); + // log::debug!("seg_to_load = {:?}", seg_to_load); if unlikely(elf_brk > elf_bss) { // debug!( // "to set brk, elf_brk = {:?}, elf_bss = {:?}", @@ -866,7 +866,7 @@ impl BinaryLoader for ElfLoader { // 加载这个段到用户空间 - log::debug!("bias: {load_bias}"); + // log::debug!("bias: {load_bias}"); let e = Self::load_elf_segment( &mut user_vm, param, @@ -881,7 +881,7 @@ impl BinaryLoader for ElfLoader { SystemError::ENOMEM => ExecError::OutOfMemory, _ => ExecError::Other(format!("load_elf_segment failed: {:?}", e)), })?; - log::debug!("e.0={:?}", e.0); + // log::debug!("e.0={:?}", e.0); // 如果地址不对,那么就报错 if !e.1 { return Err(ExecError::BadAddress(Some(e.0))); From 3e674dfa9eaa6feefad9a00ebb0c1979c462920d Mon Sep 17 00:00:00 2001 From: chiichen Date: Thu, 31 Oct 2024 22:44:25 +0800 Subject: [PATCH 57/60] chore: use rand() to init random_num --- kernel/src/arch/x86_64/process/syscall.rs | 3 ++- kernel/src/arch/x86_64/rand.rs | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/kernel/src/arch/x86_64/process/syscall.rs b/kernel/src/arch/x86_64/process/syscall.rs index 7fe4944cf..d809e09a8 100644 --- a/kernel/src/arch/x86_64/process/syscall.rs +++ b/kernel/src/arch/x86_64/process/syscall.rs @@ -5,6 +5,7 @@ use crate::{ arch::{ interrupt::TrapFrame, process::table::{USER_CS, USER_DS}, + rand::rand_bytes, CurrentIrqArch, }, exception::InterruptArch, @@ -74,7 +75,7 @@ impl Syscall { // 生成16字节随机数 // TODO 暂时设为0 - param.init_info_mut().rand_num = [0u8; 16]; + param.init_info_mut().rand_num = rand_bytes::<16>(); // 把proc_init_info写到用户栈上 let mut ustack_message = unsafe { diff --git a/kernel/src/arch/x86_64/rand.rs b/kernel/src/arch/x86_64/rand.rs index 6916c64dd..a08881e6e 100644 --- a/kernel/src/arch/x86_64/rand.rs +++ b/kernel/src/arch/x86_64/rand.rs @@ -3,3 +3,23 @@ use core::arch::x86_64::_rdtsc; pub fn rand() -> usize { return unsafe { (_rdtsc() * _rdtsc() + 998244353_u64 * _rdtsc()) as usize }; } + +//TODO move it out from arch module +pub fn rand_bytes() -> [u8; N] { + let mut bytes = [0u8; N]; + let mut remaining = N; + let mut index = 0; + + while remaining > 0 { + let random_num = rand(); + let random_bytes = random_num.to_le_bytes(); + + let to_copy = core::cmp::min(remaining, size_of::()); + bytes[index..index + to_copy].copy_from_slice(&random_bytes[..to_copy]); + + index += to_copy; + remaining -= to_copy; + } + + bytes +} From 9c1ca444b6b832d0e2f6bdcc9b9b0533a5b7e0d0 Mon Sep 17 00:00:00 2001 From: longjin Date: Mon, 11 Nov 2024 13:46:58 +0000 Subject: [PATCH 58/60] =?UTF-8?q?=E6=8A=8Arand=5Fbytes=E6=8F=90=E5=8F=96?= =?UTF-8?q?=E4=B8=BA=E5=85=AC=E5=85=B1=E7=9A=84=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/src/arch/x86_64/process/syscall.rs | 3 +- kernel/src/arch/x86_64/rand.rs | 20 ----------- kernel/src/libs/rand.rs | 42 +++++++++++++++++++++++ 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/kernel/src/arch/x86_64/process/syscall.rs b/kernel/src/arch/x86_64/process/syscall.rs index d809e09a8..a929a1fd9 100644 --- a/kernel/src/arch/x86_64/process/syscall.rs +++ b/kernel/src/arch/x86_64/process/syscall.rs @@ -5,10 +5,10 @@ use crate::{ arch::{ interrupt::TrapFrame, process::table::{USER_CS, USER_DS}, - rand::rand_bytes, CurrentIrqArch, }, exception::InterruptArch, + libs::rand::rand_bytes, mm::ucontext::AddressSpace, process::{ exec::{load_binary_file, ExecParam, ExecParamFlags}, @@ -74,7 +74,6 @@ impl Syscall { param.init_info_mut().envs = envp; // 生成16字节随机数 - // TODO 暂时设为0 param.init_info_mut().rand_num = rand_bytes::<16>(); // 把proc_init_info写到用户栈上 diff --git a/kernel/src/arch/x86_64/rand.rs b/kernel/src/arch/x86_64/rand.rs index a08881e6e..6916c64dd 100644 --- a/kernel/src/arch/x86_64/rand.rs +++ b/kernel/src/arch/x86_64/rand.rs @@ -3,23 +3,3 @@ use core::arch::x86_64::_rdtsc; pub fn rand() -> usize { return unsafe { (_rdtsc() * _rdtsc() + 998244353_u64 * _rdtsc()) as usize }; } - -//TODO move it out from arch module -pub fn rand_bytes() -> [u8; N] { - let mut bytes = [0u8; N]; - let mut remaining = N; - let mut index = 0; - - while remaining > 0 { - let random_num = rand(); - let random_bytes = random_num.to_le_bytes(); - - let to_copy = core::cmp::min(remaining, size_of::()); - bytes[index..index + to_copy].copy_from_slice(&random_bytes[..to_copy]); - - index += to_copy; - remaining -= to_copy; - } - - bytes -} diff --git a/kernel/src/libs/rand.rs b/kernel/src/libs/rand.rs index 581a04654..ff5210fa9 100644 --- a/kernel/src/libs/rand.rs +++ b/kernel/src/libs/rand.rs @@ -1,3 +1,5 @@ +use crate::arch::rand::rand; + bitflags! { pub struct GRandFlags: u8{ const GRND_NONBLOCK = 0x0001; @@ -5,3 +7,43 @@ bitflags! { const GRND_INSECURE = 0x0004; } } + +/// Generates an array of random bytes of size `N`. +/// +/// This function fills an array of size `N` with random bytes by repeatedly +/// generating random numbers and converting them to little-endian byte arrays. +/// The function ensures that the entire array is filled with random bytes, +/// even if the size of the array is not a multiple of the size of `usize`. +/// +/// # Type Parameters +/// +/// * `N`: The size of the array to be filled with random bytes. +/// +/// # Returns +/// +/// An array of size `N` filled with random bytes. +/// +/// # Example +/// +/// ```rust +/// let random_bytes = rand_bytes::<16>(); +/// assert_eq!(random_bytes.len(), 16); +/// ``` +pub fn rand_bytes() -> [u8; N] { + let mut bytes = [0u8; N]; + let mut remaining = N; + let mut index = 0; + + while remaining > 0 { + let random_num = rand(); + let random_bytes = random_num.to_le_bytes(); + + let to_copy = core::cmp::min(remaining, size_of::()); + bytes[index..index + to_copy].copy_from_slice(&random_bytes[..to_copy]); + + index += to_copy; + remaining -= to_copy; + } + + bytes +} From 182a3553f18f133b965570c7be420c016d97df5b Mon Sep 17 00:00:00 2001 From: chiichen Date: Mon, 11 Nov 2024 21:58:48 +0800 Subject: [PATCH 59/60] chore: downgrade glibc version from 2.40 to 2.35 --- user/apps/glibc/.gitignore | 4 ++-- user/apps/glibc/exec.sh | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/user/apps/glibc/.gitignore b/user/apps/glibc/.gitignore index c72011707..55c62eee1 100644 --- a/user/apps/glibc/.gitignore +++ b/user/apps/glibc/.gitignore @@ -1,2 +1,2 @@ -glibc-2.40 -glibc-2.40.tar.gz \ No newline at end of file +glibc-2.35 +glibc-2.35.tar.gz \ No newline at end of file diff --git a/user/apps/glibc/exec.sh b/user/apps/glibc/exec.sh index 915e6d6f6..cb88d61fb 100644 --- a/user/apps/glibc/exec.sh +++ b/user/apps/glibc/exec.sh @@ -1,12 +1,12 @@ -if [ ! -f "glibc-2.40.tar.gz" ]; then - wget https://ftp.gnu.org/gnu/glibc/glibc-2.40.tar.gz +if [ ! -f "glibc-2.35.tar.gz" ]; then + wget https://ftp.gnu.org/gnu/glibc/glibc-2.35.tar.gz fi -if [ ! -d "glibc-2.40" ]; then - tar -xvf glibc-2.40.tar.gz - cp ./install_deps.sh ./glibc-2.40/ - cp ./default_configure.sh ./glibc-2.40/ +if [ ! -d "glibc-2.35" ]; then + tar -xvf glibc-2.35.tar.gz + cp ./install_deps.sh ./glibc-2.35/ + cp ./default_configure.sh ./glibc-2.35/ fi -cd glibc-2.40 +cd glibc-2.35 bash install_deps.sh bash default_configure.sh cd build From e1581cfc09bd039d654d531e5173a9cc9e889e6c Mon Sep 17 00:00:00 2001 From: chiichen Date: Fri, 6 Dec 2024 12:23:12 +0800 Subject: [PATCH 60/60] fix: compile error --- kernel/src/process/syscall.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/src/process/syscall.rs b/kernel/src/process/syscall.rs index bc4488e8a..11572ab07 100644 --- a/kernel/src/process/syscall.rs +++ b/kernel/src/process/syscall.rs @@ -25,6 +25,7 @@ use crate::{ procfs::procfs_register_pid, vfs::{file::FileDescriptorVec, MAX_PATHLEN}, }, + libs::rand::rand_bytes, mm::{ ucontext::{AddressSpace, UserStack}, verify_area, MemoryManagementArch, VirtAddr, @@ -184,7 +185,7 @@ impl Syscall { }; let (user_sp, argv_ptr) = unsafe { param - .init_info() + .init_info_mut() .push_at( // address_space // .write()

3HXhTSg@`lh6LPtU;Z0JO2 zhzV#2P7Smz-Sr~LQ`FoP9eG)hdvUhnqE!m~k>G|&fXAundXS@*qDp_hnD3j z##&phw+2sXxi&Y1&a~;3G4FdAwf1nY@Y7>>XZsZ86yAz!WC3sB$84|@H1s7N_S;ZB z`2}`M7W~N8V^gr;YX?5UB0&N_4N5=y*5J#gj&UUY&nq=$J)$N32M>pzzRVAbKHLih zd>?fN5$8q%ZIEw8wW)pf8_*W4$P=hEL6nG7Wy97qzx@MUF!Kjftd@A+VdH&ct2Dz_ ztQ*-0KL9(d|0wM><%O{pi$IR=`oWK4s>!0|=p<8&_l5BLs1mI9J?FS)NtNk8;HnfC zgK9(a(FNJdTMN0uc3(Fq+D&hF2S_O-2zJo2AS4G*$|Ii?LOv;Zc%s>)qA*Q) zyp{Ny0j|26XVI6mC|@*YL6ieV0ys=WRmDv6A(r)dy=M&5JILupj2VQlL9gSYeU?_s zsas+m_@$Q6=l9qGdJ%3i4X=l1O#eO=9N$V<&F}sLl<@O!Dph=!kM+UN+#1>J@DOC- z`RNA!y~t4p6Q$q{JZyBZ#veGD040|fiJZrECDl_#2BsT z821A553=?E3;58_q2n?J>c0UPyj7W}V{b;QD2tIH!68A}<^8uD?1uzvgRm+diT-U_ zM+9rR$cXcX!S!hWe*@E}M5s;$XN6o#g_dSFAJ9@hoo|bh0ehK&ZH~rN1-AY*`#m2V zQ(~UrE&@C$B7Dmmd>g{Y`8GVKhmdtl2{J+6;acw!sB0x<4qY?Q3*y$RS_@k>ESkq) z(X6(`Z#5iJfW`+W+WK}Px4q$Z-{1-j`7GZ`3S#xs{P*dFBAu&kJK;+sv!7*8f!&7# z%KH7%Si!oIIf8j#qwO184czjRNukaA27)E{URJ=jW3yqFJ&463ReN}GKUbjN5Cbiw zqnOW$lnr??FrlRcZej8q3Sdi!M%H9Kz*`e$KGI+IZJ+5qcLv$l|-k zLBlx_d=OTqGt3`BS<{`jS>wEJ35>mt#adYjt`}nNfyal)hR0|K1YWR&n+B?|X<$VV zI?_ANa~00aY#*$+Y!-Y6|5Edz$}n>*YUQ-G+fp@iU;^cEf_CA;-8R|y6NTn3z8K(j(CLmFDit%Hxr)!+WStH{k$01$b|8Whhh609+Eg z(i;0kPE?`GeydkEC*vck`u8Rn5c>DZ(58_7eTIzGl_7xQM>L(r%XRfqcy90WUH zvay94b`$j{4rtH-a7+G}7a4%pq&Wc83)M%?oT9cSacbllM zKb6b9*np@ZiRpBIuivLex;2T)JcA&!Oqa_%PcjcOPXaQCpn!l(a`iF_2nf9Ye^u2! zdmkjoIUI5A05A#<*fpA{L_f}rezDz&C7c^eaKdA;ezUuT6Jd$BF`OG4`L-(r3B5@n zu#O-e=B8#y`c!U{f2fQQ_4Myf)tT zFI+T98=NX#|KdfHbimH`^-V9DWVW}&yREs^YCV^_Ci0BHd9B*$eJS$imjDAMczio5 zp?>tVm3p4WVgpJ`RN5`ak%$f62YA@<&2E1y7^(hfgbgQ%P5LNk0WBr_>C}>z0 z)_V1@)`L1oeW-&haSIY`#?T@`3m&z=rabypkYFP%*w@3oBc8&lbLeb5L4>TZL4SRi zItfo!9_k;SOuo?UD^MHhMke=QWXZAF-Dn8SP7Jvg1oJlo3v|{XycCfFK$WO}Ly<#H9#nu0CR98|>vt$xj3ZlaN0!lxvD%{{lvhUz?T>v=LXD){B^tQOku#yFX7|JK8~pX%SNsb#2d*3zBl zM&stWhr+l+s4EtSdQ6dMyn1LWbO?Um!_AyC@7Wg+n*1QEYkur@OQ;_|1=M+}5x7jF z7gFd$Q$FtnvAG7m&CMAo6G{WWa**^ZXN()B8-s?PmvEka8)hG2v=8kCO_E{>lh`_p zt@%F-62}enlzp;M94;8rQ8IY*&_FF1EJO43d=zum?Cs#jx@nz$@9cJ-jl8*PC{&}PO?ymMLJ~QQiDuFY^CTd)48vqtNn}Gbb74qKJ9Y^tUoHt%L-gvBK z;N9)*7w>)?;=_B!J1gF^-n1mV(~{G9E~us_kK@Huj(w=63Bf^6b}N;_mtR=r8y5dk zIlXl20UbRr_#Ra`Pif7GQ~u7BoI@W^^`N!t9>K{#{KM1Z z)9^1(x8~K;Zkgd32_BP~nqdJ`GfNPF7VpR$)G-G$Z}Nq=H?xbfl5$b@2>nO0D{|uB zmFr$49~1Einq=iOXcBUu2=K%;$OwTffYUFb3|FQqsJ&uusmePd-PS2Qz5E?W}A$*kTluZ6ncKtdf$NI6X91^9&h!#)fMymJMax5u&s-2xDsr`O>}Kc zPj7%{zsCdB?e}tiL%HR<;4Ei>^T@Uo|4<0g4^o^=JKYYYL`QVr1SekFf zLj4MTZiGGqzKw9*Rg;b*Zc9i=(m(e}yk5ng!4){-jT!vYlwv=%IQD~Jcz<*tmHM#X ziCmx@;uk$(mEYL}jSBq7n5j* zQz^2BMMW7Y+|t>s1}Ax2=Ww!(PnUFpBRCV?qMy`&{$;`1zzPZ^-i+&(c{@XwM4D+x zoasN3?IZ>l2BJGQ&)1;OAOR%Gb}?WzR`RU|(5F+QhK+$kVg+X+$Pb)unK2nStOO1T zKOB;MIOfUA@Y%YsNGXwRlm5YFne?+zx-2zpJNzt{eilrZCBwBQa><2V;l=nu;j1~H zR|AUBC!<(=%Ac7spA&Mgj@+Zn3mgeg!lX6IL7N}+#W zyHkXGP2r(2c1Dbx@IJcDR6EsiagVFkT3+aqJV3R-Q^~1X3YhB8=S-RJFZW4zDKISe zH!^4=92T+Buh4Qr1)h9nz%rOg73MuF*eWwAsFdNV3|eS;pnIl5O9%%t$2qy)%C6=N z+ML~!W&Si<{noj;sA{Ona@+nk-qud z=x<1w&#}R*zQ=xcC|bchhrJPDt)@EM&Omisf}oH@E?TFqF7Y=)7}N$14z#IhW;@;4hljmkAIY!@YPyF#z_bAXs~LAcPF>ve;EXNNkZvH5W?lhx0}X0~0)HVa zu{uAM%4Fp32F6^YfrJ}iL>xQrJGo(~VH_K20v&o%cIbsUGwiZhnC8%IGs_S)_pPk&BPcYS=>xZcaq$-F>2BA8od(rsvC`%osjJo%9V`N4)<1O;GZkjE$iiRHEF1@RpW~I@ z)XTWYItrdj&?{VT2YpY}xQu@_EL!$lU33DD zyvG}5;~0_Fq=*MV`r%AZp^Anm8W3%$rlecYS`jHS91>zUB*UP5(s2VWI#Lju!IE^V z$C_sLlIF26hO;!fERHVAgPTZV!^8BQosefN#xPfQ?ohTvs24bIwVmU#coGcMNHxJTi(524e+>8&~Z1gD*lgPHE< z<(Q*W?v^eK3AgxO}O&y{fFlTcY_KW-0)M3D@b9L!P*C0i^RiOp@!%{Oe4q3tiprm*&!i=|MW(ZiZ|6*Z z&F80Q)@aVZCUb}k|4^nv?*vq2VYdpel4M6~M!zI?RDX0Wnxc0CrWCEVJfH9PR?Fcv zywpRWnPm`%pxJDWxF$GT8MKx5fEwZ+#15W!?jYD20&Ekx5ipTZYY3?ArSQ5JUALK! zCU;Y2-OVO9M3Ygy+|j9qV*phjd?n410l9(FDXX9YYL@#GDTr*X7hqs9PVxtbgdYm^Z)e;3Qv^ zHy9>BhYtaBokcce5suJmL=Q^%RD(3PE`W86B)^Tl_BdRQhwcCu{?L1igq%aJOHO2~ z1&}o4Z!|)_Ss0UsgzrYGr*Lx)4`T74umLp5^c-61!a_oc^p6~s9i;mqCJ|B%#=mgN zl;|l_B7H?) zk#+BIG>Ch{1U0{*C4+Bat@Og6oAzq1%J!mE&hW5Q&hyYS$6vsmq=BiC+&+3YwOsEa zouE?@OyS6oXX){?3Oz8j|b zQn>D2NBxz$>Mc+Ou-8J;r>^@Em1!QtQDxa74AWL|IcMqTY^BS2N|%$9E~h12h~Bvx zG@KRvX_l-1Viv40M~#FV2UP|aTycszDZF+;a-;BCl0C*Z2aw1$x1>%8@9i5@1U@Xa z^E{1&k8z)+56fzr1de$~*ciS(`?t@poBIB7;Q6Jh1*4CijAQI z$_wWN?Rl4>HZb8bsRRy*CLEGXI4l1V-o zSu)&&(J|zjG{1@-tVR;!MgodBuO)|G;5Sd1&l9Qkf%$A7^bLSHdKLXy`m?kN{~Uk{ zKbz%r60D+^u?wsj>qt1atuHU6)~?O!9%Uu&m%eYx|MF#oac zR`U9-ZWqDbE@&XUW4yLPpSr6);`UK%G_U^%O=B^i^FCpHH7+@W&J556yNF+qe2+oz z!C}JplKnNW@9#I$y#CG981nitsot8`FAd%&um3z4L0&&2y;}47^$;~#z_#=k2*9Gf zZDye6^#e2W$?NB5j%fc8%nkS}vZpog@0EK`^ZNa{PMX(uDjKeN{qQ0{vpyDt(WwDc zzOxRRu^}=u<*h=M>>2BAZ^knN0L1w*L%rkDg8Ajb=rK*NFPt@ zFoH>c&Ch&hz6cr})+@Ilr@yx4PMfbzMg5B;tgy9~&41?yn2A5A4|Z1nHOw8d?{eN6 zF2P~fB*%nE4`AEY-9x_jCGZJO7q5#4?#)&89H^Bbgu|+HV5>V`q58pJYZR*I(~rCc z(3aDhO#>(bYxJM#uB7P3R+Bxm*>vXs|B9W+Y?eMc) z`dKhtmJBzMtyz;3D$Yj@RIwAm)fRFad4YPc7SZ->bkMjM7{BCr7g6DlI0VR9rB|9*DXx78?csrZK zc`J7Wx#I{Jf93J_NN)$dJG?#O-Qyjj_ZU=r8GGCt&sFmHu?xB9@;rGlwT(JXlPc#CBqKU7=9-J-haz3;wk7_v=B?j&zKmTbm- z?q+`baO=Ch--|)L;4on?f4}CM`~9_=Yi`#^NoP})nrrq-57g}P1f6!70h$+9WX1`e zkIT$K0J!F+%mE#9Ak&##vU7H!=8_Bb>C%zxO3f^H;%q|}*^#sIDjiZ)1bAL_h5R^f zN|xcK=CQ15T$CSMh`>sD0Ltq1+PF7dPTE?lP7gM`~Y zX;QXNu1&=XTgxpA9J^ycV&Rp+F;dcFLCbV3yk)vGX=>+mMf`ZYBD0#*b#-Qa#%qdo z`})kV*de$YRU;~|1>2;i1 z2hyO0Fldt9_6K6esM#B;9qAaa@?FA_yf!#! zv0O$Q0?UW0uLwUo@g@ZY7;*?=!6?9m{Db#HxtMs7}pMm%XG;Ln1?)o{v+9r zawK04XjmcW4A0J=#tC;-CYp4C z9vuuw?m15|@Ho%qKeL6TWnTiy4{8 zS}>Z77ee6wG&@TRMjLW1wHQ=hvUT6^LqK*Trq6vs&I))e)iNQ1o zX{*L0yQiR!4*nIh@?SgKg92QhBEek(i(uw3;XP;F`9}xRTrfI)FyF_UMiM*?4#btm zA+GH8DmdFY;+Jr8@V6PD>USib>dd`}$>=1U=B~E7ypgBiCDJ`%`|O~6?8PiXx=HvK z&Y2QDXG)~EEGxS<`_0b|*0BZHw#Tg$IP0J~7h#!u-M!~A`3SW62`2xzX7cob$%{`j zc{Ue?a)w6=59_L( zAgXZs0LkGr2PJ~DA=peNUjcGrnfxiv$_t-A43%>SPJf;+aN^VDl&8zdPM0&B?zzmK zOFe&B2SJ($LDWZz^GPynQhje$e*4W})k2m^!Zw@B7l2^>;h zIHbF9SS*NKHTxzkk!_Ry!DX5BvrxJ$HEcWlESG*3OqV6Yt^WLB63(}QJTm}A!{I;j z`&yBqEe;(_XkSPVp~&!6`YXF{avFd#Nls_G=0%1LICL=K+aNvItcgySxNkC@^1*cO zly$=k7A%1i+8eG6~MbHSQ9XI zO;BFLX~Lb%aGsd^1)*4w7X|DL%7@r5RrI#t;@eP))4bAI;1bwhy2B_548vTa7s3gZ zB>SrtGK6o#vKxbkKxshO`+h!*USCb{Cx=@klc6bypyhCwbh%%y1%UF@Xc_i*Q`;yA zY)f^P(eE6L&;r3!_7EAi26Obu!rt@&iUu7rOUd^)Ha%YHyX z;Dc;?EeN#dqM^Q8nQNuF|Dd8zpd45m2VTW}Ye;AN)&LM84_x9;JqdFkC9pAxL51oR z1~uIOSmZP#6KLjEkTX}fphqRDj|UjyAt-MWeUmWE@U8Ny=&K??@f4IA%BA{H3^II^ z)l6z^%_QV-ufm^6-JY3%2YhRSb=28eM-c{lFhyuN&YTMvwtD1wOu^@;ILVjg;^-8o zB5t0v!d*c5WdTMvx;cS1q5)-Sz#e6??os76qDioEZjMzoHfa?>yNMvKy7oz6=#y_# zP+EkG`fxts-88dv@j1Rpg4u+Is1cWI8Uh?3)(wGQd%f%*5pP(nQqOaMdRu;W}B!%o<>S_nWVv5hYlT1Vys&<$@)VMb=0;rs0DJ>LL*Wd}41r*y z=8|AS9Ky*uoHHPcaN6hI$`zKsgoLuLItfAvXD$^8AX62O#M^;U_7cNLf)Hw{u#i*= zTR?+p9%V3yUoDtH`{6AwC9Z*r2*dirkFhf_dfv#FfRS?_|D%xhMCbO&x06`r@$hPEFfeUCH)l3y%;%t@K}F%`j3TJu>hr2SMTc$p zVJP+M@L-S{Sv<4Yq@P*5A`DLq#vWCC3r8i^QeaveYzge%%$8uG*-v334VfYeI7ZXi z;c}OZLnDX$l`uIrhQ13{b^v;@ev@yX^MwNe;|p`hG{SA>@%DZ*T+boQtAr>u?!zY#PzSP?Me zmf*0shvAAGGc~<1&4_-PX%vyB!QlYy$()9$gbF%jcS1zM$KmYZtSyw^NZdU)SfuR1 zaHX)tx%VaD{oKbCpLXPSQ9pb=hY+0K2F z?Y#fg2w=hqZ5a+#J0{`lnC#A#M)%Yx-VYeXE2m|+z%-cm0|p20(Dx3mF~PSb7@3A| zWO`~EzNyslE>l(Mei``s@lF+VqB48v+mrc>E0oW2y^G*u^Yu1NVRH*NKN9L6ur&dB z4z0>{oAE~eZOx9!&AdgsIk_zmf6YUww>ATxp+i(j3FZ4M~CAP()aUT6V+A)qtM7NDx#s` zx4KSFto{so@@LrL>^AWYxyIRv#`STP#+gT9c%Hk)4Wrh$7;9+ZU`ilrm>R3(4J;ZW zptv^{t6flkUH%P zahvC!F#3W8+{BoF)Q?o5kuq`(Wg4Ok&O$hcgEN%U&jeG`VP`oX5jBn2haI2HVX7A# z#%%oNb)L?2zy=X!@O0`XOwZ2De)BSBV)`R<_uOLg0nTsudTSOl7n9)|ml#=d;`lJc zitxiJRM-k*!VmRWS8ic-)z2|};jHIQ)_RwgJJeRtg=;S`C>TvmSdpAq#CwPg8mr#r z`u|-Z#6`1qO!i13U5{k1Bwih?2Dxxl15~-{0@dRflNbb}8^EB8RwT$$7sxV+BH1L& z?TaSgq7R`efm9bwcNlnf;l>H#VRMy;M@1kY9uB>Lco!{`ICkMO1@&-22B>$@QUuE& zHv-EpT-ai$bD~MBfrEDB1Q98AE-boO`PAFG#GfkpzmC~BDOeCmI5jTQU7!Fef(F(n zB4p^l5}I&)$WatCnqZ3HNpEM9h@hJ=E6CMJLbW9s$`$&Hq6Lx?H<@H31q$5np(Ifv zl7vns0tDi>1bakU-9teFehLygg-8+z8J3wun+}vDbOdpUNjqdmi{Q%9maLE>bP5U* z$U-?n2dt(5fj2K8H0S^_ALOxJmJ!9`tj4v4DnbhpY$zSuwkWpAl7;5b3{ytxSGO6lTp+~y~Iv9hT$ECTog?-7^ErcU!D7a z#ApYkq-e0nNVvz#iHWJ`XEHqhb`3(}Q~xyn@hKtTUtQoI6yX2?!N0mdnPDMs;x`T7 z+1p%AR;4q|0+S3sDi%XJcmdkVy9`g>*iW$5kzFQMBd*F8t~ zT>z2pTSdK#;Buveg;b4N(aJ*r>2=a4?i!Da+%=x2WP}I%DdY;_LXO}H@qsI}25yJ! z+d^aBCe49gXbxO55#|?qa_=CsSj#s6kN-s17b1z5X?YE=+7PPPWFl0{Pz6LIs6r5& zWhAMJpNRJp?yfh%?)pLT9<(|Y1BH|gOk(e&+Cvjo zuJ;4*mwQ_m|EZ!rvOUzt8KI9&ra41o_HW=JcYWwh&J$ie56y!26K|#<{9?62+!c9! z3N#EDG%&fAiWFOupV9dYYC3kweJt66djl=}7J`B;{B>HNVx2usnUH#qdjs#KJ}S_s z*n4q(ilupdim$n8@O5UAK1P{j^(p%4W0di@3@yhdZx&6``V^CjR_YU#mE23Zs6NFb zBY0vm0z(-dl}w|~3~n4at$9?^+C9#-;&H-_Pvzi53|mzMTLXaY2-j>!0D>@g3m>;I zOK|lGjfSm+3^%hlRs~CU_0X>|t$#g&2O+fAI|8*uPcu;O5N=2efq^`^ez^h-h$jFF zA;Cc$jF>VeXI*G_4hgk8TYH^D zjn2V9B73wkkKcF!>~<`54th8(RHR|^CfehNQHWTc)s9&SM zW#}(pUZ%PN%Iw4rVj9Q@dMH{N?4x{eq>&D1a4LHG(Ci_cJ`lcoo-XIE=5kC|?_~!< zB~Qn}qA_p9FQ6$?lXxSvu9^oz$l(8`4(A%axK$5#U8?k*6N(6j@qndhaGuSZM2g}D zzxh(!?OMHq-XiPGBwUCe231ILaG0Gv$5KgmLeDCY7*snUHG@hWyQ!`f(d}3m(e2ol z291g6c1)rc$0T$*EX!zibj|gnX2(>}jhJQ!g!mDlpunkfn4d7y4lZ^-P7mzSK|imY z__qq=;O6jzF1iyGeE(LQGGykzAzS|Yy#q{69S_;*KVNwhVw}MaSY^czGf}H=S`?Ro zrifv5G1exx%6>oTE)k#`j$J5vywFWBxk z@JIkLG%u1aqq!`c&6cjhW%2Y|H4W(w{H%fDtb#7mpJ~4#+Izw#3;@GXE=j}x_n??RFrpD zaanX1`Ts6&jgXH`YKOS{)md@Rs-9YXVqo$t8Gi>D91>Q$#9vFUzZUCwD+p}!D;ZP? zhfuEcXK8hcS*iDBn6{@@Y5`znu%Guc_XnqBxK0IcX?=+ynaTKoI^@|*8*cnpWVg}z zEPG6Z*JIgMdjG$BZkguy%W|i<+kYy@-F~P|Y=k)!`8Zf~3T$7UD|+i<#+F8LRX)m@ z5i_GR8(SI$ z3rive%m9@!pRIXjiF+>INlF@6B*EB{q&b^x2MbH`*}PmGNH@5UQK^VaPBF2^g?** zPigkiND8y{$4Or7XaLaaSv`i6w?xUDEzz*X(Tkau0_+AF*m%zs7!3OR4b3f?k>3yA zZ=f6-Yl0m|`gZVTe~Mu+w-gD}+@Ebm89kF=BY4C!`Hr@xL?K`X?w+EUn&%>-#VQJ{ z%V2jZEch4+4Q|HJ98p~30Q3V`VZ;NNsik=ELTG%+hzILHWcf@?UJy8nSshwsIt9We z;=mXxrF5M{S>Nb zh@t^_Xj@SslP5d?E&@TZ;gDWKZGhWY(F+_>5Szi0bgSo>W)7F;u`q_SG`cK~F3W?P zx5~6eBhwnd#T?1MpV~mI*pS+8tulS<5|{jgx#S=0j18|=jODgk6PzVR9|>#A#eG}6 zr)p31+k49xw9H!(2CeY6F=(5&Jq+4ztul29uU2$PLd&IKE8xF*wZcA8Hb9F4V=rWt zX$3`r6<)cp{=8MD1J){27ugEv0^1Bo7g}Yi3bz8PU;~W&n5MuuEXh?`SXkvx)S^IW zm1%{R1da#iC<*lY23DD>xD`-!o>itXS|WHi_qqOPS2RX%1&k?LqGf_5)Ec_zRzRHr zT!syB20Om7$~4y5#vpl95$47mShnER{89@JPMnZ6Hkz*=RRqPGR6oM)8@JBtmVxv<|{t=NB_ zRi-ZHY6USYQptH+6i}Dp)J&-F*1@c=^Lxyj-*>SWDaKl>Obcwt0z6)Oo>eAkZfKRM zJq+5Q6xy|+rHi|;SFqjdL3)2_R+;`rBUJs}iht=s-a8h(gp;?*1Z2=wnS=$ihw`x< z(+KIx@sD#sCe8VFndWj6m6Q}~kW018w302rHaP7v;g}a|@Q_b!f{my`4jBbDziF50 zF1`4s7yF>vtJyExi&DEJXXe@JM(B`)RV?#Y`A9H4HIa1GX!ljY@DO}MsQpq`O-R2( z?kaYfptMK3Okn7=?*QG}?D%Y0me^&gs|Mx9kh@O6OfBTD102lBQoO}}z^NJMuCPwr z(rd@n^mLHc2zNyUpL#@um~VoXC4n3Hsq6oq=9_*tSguWQit~B|CpTSdzKIi>e$HaL zoWF3fR{M+Qn^qQ>Zz4>|J`2q^kvxjs>jK?Zp;=9@^O;X9FnhEHg|iIu>&K8x`w!?_BI#nT!brfRQG zutfI9nr~v6^s`X9)_jw+9e$R}a28CLCBv=$`b0V2wg-}op!p^qDL^gPZ*O=wYJmAB z9ojy4I{<=V(G6WZG2f)a#e9?fY);Wmugl-fEy$cc`q9 z7EwyU>f0MG1Z+4u0PjLHn%Sz<8O3a!&|>X=fsF!W|03B6OdpH*R=B$?Aya(Vg{iMb zaJvrXm{LnZt-&R!^$c1MOCgf762mBMY#)rn2TGe9?9(4-V9ZIP-i0|QtGZ-f^)x?dQ*ifiD#LnYXX8||rKl4x#UUx`Nq1Ev| zsS%uT>r-EF!hI2Z6>uMgyK`rPcX|G|ntPXJN{Sb`!H#F4OQqIt@KZP62ZnXulOT{! z);G5zpGG^e`{}GDv0aPzc}@5x?8M_59Gdlq1lH^ypE6j9}x z3{@^{RDxd9G*dmyg5lhbAFf8LBwl6*w_m7MGGrwMa&6sllRC(X z&Uo(d3XGt1rA-O73Vr}#T1R|%nQ<#!VXGT-mY?bWWpUD@Oxk(x^XRN$}p zIl(w-+XI4h4Qe&ZwBbor8z8eJ6WEj`oRmdkVL<0*CRPbnqS$hU@pw3gQ@tT-5W+0`>(v#lApdi@-v!l4uDY-OhK9>@?O9ZP; z2uJs*p}K0vUAqQ{oZelGL(!?6kWXTFec zw(*NhGs@7-vMYI(u@V-~AUs<n@9wyQ9vlV479t7FRj z(R`Ffd#3x#@-fTMLL0*yzO1-_f!el2`r8z!(nwIn_O#qR!jl3)2OJngU{I+(jCrFdbkrIjW7r5|h|A{&cngE}I6=VO{+^*_&7K?qyx?G(1f&On(BwC4<^6Tk zi?oZ`RBnitQ>JOC+tpCoB}}*y=!alwNJBNiAK~YM;gL7UN8qKdT<|*HlMl?(%S1@Y zy2kHAaxCpM4yEAw|H3Ki-n=DJ*3Od}Xlssl!{8sK#^D@GWWm{Z04{9MHh3^UwqewPOLhK{8ma=}27oXwMl`@s z56I)3fV*kp?))@H5vmVc&yB$OVxUm|||eZ1SxX>VL+&FrR@`n2;*}3`5%S z`LYIhzN}Rs-DnkH=y1~|I6~)0&?D`=0ui)FdMq!%j7`siAHAKG-c0}I^l2S`Iz5Qm zK!af2Owy0b%%y*B<~{9yFLRRqo}>~DXl%Kf38eItiBFRYPmOOj9Uz(}k`4vav@e@} zf;=fB>!np|XNa7Z!gL2|>{<_VwL}KhOa>5Y8oC{bRp41t<{|iiHtZUIlMvxefUE?x zqj@Qm&?@LA9Ccx^9xFVg+-g+5XQkg07AtfWdc2^kEYgUjoVP|SO*>vvH`O*%BYc|j zsT#rtH^95NQIy5U3Q8-uc}W9fIKV|m;jo|VH@gWhF$WGi3ukk>k`B=W4zU7`^qfVW1`83@;-1-& zL=Ok=zC4SaGK(YXh}Vp=W;2Wj82=`+CtPPGE|m&MggSubyL9d6OlX#%&g3BBWNxMIsiPzw-;=kqTMJENp_X2n(h`T@6fIC+leqqz~pnS z#ncM^6pEpMRz7Zh1ZertJ;CuH_ot|kc^uHp{Rv9A(->2MrP-8r!B%G)EaVxM(7V{K_iSPhO!ZA!r(PR=K16=d9X@q z-TgiW1A3UIY0H|-fp|Tfb}t7qq?WyqeIv1UBS4K!y>{3>fwLsFBIW%_VF^wods-ve z(;B&+hI0F!!21*Szh(gnh@F*QoEHCLqTAC64_q%UN~*-;z?{b$i@^BcAl0}pwzkM5 zg%c@NhaG4PbyqmkoZSw=wp%~n6|z!wdmRcu6fhbu3g2sAedf|xY^BmNBFJEfQ?_npV!(t*zA6+Y2rG~1+c#` zfL5fbkt=qec00%63mb^Lv6g9~ICbAoFB?ds0I{EH>%8gsb-M>8-2-sL0pa%mXoyyq zewq%q@`No6O{gw?GlVa4t{ZxHVa3f}PShUxqmg%C2P-mw*bUXDxc4@jrV2f?t!(ag zaP4;ox3ilZmbm*y&`W8G`kgk?KETP(0y}V(NOBQ0z!mkz9Sj;k+IUP*paaQ&2+^>& z7eE?NFZPdnpC>QG|x6tA9>3`49Ygxf4;MKTdApOvLM}aJWSe$2<@A5$@6x zlkj?dh)KtsubtwTV_{*pxl&U1)u|30S~{J=f$0rPPk2$qoNW5~J5rPaEX3JsapddJLI5O!`BKSSKQLr$U5c@}|M_J9I zjb%Ugj{}AC4}^hR*@#_DWsmyM_uL@c#Ty_INV*O%42|&K-1mk}eRW!R!D%%N0s*$4 zhvD>C(k)0+lE&q zD^eyr(iUip4s_En6Jx~w!|q5g;++o_yNGuO0rZ%Li~EGRv2LlIU6UtNCwCw>(oTB- zZao0Ew|0h6f;Nwlsqwr77JH2Bt>iFnpYfeCeK!ff$3-HonS1U=RR!3G1&qT8AaSMt zrrtK&ehpzA=~zNFZVV}c21{$aD$k7lF>%u?+L*;`fU)RR=O@&SH9^PGvzmXzD#N=5 zA(_N)MFn;awFj^n4ltTR@;UfIni}XPzK&L!P~{HHT0RZ4mhU@GWA(n%&sBe#kL4Kw zivXe+9O9FN1Fr>KBvef!Bo3c?dnR6b_AVm%zi2Jn{m#1@yWe%Y30ima=4!N_i!Dqh z+y*jUC#T#8{+dKOTL?Av!plIDm%DL!HBDzUwm#+yF|slJARuUv5UKl8f}ePGro7qp4&M+^WwFclY~`(Drn z=P0oF6)B=Wx{v7f=q<``O;S=Oqy)ZU=#s43R&(zS<{C7rYI>#rGQ`p^HWz+14BMf= zos(d0LUyR9*9DZVa36mFO)$a4qc_yUR_G5v$%lH_PN)Y&gHf`2VwB8A;GQc&=2FuO zHqFu4u;i0yG{RaZyK&={rF2X7PGZjXMqf+_Nn@CSy&^8oNv>eV73iCdBMweSG*?tg zY0epXdLIe(J{nki3I^5&c=bx1e?_6zP!DM>6$+N{vh$7pS9d z+bO9IIXyH9B05!vQVI3&!x-QN&PE3y+Q>qAfuuEzh*D+5;8hQIqzk-)%xyzP(&9ai zE3rNiEZXayie@+kKt!lpA8#sgZYtll$D1BhZib48cMgFB=lN)4foxDowRhfO``)4Y zQ3)P}Tki-hBqptgDu>X*h<@luv=_-oy6?C+*G)DC;(JTN1Nc60_=CglX^WmEq+m5MjDt?1?Zh zL>QkgO(ih*nqcm=3Dp(axVVhj#8f8nzYgOs16z>zUu&^fkBp>Mswu1L2^}@;`HXYc zNDJ1G^5ew4$~nT|HtsZOP*=d2sYmk(cf$hJ11H1vA?rAQMF`0KT@&ohKM6oL>B-)Y zW3e#X?0!tC^HY6`u`6$r;`Jrlg{vAtuY+7{kZOc=B;HOX!NF^Q?5JJ{s`{u~PS14z z%(w0s;y~OawKhScevS|vkutyDQ`g-tm2Prd090)-p59^CekP|_(JQ(t(TzJY-C#S+ z9>w%iIL6Au9Pz)Fc-|b9I0J9fr9wy564>Jv1j72DbA~`^;r0?(_i`t>3|;`kaD@3l zZ-%(bycWV^`>D0!uI23xq-$f0Wlu@YP13(Oxk22mJPo@KW)KQ%kNDLSB>-#*mR}9f zj23RH+oQh7+%4R`6bbrz1HCY8Al2_1KwEj4r(Q|ih<9T6`U)&Bcwd-+FTD2TGBmXo z=IW=GapQLxTBUPs;1MAAzw}Q%@f*y>%(*u(+`j=JO9YTvHsqdLb+ACx4BXu^7BToo z`g6fg^#rsc8bK?fgS%nHj<}j3KZaSP-*-FlZz&D2N&84Pnd>K%yXK8A(ke zkeV+FW4@rI*$9&6G*ysinu78~Bm8NTEQo27?9L;T?ik^Mm;o4U#LP`DE{x&uBW44x zZWZJqLWGAV>KOo%te8X9Ko7b=6(*7YfBe)l^Eg%K0nX7otbp>nF2dghlw;uwv=PKF z!ER#VrwIGH0{d7vCj-J~0P?YLfjde37x8exNZ5x%H)HAhQQ)YHKhxsj)6_DCFT)&* zrC+PQVE7m6Ks@{a9(h3c=;V}SEIlV9!Z|*%@WsiM3}1;siiK}TZesW*j9M&wYjPLE zcVXzF;dW2=iRviNr3{}c!+a<+^Pxd}K6Ird&{gI`EM|fGDPumxSQP=2jv;XPhCOo{XjIJ|{g~4bb8)w;*VmK%WEhSm`4w^szJQ%-`GHdC z8@|&F9Ug$O4-ogb6(cRr4oaBJpPP)(=)(V)s6)tl#{TQsa>E8~G5-^^F)c8bjFFvG(;Q7h- z#ZO>fA_IYGdK`7Q2l5Q$fZLJ0p`+K^@P^)${dtr)#{|sb=@ZUS0F{tgv_hK@wjFH> z!w#xNWFU*sD$PLNCmp*00|6jQ1duTXA`rC0Zbpy3;6s zBk6eou{}V23FL8m7czq0v`E}YjRPPG^33r*X3WQcNI?uo4lzRkmx7psswK&NOBfm} zi0Orc8^jF3s20SG11J&mK8R33jPPcAuCAT|sOkwcLlz-$`|q;|^~@}?hgn#jM9CR2 zxC`fJ7q@$y4Rm**s|btxc#biaekI|KJAc5qSU52r;q8ERv2cMo1WCX=7Cy)OfayN~ z;A7z*6E|Vv86y!3Z-rwqgpa}$iiMBi)(MQ4VSHlY2h}W+?^&>$6$_saH6nyB{cEuS z4Agn_=JyBrS>Je_|{ z>)jo7jyfcAJAgqX^Jnn1LrC~fD1~i@bCr%RAvC}f|!rVP@!j{P8G&* zKti6Sp2K*U)vF#4BY?|z=!gq}%!I4waE}QuP0G+9w_Pet;G+Tc@n}G0oWNE(fK7dY zO^gsNahr?w9(oxIuOKU`4_0(69)66g7(C>Jhj3%*+XC?regr*_g-gHdn}c-X;rWx` zFZ49QHEMlT0!(?@U`mWsOu)=Q%mBfMSjVl#=RhBhFZ;-)q)SP(mv*>KaE+)fBL6B#L$rNv~jx%0c!5G5qYyGEKNb- zV=yN)?luFE+?dch9y$}JJ2SzRXv}YHHgOej~-nZuqyEY2-O2U9k1J=*RdCZ z_XeA&et0!R{N*^BfPc8VS&4tM`h@!*pQznQ@$XLJmL#4dPtMPXe?ASsH&IJ7BXi;( znH!&je|)ap<>GI5dDqJ^>|HN!dky^;Zk7d6;z1;Ta2#5PH}b~e9W<0S*_naX!9T;D z%ht_x521DNH;Nx73<+g?#M1V%L@O zvFpmuuLPhzzjEN!;$L|+JFxar09@+)R+Vl+(|5eCwm=gE743HRqUrGO<;_^$h=TGL z{X_7=IQ%1%qtOHSNBek#r3v0(A%NlUn?8#k;NvXs2;!;U>|XQ${=K|vhJtd-t`fg) z-wxeAEbS-5c3K^^%|GtLEr7Z@?Y8HdsXZ+}{91L!p@!G3{0;Rkv@a37#$WFvc)hA4r~0PFKL+;4;Ga~qph*0!iaQj; z-=TO(P4RcBwHzaYjLU1Ss)dMEwYF&g@XL06OCknbQE>$#Dz2E1fkMLeSHF2R{BK^p z5(5SQK^ds7)wdSMB6wsL<00)qm-|Y^_~=sWLsW#14^bmh48N==Fcp0(jhr6ZkD3%9* zMfy{~86Te(?JGig`-+;QJouZ}Xj8-ay=sGE>94lnyO80oGkRv7@mr@*aO)Hc#ssQJ z4@E=qF|>G8G4Op<@h`K#DS%DIiHAF_zCnfaBfwR zUMae3N78SL%G}$TPjfuZLU+iS_T^5~t58y`KS&Zl9mpJ3 zs4HuOFV?;pcaCU*`{qaBV-eJfRJBV;JD&M{b#3uqc*n@UiSMI|@59i;voQ2<$kDYN z!k6=G@Z~@0b|;-@Z3o~+@?HfI_$mnD&p-&5yk&#%E*$bc6UqCGRm5T5@nOCHuk)OG z_0Qnvf5r{y=fK3k%bts5d(MoQKP$}g9JC;YJHw$Q5lPp$x^%dc$8@EUbfspu-`^OP z_cPSq(dh`>8Tm$-5>s&`=>mVTw7BGiv;}4AgT`383zk_;+G@*8xWVV9jknp;n?M?L zY4yOq&QX*9MjYzk?9_S_MqgI?RwsnR6+nS2zKyZ`_LX}4ue?d0RtgaGAOFx~i!t9r z^6%XRRJ-dTo&2F`ye2lyUF&N9T6_Uc2SfBBKJw@P*A!dGw*dPUlnm;I>GY};srrYh z9zd!Gpd7<3t-uDu&+DkcD3h!?5W*bA`%}ZL|A76Oe|TKI)co~9>Tz7)!;oUPGvRLy zQ|JT!E3T1q{%dZe!)meyE=d~73xEEOiMHvtqyM)S1pd|#N#MJg9cp3-F-48L^wA55}4xqz1wWB>6m=cDNHqh-3sWxvk% z_`kvwy2n@FAU(d}JGRH8O@e|RuN`lDEF%tU45ezO`<@x(_uQid!BMZX5)g!t!T{!T zehXOjttBp~%MzUv6yiM>r{T}4b2?|)pfG17-;c0Y`J>-szW;uL>;B;?HNll?N|-oE zn4!2;wlPrO{o&XM^f2=UdNvq-f!;4ovIz9*g*h*TFRz9vE`Tp1O@adG-80fc?_S`` zy)CE}(ZcI!@TDWKhCLPR2=V2q5DMr|G;f)@w>bgbT;~P+ByVXXZ#cc8`AXEi$04T7 zcrBlhK2wcf5;hO&9M3|#W+TQVpERWSe*G}v9v5_aue;6zoz6G>xk$s&^wweW9{`bm zP1|MDaLpIaZi=lqZ}PgMS?|1tCqoBgOV_q%W|*%g5)F4JGRueVb~q%)#C4+~!5ooR z3180L;QjfHLF$H^fiX9CaK+OB$29Qnbys=fU*)0Yjnz6h^yj-kA0|Hk`Gdk{-0c1j zs7zp=&;1GB634Uc@C)PhTaL0Ln?@Vc-wOVm_yKHU;WZvN5p*(^KG--QfJ*uwuqVJH#U(0>;hCwR#u$_X5oa3@6>^jwdhJ5+YQL7ew=x zse87O3vbhT!3!jBX(VsR3!?c-)IH-hF9<7%@B+bswP6z>lPL87Nd04+cD+eOpHoZ4 z;5~246;k3zJzNUi^HU+VKYa*T{7?@~iF<(c!@CcvsuDTMLx&ry@oOF5_V^qtxc~k<|*VMEBDN6~Y{x_4_Agc8eOMpWb^rCy|1{+ON1&7>Vh{+pA zDa_j1f3je>4@h<27@86o5O_HGwDr49W72BV;2i#U}O`lV6Q{EZb-CWSQ6g_P++ z8pH}|pbOC=n_>5CGGH$zijv zhM7*U_CIQ3cykYZSLco&RTuiB2N-%|LE3h>6H`rU%f|CHeFsgyqaOaWLWW*ONBxKf%5^lk@28?Ql*1u4hgp8kEJ*XF zNmHA&%nQ8olFEa z{90*e#E(+ZP8-c$<$r!+n4N2~BV%4I{>*HbeCFWHq%`<2B;b|^vxG?AJ2CQi?%^Ge zJ&xoB0hhduBYClu4S$imPXafd+~t6A>_T22qRDsqI6C)Jk=$e*!S$xK*SpQm?p8mue$!Gh-V1L0>VZ)>}wHHJJUxU^}*0 z;nv3rcdz0)NA1(=TDbds1nl$A(!9pm6rAV$RUv6jA}&dbUFnSTTsU(1k4X8(TF7pN z1=ns}K3O=+e^!@YrptdeR{parzl`N$8_w+f`Tw)RwmN}Xl7Q~;Q|hz>y|JfoM*yl| zPsua@`n?h+yn>wc3gBNUl)mt^hG40S=aqgOCgDM)y2*QezN2pXI~mQOk%@#w5b*xM zroG88A0Qhg(JD58%`kT15;s%eHqQ3=#hx^3m=ebGG zLbh@25ZhmA;|-}lVA1t@;%0TjUAmnsO#-$PsP(b&Vl^4}g5^eIpCg9Y#0iX`=LWW} z8~pO;rn1^-7;2My>FlFT2nH*Cyb^u9@*3&mH8aFo!mx~+W;k4UA%6bT)IP9;p)HipD_C%s ziTz&s^`lP6Jc2No^Vtht4gm+*B;pkb8ZBsT;$iWK4GO(R!VWr^_>68t-plzHi4{z= zvBX5go-lcd#WAns&Bp?5lVM`!H7mQ+P&;dG0ROn*`{Atl=U>@RN$TelexL1D|NJZ4 z{M$_tKev|{v@MxP4k`K{ZF$IQKLHN@d|QM{&>R~sIhUvhe+~ocKe(k^;^xdf&UFb_ z1vXK_R{Zy-;wu4H^gZttbbidlCUoq78SjG{P@z}j5tv>Wf&@{oPJr?|n-cD`0+XN5 z^r$ZNEtBLXQdcjqUW2UC)zu~m#2U?`ZYGZ^jG|m~o7Ys!H8u-aC1(K|XG6YUJs;d0 z)^Vk-yj}&Y>G&eF+TT5Lb%~sCV z)aEP#Hskd;y|=S7+nUFHt#h65NPy~;>p@?STp#-S zhDF6s0c+AyzS?oLI`xuo*rKm|^QRUnx z8>e8c?in?NhFzXflWExH8MT(`B-lfv+Qu_bdKii|w~!f4m7=in@9bd*?_TfE!60?- z|3H?1`%nB?M@7?TplEu6+r}rnz6!o^YPphhqiFk)89q#%(jTLP(E&s9t)~x_IkEfn z{-N%+#xz8?|2fnl>mG_DWdF4U1PVN#17pmns*{>0Tz#I}s%+F$J}Xck&k8IKWdA^S z#lI}<1W!aKI*YkR?G8h|a4Le~B9f?}he?&79Onj2jrT@f66FaSq!L$Ql&`v0B#CQp z)RIJVlb(`9Ec>&R!GB`L1vX$`5TQH_;N*%XP*_iab$UTllO-X-Vlc{h1IXrfp#1!B zzsrp$8SYYy^Bfu(?aYz2JB}a`vBXd5#82tOPwB+C2Wmz?o;XA#Dft5`X&CUr15@y~Pxi&h zjp|EP9;8lAPD<{{ya4<<&JNe$LbHVI#UmpU($#<4myoyp3QF!3(E1Vo_{qkHQWnts52a-PsMx$Qu!EKWsxCZLI3hFr*bNxrZh<)BjO&fDfmC zW;mo0bt2P+0+NL&M<~*;k7a`NR!3zv9AyR1;ZLt5=M77s%OZ$qdT@~63uhT$AkMww z{b7frMsmCQIrSdY0qHk2`o9R%{wvbX_4-g#v|Xkvj6@OBcN-XSOBO#)6dR-xQIXqB zl!GSy<&>Nc71{qV?PyO;67D#g=VW`2gb=ZU|Aw5lA2Q0bnMwyZ~{*+S^h5Ofp zO}`Fv{kl8dSJ8Jy6z2Y^-j;dVr013ken17^2E`|RZK)ynSCd})k_c?t+B`TzyL;*} z1GCkop+y#4F%r@s07eMWv?Wn0Qymb=r=^mOe-+p%GCv zPA93a%b?&JBuqbW%0{Tfe*S$X()8&z?+;L18Lk!pA@Na>nv{}{_ftE`7I&tOYX1sxyz{3pK=V68t zox#8+pA;jg6L1rhKR+LL9hjG7=38Z`?HSCo;Ei4yRW_m$B#-)aYkZqRCGJGEcmCM> z&psenQn*ww#FU^Ck9seB4`y?oKzE*K2C4)P?{2FXnt|-Lblc$Jjz`IOgV83-Xg4p# zVKE;e<}BILM7oT{pYX4INstY<`SWRXu4qdA9ZgyIjneq@551wmq7u)8uRmYTedu!D zz^{)R_}l5*4nw!ziy*}N9PpD8@RRAT_@}#T=$AJl>#Geej>qxRJ4}TGZ>n%!j3Q3* zrWfp*cwMO(-j({xC91+3LEi{o*DS&Hzq$0y_4;$L`}-q&DSU(vbu_$C?4P1@%5R&J z;I=$_#H&cD-E?-R4(i~8slJ>>eS;B!&K=C7e_pVN(#oP>8GXxwO*(c{a7a?8m+}py zrFff0GHps9r~i1mZARCX*~I1Arp!L=-z>PFPuOaI=QC$#@W&Ed7+A6{#EEgfHLE&Xe2t*eDb9;?;iGN&Y%Vz>VCIIWQ> zicJq-em($$T#fOb$~$_E1cX9qjdf{dY8^{!9F~@(4pZzG!OaKIhzF=22Q@hWy_FSy27^XjfdHVp2jFqaO`PwVN@N+YEig2h?HS!#aK z7E%a!U?K&ucbuhE?^)`t^}N?dIxwZ#l~PhyYIoO#b~l>+tW?A-((Y#?HAOWIZ9V^I zT)%!ZJNLmq)lIj#O<_#rZhYST06(8>fX@ar{r>dt>Hl|mhwqONO(x-piy;S`uf+Wd zW})2|-%vf={w~A&pZ#Q6>GAMaOk~pW=#Q_$!LX7c*W%NdEdq!bV<>M$S*JX1i7=KQZkZ zM)iGIHp-M!l4j26|9<;cwu`jJnkm*-i2K5*AF88mL)DahJ_&Ps2>9A*>*|0VZY3{< zRpO?jWI?IHKeyCnT`M9x*|(oFK{ycr`ATS}k-bGzO$xsG1W_UW7A^Q*X>wn!H*#&- zTSTTu`L2IYynM|GQ`g$^qY1u+N&KyEizxi0L-Q0+@HO8mz8S3y7aEZy zB#D0Xq~vDZ+D)c(wpNxGg?VnqxhjU?D%0jWW35Q|Hr$Mb=7u#O=h;@olCTvCz)4+u zgTU;DJN1~{W=h9W2OSJ6`z~jd8}w|WA@JQ;-L7xLsfAFY)Mdh1FZ*u3?pPAmt#Edt zZn#P6zUjLf0wYZ6T)GGZBE?D`X@wX3n`tBWg|Vh-7V+YQ(R#V3jM(1sSnK^ZVzngd z|9c$Cv~*I$#&d{)#Q6l#^_Dam`^Rx4wrP2SE#I-?I0;?< zgLwI75>gLE+8<32orGp3y*(^nR?@aGsL2u8l7@cV9UE{wE)&v~=z!miIt|LllAJf- zcWY$3(v(7EzOSjhu5Y&%N#hcybR(ZXEK+ifqQARH!$uHtXkH9cY?4XLqdZ?~hw z%6}CWAT^kALJjI@&>CX124_5Fbih!RPHA<@{eKNhybjd$x|_)$zX;BffumcoDsKn1 zm1?*hBD8N>m|2>mrPW%u+IF~pKEd^<`TE*2mulat=h8Bh+iU~aw72Sc{Hckps3a*vR9Y66m`9~kuc8 z2&)0DL8Uvwc0>|HQRxmncWyJeO^0n^cZ41G>%_}9Gw1qz`S}FVUZO4lB6!5){x(M_ z5!kfXTQS4+o5ahHFj!>ZUSU2#HH`KDGr7%FuxW3$2;ut6;^iCE3&ogdf+*^V7~=+; z;^o`IZn5Pj{Il`$^;$el0YTayO>hN9{)($)I$b4#0S>s^>WN|CZwtFQ5C7UGg&AK1 z{|G|o6GY)}5c;-w`8I9sJp3Pymw!Ho$tS3W!#rtn8~EF_TR{b#)V^GRJkK@>j? zscRcA-=;OBuENCL9^+uHZ^PrHj{GDP+9FB*H*?13uDY%((G^5c*VPSo_o5=YZx0|*Q#Fn}aU z?|-W53q6eP-uv!*zt`}a?y5R<>Qp*)>eQ)IH?TUft{C=wU)h4eRm|hAV1{)#ww6-k z62{5890h)WDi{S-s)w4!F!%8Oy374iE4ZoseE^;K@sOR)D+ zK_C9%vG{M~A|8OhmlPM7ta8K76ZN$n*oF@MhV^Wv_w;8LUeK4Y8ghxTMk*=Su-$eG7@HqreWY;i0F@ma zyI?bOl`rJ9!G)ZOtcYZ<*9c8vpSdmI#ucx6w~=lPTfmJWuDi9tb+<2g6Z3_&St=?3DYpQL%Ll$@8eMuyiUm2$W==>RJ! zdnb=#CFO#jR#JMSNhm3y>uwL7-4dmwT%rS8dx@4b5v|a{SJMAOCFLL7xt{~VavHM! ztf5d+Hshbadl9iO{vJ!h?>lk$jb5y#=%x3?6-Zj8e<}2n*YKn|C+4w>6l^m-g0o-_ z&oP$a8@Kp{K2pOO#$JTG(m}j@!0D%r%lQ5W<(i{R4v&JfXU;#1mIyuN5}Y}6cu!!z z&{HnQ%WMqyY49_lm;8+1eMb5~dx+3a4h-cBy<~o9h|o)p4v!Uj$+6*rFvIQScWRJ- zQ`}aeryLm>CG?V`B4dPJ@>(QU=p%EZJ%m1TE~YEPUyfcC-&dp8Mf~*m8A4AvBYvjP zQ;ta(C-jix5(M@={iGzh+a%19V5#F4*MT85J<_bNfJE;IH zJ~!woSGm2#ddi$X;Lqy70oGG~W_`(e%9BTQ|^m9!-~S;3Da3m zxg+6()KfYdP%J$7$A5kL$+*7Gn!qou3B6<~o*_c7ms*Ed8<}s9X1+Sw-uJWG$o~WF zV~R^cI!GwR^8_gU1gs>aB5_r)G)DUl>>_(j zY^oC!V!RLqt*Nnt>_&Z}2T+MVaenL|c3^$_#FAM2YPaW8ihBV5-JVj4?~EP94y-!~ z4J*x^y=V~F^9pIozfbW1ZrDc{b{&;l$>9zx?;|khA5*E&C;B>Evz++Y4v*0%`ao{z zaHUWDox<+w6D#I%cVlo<5nD^O=jZ(&Pef>aJplinU+EM3sN9OxIIuDL#KPDTR6n5v z+Q#qaIR!xt=t25BjFmtF(p3UYb}@QNpfADvv9My75rc@8Kq9lNPrTq}7TtQIlEVY= zS6C@If&=ADRo&{qvXTSyI7_8AvkE);Ihhk5yUA|m@esrEfrxIcRG)Z{!cUr~+Y>Xo zVs4*4v6q`i)K{$~cRTHFSRR0XExB9pE$A=@x%s^^v6FOi-7Pkjy!W$;T}V;Vjp`Q< zz~8@0{jx^oRzt~wjnOBTR$2lNrHcAQ57J-Mjrzo+E=F%C#pn|~L^tXa?@^3ZEe$u$ z9S^`il+@gLT;*03c3{1^W4#!QUp1&tpXdSjYpL{!uc_QhpXk8)^oiYL@!zOV^Z+W+ zCk|4%)l_g`efq?svG{MCY94^UebiLD5R2b|_30C9i(3WkJW33>8}*4EKqdOb`YN|- ze+Sm5Piz*8|Hc9B0r=Zr4d};X@jI}tK2ft?iN&v`CUbkmdp!XE)KpylKUHqrv}Jfd zI`DV8=~g~+S6UZqcx+KOsun$fN|Z{IRc_VQ4y;lwu288^E&3KS!*b$dxgIYqu4>T- zas$^B(*$Z|U)h4!Rm|fqW`-3QEV;895H~K_9zZ2ac7n>SYU#lGRDVr>9=~RJ05{-= zg5Qr{vmIC;{&}(ZZ&WRM0RCQ5s>L-bw^A)Sus+pdmrC*bREr*fAHSnu_S0|J4y;lw z{#MD^KGmWamO?RD`6Jje{U`nkb)wn^@^AiFFTzffKcqd5A^!M3u%rCP=e(WzT%XA< zsb}I2%q3zag4(+zS z=g{}oX%3yX7V#chVgC+CLd$uNygXdOiocTZ zA%2toQ24lrjLT!wnmm}+0Awo6mk>G==@&()pQFp0mWa@X#BGU4+m?8Vb!nFp2P7eK zK+*_k&P3aJe|*h(4`A+r(_r{n(LY!EvCnnT0Yf>a117j(U}$BpP}(-6c!JfbvOTgMLh4;|nvu)vWuY598@5R zgL25I1go<Fj~mnQ zE_bO}ec;)rN^!Likhsm9nwh(4;_8YjZBHkoj8QAMf+Z#CM z%Xchr&X;yB@FPM_c|QgQSRT#+))0gooI|XSeQ6(CQ+;Vut<}D?)z){GM`yWp#g}%) zS|9Y%)(5u-z4ErhzTZo`9K_q)Zl4Uc$J<`ocxpHSbBhoNt1 zV90*msZBxX{jdW!1{zNft{^-y?3FYTbH^Dn%j3R?^WeUSTN~%Wt%c#W12-*lBI3cF z03-CuJrnsM-plnv{HO$nictwu6TDng6TVIG;QA*HP4v=+CXP(>Fpf-|;7gm3IN6sr zIdQr#ZF=G?U)rq1)rn5os}oB+ID3tAT;US%lkC7@T>S0){z>4UO|Z7R(;ezAy2i(0 zZE3c-jKA{X2Mb??{1{SOn#p&^+QQHGA^sAVs@lru65G;pHoI(#D^-X2>vZ5Me;o)O z4JP5G-3)#LRo!zoYSPi*_d)OCGa|l+34bMUr-DBOf#rmKhPAu>59sKoe^ca7AAN_&9dc^mE{t^zLhr}aP82>S!e0>77J3@2fm+;r;@oV_&Ks=P& zcuX&0Rzf9pn3b@fQ}-tvsx0+T!grkdUBa2lQqLs($f-XjT&*niDxa>kr4JJ)Cf-;s zYn}RzlcNX{1P7-T${_1tSYvHPN684%9)kce%-(@TPYf$-otr{?jCR%QYAt?d}DRViu328 zxRe`Vc;AT6e-Tl9C5W&u`L83Yj3R*t7TKz!NW)%_)|xoLYl&y8`V*8g(9A{)WV2`p zLNn_Gab244y3SVJ%Bkq(l`04n4D1BsIF;;8K47FeK~DXj`B>-NZsIHJAJJ46x%pY+ zxOct2M-W^GcY|nsZ2WpWuZ8#$+8Z0cm)R49V=9l0FQ@Yye;!XE#o+H5$YTLM4~u+E ze16~qj{g8}>&3*64NTzp34y7x@lyju9A6Ze8yi12u$bc)2iC^MuMKSC_)Yx8kH5a# zV7ZR?eYkNO6Muwv7;Uh_I2{{*I&g*KuLOEoG4Q>te!RixXXVGn=Uc-$ez-L{Hh#1< zp5w<`lValkXZjkqg51vLW#$u_&1=YPoT0`{#wE0x<5$z>*!ay5-4MT<4#vhGF^j+X2r=AR)kIi)+eU12D*87$5xA(09 zoH_vaWn#+ufbSl)#oeRA*!V)eJkpjXTGL|UnM0H~vH_L_@Bk+m$EyU{13q+^4X1)# zIuWEnH;YwbBH#HJyL`j?Mgg+|rZs6nvF#4t&H%iX_XGGfX17arV_1jW{5Z#<8V!xk ztkJg}v~VvBJa&>=jUE)slig4S8(3JH*#8?`05~8IOOV?XNCw{>Fl{HO@r6;chp1H0 z%njfK(_{nei%oTc$uhM(Hq{AYBhHvLwVo-=P7phCPR)-^b%G+*w8#5WLGwai5N*&{ z&9sL_lZcZVCyF$G+diyVxpj|kZnmJT`;E8IwthsyadfVNs_x-+qGItvBh1NgPk0!T=tx*0tN6o`R=7M<|gX7+;gz#V-c~Hs=<+Lv?3PS1pS>&Pn5r z(=I4Hv5*p?>fkNdncsEyXFo z5oQ!+P8La-youbj6?=+-oQr-H|IaVFe>t;_9Z*r!w~yk9bXJdvCz(!{mv zbYUc!eup!WF1R&N6Nx+9k4QLH%v4SNfb5=oRj{$sT<9JLmf>(Y%9M~GN*8P-hlHp-t7H#V^f5N~j7(!XQv>Ua7~SUCMbv2f=PFRHEJGg_36$W8h#_AaxFUx90dSKvTPYe5S_f)FTQ zg^O_EDO@0mp~r1uKNa$K3@t6FX-9Y==*YA{QwdsH&>MKK;aPskiCY4Zq9tJIiIzaT zC>^$^T>7(4=`9LkN*CRBW62y7Q+i8k`V!0#U*giC5UC+s<=Uxj!02@OIdn* zS$a!ZdV8nzma(O`^xRieQe*V@!zd0tEc?5Q45;p|*vUTE2u^)sYJk3c7+oU!unT@w z+Ho;GCKMXhOYVTEit0AGUtSF7VJU|ue!w512bGG(POXYvI6$AJ*Ltu4;Cy`{f34Nm z^Vd#&H-8=BvC>e z7!OPV=X41LpIE(J8D&Tab7ktOae=_i$($d;aIGH`8am>dX8r z*$E&qAw{?$fut}-`EI~DC$sSvRZzNs6YGH!70YKIaMpuAy3QXmu})^*Wponjfs+e< z>Oh2Dq@XfYqd_O&M8)D2PF6H~fr76lr7Qf=QlST1M2&;P)!rQ8LR{8`p$hUZjs;n#Qvd1PM zJT_r=g3K{HVFez$LduGSQ;A49m3W4|q@76|%{IWJlO`l1azgTqWC=ba`E!o=Jo)=% zk?Z^9%_#_PPT9@xyX;Q6mI4dtYbpCu5xFn*N)?gIuK%cPjmlO8zEU7QG0lK^D4GE* zpPK=%U3GOD0#QfJzWfATSLz!O6-CsJxaIt~Tvu8iS;199)QW_&s2ZZqCI~_>c>IKj zrNGYKC(DPUJou+#Laa+}cg)oJ2>OG-VvE0fr;e+FZx*a8f{9`nPW)g%{^4em7(MZWj3mbe%ag)8{PMgVNo6wTN4H{xu!{X6)* zJN_W7Ym~=-ncJ5$g3%D+r_XlBAB)G=v3RJ6U!cngdlH>}PTg;vhVKG^Av!xj_NZ`w zS@*bb*`Oo4DQcX{7V=PB!*e@A{dGK=hBvm^_j*gbFAR|j8b*C9M{ecUI>fH-e{8p%6Pg=!UJrhvdUwSXI=N6tmu~+Q0k5 zn(5By95C2{#(L(fJIuYzO?w81GY=h(!v@LXF4)r*H=Q0@%G`8m+$zOQSHNXzIp`S zXQ!9%TfOjb;YhsD7#zahFGmFz!rdV)w5Q;m&OE$D8eYV1Fc*a{APlez;rHT1!h3N? zqDVL!?HP}+o^VGbqKo1uBtkyBfd@G(U3^EDgp7w$t|T*3-PvrZkH>hOp4|U zRv_hqj}*~VraOM&JpxyFm@{#LW?61ZCK|E3{u%fZ*V2iH4pTE@gD5znatZNRvu{#C z^aF6(c;Z$dZy@^0epv;pNz6_vPC~gLFW80)@`9pVF9=19Za?dmi{;6%JE|bsxPC;e zKfX9_V_(n#@y7uK-R;n)y8F)#Qc0f|6!dvPL7x{C^!?SP&(Q!_gH#l5DVUvMdX!!r zOJ4P-`8QX8v9B~#-ycMMm-+Ak_^=A{T{#T#^D0;+Y&LVpuMUo3)pQrh>b^8u72Fl{ zh26CTy(N1Lf33USyiZ{=UehnKbJi)izQ(*Wc!Zq^9|>;4Tc{9gH-#>R5FQ);6g&;# z58>Sj;ZLH)cw83YDGBqz!u{olf$s+8GuIuq) z{M=sIIFAVwz93i37sPl&?JNfz`%~tk7SvIQwmT>+kPtZ7Nd+OjaPTb^Y=9L$#8{jN z(yAC;5-Fnr{<0|G#B2N}jX!L5fkR?{gjF~^k>>8wMXjyKgs_=tU;Z?v7`E5AbJt6Ae*6f5`z;I4F$<^s69t6Y+GAHd_> z(T}fS`@4&<;7|ifP@-ECTs)6J-gsmN8@+LAJA2Oh4zBZ1@A4gyz zBf5wOK7affxRa0r1u;Cn_d7OHGr0d zy53sGyT|zkZpgQym4UCtds~u(SkPzbPJliO(ADgcRe%bosml~T*rm#R;%`hbFHmdY zHxLM3kO*D?f^T`e;ppcXg&-3w1n&zt!4w4fV|8^2t|MCc)jncg-H-^}Nb-+i4Qi>q zib26+u`{8>%QV01=mb0dNxg1BZ{yY6=CHv%kCSg^kq4rRls@4oVD}L0x&B6~v(rue` z{@D>fHyh(k=l6K%)fMa#^MDx~ysN9ocmVIy2jA^fzySyc4j*LJZgq2Mtcpgen#BVu z>t*~rWS(Vr@@LJfCMtf_oW}0tPukbnk-H!fc&rg_?y&LCsuX7b!tkNo4Gy+zHW8Bw zMT~cW@TL^iy%~c+CuE-LRuv~dxEj43CESo=6}C>R91 zF2ZU|O|_daZutfSsJZo*il%{B+pWVr%iMZJv~4-(KCRln8zRHabiEGaK49m%7^>Eby6jK|78{_=UM zwd;gI*y$hO`~R2*)>YUIw&Sp%Z-*0!d;>{L5W{W8`>G=Od%OaTM-G?zW* z_X-SQ$NxhDlU=4cneR7rz{Li<8sfQnu-3W--HBc7@1Z1u`3gIFZc88WnHQT1I3Me% z3!IATr8XCJDKnrwrn@EcwI9a znb+{ElF!pD^GhD2%lM^%=M@(~2Cx13hShar5YL!iXij{ICUlemvJ@Y@IL^5VB#KNO z{9S=^RP*yA3cjZ2yDO9QDOkx7Z#!Lri!B`dlcL?Xva70D#T%S?{t)PT=ok<9V~|9o z#_MWe6R%~PoO>#V5VFag!LjRF46fD!*9#;tydW{O#!VMsc)^Fkf0Khr^5M80bMAJ~ z0~M#F2lG>4!%J>V4-}=0?O7i-p<%2rBYhYj;tBN7L&*8iN+IhQXZZq9UlXOYlDKg~ z0^AHIkhk(7mn!v&)BOhY#TZX`k9og-SpHavLPX42vj{U6Nnhg;o`LZ!Opj-2uA?Xsp{s#G7QP0dG(>u3 zp%pv&D%w%$$9%=z@biZ9d!e=?yFoqk5zoAjcqF!_Df%+h9nrE8@T0Fe&_u#O+~g1m zxwMeDwHHD-7ZL0Ek-p}jzeupVBGFdJcr`Fubjw0t_izS}ZVvhAw641&yBBx%Gy~fU zXaTqlTG1%$BYb(8)m*ne_|lmUw}FZS{1&-iq?P!sH(V&_g{WFf45B0Yb*}hzeLN4T z9%dhA3D?YF?CNtElmZBedtp+yVtwsmfTIpt$OqAm)`iQks=4XniHV!RW=64p{O93w z>NJ1lTD`%)z-=VGg1>oWff0eC@7d|l_d1?`c^=Qd{D>YZ>?TR;Naf}=EZxjrGd4;T zc#{aP4T7n7EP*eBZD8LcoMJoiaHqsf#Z>v67k{kW1y0*Rc}5LB#2_#Gn`kj#Qe&WO z%t_@kNz~TMDc(%$Yw3^k#Ff{7;PnwnK}?5`Z%TK;u%E#`Zj`+G7*= z0#wrRJ$H%sdp#Zhuam3rivwF9(8mO%VvYq6or*E zNZK7}06!Stnl_nx!A!Uq-s@;qI~OXQYoC5UZVU|8YG#+%ch)VS`yY&Y!EDIJ5B^F; ztVKE^fWq#rP}EQ&6BD3YPNhIC>}Lha#5;h};tM7m{E~yBBYdzSbTJ%O3YD3m+F%R| z_0Gr2a$6=1!!9jD49SdF@%z;oXm~LKZTJoo8LD$%^T&GG&6ey)cFFakufHSj5Y-iC zt(e$56j%t&5V?*|^*eJEkxmku7rsE%GSP}$h~>reX>(epqfg46>vUmbOzp;>gSaV@ z*;@W2Ol?jol1aelPsC&Eb9qqBb#8ge%J@NAa(>KncM``Sc#ay+WA(#4iQqZ3hV1v= zE_EF-bUMCdkY4{w{pHm%S?^04z`KX5~*Bj1y zi{Us3m4eZSI*#078m~`6ynS66tHfx}v=|hvsh-FS>jROuwa5!(usv|Ii=aaRzGE=7 zP#7Qv2@EY93@|10V{n-yBZ|S$Qel9Nn847|!2okGKL*DfO!hfrNp%}TDh`^4-qgU3 zYx2Bm`l~n@xK{+9%F}&`ALwIso{MAk3BG8rG!GQT*sx?{qBZIJN&Lhg>u$Gn_+_F; zehmil>sRsXAGzoQ7Z@%2`@lP8QySwD!LUuZoSy+(KqXXL9BiF`3~FK4!J! z{4tqZ%FO6T&fGF4b4!_7Od)%ps^$~WUWo-!x11;#XmiJ8-{o}y-Sx1)D;=`exowq( zW_ksNbd@)c&r=UJj%J=+Rd57 z!f8_+1OM1qw$<8qQP6- zk5yjc3>J2G8O1p+&S&(giWN-d&1QQ$#T_vIU6C&@T&hvdWM*KKWnK&%k@s0&Uca42 zOx_lh16G4W44D_>M&xbf%PZF3v@McWww1wbXsLocqj&&VKe%k z(OFLU&O{pxwo>Km>*%_0`A7K?feLIV0i2GxOOm5hGU1oA4tipC8 zlc2)=W4*Y-nT5h0zY!){39Zu=9}#-}81%%_-Ik2;G47 zCl^yAH2rOu&p7tgrz3N>0-CO`;qV%LvkY(M=g#mAmV+A)8nQ(`)( zN^gl7*Kz>1=tbSEd$C0yKo^uP+=aOJ_<88}BIn~BT5dp52X(5m0`fAg2;jLW!9Ujo z&c{R3AU|S5H^(h<4C^H)@)gVWxMB(Ya~rrBn8h|}OTt@WdWKBUTtrtpB(lL6PN1Mu z0V3<{Dkr`r+UaQM6bt~6!+!4z84q3Cl5V10D?>$*_9XTa^XV(z$xA2(upV;L*Yh## zOq$GQ95X{}nU?bKXtsu%2tV2KxX3NQMghaqX1KvM4C}?*0d+k%UgW;5YAmm~AfFIn28D`v&IOCr-Es9&f19fd=JAZADe8UGBqNr~o z`y(FSZzJD(A*bd%fMPz@1h>+yjQb1dTXPW^4;A2uxWn4i^OH+5El9J{;os z0pSB-4$q1!ig36%@@0g>UxF+SGhH11Ch{$ZRhib{HO`o;52<;YF5UoKVeD6@49}?{ zSxm z`D<2ncl1}stjM?-a5py*a0}QgJgb82cijS|RA)_gr&5Tqhno2Glh%1R5r>p~*mIE2 zHgfGe?%q5*AI>?rWVzaUz(S)NxHHsQ%TQzL&bw{?)mZ}rx%*x(LCiSovRe!;1=H68LE2hd?gp6Lj$(>$*#~G+VE3 zch-2$0%P?%L;OqNq*HiMo->LM53qaIIgJ=)>`@*))qf$*nSap?!^XrvtC5SH&2LAr zK7>zU>|H8enQ+LVFvG5RzQvKih4mN`>y+=W(^n9Uq9yRPI9&ML(t3-}r+R=}$Oj8vQ;d)MPuvVP}IB$yK7rkK!_ z#IBd(74*6{i{XyuU8>3zkKPmXC>vFThOO@Vido<{cg0v0yj$2n_=4>rEQ|6T2sQ{r z^{UhsrPXdIYH3eU`7y~ET~kFY7meU>H2_i(bUs#dcdI6Z&?B(BAkU2b1 zKFvsa9s{&0uXT)g#msKo` z6cFT()z1aYK#3XkQDXfDLe9AnA{*pqgVSIyDPV-0XOB@B;7{Av@aK)rvI7H4WTCvhL66N=;CK{-gCkXt?`^|jfNd(@~w_1-@>0RLwMI$2*szY z+Fun&jj*XbB1ehfWCvAz+)u|B|I!CPtuj243|pHo>0g0^f9sOcO2h08m5IG_?huY9 za`KSI{w70DU_pAKwHQ>?`Y#Clw8%W#6AZ;s=I+Mu58T(5p?l{USs_tsFokL1uu9U8RO(4b+{-W;3u1sqzS zFXhlueJNW(EHyxAb6UY)tz+nI?Ol-{FkotS0ILvXkpUC5u`aid&F&3r6@YEyYY_f^Jm_StydHj&-vO$>;JWKZC;T=3W(D z%|G|R&|9eZNZ5>-doQbazltJJaX)UTHdMR2yrYXk`D1yhV)?o3?uRRGGpM+Jzv$(IY zURmv}H}~+3(>-{$O7glg>u3-WM}vLXnJN@)eZu3zh!`JUEF)a}KFm*JEI*A)P#tKc zDKJANy53w2x2Kgux1gs#XY0GqB{Y1J;>U>4%n<+J?#@+taIFujXSADxMB_tCJG#snx0174yWBGP z#=sDxkX3*l4d~vBDhp}b4H~=vvceIgT>YA67MU`xh#g_D2Z%K)VurQO5=lRYt;og3 zzowaUk(qzZWo+!tg0XRg#D-sJ9D8E1B(SDh0zqWM&t()Vw%ZbYi6h!Pwx}5ay+>X* zC6C0w&EV7i8Eih+5w9V^G=a^dM!;gmX?}cL_q%BM=5N{n1|DDxHiBI>oS<7{R+kL3v>}T!5Gx z0ON5OtFIH1WPAT~%l|~CpKoALW5+8>YN8FmAbz8QyuIX+^|lJ9iDWd6M>#hB9VN+P zd%-Q|C00@`xzx)R@If|4*ERmL=(*8>kJt&=M}bdZrh=aQgfE1^ha{TC?NDFNcTEgT zK>`0z3T$6`Bjl}sQULiu6bdd7D1u^%xPin73-^%-x-pVzzJpy+W&;e228|Fg8nkj# zA*a9kjbH=HNqRep3|bAh$6Fw+#jB!%SKzb_AcB)Wtj0H1LbR&|4Jk{N9aJoPn&!bS ztRrW*)0dTH;hT7pi>o-|$HKsus10lt2=D>?vpjflX*8Mb%K7FFSBs&!0`gZJU@%3h#D+%n z@X3SFRPznAcxooNr9j?NAZL=felZyuS*SJ+J_Vrjo6AtYL65z$Se8^q<*IgC#`@d&tBsI^N}OCi{tVU z>qi%cO}6kFo){^c04nBysIz7Tm&iqsHy6>4Oli|`gVT#XpyGTYcP)c?O;L zOh+*Wma}&sagW+o2V1NL!bq#7g3!^ao~`(0HFj+s#9_*lXyPY-BW0l9JPXn3+2;}S z{7d-t(#taXW#I$sO+>$m!&mzI{}Ry^5v#Bdzc2@Q3i8iWj32VnZ`xvBw0!|FFT8|l zB=95p<-Z{MFK;3Gt-m8$;D_YsUnRW4eE9!I=(k$9?IDh>8iI_5Hw${58hH>x6(jDv zFa(qsf<|<`4-4KqtV(t@^l`P?;=&Hh0sTUSR~7zcmAiHT;ugwrw%b2$oRA_mo#6Lc z+u*%c7;kof@#b`DG=Dr>po_+<(iYy}D)c5gUsv*T3bjIe2>)Uz?cB_Ect! zX5s%+a|eH&G<(A)m4?#$=5Kk;Xz zzL{H>Z@wS!V0vpUVYeve5{VOuNCDRLwM7HwZKWnkeAe{ zzt->Ec1T6Psw*{xc)L{e;Y<;Q-Y9rZTH@K*Y~dD&#qjO@uOhq&GC4~uI%{s^n^<(HW^pi+xeCiYn54?2B; z9=ms&Ts#6&eSmzz*gaiElg7gdmwDPzfiA||PW=cLEZphoVGKkX@S<@`-1j=%TmB8d{`NP$x#g5DZ;c5%t2e_x<+jZ#IpPcy zv9>mq#58>jjrSNUOra-f{X7EC&y=QP_Psw@IS*BRv_^eKg{2;-;QdqNhE=dXx7%rM z^LQBeMZ23A12ibW&_WV}KiLf7?J4z}=*AnP(b>m&e))LsnZy;;Q$T;BP z;*f(weaAG@bCR;>1Z8l!%guxUkUhu9aF*nfRgt^jebi}f7&=kv&FHh6UlOM=zyBkC z52g*oArAqEe7qZTzGj(&;#Dk+ul^Z${@jyS%$_w4RF24f*Hg<{T$~#r;lR3EI(GJn}xY$M4XgP;%hUw+y0_T%Yx&#cVi+@ zs6QR({6SuN!4i9@?6u6nCGR2v1KWvG!?qUi_yHv=b%5Bl5+@fZJk@a@S>bV_6rSo5 zj}u#6;&FpQ6fo?C3Qsbi;eh4~6$~2Px+yW=`i0wd2Xunkt^gNfR+ISUb(aYG`b*mK z8=}X27{t`AgAT9L9wK&!zu*tF@vZ6Y5xUt08(wkZ%vT>_m)eoG=m+?Vi-Sd@H?|=D zS40fJ_7X655Q@cRt6Kv$Y`C9Y*H!+p+PRQn^nAF!(WM*Je+cwG^eDn7@QymiTr|70 z%-Egsh=X|;zG4hd=Ah-!iGJWz!H49?;I#Q8)BPhlgoE7) z-q2d%PvK9}jX^$3(E^icE{|PmJ5jL?y2Vo@aM-2bm8)t8HUp9y9)3NhQnRhX#J}={ z_QzpX30(n~;Q&H`!+c1qf0w1xyVGl*jBK_}`bjs){m zjtWPDPA=z2(AO^bX=S)I6n4zDq?A@mSYzBG1GhW{4*yhh8ED=HljN3vYm&rZ7q1;N zc1UrxZpQL-Gh;{KmS+%nhOr|6?5$(4s}6;9w*YfIpg9W13biOaE~hZp9aag?gC9Qs zq9pjmzar+ZLOTRgyf*Zn-V6I_`*BqTNLaDBxKWn6=x)J^aLfH-9er*ys}nc#t#w{j z59>QvRl392YZ(7bga-B~fyINPRChmTcStZm7&Xi=JO8Tw;E>@X?DR$7ZQJf=TIB^hO z!(DMXm=FK#0}7fuWWXMOt^b zfC<{UPINtP&i2wro&AD*hJillBQIsw!KM#G!kkQ zOCsg~DM#khSQMuBC?|Hc@ zi;T(D_;<2|>|lQ{S7njU_$e$@xeiirh{}dD!9OV9HKfJSD`Ryx37!cn%@9LKb9JbB zb8v)5NM(`9K0@fXr%db*#sp%UwcSX9h`O|5?CJpg09^}eIerYyI?x~ArhI*mp+PrX zs^jj`Dg(;U?S|IS57(p}bt*QX&s%NKdOU&*kddZFz4Tr>w#Fk3__X(R>~BWuRHB~+ zKoOoIpxMS3Cap6|O|7XP3f&*BLm4+wUYq+k5Ti#W2ZjXpkkNQqQj1UuA!y)GnD8hI&FiePi+GD zyE6_0k3I$#Bity1 zZt4+7+vBds(Us`f1Uj71J%v6<`6QL*r=Cuwxz&HHt_4EBatH`g-{=QXEf9I!iGh2a z{K%9DeHSf?r^WG?6R0q8S0eREnv+E3Nmr7nIQ??E7Wm6kPKIQvdNcNnQz5r+;$e#q z1O_rKiX>6h8d{*53u;gyTcCUvo*Jc1iC-teVSmpg8k96TDHdu=WJ^Q~yn$B=9BljT z{Wjb$ejTQQxN~vTJ@R1$Z%5CI(2B?=fM)p$J^~dhoI;$_>4HAmK$>Kdf+!1B^}-fK z6SQD6)pk8=#dh9UvtJw)#r+u7s-Qkj=TXLk^zak(LKlsmdx5Y^>!HK$vY$SLhte3G z#=xdhquKf~9o|jmo3xm&63$!-F&V6$LCOyn2I=&9JdKK~Y)q$;^smzCVtSvd)VFGWRhm+@yebt|n^lbt zS36vdPFBmSPP?l2sX;|GN^4MI&ElGLpyq*^R8p(77G110qc$C=U0$2!)Y(>thG!1X zq?B&4v?Pg^CzT}8l;mm2vQ?KPuT7?fvMraTQgK>o8cnEDRE0LCm!#8I=@-+fylP=J zn&q}&UiHH2)Tc&K4cTs=)R|LFMvZ+^2-yE7+cWzzTj5F&ksYmncFRD*V>lfFj zmGzI;2d@iX4AZsn`*D&=CQGB$+<05eu|!is(?V1fnjNAOq4E%&g*RWi63Puz?{I#Y zro>H)qd9T&<7jMT0XJD`gr>)@ji-EWt-^#!2{b2h3Aa{hB9*0FOrfIG*{Nt5G|H@M z8>-QTtZP}csQ%LWR9t^0s3{Db2~e)J)S_am#G=CBr$NfK$JkV0m)UUAUKFO%$iOHK z<}p4d@uNg4O`4KS(|9NsB`-;)ZK)rmQF&VbDm19dgesI@bxKt#;URjs+R18ESbbM@ zDy>milZttC9;lgHi+a{7sYRu=F4m$MwGY&$^4g!&p}lpE)}iBdPS>GJbp~eAU>>n0 znfo*8VCH98v_5Nl7VXU1lSTV@?4HfKkVW0I`()Fg>@nH2Gy7CFUCQp2Lw$3Gt!alZ684pwYj?~L|&!GM=HN#vV5~PvA zQ9*c#zZ9gNxPnB5_GDY5e@xPG@`#HO;D`GRItc&lSOL&3Qxc04F&ImeXuj$Htn~|Z92()e?6QRhYM-D<0vn2NFt3&oPvR!)-#Rr(gvl`khI}x zv@7jk8XZeJou<(@fsq!C!E=gu^S2~kgX8IZOs;4le&+MAC<>np)5Y-hFy+M!#F$FV zPo}*o#i`UYEia7*r431=;c2_l=wRBhG&-HOr-}xcLy2^ZVJ22tQ$_nlh1Tf!G7Sri z2tZ&MAHYka^DsU_Jwud-2e)ZY=uk+buAl07nrfqtn~He8;ET|D#LY6+nQ))81*`ci zW0(m)i-oWSH76n5-HZ(n<8Yxl(WFUunq6h`N6s?g0dFO8mf;8)$FvuS*j@`x$0k^G zJnnl`Gg1(xyacd`9!dR^C_kw%iE>kVrchqW&=i`QGAD(Wq^wDyJt>D%P}S4uln;YD zoQC@$xYnj(jA(Qf^PrKD;s|C?k0k1!l#iv7d$K$^H-&np`xq z>|-^08W)FOhg(g^N&|FAT99!*#BnWL^UV#Q)7d}G3VoH1p|z6m94)%1FfcIyH`1R5 zVBvT!K)aaVo(rC`>6|?>M59B6Au42EQWh=`Lo7HMrPFvxng%5RhNpSL%$> zQ91NO&JQ_sK4)xQ8e4Z?UFubD78aaM`d*V3!5t-?3t+%cz$G@+0Tn3;mxZY;u4jZ6 zM8AmAu4q}5eu#b=Pcz~du($&TSd=g*k$%M67c?Ykd=ePc>|~mkyfB$oB)^wJ`6*jc zC|9msi&JSiFSxmB6PR;dPowTtdZp9A^x^4Ll)f~ba;pxhN<~$-R;BG#ORAwI&>W@J z%d2CpomYbv)L2miEG@q#^{Q1=i{{lTuZ4y|>y+?q=;%PzrVqS^M;^}03Q3A#d+IKuR?XD!Oy0eq# zVZluB}udG*{Hw1UML(To@BT&Y7VGQP~9uQT>$P-#Y422IK=%B1<3OI3p&%siY) z$1~4l(x{w|R15B_TUM85)!T<=EDtY;!z&)^;v^m0xlArj!)4?PCT+m`xbzLTR=Kq# zNZ;e`Hk}Eb3(<65?dGyU`m*qfFs%ufgwaOBBQ!1YS%l_C7DZ@mbOE=~t|*nopNz*N zD?^gdD07qOINnd8@|0Z6C|+0jdOy_D_*B3ZUaQogTbpWB<5p_=q(IfFlhzMb+nuOc#|Ha2OrgFkUmIv z{w&a`OuwjOh5Ojh68yl)O{wWGV|P~6knVX97BYokU=jYUQS}`0)$ksA@DX}spswBP z$3ZqFXHnf-C>P>498svC{8bYP_?1jqb?KJI)c8&9Hh9~=C zT_qLieGP0POAW1hOqToU{)fQ%dB>V(%rdkm{i$?2-E}`Tdx8G^cWgs(vQHs5rjv@n zUZ_4I@6qV?yXgLA^unL%@5^;O35i1zRwcMy#7c?`%4$H3enqYDWP?6ar)ByIZWuTE zxtM5dNs&@lql_#VG}EtOQ860)9zO76_ui`B3v_I(ON~Az%&-QVc$EFRS)sDBeWsu$ z=)J|J_L-l))G{5pk74I)mYX_6Z%AHij5l6FfaTsewERGQ9Jj^{J$9NV*QXa>*66m|=o@bAagMwUxdgFY6zA_+| z7Q=*4)oTv!1L6z=eX<8<3Mj3&p*`rY*OSlCGnlu#b?hJd!I>on`}YQ>t-cnU*yP z@K+6y817iv=KG{Q?8i{$A$sIj8nr{0tZ z9GTGf@T?6d*q@miObxy95UoqiVeE1HqZniu8T7}$(qL9>6zQ{dSasrZ*ROORcv2&3 z{Gcdy7>k;vu-S%->yv;ta!q(;AIltOE&tiT0{T{-zTg|H;r9JR4c5bGxdjjzI|c#nGx>u21?2$e*3M5rvXI7(}y=OJ_#8G8)if&s@mp1mb_ zI*fAR>VwLHXM$8@uVGxdA?h8P6r#c5!Pwx$U1WJ>9HY4sp^?$iKr;;YIECg0q=WvG z`VXxB>tRr?na`C#LrpY4Q8n3MZZc`Bxsz*h#)OgKB@>l_2t5rhVDMi3IF>gjTXeua zWYbam2OE;WqA;D}N?Z%~jiZf`k_eorltr{g{+3L?pC0@r{kEkByO9w*4lqg%;LM9Y z*r>t+Liyw9A?jr1`-`Hm$SQ*>v27 z%H$NQO!C4*!}KBRn`VSd}m^8_>>+O#0F zJw(OfP1wVWU1H=SwmH#{qS*VaMEi|k-;?8D;Ru33^bLQ9iQqo>I+m(HX@HKvdox`O%trUv z3vDW~^TKeaG9V1$4b|Kq-p{h_>f3`ca9gCN{Eb`7R>wi&2lu1Vd=-V@m@^0&qcoGC;HKxi;R!TWO;b zL&CI)>E9pzE=;o{Ya?1if4hP8E#hTJtW6j7H3l8VQ!Ma%JTE$T884p0M4>8SNjFwA?0`wxC(9h*j~+JZ9xd?DeR_;>B*Ib_W z+t6lRb9EfnVtrc4>*+7>;B#+z(xHGps}(I(n~j<7!ACfmbJhH+cY+MsjxaGu*3 z!HT^VGJCt~u(#1T*`^8XobecwenPT0ES#5&_f610!lcoxL=lx(58)n_IBvr5{j`aC zjAlkXm34dXS$K?butnSKuWZ_FAGT46Sz+j+K>uadMQwuKUUIg|Ch7oINNmx+*6{*5 zmhNFZ&;?tYXU;Qe9k|I7#p4trhQER ziSUnMS{d0D(SGY6ddWA_t@qKxkJE3S(IBXd((!IOjy2BV1`;lcVP%?*%L8DgM@+g7 z*@+7Ak6+CU;C9C>i)LB)Q)1!3p(t1ygzqE4A(k?Kg7R<--lzm?>ILmrbU>5}q8p>M z1ssmbq8H-nN_-x6>QDu1C<$|_N1#W5dNb<$0OWkZ>q_l$SQJ9VAzBqW0yY=k6~;ux z+E&1O+p=h1=u+a>#ZxKXjAi;ElucwI;v84v5(`SbSkTI6{oZtzf#*WPk<~edw>Ib; zb|%!5-sg&a1Zfz&R@P~jwbGJYdpWQ07sEL5TE&cr|q(vT^gYg z(G?g}`&jLJfD8WKfHaY7fk~9HYN{~qW9EB1;xrIfXn-Wp^$jnUK^a#z0 ze9pvy!p%%vX=F$=R=ndO_z*q%xQ5O7NI&bFXq+}^fH}aVW#%&evqA|aGiV;+$pmi- zmj&ny>lO2?6*hfgLt*Mo;UzqUkA{!(6fR(zCPk8hL}Qadvn1Aq z1fk~(f`Bs^{4Y4I@_BqP?hH=uB1P_k`y&>QkvxA2;G&}<6Fkl0)c|bSXs9__Rc8gB zO>qR3WBz$u#-|+T_|1o?#jp zo*1T2!VALmdAK}|&hb&{$Vm5i%8MU@DL9@*qcuFLH_=YUdX0bvd~j{(TUL#tFUna; z&5dH^9vKBYT^rqi9vH>kZ#>MGDmI6cUbHF;vG0(iCKbU zfLQVFc;EytS#xZfXG0FIAl?aHRUV$fGXM(9;>fTljbgqr4Qx=!c!HZwq{)P%ZOC{f z*88rEH|nQkyzY$aD;(rT&SJ<7)bsgN7>lox@vx0Pz-o=1J{hm4lJTbV9j47#%{Cqt?74GYWVyb$ME8^NWl1wmR7 zTouGQ0d{ItwS{LXe4l-G1JsirwN5xX6Vi4jLap>r|}hpcHD7O?Z6+n$!XSM7=e|`%riRBj|ORbXIX{`01&~&14@DzcC0xYlG zx@zGB5NpINUU#?JTZOn0reon^G|?tzYFjw-H#`>dgC)E`Lj)Ep${F4}6o)p1Xnb4| zp!Z2;aT&vhx17_d7!oa)QWp3zKv(%7Z;CyOcQRPr*VQAt# zYOW4rosGs8=qHS823<3HfPI?%Sr*5VTFhGsp$=P%=SFEQkBss_e{SPb7G30R)FAln z#Ghe7jq3c5W4!%tY`m}pj5fwWx%0#mIH-jLGL8+51kKx+$ja%%-g*P?O-o}a)$r~x zJ@FJZZw~~(vW0av*SO|AOvu*#S*-q;m+Dg9OHTfAsu*sg;Li!Fp*6;OR(4_G8fnhpHKUld^4Nlunukp~Y+}vI z#ngbN|G%FzgI;_aIyhL_QKLJcsD{?=81Li$MTsd@=#Gb=-NaeKTmzbB_H{+8aK9FI zT+qrO`^cJ(ou$l$_!%XxsTu0H~@Q>0prg-=VZx1`WjViCpi*eCo~nbAk9HR!E6OW35z3L3R~O)T!Vqk34}IO*wq!W zzV-+V@h}F)vm$CKcDlUXJ;k!=CG(0&y(kZ(uKxcqf=59>Z!YW@=IQ&q`g;@%#O?8a zML|+kVY-bPpc;dXVFsrB1U}h;dEf#bjBIj9yKvA0Oh7lDRUS)u_f}@Y$ng?a=nC!+ zp*n*|yT?BtlN!)1w^5UOHEM7Rt~=4aZFrLi;ds39sR32ZTJ--;Rw#dfLpALcf7U86 z*t-)_4L$l>jc^us=iSuoQTi>e;&tck$V6kUn$`Jy05_e*n!V;B9_iQsh&{<<$Zrtf zpq%@P*Qdh3Y(63QKA^ENXh0e1(@&Svb(cM|Y_3s%tNG!*>1y&nltU;%F-u}qv zAs6|;W->2YD}gfB%I=U3_3V_;lmzYluvdd%#y>D7@tI%-$37x{p>_c$mNbpRu$O#N zLE-WNHHLUUPG7F$LDU|G7MSHG;Z3UBsmXIVrGR-vpz1#*Vn$(9(E?Ao;ULA|gH3Svc_FeEACO0e>lDHBtF?7F zqUiz#t{$1#6skZ`rveDDxvnz4O;TZJxKqKYK76!ozh^|0)r&rgRWSKNl>jREG^F-n za;ny2M&?wo-NUSc{$W2K=Gj68-}#2JiK^h5Zz?*?KH2z6qY0kDgKoO4nTAzEQo+fW zC?YCoxs-vQ4^7+~s$jgJ)?lyDINP!hw&;4O$u>3F5u;6ToreNDbd(8h^Wj!|7?#U^ zZ0{Ik?`XOQOW{K{4DtL?fMdR`Vgz>7!6sPa!wq&Vh9VQJyId)T+fVFi_dU=A?|7nw z9Wu)9KE>XR)i1Q<%aqjGU0{+^e0V>5D?QD*F>!msK9P7k;YC2}@9|Xo+SbGEQ&_Dr z!A1{SI~rS?Z}H&~cFckHJ-C!6_>%|G*EF0x$UcE-bTy0_|07{s(-kV>UiMR;vojZ% z#L_Dn`p$^N1ETmQ6I>vuHC>p%{GiF+)%0p}lwbgl4!KH)*%1er;K^$=JkpMyZ-T3@ z)$lOPcJQ)Q@NiG6_{5x+%F&pKLRyoLt$AJM>mSMaOA z{yKOpz-NK6ZncdX>Kno8LH3+M7vp3QQzsj;n4)zrw2C z$If4Bg8O|K?f!FKjsVhqpDEv}PdpJeqIyB8pBVQ^6D;xJ30Ob6E~Lds@Q%ct=^3Dv zoaGbjhp%Ut;9EXC8mB0m#OfP4+glqF&xF~YD=1}q&|&sSOrHKxf9*$+4Af!a@&XLF zCWt0}G#;)dJR3=W|S>2;r8(9@JBgUT%`ne0zSz=+3&t zn<4Fhpw{GU?wX3IyL?m&ksdw7I!*$ zR6h@8ns7ipMmjm=n#S6bvL?_|uc=cT+-%28K>u6uu>ex(}AfcQ~zSu!%kFUU2%t?`&cp+i;vOZvs7vnELj*-C<03EBAzL z!~k`X54YJ_3+&=A+b^GCf_FU%p9eDNLBPai=)aJ81%Rn6F{M zKp!zt9DKd}?vg2Jk&3G}Q~ z%A*leh$)^Tuw!SKKo3jBEXEFn$8?5|Z?q#YySOFz9croq{Vc${0EWkd;_asnJeqkA z6%if@iar*;a(IsC0rqklx33Fu3eKIuQ($23)ABahM!czXpbuMXC!1ia4@01}!l_Q>;t_^-P#6CXc z7T8jV53^ZBT~Ryg{rU}Hviw)d1P&%%jr+C_BU?CrZ~1UQW5$G2P0;(0CIJcFPWzh= zV_|bg{WF;S(gOA6jn~0Z4Zr0w3(wr21jo*YH95+Lou+KHQ5r_8+i!W6*7A z`S*JmhmFB~{vXc<;WiV5pDGy#FOnRdZJvS+N52z7bKUX#nP91xo8$ZcPbdiaU;2&M z8|xDO`>7@fKbAVg9&}MfRNHTr!7lKm?Oscx>B7h+dqgxQv00M6I4EJI(qYPqQhlSR zqYrh%zWs;8&!J_dn`mk7;)-P9r^OGn*VEp}m$3&S$~XLs_&)Z}w8-)jty8U`tt;+H z!TSxjqS4*zGg^clU0AD5=(kZZs_3UsA!Ybc(9t%tCK{E{uPJ`JF5y2uXoAQW4ehBb zBQXj64(Dri3IDZC6X>@qNe#hZe6;w(5E^m#eacT_CnsFq4Rt@OU=w~wa*X}bGP`13 z7zX~Xl_x{XH^SQFlAqf~luA5N-sr~0`w{Y!IDekHjmU2Vg);Yn<^z~SE}c*938v}a+*#|GM`^JiK)eh{Z) z;#Jc>D({1Arfh$Rvf3ADOUgIvzgcf@p;v)}$ccD34-bCxR!;MHkk~UU;`M@BbAbJz z{t|jldu;{5!=3=+BKC};Re9usI=J*$up6NLTs!I?M#0|mk*;7{>0Q;gu?UEf>2a00 z6YDFR=|Ra~Xe%+^Ne=GwaR=ey!lksoW{s>gK7bWr6NG2kq1!&ze&J+$O3DOh`t$4H zGz_(S&9Mtkz{qKWNl$8Cl@SR&tM0P8#Q0DN;aPM?;h0vNUtJOVch78$Juzhx{ZBFT z_r@gl3wdl3)RLL*i7jr}u=&S^C-49@@psxs@X#px+?c=q2T3+SSLPX&-`UQy(E1+u z!%pB>JcXlfG(-%L2Z0>wX_nw{M6C92#2ae%zQi_q(G#yOUe|O>6Q0rDM0*Z5<4w8Y zcMXpgVXjY(aU8Si+@Cee@%G?DOmOO7G>lcs9|m}y{%G(hb|y5Qjdw`#O*fder302%ZR{@n`w?z3l1VsEbD3>&wt#OISC%t>MlF`|hAC5_p9Z`#5iCy0gjN+4Ki2 zLcHWD_pnDFXV=lT)n^C40uSgAFZnn-4(nGHVT@nPeF09#OD&HKvR5Q_(3XOA^z8A* zAzmD~!xwqdzz%7{O73>l_C^oFpBiFE?F-lJc`fl#Sj*W8qJv*hWT+i_3buygnbi(F zppSB&?P(e;-bN2@eo@m5wxf2l<62DcxHr?s@wPqfx4JWUage=f)B{+^eaA=Qg^A09 z`v=+cM*SD?i@jlB|J(V)FTktjw|fXntIrMFKHQ$&{5-se!~UvSPQ-IeY&-Zd-uEy; z#={umo3Rcg(}8=E-nXBc&`&v~6Z!$5S&8O3>;xTTMuM?UClY$_&xr{=%?E49tG9FB zm@w$ENfNt3(v6IT4$f79k+C-T$Ca_SnWu6j9D};a(EdwTlH6@d;VqB z0Ng|@;2pU`!`LN%FZPs2p$T?E8*L2=&hS)lOZT*U!v?O5Li=NhT#EO6qc8Lb^AyzM zwW3Gh?4Ot(c1(Jj2z5+$S4^v3VRr9?8wpfh@&t=7Fvs0+fcmcuO}6q z+mM(SrrquDTEaFgTwGM|<1nx;k+{P>2<3+-%D~{8>_|wZqGNPY+3AI+n2YkW=Cu#1 zqlW;fH@leCG9jh|#_5Wqrx%one(irqPcLi{{YC#FJ-u&5^x=^s0C2_0LnlZOeR!aV zBX@5%)hcgV=ym>mCX<^^-0QIQpk-#gL+At&y3WEs;;#>ZWI@neDtP#uh^URZ1^7} zeS-VdNuj??@NlV;>2U~)%eUobCJY!hzZN`P$m56?1rHa%7{?Pg$0uCOVtfqrMCA$> zt{9&Pyq2BM6nY&t_F!_eMDW!zoRD48wM_6fS!MbO!{!{|q-Qe=g5la<@GAu0Ec%}$ z_(tKQOHpKhbm1)`Cx5su#rVsDFO+qqR))>L1z#=rRh&ImgGJA>%(F=74-j0x<9EK` z(*@U6pE{A}6ya zj8y0^7d)Jva>VU|`x&XwKPq@QJ>`hklwQ7P@J*@TI`lVGu5eDt^kW3~b26bnK=5#a z#`H4;54|DA7XT-FUN3gL8^h*g!F#IkZoxYRm+CdEg#Y;2%mBr4*L6bQCiL1~u>B4B z4mX?LWe(C!nmdF~I4@#8zXM*YT|X)G>e}eZ_pb^bI(N)R+oO7V)$K8K`y zYlMHB^jFIN0>PJ7_hW+37hLP*2H_t%ZJhBvz-#H@p&0y8;j<+?Vb3x8qu}mMc_|a! zUJ|^`ovRy0ya$~0ws{5{zS?0U1g?_%AqM|v4E|mWJ{Sucwd!$948BhcJ}CyD9)lkpgMTRo z&&1$gjls`|!Pmv$m&M>W#NfBa;P=Gfzly;hiNUwV;Lpb3FU8e5~JB=x|__R9u-OU#9|C?p-ISM%W`E6P9NU(g2r8T;a5qx2h@r{^2 z&~=LN*(rRm;f1GIih*9O5S$(Fi* zGHh-ae4w5Ad_&ZM*T*2jZl+)46ms*J!y)Hum2y5We2na?x>yptF8Jmo)Bi*G*P-B~ zx1}PdsK*Q!yqZr7aGHN@tI~g~&{xYp-O-OWo2uqnbA^7(DV6e^2pp?A=1o~2(DHUV zKD(KXReC5pd{?vMbCvcp1qG=$Fw!>V!1oJd^1Vv%YCE|}@M^tn0zR^ay*(oMYB@E0 zq-6hu;LQ`cypLj?g02?@-+VmdH^{oyKb8L+raNaC--p4jDPO;bk73gyc(wi~3BLI( z<`1^+ny2)=9Hy6S==y@-OUoRd!8db?;Qkd1!A}#s`L7JTC)LpBa3PA^E)e=^Jzp!h z-%%=h_-PEE2Zg@6yiW?g{#Y*W?tC*Z2;N_%2OJ)1S|al4&!Nya>3N`v{vgFyFs1CS zHAe`(^BKk;5JvNWk5{IV`)SJOXAFK3rb<^y@E-Yk+Q}mSnSwWqU5#LJvsUo7uP~s? z9P0(2^d#dK3ZJV5_lM;RpACZdoXx-+LO&q*dOEfY*M)pD_bI(R_?;m5qk=D$&x^DP z{%7E|^zfe0SJ$IO*EXWYbGl)`sb027LFWnodod8yqTdblwemew_*D0ApLh9UonO{1 zb$m$)egB_0!?lucLHS6%zbyC}f^UA7fe%EV=P93)7+2XY7JTPaCLAdp@ioe)s^7j< z`G`HV*E7FO4i_OJx8DkVvpiqZe&7Y*we@<}t@`_g~D4!l;rBhi7lb|gF5cM<*UC-_qRE{BLPS@53a3|uAn zX9aJT@dDEXcP$XS+8;Yb@apk2BlslYbAa$E2;L%Fsd9o}BKX3;Ga&DxnCk^!FY?nD zA?UhEa3c@8wH|-2xOB7+i@n{e{C9Ht!J?nv3f?dM?X`k$6?~GkYXsf(7s1<>a`;}M ze_3&nNBfPp1Ya%ny9XyT2{;n!O9BKCJWwk0uz2g zI?3ryE+9QkOywsKa>UEDe@dE^j{Oa+7G-$@P(H% z;VV*+R}0=F?QWj%*#Nvnen)RV75e5KjP57$|3dIBUuXPE!GGiMCQyw}}0JM)1Q0pR}BT z>4ML6xCA1%uLym0d+8Ese&NuS|!FQg|z!$|H1_U4YBID|x+y%Tv z(nfDvg?{xmM%PI_ZWFxiO~!vAiCz`F`8@`jq#u4)@P6qhwI9IFO;_*kIBX=*`W+{D z&pEm7za-yrf}5&&qRHplm!;EFTGJ+(OtqJmmvi`C$Y&^+$sjmYgQec~Xt1j{ z9}RYAmxXD2yI178GZQl_h;ObtS59VDq>3HMVs=@wlv|ZemQ(Hdtm$2m%ypN_seC?} zE_9c(edXljnxKeqqGOw!mo3j*R0*9BLd9$6#2AjuS%z9mqACgccx3N1;gN zuYxM`0cHC1WTD41SHN@Z?a2_c3YAu4p4in+NLuJlXQ`T~mP)OYxHeFKo`Mr5r79Ms za;5B?zI3*yoGWxE7j~vf*(v5DV=EcVEfmw)jBu+;UL&qD@$77S@3Q%Y!s#(NR#Gp@ zmU_FgwXkHNm|T=CcNPmPDx+sY)#YL;EvZ7Yp4gGePR!(bVBYwZ?Cn;AOBcF&QpK#{ zD$EpCba!S`J!W~fSRw`G3#dEyC)=0K_m*Jd0qiR7`oSn9XM)c+&AKq7gzn%d`2Fk|dKYm5YUyW?2@7m& zl0-e}WHHsfj0GqmTNJ*g1Tk@8jZ_nO<3Y({;=;sL<%_1R9mPq}sk@SDg;a%^D4*-j zri#fD{-iruF&~DQRI3twMy^!O7Ev9}_DirzkFc6c7z&c5&fYRL3yd!rP&0}2c)9M5 zg0tun^OoA-HUZvUx$acCP;8y-+xuJ$Gs(HL=P7HKF7$i#<%cXK}M+bgJlv?gert^hvST%HANG{74+Ee*t z1{y3SQ@wqLOeUW#XEVsYCPlIXrAVfV#nehDvRquL-CA!~*GeRjPL4uOD;1=rj-H9= z#ZW9i<%q z>V*#>fqWrVF6GLkhz|OVC<#%5Of&%ph$vH$Ktq(E98JIhN>HvyAR+WU+3rkdN687O zp-e@Hh1a;gNL;BSN?GcNQf4YbeHEd0$*)}O?M|0FIvqp| z3S-J0EUo0z9>(-_L;{&;pcD;sN&q$28P%%>Sg)Otnk(nRP&wCL5#vJHuX1tGFq}BB zWp~ehmG5M5Wp8lutEYf*lgYZt35AV1Cvc*3qhL4EiIh&upN3E}Id{>FW9KC295*|e zL}>Qn;}<25S#ZK)7vc=ir(wt^kC}`&Jv9uYg(f?)Fhew2xJ77=tH|}mFG=Zo?XqzV z4Jg`7Qijv8=>V^C2!V7{S;LGnrGg|xTu-4VUFhvDOIA|ikT_qaw3>CY7qTQ*Bulut z#7;k9^sI^!bH18(_Lq;FaqNOwNVd%J^op2w+=_XD|Ri+QK*ggx^U|ZX~S+kOdv`%$>7|lsaxvAh4+9?9A zDkau8aa3|@cO8i77m9K2XREb--iUQ#E!=0Ar$kn(tz|x)L)<@&1T!?9kXEOtmKr|DXr5G&Bi0rOo~WT>13N!JG`~3 zv@B$WE~KG(DKJG^u+StIEIu|e@S;*^gjnpRV_JI1dkh?$-qf<}2{aq($fake$|)`+ zdQ|j4R8A=l^_Qjx&NI!s$X89a7YnIOI#mjVoZ3p(&s{ryP}R9wqA4ZU7a1vf7|w%w zYW%|BAaqF3RmhZchX7UUb{dWpg&PSGq>326DO#mM2jQl5D2UhFRO01&3PTn&6&3`y zBAG9B7D+Zu>I$?XMOGq}#buF3RcU@?TaYuGmx^#ErFx=)2pKr7Ih{G1p)rQgjuMwg zx^8r-Bn&mG$}GsjNj^Bp`E0ir%2`lonu{^SEXw9*(&T_jH+f2{GpG-#3k^VBE^jD2 zPm0WmAc+&pjT;D{HcDyV$Xu5Xl)4dLNwQV|m&kYOvSA%nsbkjM8Q=re^*OP%ZsDyZ(=gj| z*b>PqM2*qeCsNDW2!3%Hq_MY`R-}4L)YzprasZhP+B6g@(u>gG5d}TyPOTL*f%NI2ZWk`uxh&>=Pm`C2bK@ zumoBnDVE)^s~Tb!&giC+38Z^g`XWpwL!pkQgDjQnjC>6Q(wKY}lj&5tle}w@i`2qq z?mQI)khn^9PW93hxP?~MCK8dEkml9KoIj8C0h&#F*|@(Kv+k;;dY6gw#=ujS*6>UF zKGrC!!TE!Tb4i@fMq-pY^RA0kW0rXmqSED6+L{h&JUWuJJ=yh>HeGix55^RwmCD zsCao-ykyvsL>d~+|4Q1CU`%4c4rwJ@`SA1)Alr?MN9d#@rVIxQPN{E8+-o|Hli?_E zpG4~vRf58*xEE3*<{pCNmp<+g_yeRX(FuH9p9xKO=X7IIN2>#K$>WCy;$tyx(+Dp@ zqf?+Nqn@~;{~(!RkUW*5pVEGu>RkE6X55-82++cX0~sPw$bgABGI!QV)_K$Dym(2~ zdD9?Z1!mZM5D=+8Jf#IKhH2L)bZ1?ez4Bd80=;3%NLivobUp)lGHNQ7evGzhH{i0K z-8>1Yol@P@2mwtk{Is;y08cWj8>hIgw_CobCAYy{hexW zFJ_l(h!)QDH}s@G3C55Xh70LD93RiHh;>C5mIprnk2l)KM$b0oakuG_A9|lJ&xz!5 zpd8Xkd-eIEeG_kg@IgV-Z^HXObm_fQ9-LOEXDVYR;0JBV(DW_XA4-?r8|Aqf)_?ed@)%}4Juf&Dv@ zem4>bm)?g8A<2(Nb6$R;q3a9y_kJY({s%kQ=zS>%oxY>x7uZld@m6`(NqW6EQX*W{ z`d9s(hB!)3<)CZ*q{{m?io`|f@yOB3uj%PAIAtMbVoVzXbhWv^E)DwSAOO5VpV#5 zuVsg%-&ARSUVobYA64n~y`lceoWIh6%w5%C+FnPP%D;IAr`ss$b$=|UHl{^?$o+qC zClRa5sp+Rw(Q9$^PKo{R2<_97|4!{_B(9ovcue}=9L?o8{%pQ0>+1C5V$$#a2-o;Z z-53><{{b=SFU|76w?#LY#iXAm>8thEzKR>}`-@a^zN`FtZ;MI4^mm+o4mFO*rRns( zP|`<8+|Tbn%js9&|Izwe8k2s)wLI{ynqA3R^Hr3>l{s)s|29s)P{!kZ0b3BO{i<0| l|EjjjDf#ctA>46JQ9_BV&Y$*0)k;5pUrygsm7pqU{ufK64mkh- literal 0 HcmV?d00001 diff --git a/user/dadk/config/libgcc.dadk b/user/dadk/config/libgcc.dadk new file mode 100644 index 000000000..a350285cd --- /dev/null +++ b/user/dadk/config/libgcc.dadk @@ -0,0 +1,24 @@ +{ + "name": "libgcc", + "version": "0.1.0", + "description": "gcc lib", + "rust_target": null, + "task_type": { + "BuildFromSource": { + "Local": { + "path": "apps/libgcc" + } + } + }, + "depends": [], + "build": { + "build_command": "make install" + }, + "install": { + "in_dragonos_path": "/" + }, + "clean": { + "clean_command": "cd build & make clean" + }, + "envs": [] +} \ No newline at end of file From c966d612d2af506ae5ae29cf90411050afa2a35e Mon Sep 17 00:00:00 2001 From: MemoryShore <105195940+MemoryShore@users.noreply.github.com> Date: Mon, 2 Sep 2024 22:53:24 +0800 Subject: [PATCH 44/60] =?UTF-8?q?feat:=20=E5=88=9D=E6=AD=A5=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=8A=A8=E6=80=81=E9=93=BE=E6=8E=A5=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=20(#908)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs(sched):调度子系统文档即cfs文档 (#807) * 调度子系统文档以及cfs文档 * fix(net): Fix TCP Unresponsiveness and Inability to Close Connections (#791) * fix(net): Improve stability. 为RawSocket与UdpSocket实现close时调用close方法,符合smoltcp的行为。为SocketInode实现drop,保证程序任何情况下退出时都能正确close对应socket, 释放被占用的端口。 * fix(net): Correct socket close behavior. * fix: disable mm debug log to prevent system lockup due to thingbuf issue (#808) * 添加支持gentoo系统的一键安装脚本 (#809) * feat(driver/pci): add pci bus into sysfs (#792) 把pci设备加入sysfs * doc: Add Gentoo Linux In build_system.md (#810) * 增加安装文档中的Gentoo Linux提示 * doc: add v0.1.10 changelog (#813) * 完成v0.1.10 changelog * fix(driver/apic_timer): 修复local apic timer初始化顺序导致的在某些云服务器上无法收到中断的bug (#815) * chore: move setup_arch_post timepoint to before clocksource_boot_finish (#820) This commit adjusts the timing of the setup_arch_post event to occur before the clocksource_boot_finish event, allowing the time subsystem to properly register architecture-specific clock sources. * feat(log): 将内核日志统一为新的logger (#814) * fix(log): 修复pr #814 的问题 (#821) * feat(driver/pci): 完善pci root结构体,增加portio的pci配置空间访问 (#818) * feat(driver/pci): 完善pci root结构体,增加portio的pci配置空间访问 * 增加rust sparse稀疏索引选项 (#826) * fix(time):修复了issue #816 (#830) * chore(tools): add the gentoo grub_auto_install support (#827) * 20240524 3:40 * 20240527 0010 * 修复mmap未延迟分配内存的问题 * feat(procfs): update procfs (#831) 为procfs增加是否是kthread的显示 增加返回进程已经占用的文件描述符数量 * Revert "Merge branch 'patch-add-file-mapping' into patch-fix-mmap" This reverts commit 8eb687c60b43831d7e9614bca0af41e8f2175ae8, reversing changes made to 33e9f0b34f9dc35a47757137a29605e51052a26e. * 20240528 1800 * Revert "Revert "Merge branch 'patch-add-file-mapping' into patch-fix-mmap"" This reverts commit 9261cb79f08d12bbe4b3b5bf29c84625db059c13. * feat(mm): 修复mmap未延迟分配内存的问题 (#837) * 20240524 3:40 * 20240527 0010 * 修复mmap未延迟分配内存的问题 * Revert "Merge branch 'patch-add-file-mapping' into patch-fix-mmap" This reverts commit 8eb687c60b43831d7e9614bca0af41e8f2175ae8, reversing changes made to 33e9f0b34f9dc35a47757137a29605e51052a26e. * update-20240529-0347 * fix(driver): fix memory security problem in tty device ioctl (#833) * add soft link to musl-gcc * fix the tty_ioctl * modified * modified * update 20240604 0233 * feat(user): user management tool (#825) * 用户管理工具 * 重构 * 改为多个bin文件入口 * bin文件的usage显示自身程序名而非固定程序名 * update 20240606 1800 * update 20240607 0200 * update 20240617 1747 * 重写页面保护标志的构造逻辑 * update20240620 1726 * 添加Riscv64的protection_map * 简单实现fat文件系统的文件映射,添加msync系统调用 * trait FileSystem增加统一接口 * MountFS实现文件映射相关接口 * 格式化代码 * feat(time): Add syscall support for utime* (#838) * feat(vfs): Add syscall support for utime* impl sys_utimensat impl sys_utimes add utimensat test fix some warning * fix(vfs): Verify pointer validity * fix: remove bad cfg * pagecache存储方式由HashMap改为XArray * 修复mprotect系统调用未正确设置vm_flags的错误 (#847) * fix(time): modify update wall time (#836) 更改了时间子系统的update_wall_time函数,通过读取当前周期数,计算delta值进行更新,而不是通过传入delta值进行更新 * chore: 调整triagebot.toml以适应新的组织架构 (#848) * doc: 完善README.md (#849) * doc: 完善README.md * chore: 更新sphinx相关配置,适应read the docs的更新 (#850) 根据read the docs在7月15日blog,进行此修改 https://about.readthedocs.com/blog/2024/07/addons-by-default/ * feat(driver/net): 实现Loopback网卡接口 (#845) * 初步实现loopback设备 * fix: build-scripts和tools目录下的make check指定工具链版本 (#861) * fix: tcp poll没有正确处理posix socket的listen状态的问题 (#859) * 使用读写锁包装Page结构体 * chore: 将工具链更新到2024-07-23 (#864) * chore: 将工具链更新到2024-07-23 * PageCache由存放物理地址改为直接存放页面 * 优化protection_map的初始化方式 * feat(fs): add eventfd syscall support (#858) * feat(fs): add eventfd syscall support * refactor: 删除过时的va-pa转换函数,改为统一使用MMArch (#862) * 添加shrink_list方法释放页面 * 默认nightly-2024-07-23 & config改为config.toml (#872) * fix: 修复由于升级到2024-07-23工具链之后,某些机器上面内核运行一直fault的问题。 (#870) * fix: 修复由于升级到2024-07-23工具链之后,某些机器上面内核运行一直fault的问题。 * 添加页面回收机制 * 添加页面回收内核线程 * feat(cred): 初步实现Cred (#846) * 初步实现Cred * 添加seteuid和setegid * 添加cred测试程序 * 修改Cred::fscmp返回结果为CredFsCmp枚举 * 完善root用户相关信息 * fix: 修复键盘码解析器没能正确处理类似ctrl C的控制字符的问题 (#877) * fix: 修复键盘码解析器没能正确处理类似ctrl C的控制字符的问题 * fix: 解决ntty潜在的panic问题 * 缺页中断使用的锁修改为irq_save; 添加脏页回写机制 * 优化代码结构,添加部分注释 * ci: enable ci workflow on branches other than master (#891) * 修复unlink、unlinkat系统调用的路径错误 (#892) * fix: socket shutdown wrong implement (#893) * feat: 增加tokio异步运行时支持 (#894) * fix the EventFdFlags error * feat: support tokio (Single thread version) Fix deadlock issue on closing file. Add function for PipeInode and EventFdInode. * 优化PageCache的创建 * fix: pipe 读取/写入阻塞时,无法kill进程的问题 (#889) * 将入口点改为链接器;修正链接器加载地址 * 修复合并错误 * 修复do_cow_page死锁问题 * 将PageFaultMessage中的地址对齐 * auxv添加随机数指针;修复AtType序号错误 * 简单实现用户栈的16字节对齐 * 通过check fmt * 完善用户栈的字节对齐机制 * 通过riscv64编译 * 修改测试程序路径 * 添加动态库libgcc_s.so.1 --------- Co-authored-by: GnoCiYeH Co-authored-by: Samuel Dai Co-authored-by: LoGin Co-authored-by: donjuanplatinum <113148619+donjuanplatinum@users.noreply.github.com> Co-authored-by: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> Co-authored-by: Mingtao Huang <114841534+1037827920@users.noreply.github.com> Co-authored-by: BrahmaMantra <140599389+BrahmaMantra@users.noreply.github.com> Co-authored-by: laokengwt <143977175+laokengwt@users.noreply.github.com> Co-authored-by: Jomo Co-authored-by: linfeng Co-authored-by: SMALLC <121806694+smallcjy@users.noreply.github.com> Co-authored-by: linfeng <1925466036@qq.com> Co-authored-by: Chiichen Co-authored-by: Samuel Dai --- .github/workflows/cache-toolchain.yml | 21 +- .github/workflows/makefile.yml | 4 +- Makefile | 4 +- README.md | 76 +- README_EN.md | 80 +- build-scripts/.gitignore | 2 +- build-scripts/Makefile | 2 + build-scripts/kernel_build/src/lib.rs | 2 - docs/community/ChangeLog/V0.1.x/V0.1.10.md | 1062 +++++++++++++++++ docs/community/ChangeLog/index.rst | 1 + docs/conf.py | 15 +- docs/introduction/build_system.md | 1 + docs/kernel/locking/mutex.md | 4 +- docs/kernel/locking/spinlock.md | 4 +- docs/kernel/sched/cfs.md | 30 +- docs/kernel/sched/core.md | 67 +- kernel/.cargo/config.toml | 4 + kernel/Cargo.toml | 24 +- kernel/Makefile | 10 +- kernel/crates/bitmap/src/lib.rs | 1 + kernel/crates/bitmap/src/static_bitmap.rs | 9 + kernel/crates/bitmap/src/traits.rs | 5 - .../libs => crates}/intertrait/.gitignore | 0 .../libs => crates}/intertrait/Cargo.toml | 2 +- .../libs => crates}/intertrait/LICENSE-MIT | 0 .../{src/libs => crates}/intertrait/README.md | 2 +- .../intertrait/macros/Cargo.toml | 2 +- .../intertrait/macros/LICENSE-APACHE | 0 .../intertrait/macros/LICENSE-MIT | 0 .../intertrait/macros/src/args.rs | 0 .../intertrait/macros/src/gen_caster.rs | 0 .../intertrait/macros/src/item_impl.rs | 0 .../intertrait/macros/src/item_type.rs | 0 .../intertrait/macros/src/lib.rs | 0 .../libs => crates}/intertrait/src/cast.rs | 0 .../intertrait/src/cast/cast_arc.rs | 0 .../intertrait/src/cast/cast_box.rs | 0 .../intertrait/src/cast/cast_mut.rs | 0 .../intertrait/src/cast/cast_rc.rs | 0 .../intertrait/src/cast/cast_ref.rs | 0 .../libs => crates}/intertrait/src/hasher.rs | 0 .../libs => crates}/intertrait/src/lib.rs | 0 .../intertrait/tests/castable_to.rs | 0 .../intertrait/tests/on-enum.rs | 0 .../intertrait/tests/on-struct.rs | 0 .../tests/on-trait-impl-assoc-type1.rs | 0 .../tests/on-trait-impl-assoc-type2.rs | 0 .../tests/on-trait-impl-assoc-type3.rs | 0 .../intertrait/tests/on-trait-impl.rs | 0 .../intertrait/tests/on-type-multi-traits.rs | 0 .../libs => crates}/intertrait/tests/run.rs | 0 .../intertrait/tests/ui/duplicate-flags.rs | 0 .../tests/ui/duplicate-flags.stderr | 0 .../intertrait/tests/ui/on-generic-type.rs | 0 .../tests/ui/on-generic-type.stderr | 0 .../intertrait/tests/ui/on-type-impl.rs | 0 .../intertrait/tests/ui/on-type-impl.stderr | 0 .../intertrait/tests/ui/unknown-flag.rs | 0 .../intertrait/tests/ui/unknown-flag.stderr | 0 kernel/crates/klog_types/src/lib.rs | 6 + kernel/crates/rust-slabmalloc/src/pages.rs | 8 +- kernel/crates/unified-init/Cargo.toml | 2 +- kernel/crates/unified-init/macros/Cargo.toml | 2 +- kernel/crates/unified-init/src/lib.rs | 2 +- kernel/crates/wait_queue_macros/Cargo.toml | 7 + kernel/crates/wait_queue_macros/src/lib.rs | 60 + kernel/env.mk | 3 + kernel/rust-toolchain.toml | 2 +- kernel/src/Makefile | 4 +- kernel/src/arch/io.rs | 1 + kernel/src/arch/riscv64/driver/of.rs | 4 +- kernel/src/arch/riscv64/init/mod.rs | 11 +- kernel/src/arch/riscv64/interrupt/handle.rs | 40 +- kernel/src/arch/riscv64/ipc/signal.rs | 13 +- kernel/src/arch/riscv64/mm/init.rs | 24 +- kernel/src/arch/riscv64/mm/mod.rs | 74 +- kernel/src/arch/riscv64/pci/pci_host_ecam.rs | 9 +- kernel/src/arch/riscv64/process/idle.rs | 8 +- kernel/src/arch/riscv64/process/mod.rs | 10 +- kernel/src/arch/riscv64/process/syscall.rs | 28 +- kernel/src/arch/riscv64/smp/mod.rs | 14 +- kernel/src/arch/riscv64/syscall/mod.rs | 4 +- kernel/src/arch/riscv64/time.rs | 9 +- kernel/src/arch/x86_64/acpi.rs | 5 +- .../src/arch/x86_64/driver/apic/apic_timer.rs | 26 +- kernel/src/arch/x86_64/driver/apic/ioapic.rs | 14 +- .../arch/x86_64/driver/apic/lapic_vector.rs | 7 +- kernel/src/arch/x86_64/driver/apic/mod.rs | 14 +- kernel/src/arch/x86_64/driver/apic/x2apic.rs | 11 +- kernel/src/arch/x86_64/driver/apic/xapic.rs | 13 +- kernel/src/arch/x86_64/driver/hpet.rs | 22 +- kernel/src/arch/x86_64/driver/rtc.rs | 4 +- kernel/src/arch/x86_64/driver/tsc.rs | 38 +- kernel/src/arch/x86_64/init/mod.rs | 13 +- kernel/src/arch/x86_64/interrupt/entry.rs | 1 + kernel/src/arch/x86_64/interrupt/ipi.rs | 10 +- kernel/src/arch/x86_64/interrupt/mod.rs | 10 +- kernel/src/arch/x86_64/interrupt/msi.rs | 1 + kernel/src/arch/x86_64/interrupt/trap.rs | 46 +- kernel/src/arch/x86_64/ipc/signal.rs | 41 +- kernel/src/arch/x86_64/kvm/mod.rs | 15 +- kernel/src/arch/x86_64/kvm/vmx/ept.rs | 4 +- kernel/src/arch/x86_64/kvm/vmx/mmu.rs | 10 +- kernel/src/arch/x86_64/kvm/vmx/vcpu.rs | 68 +- kernel/src/arch/x86_64/kvm/vmx/vmexit.rs | 31 +- .../arch/x86_64/kvm/vmx/vmx_asm_wrapper.rs | 17 +- kernel/src/arch/x86_64/mm/fault.rs | 26 +- kernel/src/arch/x86_64/mm/mod.rs | 142 ++- kernel/src/arch/x86_64/mm/pkru.rs | 4 +- kernel/src/arch/x86_64/mod.rs | 1 + kernel/src/arch/x86_64/pci/pci.rs | 42 +- kernel/src/arch/x86_64/process/idle.rs | 5 +- kernel/src/arch/x86_64/process/kthread.rs | 3 +- kernel/src/arch/x86_64/process/mod.rs | 10 +- kernel/src/arch/x86_64/process/syscall.rs | 36 +- kernel/src/arch/x86_64/process/table.rs | 1 + kernel/src/arch/x86_64/smp/mod.rs | 7 +- kernel/src/arch/x86_64/syscall/mod.rs | 7 +- .../src/arch/x86_64/x86_64-unknown-none.json | 2 +- kernel/src/debug/klog/mm.rs | 27 +- kernel/src/driver/acpi/bus.rs | 2 + kernel/src/driver/acpi/mod.rs | 10 +- kernel/src/driver/acpi/pmtmr.rs | 2 +- kernel/src/driver/acpi/sysfs.rs | 12 +- kernel/src/driver/base/block/block_device.rs | 30 +- kernel/src/driver/base/char/mod.rs | 8 +- kernel/src/driver/base/device/bus.rs | 16 +- kernel/src/driver/base/device/dd.rs | 42 +- kernel/src/driver/base/device/driver.rs | 13 +- kernel/src/driver/base/device/init.rs | 23 +- kernel/src/driver/base/device/mod.rs | 34 +- kernel/src/driver/base/kobject.rs | 6 +- .../driver/base/platform/platform_device.rs | 1 + .../driver/base/platform/platform_driver.rs | 1 + kernel/src/driver/base/platform/subsys.rs | 5 +- .../driver/block/cache/cached_block_device.rs | 5 +- kernel/src/driver/block/virtio_blk.rs | 12 +- kernel/src/driver/clocksource/acpi_pm.rs | 69 +- kernel/src/driver/clocksource/timer_riscv.rs | 42 +- kernel/src/driver/disk/ahci/ahcidisk.rs | 74 +- kernel/src/driver/disk/ahci/hba.rs | 37 +- kernel/src/driver/disk/ahci/mod.rs | 51 +- kernel/src/driver/firmware/efi/fdt.rs | 5 +- kernel/src/driver/firmware/efi/init.rs | 34 +- kernel/src/driver/firmware/efi/memmap.rs | 5 +- kernel/src/driver/firmware/efi/mod.rs | 8 +- kernel/src/driver/firmware/efi/tables.rs | 21 +- kernel/src/driver/input/ps2_dev/ps2_device.rs | 1 + .../driver/input/ps2_mouse/ps_mouse_device.rs | 10 +- .../driver/input/ps2_mouse/ps_mouse_driver.rs | 5 +- kernel/src/driver/input/serio/i8042/mod.rs | 13 +- kernel/src/driver/input/serio/serio_device.rs | 1 + kernel/src/driver/input/serio/serio_driver.rs | 1 + kernel/src/driver/input/serio/subsys.rs | 5 +- kernel/src/driver/irqchip/riscv_intc.rs | 5 +- .../src/driver/irqchip/riscv_sifive_plic.rs | 35 +- kernel/src/driver/net/dma.rs | 4 +- kernel/src/driver/net/e1000e/e1000e.rs | 18 +- kernel/src/driver/net/e1000e/e1000e_driver.rs | 4 +- kernel/src/driver/net/loopback.rs | 483 ++++++++ kernel/src/driver/net/mod.rs | 3 +- kernel/src/driver/net/virtio_net.rs | 16 +- kernel/src/driver/open_firmware/fdt.rs | 44 +- kernel/src/driver/pci/attr.rs | 160 +++ kernel/src/driver/pci/dev_id.rs | 109 ++ kernel/src/driver/pci/device.rs | 219 ++++ kernel/src/driver/pci/driver.rs | 84 ++ kernel/src/driver/pci/ecam.rs | 30 +- kernel/src/driver/pci/mod.rs | 7 + kernel/src/driver/pci/pci.rs | 90 +- kernel/src/driver/pci/pci_irq.rs | 5 +- kernel/src/driver/pci/raw_device.rs | 194 +++ kernel/src/driver/pci/root.rs | 107 +- kernel/src/driver/pci/subsys.rs | 191 +++ kernel/src/driver/pci/test/mod.rs | 30 + kernel/src/driver/pci/test/pt_device.rs | 236 ++++ kernel/src/driver/pci/test/pt_driver.rs | 171 +++ kernel/src/driver/rtc/class.rs | 3 +- kernel/src/driver/rtc/mod.rs | 1 + kernel/src/driver/serial/mod.rs | 2 + kernel/src/driver/serial/serial8250/mod.rs | 4 +- .../serial/serial8250/serial8250_pio.rs | 2 + kernel/src/driver/tty/console.rs | 1 + kernel/src/driver/tty/tty_core.rs | 12 - kernel/src/driver/tty/tty_device.rs | 45 +- kernel/src/driver/tty/tty_driver.rs | 3 +- kernel/src/driver/tty/tty_ldisc/ntty.rs | 8 +- .../tty/virtual_terminal/virtual_console.rs | 7 +- .../fbdev/base/fbcon/framebuffer_console.rs | 3 +- .../src/driver/video/fbdev/base/fbcon/mod.rs | 13 +- kernel/src/driver/video/fbdev/base/fbmem.rs | 3 +- kernel/src/driver/video/fbdev/base/fbsysfs.rs | 5 +- kernel/src/driver/video/fbdev/base/mod.rs | 5 +- kernel/src/driver/video/fbdev/vesafb.rs | 7 +- kernel/src/driver/video/mod.rs | 10 +- kernel/src/driver/virtio/irq.rs | 3 +- kernel/src/driver/virtio/mmio.rs | 3 +- kernel/src/driver/virtio/mod.rs | 1 + kernel/src/driver/virtio/sysfs.rs | 15 +- kernel/src/driver/virtio/transport_mmio.rs | 5 +- kernel/src/driver/virtio/transport_pci.rs | 4 +- kernel/src/driver/virtio/virtio.rs | 13 +- kernel/src/driver/virtio/virtio_impl.rs | 6 +- kernel/src/exception/handle.rs | 23 +- kernel/src/exception/irqchip.rs | 10 +- kernel/src/exception/irqdomain.rs | 19 +- kernel/src/exception/manage.rs | 47 +- kernel/src/exception/msi.rs | 1 + kernel/src/exception/softirq.rs | 18 +- kernel/src/exception/sysfs.rs | 3 +- kernel/src/filesystem/devfs/mod.rs | 12 +- kernel/src/filesystem/devpts/mod.rs | 3 +- kernel/src/filesystem/eventfd.rs | 268 +++++ kernel/src/filesystem/fat/bpb.rs | 26 +- kernel/src/filesystem/fat/entry.rs | 29 +- kernel/src/filesystem/fat/fs.rs | 89 +- kernel/src/filesystem/kernfs/mod.rs | 5 +- kernel/src/filesystem/mbr.rs | 9 +- kernel/src/filesystem/mod.rs | 1 + kernel/src/filesystem/procfs/kmsg.rs | 5 +- kernel/src/filesystem/procfs/mod.rs | 26 +- kernel/src/filesystem/procfs/syscall.rs | 2 - kernel/src/filesystem/ramfs/mod.rs | 3 + kernel/src/filesystem/sysfs/file.rs | 8 +- kernel/src/filesystem/sysfs/group.rs | 13 +- kernel/src/filesystem/sysfs/mod.rs | 10 +- kernel/src/filesystem/sysfs/symlink.rs | 2 +- kernel/src/filesystem/vfs/core.rs | 24 +- kernel/src/filesystem/vfs/file.rs | 151 ++- kernel/src/filesystem/vfs/mod.rs | 45 +- kernel/src/filesystem/vfs/mount.rs | 45 +- kernel/src/filesystem/vfs/open.rs | 104 +- kernel/src/filesystem/vfs/syscall.rs | 158 ++- kernel/src/filesystem/vfs/utils.rs | 9 +- kernel/src/init/init.rs | 3 +- kernel/src/init/initial_kthread.rs | 24 +- kernel/src/init/mod.rs | 1 + kernel/src/ipc/pipe.rs | 57 +- kernel/src/ipc/shm.rs | 22 +- kernel/src/ipc/signal.rs | 47 +- kernel/src/ipc/signal_types.rs | 9 +- kernel/src/ipc/syscall.rs | 33 +- kernel/src/lib.rs | 23 +- kernel/src/libs/elf.rs | 97 +- kernel/src/libs/ffi_convert.rs | 15 - kernel/src/libs/font/mod.rs | 1 + kernel/src/libs/futex/futex.rs | 5 +- kernel/src/libs/ida/src/lib.rs | 1 + kernel/src/libs/keyboard_parser.rs | 28 +- kernel/src/libs/lib_ui/screen_manager.rs | 1 + kernel/src/libs/lib_ui/textui.rs | 14 +- kernel/src/libs/lib_ui/textui_no_alloc.rs | 2 +- kernel/src/libs/mod.rs | 2 +- kernel/src/libs/name.rs | 13 + kernel/src/libs/notifier.rs | 8 +- kernel/src/libs/printk.rs | 135 ++- kernel/src/libs/rbtree.rs | 23 +- kernel/src/libs/rwlock.rs | 2 +- kernel/src/libs/semaphore.rs | 5 +- kernel/src/libs/wait_queue.rs | 39 +- kernel/src/misc/ksysfs.rs | 3 +- kernel/src/mm/allocator/buddy.rs | 40 +- kernel/src/mm/allocator/kernel_allocator.rs | 1 + kernel/src/mm/allocator/page_frame.rs | 5 +- kernel/src/mm/allocator/slab.rs | 5 +- kernel/src/mm/c_adapter.rs | 16 +- kernel/src/mm/early_ioremap.rs | 8 +- kernel/src/mm/fault.rs | 550 +++++++-- kernel/src/mm/init.rs | 12 +- kernel/src/mm/kernel_mapper.rs | 6 +- kernel/src/mm/madvise.rs | 2 +- kernel/src/mm/memblock.rs | 5 +- kernel/src/mm/mmio_buddy.rs | 57 +- kernel/src/mm/mod.rs | 83 +- kernel/src/mm/no_init.rs | 8 +- kernel/src/mm/page.rs | 430 ++++++- kernel/src/mm/syscall.rs | 173 ++- kernel/src/mm/ucontext.rs | 428 +++++-- kernel/src/net/event_poll/mod.rs | 13 +- kernel/src/net/net_core.rs | 59 +- kernel/src/net/socket/inet.rs | 397 +++--- kernel/src/net/socket/mod.rs | 235 ++-- kernel/src/net/socket/unix.rs | 17 +- kernel/src/net/syscall.rs | 10 +- kernel/src/process/abi.rs | 4 +- kernel/src/process/cred.rs | 170 +++ kernel/src/process/exec.rs | 53 +- kernel/src/process/exit.rs | 12 +- kernel/src/process/fork.rs | 4 +- kernel/src/process/kthread.rs | 16 +- kernel/src/process/mod.rs | 81 +- kernel/src/process/syscall.rs | 144 ++- kernel/src/process/timer.rs | 2 +- kernel/src/sched/completion.rs | 4 +- kernel/src/sched/fair.rs | 32 +- kernel/src/sched/mod.rs | 33 +- kernel/src/smp/cpu/mod.rs | 9 +- kernel/src/smp/init.rs | 4 +- kernel/src/syscall/misc.rs | 3 +- kernel/src/syscall/mod.rs | 104 +- kernel/src/syscall/user_access.rs | 19 +- kernel/src/time/clocksource.rs | 184 +-- kernel/src/time/jiffies.rs | 31 +- kernel/src/time/mod.rs | 13 +- kernel/src/time/syscall.rs | 3 +- kernel/src/time/tick_common.rs | 25 + kernel/src/time/timekeep.rs | 30 +- kernel/src/time/timekeeping.rs | 314 +++-- kernel/src/time/timer.rs | 34 +- kernel/src/virt/kvm/host_mem.rs | 20 +- kernel/src/virt/kvm/kvm_dev.rs | 9 +- kernel/src/virt/kvm/mod.rs | 17 +- kernel/src/virt/kvm/vcpu.rs | 1 + kernel/src/virt/kvm/vcpu_dev.rs | 18 +- kernel/src/virt/kvm/vm.rs | 9 +- kernel/src/virt/kvm/vm_dev.rs | 15 +- tools/Makefile | 2 +- tools/bootstrap.sh | 29 +- tools/change_rust_src.sh | 31 +- .../logmonitor/src/backend/monitor/mm.rs | 2 +- tools/grub_auto_install.sh | 21 +- triagebot.toml | 29 +- user/apps/about/about.c | 2 +- user/apps/clear/Makefile | 2 +- user/apps/http_server/main.c | 2 + user/apps/libgcc/Makefile | 2 + user/apps/libgcc/lib/libgcc_s.so.1 | Bin 0 -> 732952 bytes user/apps/test-backlog/Makefile | 2 +- user/apps/test-blockcache/Makefile | 2 +- user/apps/test-for-robustfutex/Makefile | 2 +- user/apps/test-mount/Makefile | 2 +- user/apps/test_alarm/Makefile | 2 +- user/apps/test_cred/.gitignore | 1 + user/apps/test_cred/Makefile | 20 + user/apps/test_cred/main.c | 42 + user/apps/test_eventfd/.gitignore | 1 + user/apps/test_eventfd/Makefile | 20 + user/apps/test_eventfd/main.c | 52 + user/apps/test_lo/.gitignore | 3 + user/apps/test_lo/Cargo.toml | 7 + user/apps/test_lo/Makefile | 56 + user/apps/test_lo/README.md | 7 + user/apps/test_lo/src/main.rs | 25 + user/apps/test_socket/Makefile | 2 +- user/apps/test_statx/Makefile | 2 +- user/apps/test_tokio/.gitignore | 3 + user/apps/test_tokio/Cargo.toml | 13 + user/apps/test_tokio/Makefile | 56 + user/apps/test_tokio/src/main.rs | 17 + user/apps/test_utimensat/.gitignore | 1 + user/apps/test_utimensat/Makefile | 20 + user/apps/test_utimensat/main.c | 12 + user/apps/user-manage/.gitignore | 3 + user/apps/user-manage/Cargo.toml | 36 + user/apps/user-manage/Makefile | 47 + user/apps/user-manage/README.md | 142 +++ user/apps/user-manage/src/check/check.rs | 908 ++++++++++++++ user/apps/user-manage/src/check/info.rs | 95 ++ user/apps/user-manage/src/check/mod.rs | 3 + user/apps/user-manage/src/cmd/groupadd.rs | 45 + user/apps/user-manage/src/cmd/groupdel.rs | 45 + user/apps/user-manage/src/cmd/groupmod.rs | 46 + user/apps/user-manage/src/cmd/mod.rs | 7 + user/apps/user-manage/src/cmd/passwd.rs | 25 + user/apps/user-manage/src/cmd/useradd.rs | 44 + user/apps/user-manage/src/cmd/userdel.rs | 44 + user/apps/user-manage/src/cmd/usermod.rs | 46 + user/apps/user-manage/src/error/error.rs | 33 + user/apps/user-manage/src/error/mod.rs | 2 + .../apps/user-manage/src/executor/executor.rs | 729 +++++++++++ user/apps/user-manage/src/executor/mod.rs | 2 + user/apps/user-manage/src/lib.rs | 5 + user/apps/user-manage/src/parser/cmd.rs | 96 ++ user/apps/user-manage/src/parser/mod.rs | 3 + user/apps/user-manage/src/parser/parser.rs | 137 +++ user/dadk/config/held-0.1.0.dadk | 2 +- user/dadk/config/libgcc.dadk | 24 + user/dadk/config/test_cred-0.1.0.dadk | 23 + user/dadk/config/test_eventfd_0_1_0.dadk | 23 + user/dadk/config/test_glibc-0.1.0.dadk | 2 +- user/dadk/config/test_lo_0_1_0.dadk | 27 + user/dadk/config/test_tokio-0.1.0.dadk | 23 + user/dadk/config/test_utimensat_0_1_0.dadk | 23 + user/dadk/config/user_manage-0.1.0.dadk | 24 + user/sysconfig/etc/group | 1 + user/sysconfig/etc/gshadow | 1 + user/sysconfig/etc/passwd | 1 + user/sysconfig/etc/shadow | 1 + .../sysconfig/home/reach/system/shell.service | 8 + 389 files changed, 11755 insertions(+), 2767 deletions(-) create mode 100644 docs/community/ChangeLog/V0.1.x/V0.1.10.md rename kernel/{src/libs => crates}/intertrait/.gitignore (100%) rename kernel/{src/libs => crates}/intertrait/Cargo.toml (97%) rename kernel/{src/libs => crates}/intertrait/LICENSE-MIT (100%) rename kernel/{src/libs => crates}/intertrait/README.md (99%) rename kernel/{src/libs => crates}/intertrait/macros/Cargo.toml (97%) rename kernel/{src/libs => crates}/intertrait/macros/LICENSE-APACHE (100%) rename kernel/{src/libs => crates}/intertrait/macros/LICENSE-MIT (100%) rename kernel/{src/libs => crates}/intertrait/macros/src/args.rs (100%) rename kernel/{src/libs => crates}/intertrait/macros/src/gen_caster.rs (100%) rename kernel/{src/libs => crates}/intertrait/macros/src/item_impl.rs (100%) rename kernel/{src/libs => crates}/intertrait/macros/src/item_type.rs (100%) rename kernel/{src/libs => crates}/intertrait/macros/src/lib.rs (100%) rename kernel/{src/libs => crates}/intertrait/src/cast.rs (100%) rename kernel/{src/libs => crates}/intertrait/src/cast/cast_arc.rs (100%) rename kernel/{src/libs => crates}/intertrait/src/cast/cast_box.rs (100%) rename kernel/{src/libs => crates}/intertrait/src/cast/cast_mut.rs (100%) rename kernel/{src/libs => crates}/intertrait/src/cast/cast_rc.rs (100%) rename kernel/{src/libs => crates}/intertrait/src/cast/cast_ref.rs (100%) rename kernel/{src/libs => crates}/intertrait/src/hasher.rs (100%) rename kernel/{src/libs => crates}/intertrait/src/lib.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/castable_to.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/on-enum.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/on-struct.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/on-trait-impl-assoc-type1.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/on-trait-impl-assoc-type2.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/on-trait-impl-assoc-type3.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/on-trait-impl.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/on-type-multi-traits.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/run.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/ui/duplicate-flags.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/ui/duplicate-flags.stderr (100%) rename kernel/{src/libs => crates}/intertrait/tests/ui/on-generic-type.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/ui/on-generic-type.stderr (100%) rename kernel/{src/libs => crates}/intertrait/tests/ui/on-type-impl.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/ui/on-type-impl.stderr (100%) rename kernel/{src/libs => crates}/intertrait/tests/ui/unknown-flag.rs (100%) rename kernel/{src/libs => crates}/intertrait/tests/ui/unknown-flag.stderr (100%) create mode 100644 kernel/crates/wait_queue_macros/Cargo.toml create mode 100644 kernel/crates/wait_queue_macros/src/lib.rs create mode 100644 kernel/src/driver/net/loopback.rs create mode 100644 kernel/src/driver/pci/attr.rs create mode 100644 kernel/src/driver/pci/dev_id.rs create mode 100644 kernel/src/driver/pci/device.rs create mode 100644 kernel/src/driver/pci/driver.rs create mode 100644 kernel/src/driver/pci/raw_device.rs create mode 100644 kernel/src/driver/pci/subsys.rs create mode 100644 kernel/src/driver/pci/test/mod.rs create mode 100644 kernel/src/driver/pci/test/pt_device.rs create mode 100644 kernel/src/driver/pci/test/pt_driver.rs create mode 100644 kernel/src/filesystem/eventfd.rs delete mode 100644 kernel/src/libs/ffi_convert.rs create mode 100644 kernel/src/libs/name.rs create mode 100644 kernel/src/process/cred.rs create mode 100644 kernel/src/time/tick_common.rs create mode 100644 user/apps/libgcc/Makefile create mode 100644 user/apps/libgcc/lib/libgcc_s.so.1 create mode 100644 user/apps/test_cred/.gitignore create mode 100644 user/apps/test_cred/Makefile create mode 100644 user/apps/test_cred/main.c create mode 100644 user/apps/test_eventfd/.gitignore create mode 100644 user/apps/test_eventfd/Makefile create mode 100644 user/apps/test_eventfd/main.c create mode 100644 user/apps/test_lo/.gitignore create mode 100644 user/apps/test_lo/Cargo.toml create mode 100644 user/apps/test_lo/Makefile create mode 100644 user/apps/test_lo/README.md create mode 100644 user/apps/test_lo/src/main.rs create mode 100644 user/apps/test_tokio/.gitignore create mode 100644 user/apps/test_tokio/Cargo.toml create mode 100644 user/apps/test_tokio/Makefile create mode 100644 user/apps/test_tokio/src/main.rs create mode 100644 user/apps/test_utimensat/.gitignore create mode 100644 user/apps/test_utimensat/Makefile create mode 100644 user/apps/test_utimensat/main.c create mode 100644 user/apps/user-manage/.gitignore create mode 100644 user/apps/user-manage/Cargo.toml create mode 100644 user/apps/user-manage/Makefile create mode 100644 user/apps/user-manage/README.md create mode 100644 user/apps/user-manage/src/check/check.rs create mode 100644 user/apps/user-manage/src/check/info.rs create mode 100644 user/apps/user-manage/src/check/mod.rs create mode 100644 user/apps/user-manage/src/cmd/groupadd.rs create mode 100644 user/apps/user-manage/src/cmd/groupdel.rs create mode 100644 user/apps/user-manage/src/cmd/groupmod.rs create mode 100644 user/apps/user-manage/src/cmd/mod.rs create mode 100644 user/apps/user-manage/src/cmd/passwd.rs create mode 100644 user/apps/user-manage/src/cmd/useradd.rs create mode 100644 user/apps/user-manage/src/cmd/userdel.rs create mode 100644 user/apps/user-manage/src/cmd/usermod.rs create mode 100644 user/apps/user-manage/src/error/error.rs create mode 100644 user/apps/user-manage/src/error/mod.rs create mode 100644 user/apps/user-manage/src/executor/executor.rs create mode 100644 user/apps/user-manage/src/executor/mod.rs create mode 100644 user/apps/user-manage/src/lib.rs create mode 100644 user/apps/user-manage/src/parser/cmd.rs create mode 100644 user/apps/user-manage/src/parser/mod.rs create mode 100644 user/apps/user-manage/src/parser/parser.rs create mode 100644 user/dadk/config/libgcc.dadk create mode 100644 user/dadk/config/test_cred-0.1.0.dadk create mode 100644 user/dadk/config/test_eventfd_0_1_0.dadk create mode 100644 user/dadk/config/test_lo_0_1_0.dadk create mode 100644 user/dadk/config/test_tokio-0.1.0.dadk create mode 100644 user/dadk/config/test_utimensat_0_1_0.dadk create mode 100644 user/dadk/config/user_manage-0.1.0.dadk create mode 100644 user/sysconfig/etc/group create mode 100644 user/sysconfig/etc/gshadow create mode 100644 user/sysconfig/etc/passwd create mode 100644 user/sysconfig/etc/shadow create mode 100644 user/sysconfig/home/reach/system/shell.service diff --git a/.github/workflows/cache-toolchain.yml b/.github/workflows/cache-toolchain.yml index a58b700f6..94519c834 100644 --- a/.github/workflows/cache-toolchain.yml +++ b/.github/workflows/cache-toolchain.yml @@ -51,17 +51,17 @@ jobs: cargo install cargo-binutils rustup toolchain install nightly-x86_64-unknown-linux-gnu - rustup toolchain install nightly-2023-01-21-x86_64-unknown-linux-gnu + rustup toolchain install nightly-2024-07-23-x86_64-unknown-linux-gnu rustup toolchain install nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup component add rust-src --toolchain nightly-2023-01-21-x86_64-unknown-linux-gnu + rustup component add rust-src --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu rustup component add rust-src --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup target add x86_64-unknown-none --toolchain nightly-2023-01-21-x86_64-unknown-linux-gnu + rustup target add x86_64-unknown-none --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu rustup target add x86_64-unknown-none --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup toolchain install nightly-2023-01-21-riscv64gc-unknown-linux-gnu --force-non-host + rustup toolchain install nightly-2024-07-23-riscv64gc-unknown-linux-gnu --force-non-host rustup toolchain install nightly-2023-08-15-riscv64gc-unknown-linux-gnu --force-non-host - rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2023-01-21-riscv64gc-unknown-linux-gnu - rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2023-01-21-riscv64gc-unknown-linux-gnu + rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2024-07-23-riscv64gc-unknown-linux-gnu + rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2024-07-23-riscv64gc-unknown-linux-gnu rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2023-08-15-riscv64gc-unknown-linux-gnu rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2023-08-15-riscv64gc-unknown-linux-gnu @@ -71,12 +71,12 @@ jobs: rustup component add rustfmt rustup component add rustfmt --toolchain nightly-x86_64-unknown-linux-gnu - rustup component add rustfmt --toolchain nightly-2023-01-21-x86_64-unknown-linux-gnu + rustup component add rustfmt --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu rustup component add rustfmt --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup component add rustfmt --toolchain nightly-2023-01-21-riscv64gc-unknown-linux-gnu + rustup component add rustfmt --toolchain nightly-2024-07-23-riscv64gc-unknown-linux-gnu rustup component add rustfmt --toolchain nightly-2023-08-15-riscv64gc-unknown-linux-gnu - rustup default nightly + rustup default nightly-2024-07-23 cargo install dadk --version 0.1.11 @@ -86,6 +86,9 @@ jobs: rustup toolchain install ${userapp_musl_toolchain} rustup component add --toolchain ${userapp_musl_toolchain} rust-src rustup target add --toolchain ${userapp_musl_toolchain} x86_64-unknown-linux-musl + + rustup target add x86_64-unknown-linux-musl --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu + rustup component add rust-src --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu diff --git a/.github/workflows/makefile.yml b/.github/workflows/makefile.yml index efcad5302..c785dee80 100644 --- a/.github/workflows/makefile.yml +++ b/.github/workflows/makefile.yml @@ -2,9 +2,9 @@ name: Build Check on: push: - branches: [ "master" ] + branches: [ "master", "feat-*", "fix-*"] pull_request: - branches: [ "master" ] + branches: [ "master", "feat-*", "fix-*"] jobs: # ensure the toolchain is cached diff --git a/Makefile b/Makefile index 015f17e4b..333f7c1b6 100644 --- a/Makefile +++ b/Makefile @@ -156,14 +156,14 @@ log-monitor: .PHONY: update-submodules update-submodules: @echo "更新子模块" - @git submodule update --recursive + @git submodule update --recursive --init @git submodule foreach git pull origin master .PHONY: update-submodules-by-mirror update-submodules-by-mirror: @echo "从镜像更新子模块" @git config --global url."https://git.mirrors.dragonos.org.cn/DragonOS-Community/".insteadOf https://github.com/DragonOS-Community/ - @$(MAKE) update-submodules + @$(MAKE) update-submodules --init @git config --global --unset url."https://git.mirrors.dragonos.org.cn/DragonOS-Community/".insteadOf help: diff --git a/README.md b/README.md index e4379736d..1378864c4 100644 --- a/README.md +++ b/README.md @@ -26,25 +26,25 @@   DragonOS目前在社区驱动下正在快速发展中,目前DragonOS已经实现了约1/4的Linux接口,在未来我们将提供对Linux的100%兼容性,并且提供新特性。 -[关于DragonOS,你想了解的都在这儿 - DragonOS](https://dragonos.org/?p=46) +## 参与开发? -## 网站 +仔细阅读 [DragonOS社区介绍文档] ,能够帮助你了解社区的运作方式,以及如何参与贡献! -- 项目官网 **[DragonOS.org](https://dragonos.org)** +- **了解开发动态、开发任务,请访问DragonOS社区论坛**: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) +- 您也可以从项目的issue里面了解相关的开发内容。 -- 项目文档 **[docs.DragonOS.org](https://docs.dragonos.org)** -- **了解开发动态、开发任务,请访问DragonOS社区论坛**: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) +  如果你愿意加入我们,你可以查看issue,并在issue下发表讨论、想法,或者访问DragonOS的论坛,了解开发动态、开发任务: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) -- 软件镜像站 **[mirrors.DragonOS.org](https://mirrors.DragonOS.org)** -- Git镜像站 **[git.mirrors.DragonOS.org](https://git.mirrors.DragonOS.org)** -- 国内镜像站 **[mirrors.DragonOS.org.cn](https://mirrors.DragonOS.org.cn)** +  你也可以带着你的创意与想法,和社区的小伙伴一起讨论,为DragonOS创造一些新的功能。 -- 开发交流QQ群 **115763565** +## 网站 + + +- 项目官网 **[DragonOS.org](https://dragonos.org)** +- 文档:**[docs.dragonos.org](https://docs.dragonos.org)** +- 社区介绍文档: **[community.dragonos.org](https://community.dragonos.org)** -- 代码搜索引擎 [code.DragonOS.org](http://code.dragonos.org) - -   ## 如何运行? @@ -52,34 +52,20 @@ - [构建DragonOS — DragonOS dev 文档](https://docs.dragonos.org/zh_CN/latest/introduction/build_system.html) -## 系统特性 - -  请参见文档:[系统特性](https://docs.dragonos.org/zh_CN/latest/introduction/features.html) - -## 如何加入? -  如果你愿意加入我们,你可以查看issue,并在issue下发表讨论、想法,或者访问DragonOS的论坛,了解开发动态、开发任务: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) - -  你也可以带着你的创意与想法,和社区的小伙伴一起讨论,为DragonOS创造一些新的功能。 ## 如何与社区建立联系? -  你可以发邮件给Maintainer: longjin,邮件地址是 [longjin@DragonOS.org](mailto:longjin@DragonOS.org) 。 - -  或者是加入我们的开发交流QQ群:**115763565** - -  对于正式问题的讨论,请在 **[https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn)** 上的对应板块,使用正式的语言发帖讨论。亦或者是在本仓库的issue下提出问题。 +请阅读[贡献者指南](https://community.dragonos.org/contributors/#%E7%A4%BE%E5%8C%BA)~ +- 您可以通过[社区管理团队]信息,与各委员会的成员们建立联系~ +- 同时,您可以通过[SIGs]和[WGs]页面,找到对应的社区团体负责人的联系方式~ ## 贡献者名单 [Contributors to DragonOS-Community/DragonOS · GitHub](https://github.com/DragonOS-Community/DragonOS/graphs/contributors) -## 联系我们 -社区对外联系邮箱:contact@DragonOS.org - -社区负责人邮箱:longjin@DragonOS.org ## 赞助 @@ -134,32 +120,10 @@ **我们谴责**:任何不遵守开源协议的行为。包括但不限于:剽窃该项目的代码作为你的毕业设计等学术不端行为以及商业闭源使用而不付费。 -若您发现了任何违背开源协议的使用行为,我们欢迎您发邮件反馈!让我们共同建设诚信的开源社区。 - -## 参考资料 - -  本项目参考了以下资料,我对这些项目、书籍、文档的作者表示感谢! - -- 《一个64位操作系统的实现》田宇(人民邮电出版社) - -- 《现代操作系统 原理与实现》陈海波、夏虞斌(机械工业出版社) - -- [SimpleKernel](https://github.com/Simple-XX/SimpleKernel) - -- [osdev.org](https://wiki.osdev.org/Main_Page) - -- ACPI_6_3_final_Jan30 - -- the GNU GRUB manual - -- Intel® 64 and IA-32 Architectures Software Developer’s Manual - -- IA-PC HPET (High Precision Event Timers) Specification - -- [skiftOS]([GitHub - skiftOS/skift: 🥑 A hobby operating system built from scratch in modern C++. Featuring a reactive UI library and a strong emphasis on user experience.](https://github.com/skiftOS/skift)) - -- [GuideOS](https://github.com/Codetector1374/GuideOS) +若您发现了任何违背开源协议的使用行为,我们欢迎您发邮件到 pmc@dragonos.org 反馈!让我们共同建设诚信的开源社区。 -- [redox-os](https://gitlab.redox-os.org/redox-os/redox) -- [rcore](https://github.com/rcore-os/rCore) +[DragonOS社区介绍文档]: https://community.dragonos.org/ +[社区管理团队]: https://community.dragonos.org/governance/staff-info.html +[SIGs]: https://community.dragonos.org/sigs/ +[WGs]: https://community.dragonos.org/wgs/ diff --git a/README_EN.md b/README_EN.md index 8e787f861..b83b5bdba 100644 --- a/README_EN.md +++ b/README_EN.md @@ -25,52 +25,37 @@   Driven by the community, DragonOS is currently evolving rapidly. DragonOS has already implemented about 1/4 of Linux interfaces, and in the future, we will strive to provide 100% compatibility with Linux, along with new features. -[All you want to know about DragonOS is here - DragonOS](https://dragonos.org/?p=46) -## Websites +## Get Involved in Development? -- Home Page **[DragonOS.org](https://dragonos.org)** -- Documentation **[docs.DragonOS.org](https://docs.dragonos.org)** -- **To learn about development dynamics and development tasks, please visit DragonOS's BBS:** [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) -- Software mirror website **[mirrors.DragonOS.org](https://mirrors.DragonOS.org)** -- Git mirror website **[git.mirrors.DragonOS.org](https://git.mirrors.DragonOS.org)** -- QQ group **115763565** -- Code search engine [code.DragonOS.org](http://code.dragonos.org)  +Carefully read the [DragonOS Community Introduction Document] to understand how the community operates and how you can contribute! -## How to run? +- **To stay updated on development news and tasks, visit the DragonOS Community Forum**: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) +- You can also learn about the development progress by checking the project's issues. -  The steps to run DragonOS are very simple. You can refer to the following information to run DragonOS within 15 minutes at the fastest! +  If you're interested in joining us, you can check out the issues and post your discussions or ideas under them, or visit the DragonOS forum to learn about development updates and tasks: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) -- [Building DragonOS - DragonOS dev document](https://docs.dragonos.org/zh_CN/latest/introduction/build_system.html) +  You're also welcome to bring your creative ideas and discuss them with the community members, working together to create new features for DragonOS. -## DragonOS' Features -  See documentation:[Features](https://docs.dragonos.org/zh_CN/latest/introduction/features.html) +## Website -## How to join DragonOS ? +- **Project's Website**: [DragonOS.org](https://dragonos.org) +- Documentation: [docs.dragonos.org](https://docs.dragonos.org) +- Community Introduction Document: [community.dragonos.org](https://community.dragonos.org) -  If you are willing to join us, you can visit DragonOS's BBS , learn about development dynamics and development tasks: [https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) +## How to Run? -  Or, you can also bring your ideas, discuss with community members, and create some new functions for DragonOS. +  Running DragonOS is quite straightforward. You can refer to the following resources and get DragonOS up and running in as little as 15 minutes! -## How to contact the community? +- [Building DragonOS — DragonOS Development Documentation](https://docs.dragonos.org/zh_CN/latest/introduction/build_system.html) -  You can send an email to the project's maintainer: longjin. His email address is [longjin@DragonOS.org](mailto: longjin@DragonOS.org) . +## How to Connect with the Community? -  Or join our development exchange QQ group: **115763565** +Please read the [Contributor Guide](https://community.dragonos.org/contributors/#%E7%A4%BE%E5%8C%BA)~ -  For the discussion of formal issues, we recommend that you use the official language to post on the corresponding section of **[https://bbs.dragonos.org.cn](https://bbs.dragonos.org.cn)**. Or you can post questions under the issue of this repository. - - -## List of contributors - -[Contributors to DragonOS-Community/DragonOS · GitHub](https://github.com/DragonOS-Community/DragonOS/graphs/contributors) - -## Get contact with us - -Community Contact Email: contact@DragonOS.org - -Maintainer longjin's Email:longjin@DragonOS.org +- You can establish contact with the members of various committees through the [Community Management Team] information. +- Additionally, you can find the contact information of the respective community group leaders via the [SIGs] and [WGs] pages. ## Reward @@ -128,30 +113,7 @@ We guarantee that all sponsorship funds and items will be used for: If you find any violation of the open source license, we welcome you to send email feedback! Let's build an honest open source community together! -## References - -  This project refers to the following materials. I sincerely give my thanks to the authors of these projects, books and documents! - -- Implementation of a 64 bit operating system, Tian Yu (POSTS&TELECOM PRESS) - -- Principle and implementation of modern operating system, Chen Haibo, Xia Yubin (China Machine Press) - -- [SimpleKernel](https://github.com/Simple-XX/SimpleKernel) - -- [osdev.org](https://wiki.osdev.org/Main_Page) - -- ACPI_6_3_final_Jan30 - -- the GNU GRUB manual - -- Intel® 64 and IA-32 Architectures Software Developer’s Manual - -- IA-PC HPET (High Precision Event Timers) Specification - -- [skiftOS]([GitHub - skiftOS/skift: 🥑 A hobby operating system built from scratch in modern C++. Featuring a reactive UI library and a strong emphasis on user experience.](https://github.com/skiftOS/skift)) - -- [GuideOS](https://github.com/Codetector1374/GuideOS) - -- [redox-os](https://gitlab.redox-os.org/redox-os/redox) - -- [rcore](https://github.com/rcore-os/rCore) +[DragonOS Community Introduction Document]: https://community.dragonos.org/ +[Community Management Team]: https://community.dragonos.org/governance/staff-info.html +[SIGs]: https://community.dragonos.org/sigs/ +[WGs]: https://community.dragonos.org/wgs/ diff --git a/build-scripts/.gitignore b/build-scripts/.gitignore index 1de565933..a6f89c2da 100644 --- a/build-scripts/.gitignore +++ b/build-scripts/.gitignore @@ -1 +1 @@ -target \ No newline at end of file +/target/ \ No newline at end of file diff --git a/build-scripts/Makefile b/build-scripts/Makefile index ba60f4b53..14c1bd930 100644 --- a/build-scripts/Makefile +++ b/build-scripts/Makefile @@ -4,3 +4,5 @@ fmt: clean: @cargo clean +check: + @cargo +nightly-2024-07-23 check --workspace $(CARGO_ZBUILD) --message-format=json diff --git a/build-scripts/kernel_build/src/lib.rs b/build-scripts/kernel_build/src/lib.rs index d0de77f84..21844c53b 100644 --- a/build-scripts/kernel_build/src/lib.rs +++ b/build-scripts/kernel_build/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(cfg_target_abi)] - #[macro_use] extern crate lazy_static; extern crate cc; diff --git a/docs/community/ChangeLog/V0.1.x/V0.1.10.md b/docs/community/ChangeLog/V0.1.x/V0.1.10.md new file mode 100644 index 000000000..b09ef3a07 --- /dev/null +++ b/docs/community/ChangeLog/V0.1.x/V0.1.10.md @@ -0,0 +1,1062 @@ +# V0.1.10 + +:::{note} +本文作者:龙进 + +DragonOS官方论坛:[bbs.dragonos.org.cn](https://bbs.dragonos.org.cn) + +2024年5月13日 +::: + +## 简介 + +  本次版本更新,引入了42个feature类型的PR,24个bug修复,5个文档更新,以及一些软件移植、ci相关的内容。 + +  当前版本核心看点: + +- 对调度子系统进行了重构 +- 能在riscv64下运行到hello world应用程序 +- 内存管理子系统引入了匿名页反向映射、写时拷贝以及延迟分配的特性 +- 文件系统引入了大量的新的系统接口 +- 实现了pty,并能运行简单的ssh服务端 + + +## 赞助商列表 + +- **[中国雅云](https://yacloud.net)** 雅安大数据产业园为DragonOS提供了云服务器支持。 + +## 更新内容-内核 + +- feat(fs): 实现了sys_rename (#578) +- feat(fs): 实现get_pathname (#615) +- feat(kernel): 实现uname系统调用 (#614) +- feat(fs): 添加mount系统调用 (#561) +- feat(smp): 重写SMP模块 (#633) +- feat(fs): 添加Statx系统调用 (#632) +- feat(riscv64): 添加flush tlb的ipi (#636) +- feat(fs): 实现SYS_LINK和SYS_LINKAT (#611) +- fix(fs): mkdir输出错误信息; +- fix(clippy): 修复内核的clippy检查报错 (#637) +- feat(net): 实现socketpair (#576) +- feat(process/riscv): 进程管理初始化 (#654) +- fix(time): 修复clock_gettime返回类型错误,修复小时间间隔duration返回0问题 (#664) +- fix(driver/base): 把Device trait的set_class改为设置Weak指针,以避免循环引用问题。 (#666) +- feat(textui): 支持绘制24位深和16位深显示缓冲区 (#640) +- fix(driver/tty): 修复tty设备显示在/sys目录下的bug (#668) +- feat(fs): 新加结构体POSIXSTATFS与SuperBlock用于处理statfs系统调用 (#667) +- feat(driver/rtc):实现了rtc的抽象,并且把x86的cmos rtc接入到设备驱动模型 (#674) +- fix(net): 修复udp bind的时候,对port0处理不正确的问题(#676) +- fix(fs/ramfs): 修复了ramfs中move_to未更新parent字段的bug (#673) +- feat(mm): 实现页面反向映射 (#670) +- fix(misc): 修复get_ramdom的长度错误问题() (#677) +- feat(process/riscv): riscv64: switch process (#678) +- fix(misc): 使nproc可以正确获取到cpu核心数 (#689) +- fix(time): 修复jiffy时钟过快问题,启用gettimeofday测试,修改mount测试 (#680) +- feat(driver/pty): 实现pty,附带测试程序 (#685) +- feat(process/riscv): 实现copy-thread (#696) +- feat(sched): 重写调度模块 (#679) +- fix(riscv): 把内核编译target改为riscv64gc & 获取time csr的频率 & 修正浮点保存与恢复的汇编的问题 (#699) +- feat(lock): 实现robust futex (#682) +- feat(fs): BlockCache-read cache支持 (#521) +- feat(mm): 实现SystemV共享内存 (#690) +- chore(tools): add bootstrap support for Centos/RHEL8/fedora (#713) +- feat(driver/pty): 完善pty,目前pty能够支持ssh (#708) +- fix(smp): 修复smp启动的时候,损坏0号核心的idle进程的内核栈的问题 (#711) +- feat(driver/riscv): 初始化riscv-sbi-timer (#716) +- doc: Update DragonOS description and introduction (#717) +- feat(riscv): 让riscv64能正常切换进程,并运行完所有的initcall (#721) +- feat(net): 实现tcp backlog功能 (#714) +- feat(mm): 添加slab内存分配器 (#683) +- feat(fs): 引入Umount系统调用 (#719) +- doc: Update build instructions for riscv64 architecture (#725) +- fix(fs): socket统一改用`GlobalSocketHandle`,并且修复fcntl SETFD的错误 (#730) +- feat: alarm系统调用实现 (#710) +- feat(tty): add dummy console (#735) +- fix(driver/pci): pci: 统一使用ecam root (#744) +- feat(driver/pci): pci: 添加pci root manager来管理pci root,并使得riscv能够正常扫描pci设备. (#745) +- build: 将smoltcp升级到0.11.0版本 (#740) +- fix(unified-init): 修复unified-init导致cargo check失败的问题 (#747) +- chore: Update virtio-drivers to commit 61ece509c4 and modify max_queue_size implementation (#748) +- feat(net): 实现raw socket的poll (#739) +- feat(mm): 实现缺页中断处理,支持页面延迟分配和写时拷贝,以及用户栈自动拓展 (#715) +- feat(driver): 把virtio添加到sysfs (#752) +- fix(dog): 添加CC环境变量,解决编译时找不到musl-gcc的问题 (#753) +- doc(community): add description of conventional commit standard (#754) +- feat(driver/virtio): riscv: 添加virtio-blk driver,并在riscv下能够正确挂载FAT32 (#761) +- feat(fs): add sys_dup3 (#755) +- feat(riscv): riscv下能够运行hello world用户程序 (#770) +- feat(sched): add sched_yield (#766) +- refactor(process): 调整arch_switch_to_user函数,把riscv和x86_64的共用逻辑抽取出来。 (#773) +- feat(driver/acpi_pm): Implement ACPI PM Timer (#772) +- chore: 适配dadk 0.1.11 (#777) +- fix(libs/lib_ui): fix the display errors when system initialize (#779) +- fix(riscv/process): 把riscv的调度时钟节拍率与HZ同步,并且修复切换到用户态的时候忘了在内核态关中断的bug (#780) +- fix: (riscv/timer): 修复riscv下没有更新墙上时钟以及没有处理软中断的bug (#783) +- feat(mm): add slab usage calculation (#768) +- feat(bitmap): Add bit and for AllocBitMap (#793) +- fix(mm): 修复vma映射标志错误 (#801) +- feat:(riscv/intr) 实现riscv plic驱动,能处理外部中断 (#799) +- doc(sched):调度子系统文档即cfs文档 (#807) +- fix(net): Fix TCP Unresponsiveness and Inability to Close Connections (#791) +- fix: disable mm debug log to prevent system lockup due to thingbuf issue (#808) +- feat(driver/pci): add pci bus into sysfs (#792) +- doc: Add Gentoo Linux In build_system.md (#810) + +## 更新内容-用户环境 + +### 新特性/新应用移植 + +- 添加core utils到系统 (#624) +- 移植dns查询工具dog的--tcp功能 (#652) + + +## 更新内容-CI + +- 引入triagebot对issue和PR进行分类 +- 添加clippy检测的自动化工作流 (#649) +- ci: import issue checker (#750) +- ci: update the match regex of issue checker (#784) +- ci: 添加支持gentoo系统的一键安装脚本 (#809) + +## 源码、发布版镜像下载 + +  您可以通过以下方式获得源代码: + +### 通过Git获取 + +- 您可以访问DragonOS的仓库获取源代码:[https://github.com/DragonOS-Community/DragonOS](https://github.com/DragonOS-Community/DragonOS) +- 您可以访问[https://github.com/DragonOS-Community/DragonOS/releases](https://github.com/DragonOS-Community/DragonOS/releases)下载发布版的代码。 + +### 通过DragonOS软件镜像站获取 + +  为解决国内访问GitHub慢、不稳定的问题,同时为了方便开发者们下载DragonOS的每个版本的代码,我们特意搭建了镜像站,您可以通过以下地址访问镜像站: + +  您可以通过镜像站获取到DragonOS的代码压缩包,以及编译好的可运行的磁盘镜像。 + +- [https://mirrors.DragonOS.org.cn](https://mirrors.DragonOS.org.cn) +- [https://git.mirrors.DragonOS.org.cn](https://git.mirrors.DragonOS.org.cn) + +## 开放源代码声明 + +:::{note} +为促进DragonOS项目的健康发展,DragonOS以GPLv2开源协议进行发布。所有能获得到DragonOS源代码以及相应的软件制品(包括但不限于二进制副本、文档)的人,都能享有我们通过GPLv2协议授予您的权利,同时您也需要遵守协议中规定的义务。 + +这是一个相当严格的,保护开源软件健康发展,不被侵占的协议。 + +对于大部分的善意的人们而言,您不会违反我们的开源协议。 + +我们鼓励DragonOS的自由传播、推广,但是请确保所有行为没有侵犯他人的合法权益,也没有违反GPLv2协议。 + +请特别注意,对于违反开源协议的,尤其是**商业闭源使用以及任何剽窃、学术不端行为将会受到严肃的追责**。(这是最容易违反我们的开源协议的场景)。 + +并且,请注意,按照GPLv2协议的要求,基于DragonOS修改或二次开发的软件,必须同样采用GPLv2协议开源,并标明其基于DragonOS进行了修改。亦需保证这些修改版本的用户能方便的获取到DragonOS的原始版本。 + +您必须使得DragonOS的开发者们,能够以同样的方式,从公开渠道获取到您二次开发的版本的源代码,否则您将违反GPLv2协议。 + +关于协议详细内容,还敬请您请阅读项目根目录下的**LICENSE**文件。请注意,按照GPLv2协议的要求,**只有英文原版才具有法律效力**。任何翻译版本都仅供参考。 +::: + +### 开源软件使用情况 + +  DragonOS在开发的过程中,参考了Linux社区的一些设计,或者引入了他们的部分思想,亦或是受到了他们的启发。我们在这里对Linux社区以及Linux社区的贡献者们致以最衷心的感谢! + +## 当前版本的所有提交记录 + +```text +commit 9a0802fd2ddda39e96342997abbfc30bf65f1f0e +Author: donjuanplatinum <113148619+donjuanplatinum@users.noreply.github.com> +Date: Mon May 13 15:36:23 2024 +0800 + + doc: Add Gentoo Linux In build_system.md (#810) + + * 增加安装文档中的Gentoo Linux提示 + +commit 1f4877a4c512eb5ad232436128a0c52287b39aaa +Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> +Date: Mon May 13 15:27:08 2024 +0800 + + feat(driver/pci): add pci bus into sysfs (#792) + + 把pci设备加入sysfs + +commit 1df85daf8f1b4426fe09d489d815997cdf989a87 +Author: donjuanplatinum <113148619+donjuanplatinum@users.noreply.github.com> +Date: Sun May 12 22:58:59 2024 +0800 + + 添加支持gentoo系统的一键安装脚本 (#809) + +commit 352ee04918f4585ad4f8a896ca6e18b1ef7d7934 +Author: LoGin +Date: Sat May 11 18:02:13 2024 +0800 + + fix: disable mm debug log to prevent system lockup due to thingbuf issue (#808) + +commit 37cef00bb404c9cc01509c12df57548029967dc2 +Author: Samuel Dai +Date: Sat May 11 17:17:43 2024 +0800 + + fix(net): Fix TCP Unresponsiveness and Inability to Close Connections (#791) + + * fix(net): Improve stability. 为RawSocket与UdpSocket实现close时调用close方法,符合smoltcp的行为。为SocketInode实现drop,保证程序任何情况下退出时都能正确close对应socket, 释放被占用的端口。 + + * fix(net): Correct socket close behavior. + +commit b941261d943fac38d3154495e19ec99c90ebea8d +Author: GnoCiYeH +Date: Tue May 7 22:01:01 2024 +0800 + + docs(sched):调度子系统文档即cfs文档 (#807) + + * 调度子系统文档以及cfs文档 + +commit 0102d69fdd231e472d7bb3d609a41ae56a3799ee +Author: LoGin +Date: Wed May 1 21:11:32 2024 +0800 + + feat:(riscv/intr) 实现riscv plic驱动,能处理外部中断 (#799) + + * feat:(riscv/intr) 实现riscv plic驱动,能处理外部中断 + + - 实现riscv plic驱动,能处理外部中断 + - 能收到virtio-blk的中断 + - 实现fasteoi interrupt handler + +commit 17dc558977663433bd0181aa73ad131a1a265c1f +Author: MemoryShore <105195940+MemoryShore@users.noreply.github.com> +Date: Wed May 1 21:09:51 2024 +0800 + + 修复vma映射标志错误 (#801) + +commit 7db6e06354328ea7c6164723f504e8ba58d0c4a4 +Author: LoGin +Date: Tue Apr 30 18:45:01 2024 +0800 + + feat(bitmap): Add bit and for AllocBitMap (#793) + +commit 7401bec5e3c42015399a46e29c370abe7c7388b5 +Author: laokengwt <143977175+laokengwt@users.noreply.github.com> +Date: Mon Apr 29 23:03:33 2024 +0800 + + feat(mm): add slab usage calculation (#768) + + * Add slab free space calculation and add it to freeram of sysinfo + +commit bde4a334c1ff2ae27989de4f6f8b45f5154b684d +Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> +Date: Mon Apr 29 18:55:17 2024 +0800 + + 修复了未初始化时ui显示模块内存越界的问题,优化了代码结构 (#789) + +commit 0722a06a09ed52cb980a6147123453f86d0ea267 +Author: LoGin +Date: Sun Apr 28 19:40:09 2024 +0800 + + fix: (riscv/timer): 修复riscv下没有更新墙上时钟以及没有处理软中断的bug (#783) + +commit ab53b2eb75fe79167aa100e655b3589ee306f793 +Author: Chiichen +Date: Sun Apr 28 19:37:58 2024 +0800 + + ci: update the match regex of issue checker (#784) + + The previous regex can not successfully match the pattern like `feat(driver/pci)`, which has a slash in the scope + +commit 942cf26b48c8b024a6fa7867bb0c8ae39bb1ae09 +Author: LoGin +Date: Sun Apr 28 16:49:40 2024 +0800 + + fix(riscv/process): 把riscv的调度时钟节拍率与HZ同步,并且修复切换到用户态的时候忘了在内核态关中断的bug (#780) + +commit 13b057cc0fda0cf9630c98d246937b85fa01a7c9 +Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> +Date: Sun Apr 28 16:49:19 2024 +0800 + + fix(libs/lib_ui): fix the display errors when system initialize (#779) + + * 修复了系统初启动时会花屏的bug + +commit 182b778a3ca8c633b605ae7dd90a5e9f1131cc6d +Author: LoGin +Date: Sun Apr 28 13:39:51 2024 +0800 + + chore: 适配dadk 0.1.11 (#777) + + * chore: 适配dadk 0.1.11 + +commit dd8e74ef0d7f91a141bd217736bef4fe7dc6df3d +Author: Mingtao Huang <114841534+1037827920@users.noreply.github.com> +Date: Sun Apr 28 13:25:12 2024 +0800 + + feat(driver/acpi_pm): Implement ACPI PM Timer (#772) + + * feat: Implement ACPI PM Timer + +commit f75cb0f8ed754d94c3b2924519b785db3321c1d9 +Author: LoGin +Date: Sat Apr 27 15:35:24 2024 +0800 + + refactor(process): 调整arch_switch_to_user函数,把riscv和x86_64的共用逻辑抽取出来。 (#773) + + * refactor(process): Extract common logic for riscv and x86_64 in arch_switch_to_user to run_init_process + + 调整arch_switch_to_user函数,把riscv和x86_64的共用逻辑抽取出来。写成run_init_process函数,并且能够尝试运行多个不同的init程序,直到某个运行成功 + +commit 173c4567cf4fb2276ef3f4614b69da7913fc8381 +Author: zwb0x00 <163394849+zwb0x00@users.noreply.github.com> +Date: Fri Apr 26 15:33:29 2024 +0800 + + feat(sched): add sched_yield (#766) + + * 实现sched_yield系统调用 + +commit 471d65cf158c9bf741c21f5d0ab92efe7bf1c3d4 +Author: LoGin +Date: Fri Apr 26 11:59:47 2024 +0800 + + feat(riscv): riscv下能够运行hello world用户程序 (#770) + + * feat(riscv): riscv下能够运行hello world用户程序 + +commit 40348dd8d5a008ecc9eb3aab931933e4eba0e6da +Author: zwb0x00 <163394849+zwb0x00@users.noreply.github.com> +Date: Tue Apr 23 19:35:02 2024 +0800 + + feat(fs): add sys_dup3 (#755) + + * feat(fs): add sys_dup3 + +commit 3b799d13beeb80900d728937308e47f8011835e1 +Author: LoGin +Date: Tue Apr 23 19:14:41 2024 +0800 + + Create FUNDING.yml (#763) + +commit 731bc2b32d7b37298883d7a15b6dca659b436ee4 +Author: LoGin +Date: Tue Apr 23 17:19:54 2024 +0800 + + feat(virtio): riscv: 添加virtio-blk driver,并在riscv下能够正确挂载FAT32 (#761) + +commit 0c1ef30087d10035c256fed08097f5897041979d +Author: Chiichen +Date: Tue Apr 23 00:27:05 2024 +0800 + + docs(community): add description of conventional commit standard (#754) + + * docs(community): add description of conventional commit standard + + * docs: add index + +commit 70c991af204167db26ec1d9494efcff010893482 +Author: laokengwt <143977175+laokengwt@users.noreply.github.com> +Date: Mon Apr 22 17:40:03 2024 +0800 + + fix(dog): 添加CC环境变量,解决编译时找不到musl-gcc的问题 (#753) + +commit e32effb1507773d32c216d9e77b963786e275c06 +Author: LoGin +Date: Mon Apr 22 15:11:47 2024 +0800 + + feat(driver): 把virtio添加到sysfs (#752) + +commit a17651b14b86dd70655090381db4a2f710853aa1 +Author: MemoryShore <105195940+MemoryShore@users.noreply.github.com> +Date: Mon Apr 22 15:10:47 2024 +0800 + + feat(mm): 实现缺页中断处理,支持页面延迟分配和写时拷贝,以及用户栈自动拓展 (#715) + + * 实现缺页中断处理 + + * 完善页表拷贝逻辑 + + * 优化代码结构 + + * 完善缺页异常信息 + + * 修改大页映射逻辑 + + * 修正大页映射错误 + + * 添加缺页中断支持标志 + + * 实现用户栈自动拓展功能 + +commit cb02d0bbc213867ac845b7e8a0fb337f723d396a +Author: Chiichen +Date: Sun Apr 21 23:23:21 2024 +0800 + + ci: import issue checker (#750) + + * ci: supprot auto tag on pull request + + * ci: update issue checker config + + * ci: update issue checker & block merge while + +commit 93c379703e3be210799953bc0686d02f97119b39 +Author: sun5etop <146408999+sun5etop@users.noreply.github.com> +Date: Sun Apr 21 13:36:44 2024 +0800 + + feat(net): 实现raw socket的poll (#739) + + feat(net): 实现raw socket的poll + +commit b502fbf0b9c575a4c04e103d0fb708c4e383ab06 +Author: LoGin +Date: Sun Apr 21 13:30:29 2024 +0800 + + chore: Update virtio-drivers to commit 61ece509c4 and modify max_queue_size implementation (#748) + +commit d770de5d53ce9b598fb0024800a347b081f92a73 +Author: LoGin +Date: Sun Apr 21 13:12:31 2024 +0800 + + fix: 修复unified-init导致cargo check失败的问题 (#747) + +commit 881ff6f95e4addc373d815d66cb912bf721c20e6 +Author: yuyi2439 <68320855+yuyi2439@users.noreply.github.com> +Date: Sun Apr 21 11:39:00 2024 +0800 + + 将smoltcp升级到0.11.0版本 (#740) + +commit 370472f7288b568c7b80815f5b150daf4496446c +Author: LoGin +Date: Sun Apr 21 11:27:36 2024 +0800 + + pci: 添加pci root manager来管理pci root,并使得riscv能够正常扫描pci设备. (#745) + + * pci: 添加pci root manager来管理pci root. + pci: 使得riscv能够正常扫描pci设备. + + * doc: 添加注释 + +commit 2709e017d0d216d61b2caed3c7286459de7794c7 +Author: LoGin +Date: Sat Apr 20 18:31:56 2024 +0800 + + pci: 统一使用ecam root (#744) + +commit 418ad41fd84c15ed7e132e56970150ac38fc24a9 +Author: LoGin +Date: Wed Apr 17 10:03:22 2024 +0800 + + Feat(tty): add dummy console (#735) + + 使得riscv能暂时完成stdio_init(将来需要实现riscv的串口console) + +commit 1012552dea71bf04cf1d329d570c4c9ca9b2a2f8 +Author: Saga1718 <161323888+Saga1718@users.noreply.github.com> +Date: Tue Apr 16 21:37:42 2024 +0800 + + 删除无用的hid代码 (#734) + +commit fbd63a301c5648f906eeb802f10ac03518ba1264 +Author: SMALLC <121806694+SMALLC04@users.noreply.github.com> +Date: Tue Apr 16 21:34:36 2024 +0800 + + feat: alarm系统调用实现 (#710) + + * alarm系统调用实现 + +commit d623e90231ef6a31d091c3f611c0af3a83d3343b +Author: GnoCiYeH +Date: Mon Apr 15 22:01:32 2024 +0800 + + socket统一改用`GlobalSocketHandle`,并且修复fcntl SETFD的错误 (#730) + + * socket统一改用`GlobalSocketHandle`,并且修复fcntl SETFD的错误 + + --------- + + Co-authored-by: longjin + +commit 7162a8358d94c7799dd2b5300192b6a794b23d79 +Author: LoGin +Date: Mon Apr 15 13:20:46 2024 +0800 + + doc: Update build instructions for riscv64 architecture (#725) + +commit 1074eb34e784aa2adfc5b9e0d89fa4b7e6ea03ef +Author: Samuel Dai +Date: Mon Apr 15 13:02:04 2024 +0800 + + feat(filesystem): 引入Umount系统调用 (#719) + + * feat(filesystem): 引入Umount系统调用 + + * 将所有ENOSYS误用更正 + + * 修复了一个使同一个挂载点可以挂载2个文件系统的bug + + * 统一注释,增强程序稳定性,统一接口。注意:Umount时在fatfs的路径要使用大写,此受限于当前文件系统设计。 + +commit ceeb2e943ca7645609920ec7ad8bfceea2b13de6 +Author: laokengwt <143977175+laokengwt@users.noreply.github.com> +Date: Mon Apr 15 12:51:14 2024 +0800 + + feat(mm): 添加slab内存分配器 (#683) + + feat(mm): 添加slab内存分配器 + --------- + + Co-authored-by: longjin + +commit c719ddc6312acd7976e0f6fd449a94ff9abad5a6 +Author: Saga1718 <161323888+Saga1718@users.noreply.github.com> +Date: Sun Apr 14 23:51:47 2024 +0800 + + feat(net): 实现tcp backlog功能 (#714) + + * feat:实现tcp的backlog功能 + +commit 9621ab16ef27bc94f223e6254fafb9bb07d46d57 +Author: LoGin +Date: Sun Apr 14 20:39:20 2024 +0800 + + 让riscv64能正常切换进程,并运行完所有的initcall (#721) + +commit 9fab312ea9921618629924ab15c28c2d255b21c6 +Author: LoGin +Date: Fri Apr 12 15:27:44 2024 +0800 + + Update DragonOS description and introduction (#717) + +commit f049d1af01da7b92f312245ed411b22475b76065 +Author: LoGin +Date: Fri Apr 12 14:46:47 2024 +0800 + + 初始化riscv-sbi-timer (#716) + +commit 3959e94df38073fdb80b199777015f95611ba05f +Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> +Date: Wed Apr 10 19:00:32 2024 +0800 + + bugfix: 修复smp启动的时候,损坏0号核心的idle进程的内核栈的问题 (#711) + + --------- + + Co-authored-by: longjin + Co-authored-by: heyicong + +commit 9365e8017b39582eca620ba93c64f1b3c87c73d4 +Author: GnoCiYeH +Date: Wed Apr 10 19:00:12 2024 +0800 + + 完善pty,目前pty能够支持ssh (#708) + +commit 4b0170bd6bb374d0e9699a0076cc23b976ad6db7 +Author: Chiichen +Date: Wed Apr 10 18:58:54 2024 +0800 + + chore(tools): add bootstrap support for Centos/RHEL8/fedora (#713) + + Co-authored-by: kejianchi + +commit 15b94df01adc7e8931961b9b9a89db4e7c014b64 +Author: Jomo +Date: Wed Apr 10 10:58:07 2024 +0800 + + add xuzihao (#712) + +commit 6fc066ac11d2f9a3ac629d57487a6144fda1ac63 +Author: Jomo <2512364506@qq.com> +Date: Sun Apr 7 14:04:19 2024 +0800 + + 实现SystemV共享内存 (#690) + + * 实现SystemV共享内存 + + * 测试shm + + * 添加测试程序 + + * 完善细节 + + * 修正shm的时间数据错误的问题 + + * fix: devfs的metadata权限为0x777的错误 + + --------- + + Co-authored-by: longjin + +commit eb49bb993a39964f92494ec3effafed3fb9adfd8 +Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> +Date: Sun Apr 7 14:03:51 2024 +0800 + + BlockCache-read cache支持 (#521) + + 支持block cache的读缓存 + +commit 06560afa2aa4db352526f4be8b6262719b8b3eac +Author: hmt <114841534+1037827920@users.noreply.github.com> +Date: Sat Apr 6 22:26:34 2024 +0800 + + Patch feat robust futex (#682) + + * feat: 实现robust lock机制 + + * 前面更改vscode,修改回来 + + * 修改dadk的路径 + + * 提交.gitnore和.cargo,删除LICENSE,修改README + + * 修改一个warn + + * 删除.rustc_info.json + + * 删除target文件夹 + + * 恢复DragonOS的LICENSE,删除Cargo.lock + + * 将校验用户空间地址的代码写入函数内;将部分match分支用ok_or代替 + + * 修改wakeup函数获取running queue时unwrap一个None值发生panic + + * 测试程序使用syscalls库进行系统调用 + +commit 23ef2b33d1e3cfd2506eb7449a33df4ec42f11d3 +Author: LoGin +Date: Sat Apr 6 22:13:26 2024 +0800 + + riscv: 把内核编译target改为riscv64gc & 获取time csr的频率 & 修正浮点保存与恢复的汇编的问题 (#699) + + * 1. 把内核编译target改为riscv64gc + 2. fix: 修正浮点保存与恢复的汇编的问题 + + * riscv: 获取time csr的频率 + +commit f0c87a897fe813b7f06bf5a9e93c43ad9519dafd +Author: GnoCiYeH +Date: Fri Apr 5 17:54:48 2024 +0800 + + 重写调度模块 (#679) + + ## PR:重写调度模块 + --- + ### 完成的部分 + - 实现cfs调度策略 + - 搭建框架,后续功能可以迭代开发 + - 目前能跑,未测试性能 + + ### 需要后续接力的部分 + - 实现组内调度(task_group) + - 实现跨核负载均衡(pelt算法) + - 接入sysfs,实现参数动态调节(sched_stat等) + - nice值以及priority等参数的设置及调优 + +commit e8eab1ac824e1b1e638e50debb8326dfed4f05e5 +Author: LoGin +Date: Fri Apr 5 16:37:08 2024 +0800 + + riscv: copy-thread (#696) + +commit dfe53cf087ef4c7b6db63d992906b062dc63e93f +Author: GnoCiYeH +Date: Fri Apr 5 00:21:55 2024 +0800 + + 实现pty,附带测试程序 (#685) + + * 实现pty,附带测试程序 + + * fmt ** clippy + + * 将file层的锁粒度缩小,从而不使用no_preempt。更改pipe在sleep部分的bug + + * 修复拼写错误 + +commit b8ed38251dc255b0c525801b5dbf37d3b0d0d61e +Author: Donkey Kane <109840258+xiaolin2004@users.noreply.github.com> +Date: Fri Apr 5 00:06:26 2024 +0800 + + 修复jiffy时钟过快问题,启用gettimeofday测试,修改mount测试 (#680) + + 1. 把clock tick rate与hpet频率关联起来 + 2. 修复墙上时间同步错误的问题 + 3. 启用时间watch dog. + 4. 修复时间流逝速度异常 + + --------- + + Co-authored-by: longjin + +commit 9430523b465b19db4dd476e9fd3038bdc2aa0c8d +Author: yuyi2439 <68320855+yuyi2439@users.noreply.github.com> +Date: Thu Apr 4 12:41:19 2024 +0800 + + 使nproc可以正确获取到cpu核心数 (#689) + +commit 9b96c5b547c337502db7ec820312f119f95eece1 +Author: LoGin +Date: Sun Mar 31 22:53:01 2024 +0800 + + riscv64: switch process (#678) + + * riscv64: switch process + + * fixname + +commit 7d580ef99d2a52250b384afd49c7f87ab66a8c84 +Author: Val213 <112376067+val213@users.noreply.github.com> +Date: Sun Mar 31 18:01:32 2024 +0800 + + 修复get_ramdom的长度错误问题() (#677) + +commit 56cc4dbe27e132aac5c61b8bd4f4ec9a223b49ee +Author: Jomo <2512364506@qq.com> +Date: Sun Mar 31 16:33:49 2024 +0800 + + 实现页面反向映射 (#670) + + * 实现页面反向映射 + + * 完善PAGE_MANAGER初始化时机 && 封装lock函数 && 删掉过时注释 + +commit 924d64de8def99488f57dc618de763f7aca4a68b +Author: BrahmaMantra <140599389+BrahmaMantra@users.noreply.github.com> +Date: Sun Mar 31 15:19:12 2024 +0800 + + 修复了ramfs中move_to未更新parent字段的bug (#673) + + 修复了ramfs中move_to未更新parent字段的bug + + --------- + + Co-authored-by: Samuel Dai + +commit 9d9a09841ce2d650a41fed776916c0a11d52f92e +Author: sun5etop <146408999+sun5etop@users.noreply.github.com> +Date: Sun Mar 31 15:11:10 2024 +0800 + + 修复udp bind的时候,对port0处理不正确的问题(#676) + +commit da152319797436368304cbc3f85a3b9ec049134b +Author: LoGin +Date: Thu Mar 28 00:28:13 2024 +0800 + + 实现了rtc的抽象,并且把x86的cmos rtc接入到设备驱动模型 (#674) + + * 实现了rtc的抽象,并且把x86的cmos rtc接入到设备驱动模型。 + +commit 597ecc08c2444dcc8f527eb021932718b69c9cc5 +Author: TTaq <103996388+TTaq@users.noreply.github.com> +Date: Tue Mar 26 18:28:26 2024 +0800 + + 新加结构体POSIXSTATFS与SuperBlock用于处理statfs系统调用 (#667) + + * 新加结构体POSIXSTATFS与SuperBlock用于处理statfs系统调用 + +commit 0cb807346cb3c47924538585087d9fc846cf5e6f +Author: LoGin +Date: Tue Mar 26 18:26:02 2024 +0800 + + 修复tty设备显示在/sys目录下的bug (#668) + +commit 2755467c790d6510fa97cbf052ce8e91ad1372c6 +Author: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> +Date: Mon Mar 25 16:39:36 2024 +0800 + + 支持绘制24位深和16位深显示缓冲区 (#640) + + * 修复了初始化时显示,边界条件的一个bug + + * 解决了内存未初始前字体显示的兼容性问题 + * 支持绘制24位深和16位深显示缓冲区 + +commit 4256da7fb6ad25a3caab6f656607aaf047cb6446 +Author: LoGin +Date: Mon Mar 25 15:47:05 2024 +0800 + + 把Device trait的set_class改为设置Weak指针,以避免循环引用问题。 (#666) + +commit 5c20e05a2eb82da6dd73104fcf51d538500c2856 +Author: LoGin +Date: Mon Mar 25 13:59:00 2024 +0800 + + 修改bug report模版label (#665) + +commit 7c958c9ef0cd25eb15abb21d0d3420aac1c67c88 +Author: Val213 <112376067+val213@users.noreply.github.com> +Date: Mon Mar 25 13:04:53 2024 +0800 + + 移植dns查询工具dog的--tcp功能 (#652) + + * add dog, modify user/Makefile and user.sysconfig + + * add dog, modify user/Makefile and user.sysconfig + + * fix tty unicode + + * 修正无法正确编译dog的问题 + + --------- + + Co-authored-by: val213 + Co-authored-by: GnoCiYeH + Co-authored-by: longjin + +commit 911132c4b8ea0e9c49a4e84b9fa1db114102acbb +Author: Donkey Kane <109840258+xiaolin2004@users.noreply.github.com> +Date: Mon Mar 25 13:04:32 2024 +0800 + + 修复clock_gettime返回类型错误,修复小时间间隔duration返回0问题 (#664) + + * 修复clock_gettime返回类型错误,修正wtm初始化逻辑 + + * 修复duration在小时间间隔下为0的问题 + + * 临时修复时间流逝速度异常,在test-mount中加入运行时间检测 + +commit 401699735b5ec29768c3c0c47df6c529991f108f +Author: LoGin +Date: Sat Mar 23 16:25:56 2024 +0800 + + riscv: 进程管理初始化 (#654) + +commit 6046f77591cf23dc9cc53b68b25c0d74f94fa493 +Author: 裕依 <68320855+yuyi2439@users.noreply.github.com> +Date: Sat Mar 23 15:56:49 2024 +0800 + + Patch socketpair (#576) + + * 将sockets分成inet和unix域 + - 添加File端点 + - 添加SocketPair trait并将Socket trait中的pair相关方法移动 + - 添加对SockAddrUn的处理 + + * 精简SocketHandleItem + + * 重构socketpair相关逻辑 + - 将File端点换成Inode端点 + - 尝试使用SocketInode进行socketpair(未成功) + + + * 将SocketPair trait合并到Socket trait中,去除downcast + +commit 3660256a9ee94abc30b5b22508cbd48c44c86089 +Author: LoGin +Date: Sat Mar 23 11:51:30 2024 +0800 + + 只对x86_64进行clippy check (#651) + +commit 4e4c8c41e90989c1f732995511e0f9a77a33f650 +Author: LoGin +Date: Fri Mar 22 23:56:30 2024 +0800 + + 添加clippy检测的自动化工作流 (#649) + + * 添加clippy检测的自动化工作流 + + * fmt + + * 1 + +commit b5b571e02693d91eb6918d3b7561e088c3e7ee81 +Author: LoGin +Date: Fri Mar 22 23:26:39 2024 +0800 + + 修复内核的clippy检查报错 (#637) + + 修复内核的clippy检查报错 + --------- + + Co-authored-by: Samuel Dai <947309196@qq.com> + Co-authored-by: Donkey Kane <109840258+xiaolin2004@users.noreply.github.com> + Co-authored-by: themildwind <107623059+themildwind@users.noreply.github.com> + Co-authored-by: GnoCiYeH + Co-authored-by: MemoryShore <105195940+MemoryShore@users.noreply.github.com> + Co-authored-by: 曾俊 <110876916+ZZJJWarth@users.noreply.github.com> + Co-authored-by: sun5etop <146408999+sun5etop@users.noreply.github.com> + Co-authored-by: hmt <114841534+1037827920@users.noreply.github.com> + Co-authored-by: laokengwt <143977175+laokengwt@users.noreply.github.com> + Co-authored-by: TTaq <103996388+TTaq@users.noreply.github.com> + Co-authored-by: Jomo <2512364506@qq.com> + Co-authored-by: Samuel Dai + Co-authored-by: sspphh <112558065+sspphh@users.noreply.github.com> + +commit 4695947e1b601c83641676485571d42c692a2bbd +Author: Chenzx <109664121+schulice@users.noreply.github.com> +Date: Fri Mar 22 18:27:07 2024 +0800 + + 实现SYS_LINK和SYS_LINKAT (#611) + + * 实现do_linkat及SYS_LINK和SYS_LINKAT + + * 未在riscv上测试,添加target_arch + + * 将c字符串检查移动到vfs/syscall.rs,修改do_linkat()逻辑 + + * 修改部分注释 + +commit 70f159a3988eab656ea1d2b204fde87948526ecf +Author: LoGin +Date: Thu Mar 21 21:35:39 2024 +0800 + + riscv64: 添加flush tlb的ipi (#636) + + * riscv64: 添加flush tlb的ipi + + * update triagebot + +commit b4eb05a17f0f65668f69e7979660874ef8e01a2e +Author: TTaq <103996388+TTaq@users.noreply.github.com> +Date: Thu Mar 21 19:59:10 2024 +0800 + + Statx (#632) + + + * 实现statx及测试的应用程序 + +commit 8cb2e9b344230227fe5f3ab3ebeb2522f1c5e289 +Author: LoGin +Date: Thu Mar 21 19:19:32 2024 +0800 + + 重写SMP模块 (#633) + + * 修复cpumask的迭代器的错误。 + + * 能进系统(AP核心还没有初始化自身) + + * 初始化ap core + + * 修改percpu + + * 删除无用的cpu.c + + * riscv64编译通过 + +commit 1d37ca6d172e01a98fa6785d2b3e07fb8202a4a9 +Author: Donkey Kane <109840258+xiaolin2004@users.noreply.github.com> +Date: Wed Mar 20 15:31:20 2024 +0800 + + 添加mount系统调用 (#561) + + * Modify dadk config to switch NovaShell revision + + * finish primary build of mount(2), usable now + + * 使用read_from_cstr函数优化代码可读性 , 针对文件系统新增错误EUNSUPFS + + * small changes + + * 添加系统调用文档 + + * cargo fmt + + * Revert "small changes" + + This reverts commit e1991314ce687faa2d652479e8ef64f5bea25fa1. + + * 修复用户程序参数传入错误 + + * Revert "small changes" + + This reverts commit e1991314ce687faa2d652479e8ef64f5bea25fa1. + + * 解决合并冲突,最终提交 + + * 将dadk_config切换为相对路径以修复依赖问题 + + * Update settings.json + + * Delete user/apps/test-mount/LICENSE + + * 换用更好的c字符串读取函数,优化系统调用函数注释,修复错误处理bug,删除无用文件,修改测试程序readme + + * 修改用户程序readme + + * 代码格式化,初级版本 + + * 初级版本,未实现文件系统管理器,未支持设备挂载 + + * 为文件系统添加name方法,返回文件系统名字字符串,为挂载查询服务 + + * mount系统调用:添加统一文件系统初始化管理器 + + * null + + * 解除冲突 + + * 删除无用kdebug + +commit 1cd9bb43f0256aecf19a090dd71e4ac2b86a5e29 +Author: LoGin +Date: Tue Mar 19 21:31:02 2024 +0800 + + 添加core utils到系统 (#624) + +commit 8c6f21840f820a161d4386000aea1d79e3bc8d13 +Author: sspphh <112558065+sspphh@users.noreply.github.com> +Date: Tue Mar 19 17:01:20 2024 +0800 + + 实现uname系统调用 (#614) + + * 实现uname系统调用 + + Co-authored-by: longjin + +commit 82df0a13109e400602ddaec049d04ae230eb485b +Author: hmt <114841534+1037827920@users.noreply.github.com> +Date: Tue Mar 19 16:45:44 2024 +0800 + + fix: mkdir输出错误信息; feat: 实现get_pathname (#615) + + * fix: mkdir输出错误信息; feat: 实现get_pathname + + * fix: 将处理路径的操作放入vfs而不是在syscall/mod.rs中 + + * 调整入参类型 + + --------- + + Co-authored-by: longjin + +commit 9e481b3bfe303e0b104694da9750ae978dfeecae +Author: TTaq <103996388+TTaq@users.noreply.github.com> +Date: Mon Mar 18 14:47:59 2024 +0800 + + 实现了sys_rename (#578) + + * 基本实现了rename的系统调用 + + * 实现相对路径的mv + + * confilct resolve + + * make fmt + + * 更改校验位置, + 增加了SYS_RENAMEAT与SYS_RENAMEAT2两个系统调用,其实现与SYS_RENAME基本一致 + + * 删除了fat中的link + + * fix + + * 修改注释格式,删除管道文件判断 + + * 1 + +commit c3c73444516b7b47b6327cd66f5453133f47998d +Author: LoGin +Date: Sat Mar 16 22:28:59 2024 +0800 + + 更新triagebot配置 (#616) + + * 更新triagebot配置 + +commit 4fd916113e576a1c5d8ca9faae7a9d6b25afb9ae +Author: LoGin +Date: Sat Mar 16 18:09:32 2024 +0800 + + triagebot-add-shortcut (#612) + +commit fbc174499f5200924c732263e461c79b4a936c5b +Author: LoGin +Date: Fri Mar 15 20:06:24 2024 +0800 + + 添加triagebot文件 (#608) + + * 添加triagebot文件 + +``` \ No newline at end of file diff --git a/docs/community/ChangeLog/index.rst b/docs/community/ChangeLog/index.rst index 44336db67..4c123b595 100644 --- a/docs/community/ChangeLog/index.rst +++ b/docs/community/ChangeLog/index.rst @@ -6,6 +6,7 @@ .. toctree:: :maxdepth: 1 + V0.1.x/V0.1.10 V0.1.x/V0.1.9 V0.1.x/V0.1.8 V0.1.x/V0.1.7 diff --git a/docs/conf.py b/docs/conf.py index a772f3d60..6410fc765 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,15 +10,14 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -# import os +import os # import sys # sys.path.insert(0, os.path.abspath('.')) - # -- Project information ----------------------------------------------------- project = 'DragonOS' -copyright = '2022-2023, DragonOS Community' +copyright = '2022-2024, DragonOS Community' author = 'longjin' # The full version, including alpha/beta/rc tags @@ -73,4 +72,12 @@ "strikethrough", "substitution", "tasklist", -] \ No newline at end of file +] + + +# Define the canonical URL if you are using a custom domain on Read the Docs +html_baseurl = os.environ.get("READTHEDOCS_CANONICAL_URL", "") + +# Tell Jinja2 templates the build is running on Read the Docs +if os.environ.get("READTHEDOCS", "") == "True": + html_context["READTHEDOCS"] = True \ No newline at end of file diff --git a/docs/introduction/build_system.md b/docs/introduction/build_system.md index c746e66ae..f6098e912 100644 --- a/docs/introduction/build_system.md +++ b/docs/introduction/build_system.md @@ -48,6 +48,7 @@ bash bootstrap.sh # 这里请不要加上sudo, 因为需要安装的开发依 一键配置脚本目前只支持以下系统: - Ubuntu/Debian/Deepin/UOS 等基于Debian的衍生版本 +- Gentoo 由于Gentoo系统的特性 当gentoo出现USE或循环依赖问题时 请根据emerge提示信息进行对应的处理 官方的依赖处理实例[GentooWiki](https://wiki.gentoo.org/wiki/Handbook:AMD64/Full/Working/zh-cn#.E5.BD.93_Portage_.E6.8A.A5.E9.94.99.E7.9A.84.E6.97.B6.E5.80.99) 欢迎您为其他的系统完善构建脚本! ::: diff --git a/docs/kernel/locking/mutex.md b/docs/kernel/locking/mutex.md index 9071de7b6..a3eb84848 100644 --- a/docs/kernel/locking/mutex.md +++ b/docs/kernel/locking/mutex.md @@ -59,10 +59,10 @@ let x :Mutex>= Mutex::new(Vec::new()); g.push(2); assert!(g.as_slice() == [1, 2, 2] || g.as_slice() == [2, 2, 1]); // 在此处,Mutex是加锁的状态 - kdebug!("x={:?}", x); + debug!("x={:?}", x); } // 由于上方的变量`g`,也就是Mutex守卫的生命周期结束,自动释放了Mutex。因此,在此处,Mutex是放锁的状态 - kdebug!("x={:?}", x); + debug!("x={:?}", x); ```   对于结构体内部的变量,我们可以使用Mutex进行细粒度的加锁,也就是使用Mutex包裹需要细致加锁的成员变量,比如这样: diff --git a/docs/kernel/locking/spinlock.md b/docs/kernel/locking/spinlock.md index c4b90f01c..a9bcf7bdd 100644 --- a/docs/kernel/locking/spinlock.md +++ b/docs/kernel/locking/spinlock.md @@ -65,10 +65,10 @@ let x :SpinLock>= SpinLock::new(Vec::new()); g.push(2); assert!(g.as_slice() == [1, 2, 2] || g.as_slice() == [2, 2, 1]); // 在此处,SpinLock是加锁的状态 - kdebug!("x={:?}", x); + debug!("x={:?}", x); } // 由于上方的变量`g`,也就是SpinLock守卫的生命周期结束,自动释放了SpinLock。因此,在此处,SpinLock是放锁的状态 - kdebug!("x={:?}", x); + debug!("x={:?}", x); ```   对于结构体内部的变量,我们可以使用SpinLock进行细粒度的加锁,也就是使用SpinLock包裹需要细致加锁的成员变量,比如这样: diff --git a/docs/kernel/sched/cfs.md b/docs/kernel/sched/cfs.md index caaeb538e..284f11ec2 100644 --- a/docs/kernel/sched/cfs.md +++ b/docs/kernel/sched/cfs.md @@ -2,23 +2,27 @@    CFS(Completely Fair Scheduler),顾名思义,完全公平调度器。CFS作为主线调度器之一,也是最典型的O(1)调度器之一 -## 1. CFSQueue 介绍 +## 结构体介绍 -   CFSQueue是用来存放普通进程的调度队列,每个CPU维护一个CFSQueue,主要使用Vec作为主要存储结构来实现。 +- ``CompletelyFairScheduler`` +   ``CompletelyFairScheduler``实现了``Scheduler``trait,他是完全调度算法逻辑的主要实施者。 -### 1.1 主要函数 -1. enqueue(): 将pcb入队列 -2. dequeue(): 将pcb从调度队列中弹出,若队列为空,则返回IDLE进程的pcb -3. sort(): 将进程按照虚拟运行时间的升序进行排列 +- ``FairSchedEntity`` + - **重要字段** + - ``cfs_rq``: 它指向了自己所在的完全公平调度队列。 + - ``my_cfs_rq``: 为一个``Option``变量,当该实体作为一个单独进程时,这个值为``None``,但是若这个实体为一个组,那这个变量必需为这个组内的私有调度队列。这个``cfs_rq``还可以继续往下深入,就构成了上述的树型结构。 + - ``pcb``: 它指向了当前实体对应的``PCB``,同样,若当前实体为一个组,则这个``Weak``指针不指向任何值。 -## 2. SchedulerCFS 介绍 +  ``FairSchedEntity``是完全公平调度器中最重要的结构体,他代表一个实体单位,它不止表示一个进程,它还可以是一个组或者一个用户,但是它在cfs队列中所表示的就单单是一个调度实体。这样的设计可以为上层提供更多的思路,比如上层可以把不同的进程归纳到一个调度实体从而实现组调度等功能而不需要改变调度算法。 -   CFS调度器类,主要实现了CFS调度器类的初始化以及调度功能函数。 +  在cfs中,整体的结构是**一棵树**,每一个调度实体作为``cfs_rq``中的一个节点,若该调度实体不是单个进程(它可能是一个进程组),则在该调度实体中还需要维护一个自己的``cfs_rq``,这样的嵌套展开后,每一个叶子节点就是一个单独的进程。需要理解这样一棵树,**在后续文档中会以这棵树为核心讲解**。 +  该结构体具体的字段意义请查阅源代码。这里提及几个重要的字段: -### 2.1 主要函数 -1. sched(): 是对于Scheduler trait的sched()实现,是普通进程进行调度时的逻辑处理,该函数会返回接下来要执行的pcb,若没有符合要求的pcb,返回None -2. enqueue(): 同样是对于Scheduler trait的sched()实现,将一个pcb加入调度器的调度队列 -3. update_cpu_exec_proc_jiffies(): 更新这个cpu上,这个进程的可执行时间。 -4. timer_update_jiffies(): 时钟中断到来时,由sched的core模块中的函数,调用本函数,更新CFS进程的可执行时间 +- ``CfsRunQueue`` +  ``CfsRunQueue``完全公平调度算法中管理``FairSchedEntity``的队列,它可以挂在总的``CpuRunQueue``下,也可以作为子节点挂在``FairSchedEntity``上,详见上文``FairSchedEntity``。 + + - **重要字段** + - ``entities``: 存储调度实体的红黑树 + - ``current``: 当前正在运行的实体 diff --git a/docs/kernel/sched/core.md b/docs/kernel/sched/core.md index 760f6aae6..4f94dff31 100644 --- a/docs/kernel/sched/core.md +++ b/docs/kernel/sched/core.md @@ -1,14 +1,65 @@ # 进程调度器相关的api -   定义了DragonOS的进程调度相关的api,是系统进行进程调度的接口。同时也抽象出了Scheduler的trait,以供具体的调度器实现 +   定义了DragonOS的进程调度相关的api,是系统进行进程调度的接口。同时也抽象出了Scheduler的trait,以供具体的调度器实现。 -## 1. 调度器介绍 +## 调度器介绍    一般来说,一个系统会同时处理多个请求,但是其资源是优先的,调度就是用来协调每个请求对资源的使用的方法。 -### 1.1 主要函数 -1. cpu_executing(): 获取指定的cpu上正在执行的进程的pcb -2. sched_enqueue(): 将进程加入调度队列 -3. sched_init(): 初始化进程调度器模块 -4. sched_update_jiffies(): 当时钟中断到达时,更新时间片。*请注意,该函数只能被时钟中断处理程序调用* -5. sys_sched(): 让系统立即运行调度器的系统调用。*请注意,该系统调用不能由ring3的程序发起* +## 整体架构 +  整个调度子系统以**树形结构**来组织,每个CPU都会管理这样一棵树,每个CPU的``CpuRunQueue``即可以理解为树的根节点。每个``CpuRunQueue``下会管理着不同调度策略的子树,根据不同的调度策略深入到对应子树中实施调度。大体结构如下: + +- CpuRunQueue + - Cfs + - CfsRunQueue + - FairSchedEntity + - CfsRunQueue + - ...(嵌套) + - Rt + - ... + - Idle + - ... + - RR + - ... + - ... + +  基于这个结构,调度子系统能够更轻松地解耦以及添加其他调度策略。 +   + +## 重要结构 +- ``Scheduler:`` +  ``Scheduler``是各个调度算法提供给上层的接口,实现不同的调度算法,只需要向外提供这样一组接口即可。 + +- ``CpuRunQueue:`` +  ``CpuRunQueue``为总的CPU运行队列,他会根据不同的调度策略来进行调度。他作为调度子系统的根节点来组织调度。 + - **重要字段** + - ``lock``: 过程锁,因为在深入到具体调度策略后的调度过程中还会需要访问``CpuRunQueue``中的信息,在cfs中保存了``CpuRunQueue``对象,我们需要确保在整体过程上锁后,子对象中不需要二次加锁即可访问,所以过程锁比较适合这个场景,若使用对象锁,则在对应调度策略中想要访问``CpuRunQueue``中的信息时需要加锁,但是最外层已经将``CpuRunQueue``对象上锁,会导致内层永远拿不到锁。对于该字段,详见[CpuRunQueue的self_lock方法及其注释](https://code.dragonos.org.cn/xref/DragonOS/kernel/src/sched/mod.rs?r=dd8e74ef0d7f91a141bd217736bef4fe7dc6df3d#360)。 + - ``cfs``: Cfs调度器的根节点,往下伸展为一棵子树,详见完全公平调度文档。 + - ``current``: 当前在CPU上运行的进程。 + - ``idle``: 当前CPU的Idle进程。 + + +## 调度流程 +  一次有效的调度分两种情况,第一是主动调用``__schedule``或者``schedule``函数进行调度,第二是通过时钟中断,判断当前运行的任务时间是否到期。 + +- **主动调度** + - ``__schedule``和``schedule``函数: + - ``__schedule``:真正执行调度。会按照当前调度策略来选择下一个任务执行。 + - ``schedule``: ``__schedule``的上层封装,它需要该任务在内核中的所有资源释放干净才能进行调度,即判断当前进程的``preempt_count``是否为0,若不为0则会**panic**。 + - 参数:这两个函数都需要提供一个参数:``SchedMode``。用于控制此次调度的行为,可选参数主要有以下两个: + - ``SchedMode::SM_NONE``: 标志当前进程没有被抢占而是主动让出,他**不会**被再次加入队列,直到有其他进程主动唤醒它,这个标志位主要用于信号量、等待队列以及一些主动唤醒场景的实现。 + - ``SchedMode::SM_PREEMPT``:标志当前是被**抢占**运行的,他**会**再次被加入调度队列等待下次调度,通俗来说:它是被别的进程抢占了运行时间,有机会运行时他会继续执行。 + +- **时钟调度** +  时钟中断到来的时候,调度系统会进行更新,包括判断是否需要下一次调度。以下为主要的函数调用栈: + - ``LocalApicTimer::handle_irq``: 中断处理函数 + - ``ProcessManager::update_process_times``: 更新当前进程的时钟信息(统计运行时等) + - ``scheduler_tick``: 调度子系统tick入口 + - ``CompletelyFairScheduler::tick``: 以cfs为例,此为cfs调度算法的tick入口 + - ``CfsRunQueue::entity_tick``: 对所有调度实体进行tick + - ``CfsRunQueue::update_current``: 更新当前运行任务的运行时间及判断是否到期 + - ``CfsRunQueue::account_cfs_rq_runtime``: 计算当前队列的运行时间 + - ``CpuRunQueue::resched_current``: 若上一步计算的时间超时则到这一步,这里会设置进程标志为``NEED_SCHEDULE``. + + - 退出中断:退出中断时检查当前进程是否存在标志位``NEED_SCHEDULE``,若存在则调用``__schedule``进行调度。 + diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml index 146246b45..c12d67fb7 100644 --- a/kernel/.cargo/config.toml +++ b/kernel/.cargo/config.toml @@ -5,4 +5,8 @@ [target.'cfg(target_os = "none")'] runner = "bootimage runner" +[build] +rustflags = ["-Clink-args=-znostart-stop-gc"] +rustdocflags = ["-Clink-args=-znostart-stop-gc"] + [env] diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 376709b3c..2e4938625 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dragonos_kernel" -version = "0.1.9" +version = "0.1.10" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -12,16 +12,18 @@ crate-type = ["staticlib"] [workspace] members = [ "crates/*", - "src/libs/intertrait" ] [features] -default = ["backtrace", "kvm"] +default = ["backtrace", "kvm", "fatfs", "fatfs-secure"] # 内核栈回溯 backtrace = [] # kvm kvm = [] +fatfs = [] +fatfs-secure = ["fatfs"] + # 运行时依赖项 [dependencies] @@ -35,26 +37,28 @@ bitmap = { path = "crates/bitmap" } driver_base_macros = { "path" = "crates/driver_base_macros" } # 一个no_std的hashmap、hashset elf = { version = "=0.7.2", default-features = false } +fdt = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/fdt", rev = "9862813020" } hashbrown = "=0.13.2" ida = { path = "src/libs/ida" } -intertrait = { path = "src/libs/intertrait" } +intertrait = { path = "crates/intertrait" } kdepends = { path = "crates/kdepends" } klog_types = { path = "crates/klog_types" } -linkme = "=0.2" +linkme = "=0.3.27" num = { version = "=0.4.0", default-features = false } num-derive = "=0.3" num-traits = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/num-traits.git", rev="1597c1c", default-features = false } smoltcp = { version = "=0.11.0", default-features = false, features = ["log", "alloc", "socket-raw", "socket-udp", "socket-tcp", "socket-icmp", "socket-dhcpv4", "socket-dns", "proto-ipv4", "proto-ipv6"]} system_error = { path = "crates/system_error" } -unified-init = { path = "crates/unified-init" } -virtio-drivers = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/virtio-drivers", rev = "f91c807965" } -fdt = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/fdt", rev = "9862813020" } uefi = { version = "=0.26.0", features = ["alloc"] } uefi-raw = "=0.5.0" +unified-init = { path = "crates/unified-init" } +virtio-drivers = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/virtio-drivers", rev = "f91c807965" } +wait_queue_macros = { path = "crates/wait_queue_macros" } paste = "=1.0.14" slabmalloc = { path = "crates/rust-slabmalloc" } log = "0.4.21" - +xarray = "0.1.0" +lru = "0.12.3" # target为x86_64时,使用下面的依赖 [target.'cfg(target_arch = "x86_64")'.dependencies] @@ -85,4 +89,4 @@ debug = true # Controls whether the compiler passes `-g` # The release profile, used for `cargo build --release` [profile.release] -debug = false +debug = true diff --git a/kernel/Makefile b/kernel/Makefile index c50007c6d..be85d3e7b 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -25,9 +25,9 @@ clean: .PHONY: fmt fmt: - @cargo fmt --all $(FMT_CHECK) + RUSTFLAGS="$(RUSTFLAGS)" cargo fmt --all $(FMT_CHECK) ifeq ($(ARCH), x86_64) - @cargo clippy --all-features + RUSTFLAGS="$(RUSTFLAGS)" cargo clippy --all-features endif @@ -36,12 +36,12 @@ check: ECHO # @echo "Checking kernel... ARCH=$(ARCH)" # @exit 1 ifeq ($(ARCH), x86_64) - @cargo +nightly-2023-08-15 check --workspace $(CARGO_ZBUILD) --message-format=json --target ./src/$(TARGET_JSON) + RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-07-23 check --workspace $(CARGO_ZBUILD) --message-format=json --target ./src/$(TARGET_JSON) else ifeq ($(ARCH), riscv64) - @cargo +nightly-2023-08-15 check --workspace $(CARGO_ZBUILD) --message-format=json --target $(TARGET_JSON) + RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-07-23 check --workspace $(CARGO_ZBUILD) --message-format=json --target $(TARGET_JSON) endif test: # 测试内核库 - @cargo +nightly-2023-08-15 test --workspace --exclude dragonos_kernel + RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-07-23 test --workspace --exclude dragonos_kernel diff --git a/kernel/crates/bitmap/src/lib.rs b/kernel/crates/bitmap/src/lib.rs index 7af67331e..4d7991316 100644 --- a/kernel/crates/bitmap/src/lib.rs +++ b/kernel/crates/bitmap/src/lib.rs @@ -2,6 +2,7 @@ #![feature(core_intrinsics)] #![allow(incomplete_features)] // for const generics #![feature(generic_const_exprs)] +#![allow(internal_features)] #![allow(clippy::needless_return)] #[macro_use] diff --git a/kernel/crates/bitmap/src/static_bitmap.rs b/kernel/crates/bitmap/src/static_bitmap.rs index 96281aafc..c391a7da0 100644 --- a/kernel/crates/bitmap/src/static_bitmap.rs +++ b/kernel/crates/bitmap/src/static_bitmap.rs @@ -14,6 +14,15 @@ where core: BitMapCore, } +impl Default for StaticBitmap +where + [(); (N + usize::BITS as usize - 1) / (usize::BITS as usize)]:, +{ + fn default() -> Self { + Self::new() + } +} + impl StaticBitmap where [(); (N + usize::BITS as usize - 1) / (usize::BITS as usize)]:, diff --git a/kernel/crates/bitmap/src/traits.rs b/kernel/crates/bitmap/src/traits.rs index 8fc3a4ca8..ba7cfd888 100644 --- a/kernel/crates/bitmap/src/traits.rs +++ b/kernel/crates/bitmap/src/traits.rs @@ -182,11 +182,6 @@ macro_rules! bitops_for { } } - #[cfg(feature = "std")] - fn to_hex(bits: &Self) -> String { - format!("{:x}", bits) - } - #[inline] fn bit_size() -> usize { <$target>::BITS as usize diff --git a/kernel/src/libs/intertrait/.gitignore b/kernel/crates/intertrait/.gitignore similarity index 100% rename from kernel/src/libs/intertrait/.gitignore rename to kernel/crates/intertrait/.gitignore diff --git a/kernel/src/libs/intertrait/Cargo.toml b/kernel/crates/intertrait/Cargo.toml similarity index 97% rename from kernel/src/libs/intertrait/Cargo.toml rename to kernel/crates/intertrait/Cargo.toml index fe7febd78..697bccc2e 100644 --- a/kernel/src/libs/intertrait/Cargo.toml +++ b/kernel/crates/intertrait/Cargo.toml @@ -14,7 +14,7 @@ include = ["src/**/*", "Cargo.toml", "LICENSE-*", "README.md"] [dependencies] -linkme = "0.2" +linkme = "=0.3.27" hashbrown = "0.13.2" intertrait-macros = { version = "=0.2.2", path = "macros" } diff --git a/kernel/src/libs/intertrait/LICENSE-MIT b/kernel/crates/intertrait/LICENSE-MIT similarity index 100% rename from kernel/src/libs/intertrait/LICENSE-MIT rename to kernel/crates/intertrait/LICENSE-MIT diff --git a/kernel/src/libs/intertrait/README.md b/kernel/crates/intertrait/README.md similarity index 99% rename from kernel/src/libs/intertrait/README.md rename to kernel/crates/intertrait/README.md index f4db29532..6f22c6616 100644 --- a/kernel/src/libs/intertrait/README.md +++ b/kernel/crates/intertrait/README.md @@ -23,7 +23,7 @@ Add the following two dependencies to your `Cargo.toml`: ```toml [dependencies] intertrait = "0.2" -linkme = "0.2" +linkme = "=0.3.27" ``` The `linkme` dependency is required due to the use of `linkme` macro in the output of `intertrait` macros. diff --git a/kernel/src/libs/intertrait/macros/Cargo.toml b/kernel/crates/intertrait/macros/Cargo.toml similarity index 97% rename from kernel/src/libs/intertrait/macros/Cargo.toml rename to kernel/crates/intertrait/macros/Cargo.toml index 6e05adf2b..769db8332 100644 --- a/kernel/src/libs/intertrait/macros/Cargo.toml +++ b/kernel/crates/intertrait/macros/Cargo.toml @@ -20,4 +20,4 @@ uuid = { version = "0.8", features = ["v4"] } [dev-dependencies] intertrait = { version = "=0.2.2", path = ".." } -linkme = "0.2" +linkme = "=0.3.27" diff --git a/kernel/src/libs/intertrait/macros/LICENSE-APACHE b/kernel/crates/intertrait/macros/LICENSE-APACHE similarity index 100% rename from kernel/src/libs/intertrait/macros/LICENSE-APACHE rename to kernel/crates/intertrait/macros/LICENSE-APACHE diff --git a/kernel/src/libs/intertrait/macros/LICENSE-MIT b/kernel/crates/intertrait/macros/LICENSE-MIT similarity index 100% rename from kernel/src/libs/intertrait/macros/LICENSE-MIT rename to kernel/crates/intertrait/macros/LICENSE-MIT diff --git a/kernel/src/libs/intertrait/macros/src/args.rs b/kernel/crates/intertrait/macros/src/args.rs similarity index 100% rename from kernel/src/libs/intertrait/macros/src/args.rs rename to kernel/crates/intertrait/macros/src/args.rs diff --git a/kernel/src/libs/intertrait/macros/src/gen_caster.rs b/kernel/crates/intertrait/macros/src/gen_caster.rs similarity index 100% rename from kernel/src/libs/intertrait/macros/src/gen_caster.rs rename to kernel/crates/intertrait/macros/src/gen_caster.rs diff --git a/kernel/src/libs/intertrait/macros/src/item_impl.rs b/kernel/crates/intertrait/macros/src/item_impl.rs similarity index 100% rename from kernel/src/libs/intertrait/macros/src/item_impl.rs rename to kernel/crates/intertrait/macros/src/item_impl.rs diff --git a/kernel/src/libs/intertrait/macros/src/item_type.rs b/kernel/crates/intertrait/macros/src/item_type.rs similarity index 100% rename from kernel/src/libs/intertrait/macros/src/item_type.rs rename to kernel/crates/intertrait/macros/src/item_type.rs diff --git a/kernel/src/libs/intertrait/macros/src/lib.rs b/kernel/crates/intertrait/macros/src/lib.rs similarity index 100% rename from kernel/src/libs/intertrait/macros/src/lib.rs rename to kernel/crates/intertrait/macros/src/lib.rs diff --git a/kernel/src/libs/intertrait/src/cast.rs b/kernel/crates/intertrait/src/cast.rs similarity index 100% rename from kernel/src/libs/intertrait/src/cast.rs rename to kernel/crates/intertrait/src/cast.rs diff --git a/kernel/src/libs/intertrait/src/cast/cast_arc.rs b/kernel/crates/intertrait/src/cast/cast_arc.rs similarity index 100% rename from kernel/src/libs/intertrait/src/cast/cast_arc.rs rename to kernel/crates/intertrait/src/cast/cast_arc.rs diff --git a/kernel/src/libs/intertrait/src/cast/cast_box.rs b/kernel/crates/intertrait/src/cast/cast_box.rs similarity index 100% rename from kernel/src/libs/intertrait/src/cast/cast_box.rs rename to kernel/crates/intertrait/src/cast/cast_box.rs diff --git a/kernel/src/libs/intertrait/src/cast/cast_mut.rs b/kernel/crates/intertrait/src/cast/cast_mut.rs similarity index 100% rename from kernel/src/libs/intertrait/src/cast/cast_mut.rs rename to kernel/crates/intertrait/src/cast/cast_mut.rs diff --git a/kernel/src/libs/intertrait/src/cast/cast_rc.rs b/kernel/crates/intertrait/src/cast/cast_rc.rs similarity index 100% rename from kernel/src/libs/intertrait/src/cast/cast_rc.rs rename to kernel/crates/intertrait/src/cast/cast_rc.rs diff --git a/kernel/src/libs/intertrait/src/cast/cast_ref.rs b/kernel/crates/intertrait/src/cast/cast_ref.rs similarity index 100% rename from kernel/src/libs/intertrait/src/cast/cast_ref.rs rename to kernel/crates/intertrait/src/cast/cast_ref.rs diff --git a/kernel/src/libs/intertrait/src/hasher.rs b/kernel/crates/intertrait/src/hasher.rs similarity index 100% rename from kernel/src/libs/intertrait/src/hasher.rs rename to kernel/crates/intertrait/src/hasher.rs diff --git a/kernel/src/libs/intertrait/src/lib.rs b/kernel/crates/intertrait/src/lib.rs similarity index 100% rename from kernel/src/libs/intertrait/src/lib.rs rename to kernel/crates/intertrait/src/lib.rs diff --git a/kernel/src/libs/intertrait/tests/castable_to.rs b/kernel/crates/intertrait/tests/castable_to.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/castable_to.rs rename to kernel/crates/intertrait/tests/castable_to.rs diff --git a/kernel/src/libs/intertrait/tests/on-enum.rs b/kernel/crates/intertrait/tests/on-enum.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/on-enum.rs rename to kernel/crates/intertrait/tests/on-enum.rs diff --git a/kernel/src/libs/intertrait/tests/on-struct.rs b/kernel/crates/intertrait/tests/on-struct.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/on-struct.rs rename to kernel/crates/intertrait/tests/on-struct.rs diff --git a/kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type1.rs b/kernel/crates/intertrait/tests/on-trait-impl-assoc-type1.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type1.rs rename to kernel/crates/intertrait/tests/on-trait-impl-assoc-type1.rs diff --git a/kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type2.rs b/kernel/crates/intertrait/tests/on-trait-impl-assoc-type2.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type2.rs rename to kernel/crates/intertrait/tests/on-trait-impl-assoc-type2.rs diff --git a/kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type3.rs b/kernel/crates/intertrait/tests/on-trait-impl-assoc-type3.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/on-trait-impl-assoc-type3.rs rename to kernel/crates/intertrait/tests/on-trait-impl-assoc-type3.rs diff --git a/kernel/src/libs/intertrait/tests/on-trait-impl.rs b/kernel/crates/intertrait/tests/on-trait-impl.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/on-trait-impl.rs rename to kernel/crates/intertrait/tests/on-trait-impl.rs diff --git a/kernel/src/libs/intertrait/tests/on-type-multi-traits.rs b/kernel/crates/intertrait/tests/on-type-multi-traits.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/on-type-multi-traits.rs rename to kernel/crates/intertrait/tests/on-type-multi-traits.rs diff --git a/kernel/src/libs/intertrait/tests/run.rs b/kernel/crates/intertrait/tests/run.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/run.rs rename to kernel/crates/intertrait/tests/run.rs diff --git a/kernel/src/libs/intertrait/tests/ui/duplicate-flags.rs b/kernel/crates/intertrait/tests/ui/duplicate-flags.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/ui/duplicate-flags.rs rename to kernel/crates/intertrait/tests/ui/duplicate-flags.rs diff --git a/kernel/src/libs/intertrait/tests/ui/duplicate-flags.stderr b/kernel/crates/intertrait/tests/ui/duplicate-flags.stderr similarity index 100% rename from kernel/src/libs/intertrait/tests/ui/duplicate-flags.stderr rename to kernel/crates/intertrait/tests/ui/duplicate-flags.stderr diff --git a/kernel/src/libs/intertrait/tests/ui/on-generic-type.rs b/kernel/crates/intertrait/tests/ui/on-generic-type.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/ui/on-generic-type.rs rename to kernel/crates/intertrait/tests/ui/on-generic-type.rs diff --git a/kernel/src/libs/intertrait/tests/ui/on-generic-type.stderr b/kernel/crates/intertrait/tests/ui/on-generic-type.stderr similarity index 100% rename from kernel/src/libs/intertrait/tests/ui/on-generic-type.stderr rename to kernel/crates/intertrait/tests/ui/on-generic-type.stderr diff --git a/kernel/src/libs/intertrait/tests/ui/on-type-impl.rs b/kernel/crates/intertrait/tests/ui/on-type-impl.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/ui/on-type-impl.rs rename to kernel/crates/intertrait/tests/ui/on-type-impl.rs diff --git a/kernel/src/libs/intertrait/tests/ui/on-type-impl.stderr b/kernel/crates/intertrait/tests/ui/on-type-impl.stderr similarity index 100% rename from kernel/src/libs/intertrait/tests/ui/on-type-impl.stderr rename to kernel/crates/intertrait/tests/ui/on-type-impl.stderr diff --git a/kernel/src/libs/intertrait/tests/ui/unknown-flag.rs b/kernel/crates/intertrait/tests/ui/unknown-flag.rs similarity index 100% rename from kernel/src/libs/intertrait/tests/ui/unknown-flag.rs rename to kernel/crates/intertrait/tests/ui/unknown-flag.rs diff --git a/kernel/src/libs/intertrait/tests/ui/unknown-flag.stderr b/kernel/crates/intertrait/tests/ui/unknown-flag.stderr similarity index 100% rename from kernel/src/libs/intertrait/tests/ui/unknown-flag.stderr rename to kernel/crates/intertrait/tests/ui/unknown-flag.stderr diff --git a/kernel/crates/klog_types/src/lib.rs b/kernel/crates/klog_types/src/lib.rs index a9b180d79..22db10681 100644 --- a/kernel/crates/klog_types/src/lib.rs +++ b/kernel/crates/klog_types/src/lib.rs @@ -175,6 +175,12 @@ impl MMLogCycle { } } +impl Default for MMLogCycle { + fn default() -> Self { + Self::new() + } +} + impl kdepends::thingbuf::Recycle for MMLogCycle { fn new_element(&self) -> AllocatorLog { AllocatorLog::zeroed() diff --git a/kernel/crates/rust-slabmalloc/src/pages.rs b/kernel/crates/rust-slabmalloc/src/pages.rs index ba667148f..6d2f65162 100644 --- a/kernel/crates/rust-slabmalloc/src/pages.rs +++ b/kernel/crates/rust-slabmalloc/src/pages.rs @@ -38,7 +38,7 @@ impl Bitfield for [AtomicU64] { fn initialize(&mut self, for_size: usize, capacity: usize) { // Set everything to allocated for bitmap in self.iter_mut() { - *bitmap = AtomicU64::new(u64::max_value()); + *bitmap = AtomicU64::new(u64::MAX); } // Mark actual slots as free @@ -64,7 +64,7 @@ impl Bitfield for [AtomicU64] { for (base_idx, b) in self.iter().enumerate() { let bitval = b.load(Ordering::Relaxed); - if bitval == u64::max_value() { + if bitval == u64::MAX { continue; } else { let negated = !bitval; @@ -125,7 +125,7 @@ impl Bitfield for [AtomicU64] { #[inline(always)] fn is_full(&self) -> bool { self.iter() - .filter(|&x| x.load(Ordering::Relaxed) != u64::max_value()) + .filter(|&x| x.load(Ordering::Relaxed) != u64::MAX) .count() == 0 } @@ -410,6 +410,7 @@ impl<'a, T: AllocablePage> PageList<'a, T> { } /// Removes `slab_page` from the list. + #[allow(clippy::manual_inspect)] pub(crate) fn pop<'b>(&'b mut self) -> Option<&'a mut T> { match self.head { None => None, @@ -453,6 +454,7 @@ impl<'a, P: AllocablePage + 'a> Iterator for ObjectPageIterMut<'a, P> { type Item = &'a mut P; #[inline] + #[allow(clippy::manual_inspect)] fn next(&mut self) -> Option<&'a mut P> { unsafe { self.head.resolve_mut().map(|next| { diff --git a/kernel/crates/unified-init/Cargo.toml b/kernel/crates/unified-init/Cargo.toml index 893819976..d52fce584 100644 --- a/kernel/crates/unified-init/Cargo.toml +++ b/kernel/crates/unified-init/Cargo.toml @@ -10,5 +10,5 @@ path = "src/main.rs" [dependencies] unified-init-macros = { path = "macros" } -linkme = "0.2" +linkme = "=0.3.27" system_error = { path = "../system_error" } \ No newline at end of file diff --git a/kernel/crates/unified-init/macros/Cargo.toml b/kernel/crates/unified-init/macros/Cargo.toml index 0fe25a1f8..a6a746f9c 100644 --- a/kernel/crates/unified-init/macros/Cargo.toml +++ b/kernel/crates/unified-init/macros/Cargo.toml @@ -16,5 +16,5 @@ uuid = { version = "0.8", features = ["v4"] } [dev-dependencies] unified-init = { path = ".." } -linkme = "0.2" +linkme = "=0.3.27" system_error = { path = "../../system_error" } diff --git a/kernel/crates/unified-init/src/lib.rs b/kernel/crates/unified-init/src/lib.rs index b1dac7ef0..b4e75078d 100644 --- a/kernel/crates/unified-init/src/lib.rs +++ b/kernel/crates/unified-init/src/lib.rs @@ -66,7 +66,7 @@ macro_rules! unified_init { ($initializer_slice:ident) => { for initializer in $initializer_slice.iter() { initializer.call().unwrap_or_else(|e| { - kerror!("Failed to call initializer {}: {:?}", initializer.name(), e); + log::error!("Failed to call initializer {}: {:?}", initializer.name(), e); }); } }; diff --git a/kernel/crates/wait_queue_macros/Cargo.toml b/kernel/crates/wait_queue_macros/Cargo.toml new file mode 100644 index 000000000..7a47a8a48 --- /dev/null +++ b/kernel/crates/wait_queue_macros/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "wait_queue_macros" +version = "0.1.0" +edition = "2021" +authors = ["longjin "] + +[dependencies] diff --git a/kernel/crates/wait_queue_macros/src/lib.rs b/kernel/crates/wait_queue_macros/src/lib.rs new file mode 100644 index 000000000..122fd27a6 --- /dev/null +++ b/kernel/crates/wait_queue_macros/src/lib.rs @@ -0,0 +1,60 @@ +#![no_std] + +/// Wait for a condition to become true. +/// +/// This macro will wait for a condition to become true. +/// +/// ## Parameters +/// +/// - `$wq`: The wait queue to wait on. +/// - `$condition`: The condition to wait for. (you can pass a function or a boolean expression) +/// - `$cmd`: The command to execute while waiting. +#[macro_export] +macro_rules! wq_wait_event_interruptible { + ($wq:expr, $condition: expr, $cmd: expr) => {{ + let mut retval = Ok(()); + if !$condition { + retval = wait_queue_macros::_wq_wait_event_interruptible!($wq, $condition, $cmd); + } + + retval + }}; +} + +#[macro_export] +#[allow(clippy::crate_in_macro_def)] +macro_rules! _wq_wait_event_interruptible { + ($wq:expr, $condition: expr, $cmd: expr) => {{ + wait_queue_macros::__wq_wait_event!($wq, $condition, true, Ok(()), { + $cmd; + crate::sched::schedule(SchedMode::SM_NONE) + }) + }}; +} + +#[macro_export] +macro_rules! __wq_wait_event( + ($wq:expr, $condition: expr, $interruptible: expr, $ret: expr, $cmd:expr) => {{ + let mut retval = $ret; + let mut exec_finish_wait = true; + loop { + let x = $wq.prepare_to_wait_event($interruptible); + if $condition { + break; + } + + if $interruptible && !x.is_ok() { + retval = x; + exec_finish_wait = false; + break; + } + + $cmd; + } + if exec_finish_wait { + $wq.finish_wait(); + } + + retval + }}; +); diff --git a/kernel/env.mk b/kernel/env.mk index 666dcc5f5..7f3f3f605 100644 --- a/kernel/env.mk +++ b/kernel/env.mk @@ -42,3 +42,6 @@ endif ifeq ($(DEBUG), DEBUG) GLOBAL_CFLAGS += -g endif + +export RUSTFLAGS := -C link-args=-znostart-stop-gc +export RUSTDOCFLAGS := -C link-args=-znostart-stop-gc \ No newline at end of file diff --git a/kernel/rust-toolchain.toml b/kernel/rust-toolchain.toml index ddfcd41a7..325731828 100644 --- a/kernel/rust-toolchain.toml +++ b/kernel/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-08-15" +channel = "nightly-2024-07-23" components = ["rust-src", "clippy"] \ No newline at end of file diff --git a/kernel/src/Makefile b/kernel/src/Makefile index 40299bc9a..e9da4b024 100644 --- a/kernel/src/Makefile +++ b/kernel/src/Makefile @@ -21,7 +21,7 @@ ifeq ($(ARCH), x86_64) endif endif -RUSTFLAGS = $(RUSTFLAGS_UNWIND) +RUSTFLAGS += $(RUSTFLAGS_UNWIND) CFLAGS = $(GLOBAL_CFLAGS) -fno-pie $(CFLAGS_UNWIND) -I $(shell pwd) -I $(shell pwd)/include @@ -40,7 +40,7 @@ kernel_subdirs := common driver debug syscall libs kernel_rust: - RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2023-08-15 $(CARGO_ZBUILD) build --release --target $(TARGET_JSON) + RUSTFLAGS="$(RUSTFLAGS)" cargo +nightly-2024-07-23 $(CARGO_ZBUILD) build --release --target $(TARGET_JSON) all: kernel diff --git a/kernel/src/arch/io.rs b/kernel/src/arch/io.rs index 498f5793b..4b6384b6f 100644 --- a/kernel/src/arch/io.rs +++ b/kernel/src/arch/io.rs @@ -1,4 +1,5 @@ /// 每个架构都需要实现的IO接口 +#[allow(unused)] pub trait PortIOArch { unsafe fn in8(port: u16) -> u8; unsafe fn in16(port: u16) -> u16; diff --git a/kernel/src/arch/riscv64/driver/of.rs b/kernel/src/arch/riscv64/driver/of.rs index 2aa6adbee..4c93b91ad 100644 --- a/kernel/src/arch/riscv64/driver/of.rs +++ b/kernel/src/arch/riscv64/driver/of.rs @@ -17,7 +17,7 @@ impl OpenFirmwareFdtDriver { let offset = fdt_paddr.data() & crate::arch::MMArch::PAGE_OFFSET_MASK; let map_size = page_align_up(fdt_size + offset); let map_paddr = PhysAddr::new(fdt_paddr.data() & crate::arch::MMArch::PAGE_MASK); - // kdebug!( + // debug!( // "map_fdt paddr: {:?}, map_pa: {:?},fdt_size: {}, size: {:?}", // fdt_paddr, // map_paddr, @@ -28,7 +28,7 @@ impl OpenFirmwareFdtDriver { // drop the boot params guard in order to avoid deadlock drop(bp_guard); - // kdebug!("map_fdt: map fdt to {:?}, size: {}", map_paddr, map_size); + // debug!("map_fdt: map fdt to {:?}, size: {}", map_paddr, map_size); mmio_guard.map_phys(map_paddr, map_size)?; let mut bp_guard = boot_params().write(); let vaddr = mmio_guard.vaddr() + offset; diff --git a/kernel/src/arch/riscv64/init/mod.rs b/kernel/src/arch/riscv64/init/mod.rs index 166965c7e..b71c077d2 100644 --- a/kernel/src/arch/riscv64/init/mod.rs +++ b/kernel/src/arch/riscv64/init/mod.rs @@ -1,11 +1,11 @@ use fdt::node::FdtNode; +use log::{debug, info}; use system_error::SystemError; use crate::{ arch::{driver::sbi::SbiDriver, mm::init::mm_early_init}, driver::{firmware::efi::init::efi_init, open_firmware::fdt::open_firmware_fdt_driver}, init::{boot_params, init::start_kernel}, - kdebug, kinfo, mm::{memblock::mem_block_manager, PhysAddr, VirtAddr}, print, println, smp::cpu::ProcessorId, @@ -112,13 +112,12 @@ pub fn early_setup_arch() -> Result<(), SystemError> { arch_boot_params_guard.arch.fdt_paddr = fdt_paddr; arch_boot_params_guard.arch.fdt_size = fdt.total_size(); arch_boot_params_guard.arch.boot_hartid = ProcessorId::new(hartid); - // kdebug!("fdt_paddr: {:?}, fdt_size: {}", fdt_paddr, fdt.total_size()); + // debug!("fdt_paddr: {:?}, fdt_size: {}", fdt_paddr, fdt.total_size()); drop(arch_boot_params_guard); - kinfo!( + info!( "DragonOS kernel is running on hart {}, fdt address:{:?}", - hartid, - fdt_paddr + hartid, fdt_paddr ); mm_early_init(); @@ -127,7 +126,7 @@ pub fn early_setup_arch() -> Result<(), SystemError> { unsafe { parse_dtb() }; for x in mem_block_manager().to_iter() { - kdebug!("before efi: {x:?}"); + debug!("before efi: {x:?}"); } efi_init(); diff --git a/kernel/src/arch/riscv64/interrupt/handle.rs b/kernel/src/arch/riscv64/interrupt/handle.rs index 97f94d29f..faff690f2 100644 --- a/kernel/src/arch/riscv64/interrupt/handle.rs +++ b/kernel/src/arch/riscv64/interrupt/handle.rs @@ -3,9 +3,10 @@ //! 架构相关的处理逻辑参考: https://code.dragonos.org.cn/xref/linux-6.6.21/arch/riscv/kernel/traps.c use core::hint::spin_loop; +use log::error; use system_error::SystemError; -use crate::{arch::syscall::syscall_handler, driver::irqchip::riscv_intc::riscv_intc_irq, kerror}; +use crate::{arch::syscall::syscall_handler, driver::irqchip::riscv_intc::riscv_intc_irq}; use super::TrapFrame; @@ -52,7 +53,7 @@ fn riscv64_do_exception(trap_frame: &mut TrapFrame) { let handler = EXCEPTION_HANDLERS[code]; handler(trap_frame).ok(); } else { - kerror!("riscv64_do_irq: exception code out of range"); + error!("riscv64_do_irq: exception code out of range"); loop { // kernel die spin_loop(); @@ -61,7 +62,7 @@ fn riscv64_do_exception(trap_frame: &mut TrapFrame) { } fn default_handler(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - kerror!("riscv64_do_irq: handler not found"); + error!("riscv64_do_irq: handler not found"); loop { spin_loop(); } @@ -69,7 +70,7 @@ fn default_handler(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { /// 处理指令地址不对齐异常 #0 fn do_trap_insn_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - kerror!("riscv64_do_irq: do_trap_insn_misaligned"); + error!("riscv64_do_irq: do_trap_insn_misaligned"); loop { spin_loop(); } @@ -77,7 +78,7 @@ fn do_trap_insn_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemErro /// 处理指令访问异常 #1 fn do_trap_insn_access_fault(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - kerror!("riscv64_do_irq: do_trap_insn_access_fault"); + error!("riscv64_do_irq: do_trap_insn_access_fault"); loop { spin_loop(); } @@ -85,7 +86,7 @@ fn do_trap_insn_access_fault(_trap_frame: &mut TrapFrame) -> Result<(), SystemEr /// 处理非法指令异常 #2 fn do_trap_insn_illegal(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - kerror!("riscv64_do_irq: do_trap_insn_illegal"); + error!("riscv64_do_irq: do_trap_insn_illegal"); loop { spin_loop(); } @@ -93,7 +94,7 @@ fn do_trap_insn_illegal(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> /// 处理断点异常 #3 fn do_trap_break(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - kerror!("riscv64_do_irq: do_trap_break"); + error!("riscv64_do_irq: do_trap_break"); loop { spin_loop(); } @@ -101,7 +102,7 @@ fn do_trap_break(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { /// 处理加载地址不对齐异常 #4 fn do_trap_load_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - kerror!("riscv64_do_irq: do_trap_load_misaligned"); + error!("riscv64_do_irq: do_trap_load_misaligned"); loop { spin_loop(); } @@ -109,7 +110,7 @@ fn do_trap_load_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemErro /// 处理加载访问异常 #5 fn do_trap_load_access_fault(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - kerror!("riscv64_do_irq: do_trap_load_access_fault"); + error!("riscv64_do_irq: do_trap_load_access_fault"); loop { spin_loop(); } @@ -117,7 +118,7 @@ fn do_trap_load_access_fault(_trap_frame: &mut TrapFrame) -> Result<(), SystemEr /// 处理存储地址不对齐异常 #6 fn do_trap_store_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - kerror!("riscv64_do_irq: do_trap_store_misaligned"); + error!("riscv64_do_irq: do_trap_store_misaligned"); loop { spin_loop(); } @@ -125,7 +126,7 @@ fn do_trap_store_misaligned(_trap_frame: &mut TrapFrame) -> Result<(), SystemErr /// 处理存储访问异常 #7 fn do_trap_store_access_fault(_trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - kerror!("riscv64_do_irq: do_trap_store_access_fault"); + error!("riscv64_do_irq: do_trap_store_access_fault"); loop { spin_loop(); } @@ -151,11 +152,9 @@ fn do_trap_insn_page_fault(trap_frame: &mut TrapFrame) -> Result<(), SystemError let vaddr = trap_frame.badaddr; let cause = trap_frame.cause; let epc = trap_frame.epc; - kerror!( + error!( "riscv64_do_irq: do_insn_page_fault vaddr: {:#x}, cause: {:?} epc: {:#x}", - vaddr, - cause, - epc + vaddr, cause, epc ); loop { spin_loop(); @@ -167,10 +166,9 @@ fn do_trap_load_page_fault(trap_frame: &mut TrapFrame) -> Result<(), SystemError let vaddr = trap_frame.badaddr; let cause = trap_frame.cause; let epc = trap_frame.epc; - kerror!( + error!( "riscv64_do_irq: do_trap_load_page_fault: epc: {epc:#x}, vaddr={:#x}, cause={:?}", - vaddr, - cause + vaddr, cause ); loop { @@ -182,11 +180,9 @@ fn do_trap_load_page_fault(trap_frame: &mut TrapFrame) -> Result<(), SystemError /// 处理页存储错误异常 #15 fn do_trap_store_page_fault(trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - kerror!( + error!( "riscv64_do_irq: do_trap_store_page_fault: epc: {:#x}, vaddr={:#x}, cause={:?}", - trap_frame.epc, - trap_frame.badaddr, - trap_frame.cause + trap_frame.epc, trap_frame.badaddr, trap_frame.cause ); loop { spin_loop(); diff --git a/kernel/src/arch/riscv64/ipc/signal.rs b/kernel/src/arch/riscv64/ipc/signal.rs index 3ebb66c17..f902e6965 100644 --- a/kernel/src/arch/riscv64/ipc/signal.rs +++ b/kernel/src/arch/riscv64/ipc/signal.rs @@ -1,7 +1,8 @@ +use log::error; + use crate::{ arch::{sched::sched, CurrentIrqArch}, exception::InterruptArch, - kerror, process::ProcessManager, }; @@ -68,7 +69,7 @@ impl From for Signal { let ret: Signal = unsafe { core::mem::transmute(value) }; return ret; } else { - kerror!("Try to convert an invalid number to Signal"); + error!("Try to convert an invalid number to Signal"); return Signal::INVALID; } } @@ -83,7 +84,7 @@ impl Into for Signal { impl From for Signal { fn from(value: i32) -> Self { if value < 0 { - kerror!("Try to convert an invalid number to Signal"); + error!("Try to convert an invalid number to Signal"); return Signal::INVALID; } else { return Self::from(value as usize); @@ -127,7 +128,7 @@ impl Signal { pub fn handle_default(&self) { match self { Signal::INVALID => { - kerror!("attempting to handler an Invalid"); + error!("attempting to handler an Invalid"); } Signal::SIGHUP => sig_terminate(self.clone()), Signal::SIGINT => sig_terminate(self.clone()), @@ -312,7 +313,7 @@ fn sig_terminate_dump(sig: Signal) { fn sig_stop(sig: Signal) { let guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_stop().unwrap_or_else(|e| { - kerror!( + error!( "sleep error :{:?},failed to sleep process :{:?}, with signal :{:?}", e, ProcessManager::current_pcb(), @@ -327,7 +328,7 @@ fn sig_stop(sig: Signal) { /// 信号默认处理函数——继续进程 fn sig_continue(sig: Signal) { ProcessManager::wakeup_stop(&ProcessManager::current_pcb()).unwrap_or_else(|_| { - kerror!( + error!( "Failed to wake up process pid = {:?} with signal :{:?}", ProcessManager::current_pcb().pid(), sig diff --git a/kernel/src/arch/riscv64/mm/init.rs b/kernel/src/arch/riscv64/mm/init.rs index 79a5fade2..f545f1098 100644 --- a/kernel/src/arch/riscv64/mm/init.rs +++ b/kernel/src/arch/riscv64/mm/init.rs @@ -1,5 +1,6 @@ use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; +use log::{debug, info}; use system_error::SystemError; use crate::{ @@ -11,7 +12,6 @@ use crate::{ MMArch, }, driver::firmware::efi::efi_manager, - kdebug, kinfo, libs::lib_ui::screen_manager::scm_disable_put_to_window, mm::{ allocator::{buddy::BuddyAllocator, bump::BumpAllocator, page_frame::FrameAllocator}, @@ -56,7 +56,7 @@ unsafe fn init_kernel_addr() { KERNEL_BEGIN_VA = VirtAddr::new(boot_text_start_pa as usize); KERNEL_END_VA = VirtAddr::new(_end as usize); - kdebug!( + debug!( "init_kernel_addr: \n\tKERNEL_BEGIN_PA: {KERNEL_BEGIN_PA:?} \tKERNEL_END_PA: {KERNEL_END_PA:?} \tKERNEL_BEGIN_VA: {KERNEL_BEGIN_VA:?} @@ -78,7 +78,7 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { // 使用bump分配器,把所有的内存页都映射到页表 { - // kdebug!("to create new page table"); + // debug!("to create new page table"); // 用bump allocator创建新的页表 let mut mapper: crate::mm::page::PageMapper> = crate::mm::page::PageMapper::::create( @@ -87,7 +87,7 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { ) .expect("Failed to create page mapper"); new_page_table = mapper.table().phys(); - // kdebug!("PageMapper created"); + // debug!("PageMapper created"); // 取消最开始时候,在head.S中指定的映射(暂时不刷新TLB) { @@ -99,12 +99,12 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { .expect("Failed to empty page table entry"); } } - kdebug!("Successfully emptied page table"); + debug!("Successfully emptied page table"); let total_num = mem_block_manager().total_initial_memory_regions(); for i in 0..total_num { let area = mem_block_manager().get_initial_memory_region(i).unwrap(); - // kdebug!("area: base={:?}, size={:#x}, end={:?}", area.base, area.size, area.base + area.size); + // debug!("area: base={:?}, size={:#x}, end={:?}", area.base, area.size, area.base + area.size); for i in 0..((area.size + MMArch::PAGE_SIZE - 1) / MMArch::PAGE_SIZE) { let paddr = area.base.add(i * MMArch::PAGE_SIZE); let vaddr = unsafe { MMArch::phys_2_virt(paddr) }.unwrap(); @@ -125,7 +125,7 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { unsafe { INITIAL_PGTABLE_VALUE = new_page_table; } - kdebug!( + debug!( "After mapping all physical memory, DragonOS used: {} KB", bump_allocator.usage().used().bytes() / 1024 ); @@ -134,7 +134,7 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { let buddy_allocator = unsafe { BuddyAllocator::::new(bump_allocator).unwrap() }; // 设置全局的页帧分配器 unsafe { set_inner_allocator(buddy_allocator) }; - kinfo!("Successfully initialized buddy allocator"); + info!("Successfully initialized buddy allocator"); // 关闭显示输出 scm_disable_put_to_window(); @@ -142,7 +142,7 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { { let mut binding = INNER_ALLOCATOR.lock(); let mut allocator_guard = binding.as_mut().unwrap(); - kdebug!("To enable new page table."); + debug!("To enable new page table."); compiler_fence(Ordering::SeqCst); let mapper = crate::mm::page::PageMapper::::new( PageTableKind::Kernel, @@ -152,10 +152,10 @@ pub(super) unsafe fn riscv_mm_init() -> Result<(), SystemError> { compiler_fence(Ordering::SeqCst); mapper.make_current(); compiler_fence(Ordering::SeqCst); - // kdebug!("New page table enabled"); + // debug!("New page table enabled"); } - kdebug!("Successfully enabled new page table"); - kinfo!("riscv mm init done"); + debug!("Successfully enabled new page table"); + info!("riscv mm init done"); return Ok(()); } diff --git a/kernel/src/arch/riscv64/mm/mod.rs b/kernel/src/arch/riscv64/mm/mod.rs index 76bfbed04..1970d3d6f 100644 --- a/kernel/src/arch/riscv64/mm/mod.rs +++ b/kernel/src/arch/riscv64/mm/mod.rs @@ -12,9 +12,9 @@ use crate::{ page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage, PhysPageFrame}, }, kernel_mapper::KernelMapper, - page::{PageEntry, PageFlags, PAGE_1G_SHIFT}, + page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}, ucontext::UserMapper, - MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, + MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, VmFlags, }, smp::cpu::ProcessorId, }; @@ -256,8 +256,74 @@ impl MemoryManagementArch for RiscV64MMArch { ) -> bool { true } + + const PAGE_NONE: usize = Self::ENTRY_FLAG_GLOBAL | Self::ENTRY_FLAG_READONLY; + + const PAGE_READ: usize = PAGE_ENTRY_BASE | Self::ENTRY_FLAG_READONLY; + + const PAGE_WRITE: usize = + PAGE_ENTRY_BASE | Self::ENTRY_FLAG_READONLY | Self::ENTRY_FLAG_WRITEABLE; + + const PAGE_EXEC: usize = PAGE_ENTRY_BASE | Self::ENTRY_FLAG_EXEC; + + const PAGE_READ_EXEC: usize = + PAGE_ENTRY_BASE | Self::ENTRY_FLAG_READONLY | Self::ENTRY_FLAG_EXEC; + + const PAGE_WRITE_EXEC: usize = PAGE_ENTRY_BASE + | Self::ENTRY_FLAG_READONLY + | Self::ENTRY_FLAG_EXEC + | Self::ENTRY_FLAG_WRITEABLE; + + const PAGE_COPY: usize = Self::PAGE_READ; + const PAGE_COPY_EXEC: usize = Self::PAGE_READ_EXEC; + const PAGE_SHARED: usize = Self::PAGE_WRITE; + const PAGE_SHARED_EXEC: usize = Self::PAGE_WRITE_EXEC; + + const PAGE_COPY_NOEXEC: usize = 0; + const PAGE_READONLY: usize = 0; + const PAGE_READONLY_EXEC: usize = 0; + + const PROTECTION_MAP: [EntryFlags; 16] = protection_map(); +} + +const fn protection_map() -> [EntryFlags; 16] { + let mut map = [0; 16]; + map[VmFlags::VM_NONE.bits()] = MMArch::PAGE_NONE; + map[VmFlags::VM_READ.bits()] = MMArch::PAGE_READONLY; + map[VmFlags::VM_WRITE.bits()] = MMArch::PAGE_COPY; + map[VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = MMArch::PAGE_COPY; + map[VmFlags::VM_EXEC.bits()] = MMArch::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = MMArch::PAGE_READONLY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = MMArch::PAGE_COPY_EXEC; + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + MMArch::PAGE_COPY_EXEC; + map[VmFlags::VM_SHARED.bits()] = MMArch::PAGE_NONE; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_READ.bits()] = MMArch::PAGE_READONLY; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits()] = MMArch::PAGE_SHARED; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + MMArch::PAGE_SHARED; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits()] = MMArch::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = + MMArch::PAGE_READONLY_EXEC; + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = + MMArch::PAGE_SHARED_EXEC; + map[VmFlags::VM_SHARED.bits() + | VmFlags::VM_EXEC.bits() + | VmFlags::VM_WRITE.bits() + | VmFlags::VM_READ.bits()] = MMArch::PAGE_SHARED_EXEC; + let mut ret = [unsafe { EntryFlags::from_data(0) }; 16]; + let mut index = 0; + while index < 16 { + ret[index] = unsafe { EntryFlags::from_data(map[index]) }; + index += 1; + } + ret } +const PAGE_ENTRY_BASE: usize = RiscV64MMArch::ENTRY_FLAG_PRESENT + | RiscV64MMArch::ENTRY_FLAG_ACCESSED + | RiscV64MMArch::ENTRY_FLAG_USER; + impl VirtAddr { /// 判断虚拟地址是否合法 #[inline(always)] @@ -270,8 +336,8 @@ impl VirtAddr { } /// 获取内核地址默认的页面标志 -pub unsafe fn kernel_page_flags(_virt: VirtAddr) -> PageFlags { - PageFlags::from_data(RiscV64MMArch::ENTRY_FLAG_DEFAULT_PAGE) +pub unsafe fn kernel_page_flags(_virt: VirtAddr) -> EntryFlags { + EntryFlags::from_data(RiscV64MMArch::ENTRY_FLAG_DEFAULT_PAGE) .set_user(false) .set_execute(true) } diff --git a/kernel/src/arch/riscv64/pci/pci_host_ecam.rs b/kernel/src/arch/riscv64/pci/pci_host_ecam.rs index 59d658c26..4e1b4ed08 100644 --- a/kernel/src/arch/riscv64/pci/pci_host_ecam.rs +++ b/kernel/src/arch/riscv64/pci/pci_host_ecam.rs @@ -1,4 +1,5 @@ use fdt::{node::FdtNode, Fdt}; +use log::debug; use system_error::SystemError; use crate::{ @@ -6,7 +7,6 @@ use crate::{ open_firmware::fdt::open_firmware_fdt_driver, pci::ecam::{pci_ecam_root_info_manager, EcamRootInfo}, }, - kdebug, mm::PhysAddr, }; @@ -39,7 +39,7 @@ pub(super) fn pci_host_ecam_driver_init(fdt: &Fdt<'_>) -> Result<(), SystemError _ => panic!("Unexpected linux,pci-domain length"), }; - kdebug!( + debug!( "pci_host_ecam_driver_init(): {} paddr: {:#x} size: {:#x} bus-range: {}-{} segement_group_number: {}", node.name, paddr, @@ -61,10 +61,9 @@ pub(super) fn pci_host_ecam_driver_init(fdt: &Fdt<'_>) -> Result<(), SystemError for node in open_firmware_fdt_driver().find_node_by_compatible(&fdt, "pci-host-ecam-generic") { if let Err(err) = do_check(node) { - kdebug!( + debug!( "pci_host_ecam_driver_init(): check {} error: {:?}", - node.name, - err + node.name, err ); } } diff --git a/kernel/src/arch/riscv64/process/idle.rs b/kernel/src/arch/riscv64/process/idle.rs index 196a709ec..ed21f51ee 100644 --- a/kernel/src/arch/riscv64/process/idle.rs +++ b/kernel/src/arch/riscv64/process/idle.rs @@ -1,6 +1,8 @@ use core::hint::spin_loop; -use crate::{arch::CurrentIrqArch, exception::InterruptArch, kBUG, process::ProcessManager}; +use log::error; + +use crate::{arch::CurrentIrqArch, exception::InterruptArch, process::ProcessManager}; impl ProcessManager { /// 每个核的idle进程 @@ -9,11 +11,11 @@ impl ProcessManager { if CurrentIrqArch::is_irq_enabled() { riscv::asm::wfi(); } else { - kBUG!("Idle process should not be scheduled with IRQs disabled."); + error!("Idle process should not be scheduled with IRQs disabled."); spin_loop(); } - // kdebug!("idle loop"); + // debug!("idle loop"); } } } diff --git a/kernel/src/arch/riscv64/process/mod.rs b/kernel/src/arch/riscv64/process/mod.rs index 32e699294..89ec982d3 100644 --- a/kernel/src/arch/riscv64/process/mod.rs +++ b/kernel/src/arch/riscv64/process/mod.rs @@ -6,6 +6,7 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, }; use kdepends::memoffset::offset_of; +use log::error; use riscv::register::sstatus::Sstatus; use system_error::SystemError; @@ -15,7 +16,6 @@ use crate::{ CurrentIrqArch, }, exception::InterruptArch, - kerror, libs::spinlock::SpinLockGuard, mm::VirtAddr, process::{ @@ -166,7 +166,7 @@ impl ProcessManager { /// 参考: https://code.dragonos.org.cn/xref/linux-6.6.21/arch/riscv/include/asm/switch_to.h#76 pub unsafe fn switch_process(prev: Arc, next: Arc) { assert!(!CurrentIrqArch::is_irq_enabled()); - // kdebug!( + // debug!( // "riscv switch process: prev: {:?}, next: {:?}", // prev.pid(), // next.pid() @@ -182,7 +182,7 @@ impl ProcessManager { drop(next_addr_space); compiler_fence(Ordering::SeqCst); - // kdebug!("current sum={}, prev sum={}, next_sum={}", riscv::register::sstatus::read().sum(), prev.arch_info_irqsave().sstatus.sum(), next.arch_info_irqsave().sstatus.sum()); + // debug!("current sum={}, prev sum={}, next_sum={}", riscv::register::sstatus::read().sum(), prev.arch_info_irqsave().sstatus.sum(), next.arch_info_irqsave().sstatus.sum()); // 获取arch info的锁,并强制泄露其守卫(切换上下文后,在switch_finish_hook中会释放锁) let next_arch = SpinLockGuard::leak(next.arch_info_irqsave()) as *mut ArchPCBInfo; @@ -193,7 +193,7 @@ impl ProcessManager { ProcessManager::current_pcb().preempt_enable(); PROCESS_SWITCH_RESULT.as_mut().unwrap().get_mut().prev_pcb = Some(prev); PROCESS_SWITCH_RESULT.as_mut().unwrap().get_mut().next_pcb = Some(next); - // kdebug!("riscv switch process: before to inner"); + // debug!("riscv switch process: before to inner"); compiler_fence(Ordering::SeqCst); // 正式切换上下文 switch_to_inner(prev_arch, next_arch); @@ -326,7 +326,7 @@ impl ProcessControlBlock { // 从内核栈的最低地址处取出pcb的地址 let p = stack_base.data() as *const *const ProcessControlBlock; if core::intrinsics::unlikely((unsafe { *p }).is_null()) { - kerror!("p={:p}", p); + error!("p={:p}", p); panic!("current_pcb is null"); } unsafe { diff --git a/kernel/src/arch/riscv64/process/syscall.rs b/kernel/src/arch/riscv64/process/syscall.rs index 0b446f41c..f914a3402 100644 --- a/kernel/src/arch/riscv64/process/syscall.rs +++ b/kernel/src/arch/riscv64/process/syscall.rs @@ -1,4 +1,4 @@ -use alloc::{string::String, vec::Vec}; +use alloc::{ffi::CString, string::String, vec::Vec}; use riscv::register::sstatus::{FS, SPP}; use system_error::SystemError; @@ -16,14 +16,14 @@ use crate::{ impl Syscall { pub fn do_execve( path: String, - argv: Vec, - envp: Vec, + argv: Vec, + envp: Vec, regs: &mut TrapFrame, ) -> Result<(), SystemError> { // 关中断,防止在设置地址空间的时候,发生中断,然后进调度器,出现错误。 let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; let pcb = ProcessManager::current_pcb(); - // crate::kdebug!( + // crate::debug!( // "pid: {:?} do_execve: path: {:?}, argv: {:?}, envp: {:?}\n", // pcb.pid(), // path, @@ -52,20 +52,20 @@ impl Syscall { AddressSpace::is_current(&address_space), "Failed to set address space" ); - // kdebug!("Switch to new address space"); + // debug!("Switch to new address space"); // 切换到新的用户地址空间 unsafe { address_space.read().user_mapper.utable.make_current() }; drop(old_address_space); drop(irq_guard); - // kdebug!("to load binary file"); + // debug!("to load binary file"); let mut param = ExecParam::new(path.as_str(), address_space.clone(), ExecParamFlags::EXEC)?; // 加载可执行文件 let load_result = load_binary_file(&mut param)?; - // kdebug!("load binary file done"); - // kdebug!("argv: {:?}, envp: {:?}", argv, envp); + // debug!("load binary file done"); + // debug!("argv: {:?}, envp: {:?}", argv, envp); param.init_info_mut().args = argv; param.init_info_mut().envs = envp; @@ -79,19 +79,13 @@ impl Syscall { }; let (user_sp, argv_ptr) = unsafe { param - .init_info() - .push_at( - // address_space - // .write() - // .user_stack_mut() - // .expect("No user stack found"), - &mut ustack_message, - ) + .init_info_mut() + .push_at(&mut ustack_message) .expect("Failed to push proc_init_info to user stack") }; address_space.write().user_stack = Some(ustack_message); - // kdebug!("write proc_init_info to user stack done"); + // debug!("write proc_init_info to user stack done"); regs.a0 = param.init_info().args.len(); regs.a1 = argv_ptr.data(); diff --git a/kernel/src/arch/riscv64/smp/mod.rs b/kernel/src/arch/riscv64/smp/mod.rs index a283b4502..32969d8cc 100644 --- a/kernel/src/arch/riscv64/smp/mod.rs +++ b/kernel/src/arch/riscv64/smp/mod.rs @@ -1,11 +1,9 @@ +use log::warn; use system_error::SystemError; -use crate::{ - kwarn, - smp::{ - cpu::{CpuHpCpuState, ProcessorId}, - SMPArch, - }, +use crate::smp::{ + cpu::{CpuHpCpuState, ProcessorId}, + SMPArch, }; pub struct RiscV64SMPArch; @@ -13,12 +11,12 @@ pub struct RiscV64SMPArch; impl SMPArch for RiscV64SMPArch { #[inline(never)] fn prepare_cpus() -> Result<(), SystemError> { - kwarn!("RiscV64SMPArch::prepare_cpus() is not implemented"); + warn!("RiscV64SMPArch::prepare_cpus() is not implemented"); Ok(()) } fn start_cpu(_cpu_id: ProcessorId, _hp_state: &CpuHpCpuState) -> Result<(), SystemError> { - kwarn!("RiscV64SMPArch::start_cpu() is not implemented"); + warn!("RiscV64SMPArch::start_cpu() is not implemented"); Ok(()) } } diff --git a/kernel/src/arch/riscv64/syscall/mod.rs b/kernel/src/arch/riscv64/syscall/mod.rs index fe1a1908d..5609084a8 100644 --- a/kernel/src/arch/riscv64/syscall/mod.rs +++ b/kernel/src/arch/riscv64/syscall/mod.rs @@ -18,7 +18,7 @@ macro_rules! syscall_return { if $show { let pid = ProcessManager::current_pcb().pid(); - crate::kdebug!("syscall return:pid={:?},ret= {:?}\n", pid, ret as isize); + log::debug!("syscall return:pid={:?},ret= {:?}\n", pid, ret as isize); } unsafe { @@ -29,7 +29,7 @@ macro_rules! syscall_return { } pub(super) fn syscall_handler(syscall_num: usize, frame: &mut TrapFrame) -> () { - // kdebug!("syscall_handler: syscall_num: {}", syscall_num); + // debug!("syscall_handler: syscall_num: {}", syscall_num); unsafe { CurrentIrqArch::interrupt_enable(); } diff --git a/kernel/src/arch/riscv64/time.rs b/kernel/src/arch/riscv64/time.rs index d6be314a8..0b2f11288 100644 --- a/kernel/src/arch/riscv64/time.rs +++ b/kernel/src/arch/riscv64/time.rs @@ -1,6 +1,7 @@ +use log::{debug, info}; + use crate::{ driver::open_firmware::fdt::open_firmware_fdt_driver, - kdebug, kinfo, time::{clocksource::HZ, TimeArch}, }; pub struct RiscV64TimeArch; @@ -14,12 +15,12 @@ static mut TIME_FREQ: usize = 0; /// /// todo: 支持从acpi中获取 fn init_time_freq() { - kdebug!("init_time_freq: init"); + debug!("init_time_freq: init"); let fdt = open_firmware_fdt_driver().fdt_ref(); if fdt.is_err() { panic!("init_time_freq: failed to get fdt"); } - kdebug!("init_time_freq: get fdt"); + debug!("init_time_freq: get fdt"); let fdt = fdt.unwrap(); let cpu_node = fdt.find_node("/cpus"); if cpu_node.is_none() { @@ -36,7 +37,7 @@ fn init_time_freq() { } let time_freq: usize = time_freq.unwrap(); - kinfo!("init_time_freq: timebase-frequency: {}", time_freq); + info!("init_time_freq: timebase-frequency: {}", time_freq); unsafe { TIME_FREQ = time_freq; } diff --git a/kernel/src/arch/x86_64/acpi.rs b/kernel/src/arch/x86_64/acpi.rs index d29b3bcd1..0b834ee68 100644 --- a/kernel/src/arch/x86_64/acpi.rs +++ b/kernel/src/arch/x86_64/acpi.rs @@ -1,5 +1,6 @@ use super::smp::SMP_BOOT_DATA; -use crate::{driver::acpi::acpi_manager, kinfo, mm::percpu::PerCpu, smp::cpu::ProcessorId}; +use crate::{driver::acpi::acpi_manager, mm::percpu::PerCpu, smp::cpu::ProcessorId}; +use log::info; use system_error::SystemError; pub(super) fn early_acpi_boot_init() -> Result<(), SystemError> { @@ -24,7 +25,7 @@ pub(super) fn early_acpi_boot_init() -> Result<(), SystemError> { SMP_BOOT_DATA.set_cpu_count(cnt.data()); SMP_BOOT_DATA.mark_initialized(); } - kinfo!( + info!( "early_acpi_boot_init: cpu_count: {}\n", SMP_BOOT_DATA.cpu_count() ); diff --git a/kernel/src/arch/x86_64/driver/apic/apic_timer.rs b/kernel/src/arch/x86_64/driver/apic/apic_timer.rs index 81928da47..1a18e5436 100644 --- a/kernel/src/arch/x86_64/driver/apic/apic_timer.rs +++ b/kernel/src/arch/x86_64/driver/apic/apic_timer.rs @@ -11,15 +11,15 @@ use crate::exception::irqdesc::{ use crate::exception::manage::irq_manager; use crate::exception::IrqNumber; -use crate::kdebug; use crate::mm::percpu::PerCpu; -use crate::process::ProcessManager; use crate::smp::core::smp_get_processor_id; use crate::smp::cpu::ProcessorId; use crate::time::clocksource::HZ; +use crate::time::tick_common::tick_handle_periodic; use alloc::string::ToString; use alloc::sync::Arc; pub use drop; +use log::debug; use system_error::SystemError; use x86::cpuid::cpuid; use x86::msr::{wrmsr, IA32_X2APIC_DIV_CONF, IA32_X2APIC_INIT_COUNT}; @@ -105,7 +105,7 @@ pub(super) fn local_apic_timer_irq_desc_init() { /// 初始化BSP的APIC定时器 /// fn init_bsp_apic_timer() { - kdebug!("init_bsp_apic_timer"); + debug!("init_bsp_apic_timer"); assert!(smp_get_processor_id().data() == 0); let mut local_apic_timer = local_apic_timer_instance_mut(ProcessorId::new(0)); local_apic_timer.init( @@ -113,11 +113,11 @@ fn init_bsp_apic_timer() { LocalApicTimer::periodic_default_initial_count(), LocalApicTimer::DIVISOR as u32, ); - kdebug!("init_bsp_apic_timer done"); + debug!("init_bsp_apic_timer done"); } fn init_ap_apic_timer() { - kdebug!("init_ap_apic_timer"); + debug!("init_ap_apic_timer"); let cpu_id = smp_get_processor_id(); assert!(cpu_id.data() != 0); @@ -127,14 +127,14 @@ fn init_ap_apic_timer() { LocalApicTimer::periodic_default_initial_count(), LocalApicTimer::DIVISOR as u32, ); - kdebug!("init_ap_apic_timer done"); + debug!("init_ap_apic_timer done"); } pub(super) struct LocalApicTimerIntrController; impl LocalApicTimerIntrController { pub(super) fn install(&self) { - kdebug!("LocalApicTimerIntrController::install"); + debug!("LocalApicTimerIntrController::install"); if smp_get_processor_id().data() == 0 { init_bsp_apic_timer(); } else { @@ -150,12 +150,13 @@ impl LocalApicTimerIntrController { } pub(super) fn enable(&self) { - kdebug!("LocalApicTimerIntrController::enable"); + debug!("LocalApicTimerIntrController::enable"); let cpu_id = smp_get_processor_id(); let mut local_apic_timer = local_apic_timer_instance_mut(cpu_id); local_apic_timer.start_current(); } + #[allow(dead_code)] pub(super) fn disable(&self) { let cpu_id = smp_get_processor_id(); let local_apic_timer = local_apic_timer_instance_mut(cpu_id); @@ -221,19 +222,18 @@ impl LocalApicTimer { } fn install_periodic_mode(&mut self, initial_count: u64, divisor: u32) { - kdebug!( + debug!( "install_periodic_mode: initial_count = {}, divisor = {}", - initial_count, - divisor + initial_count, divisor ); self.mode = LocalApicTimerMode::Periodic; self.set_divisor(divisor); - self.set_initial_cnt(initial_count); self.setup_lvt( APIC_TIMER_IRQ_NUM.data() as u8, true, LocalApicTimerMode::Periodic, ); + self.set_initial_cnt(initial_count); } fn setup_lvt(&mut self, vector: u8, mask: bool, mode: LocalApicTimerMode) { @@ -278,7 +278,7 @@ impl LocalApicTimer { pub(super) fn handle_irq(trap_frame: &TrapFrame) -> Result { // sched_update_jiffies(); - ProcessManager::update_process_times(trap_frame.is_from_user()); + tick_handle_periodic(trap_frame); return Ok(IrqReturn::Handled); } } diff --git a/kernel/src/arch/x86_64/driver/apic/ioapic.rs b/kernel/src/arch/x86_64/driver/apic/ioapic.rs index fcfbbb94f..b2b46b46e 100644 --- a/kernel/src/arch/x86_64/driver/apic/ioapic.rs +++ b/kernel/src/arch/x86_64/driver/apic/ioapic.rs @@ -4,6 +4,7 @@ use acpi::madt::Madt; use alloc::sync::Arc; use bit_field::BitField; use bitflags::bitflags; +use log::{debug, info}; use system_error::SystemError; use crate::{ @@ -16,7 +17,6 @@ use crate::{ manage::irq_manager, IrqNumber, }, - kdebug, kinfo, libs::{ cpumask::CpuMask, once::Once, @@ -68,7 +68,7 @@ impl IoApic { let mut result: Option = None; INIT_STATE.call_once(|| { - kinfo!("Initializing ioapic..."); + info!("Initializing ioapic..."); // get ioapic base from acpi @@ -104,7 +104,7 @@ impl IoApic { mmio_guard.map_phys(phys_base, 0x1000).is_ok(), "IoApic::new(): failed to map phys" ); - kdebug!("Ioapic map ok"); + debug!("Ioapic map ok"); let reg = mmio_guard.vaddr(); result = Some(IoApic { @@ -114,13 +114,13 @@ impl IoApic { phys_base, mmio_guard, }); - kdebug!("IOAPIC: to mask all RTE"); + debug!("IOAPIC: to mask all RTE"); // 屏蔽所有的RTE let res_mut = result.as_mut().unwrap(); for i in 0..res_mut.supported_interrupts() { res_mut.write_rte(i, 0x20 + i, RedirectionEntry::DISABLED, 0); } - kdebug!("Ioapic init done"); + debug!("Ioapic init done"); }); assert!( @@ -393,7 +393,7 @@ impl InnerIoApicChipData { #[inline(never)] pub fn ioapic_init(ignore: &'static [IrqNumber]) { - kinfo!("Initializing ioapic..."); + info!("Initializing ioapic..."); let ioapic = unsafe { IoApic::new() }; unsafe { __IOAPIC = Some(SpinLock::new(ioapic)); @@ -424,7 +424,7 @@ pub fn ioapic_init(ignore: &'static [IrqNumber]) { register_handler(&desc, level); } - kinfo!("IO Apic initialized."); + info!("IO Apic initialized."); } fn register_handler(desc: &Arc, level_triggered: bool) { diff --git a/kernel/src/arch/x86_64/driver/apic/lapic_vector.rs b/kernel/src/arch/x86_64/driver/apic/lapic_vector.rs index 74a1ee79d..8b46f0409 100644 --- a/kernel/src/arch/x86_64/driver/apic/lapic_vector.rs +++ b/kernel/src/arch/x86_64/driver/apic/lapic_vector.rs @@ -2,6 +2,7 @@ use core::intrinsics::unlikely; use alloc::{string::ToString, sync::Arc}; use intertrait::CastFrom; +use log::warn; use system_error::SystemError; use crate::{ @@ -25,7 +26,6 @@ use crate::{ msi::MsiMsg, HardwareIrqNumber, IrqNumber, }, - kwarn, libs::spinlock::{SpinLock, SpinLockGuard}, smp::{core::smp_get_processor_id, cpu::ProcessorId}, }; @@ -179,6 +179,7 @@ bitflags! { } } +#[allow(dead_code)] pub(super) fn irq_msi_compose_msg(cfg: &HardwareIrqConfig, msg: &mut MsiMsg, dmar: bool) { *msg = MsiMsg::new_zeroed(); @@ -206,7 +207,7 @@ pub(super) fn irq_msi_compose_msg(cfg: &HardwareIrqConfig, msg: &mut MsiMsg, dma // 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/arch/x86/kernel/apic/apic.c?fi=__irq_msi_compose_msg#2580 address_lo.set_virt_destid_8_14(cfg.apic_id.data() >> 8); } else if unlikely(cfg.apic_id.data() > 0xff) { - kwarn!( + warn!( "irq_msi_compose_msg: Invalid APIC ID: {}", cfg.apic_id.data() ); @@ -252,7 +253,7 @@ pub fn arch_early_irq_init() -> Result<(), SystemError> { // todo: add vector matrix // 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/arch/x86/kernel/apic/vector.c#803 - kwarn!("arch_early_irq_init: todo: add vector matrix"); + warn!("arch_early_irq_init: todo: add vector matrix"); local_apic_timer_irq_desc_init(); arch_ipi_handler_init(); diff --git a/kernel/src/arch/x86_64/driver/apic/mod.rs b/kernel/src/arch/x86_64/driver/apic/mod.rs index ae78415b4..af89dedfb 100644 --- a/kernel/src/arch/x86_64/driver/apic/mod.rs +++ b/kernel/src/arch/x86_64/driver/apic/mod.rs @@ -1,6 +1,7 @@ use core::sync::atomic::Ordering; use atomic_enum::atomic_enum; +use log::{debug, info}; use system_error::SystemError; use x86::{apic::Icr, msr::IA32_APIC_BASE}; @@ -10,7 +11,6 @@ use crate::{ io::PortIOArch, CurrentPortIOArch, }, - kdebug, kinfo, mm::PhysAddr, smp::core::smp_get_processor_id, }; @@ -468,7 +468,7 @@ impl CurrentApic { CurrentPortIOArch::out8(0x20, 0x20); CurrentPortIOArch::out8(0xa0, 0x20); - kdebug!("8259A Masked."); + debug!("8259A Masked."); // enable IMCR CurrentPortIOArch::out8(0x22, 0x70); @@ -488,14 +488,14 @@ impl LocalAPIC for CurrentApic { self.mask8259a(); } } - kinfo!("Initializing apic for cpu {:?}", cpu_id); + info!("Initializing apic for cpu {:?}", cpu_id); if X2Apic::support() && X2Apic.init_current_cpu() { if cpu_id.data() == 0 { LOCAL_APIC_ENABLE_TYPE.store(LocalApicEnableType::X2Apic, Ordering::SeqCst); } - kinfo!("x2APIC initialized for cpu {:?}", cpu_id); + info!("x2APIC initialized for cpu {:?}", cpu_id); } else { - kinfo!("x2APIC not supported or failed to initialize, fallback to xAPIC."); + info!("x2APIC not supported or failed to initialize, fallback to xAPIC."); if cpu_id.data() == 0 { LOCAL_APIC_ENABLE_TYPE.store(LocalApicEnableType::XApic, Ordering::SeqCst); } @@ -514,10 +514,10 @@ impl LocalAPIC for CurrentApic { xapic.init_current_cpu(); } - kinfo!("xAPIC initialized for cpu {:?}", cpu_id); + info!("xAPIC initialized for cpu {:?}", cpu_id); } - kinfo!("Apic initialized."); + info!("Apic initialized."); return true; } diff --git a/kernel/src/arch/x86_64/driver/apic/x2apic.rs b/kernel/src/arch/x86_64/driver/apic/x2apic.rs index 7718cb45d..e34715eae 100644 --- a/kernel/src/arch/x86_64/driver/apic/x2apic.rs +++ b/kernel/src/arch/x86_64/driver/apic/x2apic.rs @@ -1,12 +1,11 @@ use core::sync::atomic::{fence, Ordering}; +use log::info; use x86::msr::{ rdmsr, wrmsr, IA32_APIC_BASE, IA32_X2APIC_APICID, IA32_X2APIC_EOI, IA32_X2APIC_SIVR, IA32_X2APIC_VERSION, }; -use crate::kinfo; - use super::{hw_irq::ApicId, LVTRegister, LocalAPIC, LVT}; #[derive(Debug)] @@ -45,19 +44,19 @@ impl LocalAPIC for X2Apic { (rdmsr(IA32_X2APIC_SIVR) & 0x100) == 0x100, "x2APIC software enable failed." ); - kinfo!("x2APIC software enabled."); + info!("x2APIC software enabled."); if self.support_eoi_broadcast_suppression() { assert!( (rdmsr(IA32_X2APIC_SIVR) & 0x1000) == 0x1000, "x2APIC EOI broadcast suppression enable failed." ); - kinfo!("x2APIC EOI broadcast suppression enabled."); + info!("x2APIC EOI broadcast suppression enabled."); } } - // kdebug!("x2apic: to mask all lvt"); + // debug!("x2apic: to mask all lvt"); self.mask_all_lvt(); - // kdebug!("x2apic: all lvt masked"); + // debug!("x2apic: all lvt masked"); } true } diff --git a/kernel/src/arch/x86_64/driver/apic/xapic.rs b/kernel/src/arch/x86_64/driver/apic/xapic.rs index 49211e0c2..f764d1df8 100644 --- a/kernel/src/arch/x86_64/driver/apic/xapic.rs +++ b/kernel/src/arch/x86_64/driver/apic/xapic.rs @@ -4,8 +4,9 @@ use core::{ ptr::{read_volatile, write_volatile}, }; +use log::{debug, error, info}; + use crate::{ - kdebug, kerror, kinfo, mm::{ mmio_buddy::{mmio_pool, MMIOSpaceGuard}, percpu::PerCpu, @@ -157,7 +158,7 @@ impl XApic { g.map_phys(paddr, 4096).expect("Fail to map MMIO for XAPIC"); let addr = g.vaddr() + offset; - kdebug!( + debug!( "XAPIC: {:#x} -> {:#x}, offset={offset}", xapic_base.data(), addr.data() @@ -219,7 +220,7 @@ impl LocalAPIC for XApic { x86::msr::wrmsr(x86::msr::APIC_BASE, (self.xapic_base.data() | 0x800) as u64); let val = x86::msr::rdmsr(x86::msr::APIC_BASE); if val & 0x800 != 0x800 { - kerror!("xAPIC enable failed: APIC_BASE & 0x800 != 0x800"); + error!("xAPIC enable failed: APIC_BASE & 0x800 != 0x800"); return false; } // 设置 Spurious Interrupt Vector Register @@ -229,15 +230,15 @@ impl LocalAPIC for XApic { let val = self.read(XApicOffset::LOCAL_APIC_OFFSET_Local_APIC_SVR); if val & ENABLE == 0 { - kerror!("xAPIC software enable failed."); + error!("xAPIC software enable failed."); return false; } else { - kinfo!("xAPIC software enabled."); + info!("xAPIC software enabled."); } if val & 0x1000 != 0 { - kinfo!("xAPIC EOI broadcast suppression enabled."); + info!("xAPIC EOI broadcast suppression enabled."); } self.mask_all_lvt(); diff --git a/kernel/src/arch/x86_64/driver/hpet.rs b/kernel/src/arch/x86_64/driver/hpet.rs index 36202dbf1..89a7d5da1 100644 --- a/kernel/src/arch/x86_64/driver/hpet.rs +++ b/kernel/src/arch/x86_64/driver/hpet.rs @@ -7,6 +7,7 @@ use core::{ use acpi::HpetInfo; use alloc::{string::ToString, sync::Arc}; +use log::{debug, error, info}; use system_error::SystemError; use crate::{ @@ -21,7 +22,6 @@ use crate::{ manage::irq_manager, InterruptArch, IrqNumber, }, - kdebug, kerror, kinfo, libs::{ rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, volatile::volwrite, @@ -30,10 +30,7 @@ use crate::{ mmio_buddy::{mmio_pool, MMIOSpaceGuard}, PhysAddr, }, - time::{ - jiffies::NSEC_PER_JIFFY, - timer::{try_raise_timer_softirq, update_timer_jiffies}, - }, + time::jiffies::NSEC_PER_JIFFY, }; static mut HPET_INSTANCE: Option = None; @@ -80,8 +77,8 @@ impl Hpet { .unwrap() }; let tm_num = hpet.timers_num(); - kdebug!("HPET0_INTERVAL_USEC: {}", Self::HPET0_INTERVAL_USEC); - kinfo!("HPET has {} timers", tm_num); + debug!("HPET0_INTERVAL_USEC: {}", Self::HPET0_INTERVAL_USEC); + info!("HPET has {} timers", tm_num); hpet_info.hpet_number = tm_num as u8; drop(mmio); @@ -124,10 +121,10 @@ impl Hpet { // !!!这里是临时糊代码的,需要在apic重构的时候修改!!! let (inner_guard, regs) = unsafe { self.hpet_regs_mut() }; let freq = regs.frequency(); - kdebug!("HPET frequency: {} Hz", freq); + debug!("HPET frequency: {} Hz", freq); let ticks = Self::HPET0_INTERVAL_USEC * freq / 1000000; if ticks == 0 || ticks > freq * 8 { - kerror!("HPET enable: ticks '{ticks}' is invalid"); + error!("HPET enable: ticks '{ticks}' is invalid"); return Err(SystemError::EINVAL); } if unlikely(regs.timers_num() == 0) { @@ -166,7 +163,7 @@ impl Hpet { drop(inner_guard); - kinfo!("HPET enabled"); + info!("HPET enabled"); drop(irq_guard); return Ok(()); @@ -239,7 +236,7 @@ impl Hpet { pub fn period(&self) -> u64 { let (inner_guard, regs) = unsafe { self.hpet_regs() }; let period = regs.counter_clock_period(); - kdebug!("HPET period: {}", period); + debug!("HPET period: {}", period); drop(inner_guard); return period; @@ -249,9 +246,6 @@ impl Hpet { pub(super) fn handle_irq(&self, timer_num: u32) { if timer_num == 0 { assert!(!CurrentIrqArch::is_irq_enabled()); - update_timer_jiffies(1, Self::HPET0_INTERVAL_USEC as i64); - - try_raise_timer_softirq(); } } } diff --git a/kernel/src/arch/x86_64/driver/rtc.rs b/kernel/src/arch/x86_64/driver/rtc.rs index 0612038e1..bb6486a11 100644 --- a/kernel/src/arch/x86_64/driver/rtc.rs +++ b/kernel/src/arch/x86_64/driver/rtc.rs @@ -4,6 +4,7 @@ use alloc::{ string::{String, ToString}, sync::{Arc, Weak}, }; +use log::error; use system_error::SystemError; use unified_init::macros::unified_init; @@ -25,7 +26,6 @@ use crate::{ exception::InterruptArch, filesystem::kernfs::KernFSInode, init::initcall::INITCALL_DEVICE, - kerror, libs::{ mutex::Mutex, rwlock::{RwLockReadGuard, RwLockWriteGuard}, @@ -286,7 +286,7 @@ impl RtcClassOps for CmosRtcClassOps { } fn set_time(&self, _dev: &Arc, _time: &RtcTime) -> Result<(), SystemError> { - kerror!("set_time is not implemented for CmosRtcClassOps"); + error!("set_time is not implemented for CmosRtcClassOps"); Err(SystemError::ENOSYS) } } diff --git a/kernel/src/arch/x86_64/driver/tsc.rs b/kernel/src/arch/x86_64/driver/tsc.rs index f1aab65dc..ef97ec6a9 100644 --- a/kernel/src/arch/x86_64/driver/tsc.rs +++ b/kernel/src/arch/x86_64/driver/tsc.rs @@ -2,13 +2,13 @@ use crate::{ arch::{io::PortIOArch, CurrentIrqArch, CurrentPortIOArch, CurrentTimeArch}, driver::acpi::pmtmr::{acpi_pm_read_early, ACPI_PM_OVERRUN, PMTMR_TICKS_PER_SEC}, exception::InterruptArch, - kdebug, kerror, kinfo, kwarn, time::{TimeArch, PIT_TICK_RATE}, }; use core::{ cmp::{max, min}, intrinsics::unlikely, }; +use log::{debug, error, info, warn}; use system_error::SystemError; use super::hpet::{hpet_instance, is_hpet_enabled}; @@ -31,13 +31,13 @@ impl TSCManager { let cpuid = x86::cpuid::CpuId::new(); let feat = cpuid.get_feature_info().ok_or(SystemError::ENODEV)?; if !feat.has_tsc() { - kerror!("TSC is not available"); + error!("TSC is not available"); return Err(SystemError::ENODEV); } if unsafe { TSC_KHZ == 0 } { if let Err(e) = Self::determine_cpu_tsc_frequency(false) { - kerror!("Failed to determine CPU TSC frequency: {:?}", e); + error!("Failed to determine CPU TSC frequency: {:?}", e); // todo: mark TSC as unstable clock source return Err(e); } @@ -57,7 +57,7 @@ impl TSCManager { /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#1438 fn determine_cpu_tsc_frequency(early: bool) -> Result<(), SystemError> { if unlikely(Self::cpu_khz() != 0 || Self::tsc_khz() != 0) { - kwarn!("TSC and CPU frequency already determined"); + warn!("TSC and CPU frequency already determined"); } if early { @@ -79,16 +79,16 @@ impl TSCManager { } if Self::cpu_khz() == 0 { - kerror!("Failed to determine CPU frequency"); + error!("Failed to determine CPU frequency"); return Err(SystemError::ENODEV); } - kinfo!( + info!( "Detected {}.{} MHz processor", Self::cpu_khz() / 1000, Self::cpu_khz() % 1000 ); - kinfo!( + info!( "Detected {}.{} MHz TSC", Self::tsc_khz() / 1000, Self::tsc_khz() % 1000 @@ -102,7 +102,7 @@ impl TSCManager { /// 使用pit、hpet、ptimer来测量CPU总线的频率 fn calibrate_cpu_by_pit_hpet_ptimer() -> Result { let hpet = is_hpet_enabled(); - kdebug!( + debug!( "Calibrating TSC with {}", if hpet { "HPET" } else { "PMTIMER" } ); @@ -143,7 +143,7 @@ impl TSCManager { // HPET或者PTIMER可能是不可用的 if ref1 == ref2 { - kdebug!("HPET/PMTIMER not available"); + debug!("HPET/PMTIMER not available"); continue; } @@ -169,7 +169,7 @@ impl TSCManager { // 如果误差在10%以内,那么认为测量成功 // 返回参考值,因为它是更精确的 if (90..=110).contains(&delta) { - kinfo!( + info!( "PIT calibration matches {}. {} loops", if hpet { "HPET" } else { "PMTIMER" }, i + 1 @@ -185,20 +185,20 @@ impl TSCManager { } if tsc_pit_min == u64::MAX { - kwarn!("Unable to calibrate against PIT"); + warn!("Unable to calibrate against PIT"); // 如果没有参考值,那么禁用tsc if (!hpet) && (global_ref1 == 0) && (global_ref2 == 0) { - kwarn!("No reference (HPET/PMTIMER) available"); + warn!("No reference (HPET/PMTIMER) available"); return Err(SystemError::ENODEV); } if tsc_ref_min == u64::MAX { - kwarn!("Unable to calibrate against HPET/PMTIMER"); + warn!("Unable to calibrate against HPET/PMTIMER"); return Err(SystemError::ENODEV); } - kinfo!( + info!( "Using {} reference calibration", if hpet { "HPET" } else { "PMTIMER" } ); @@ -207,27 +207,27 @@ impl TSCManager { // We don't have an alternative source, use the PIT calibration value if (!hpet) && (global_ref1 == 0) && (global_ref2 == 0) { - kinfo!("Using PIT calibration value"); + info!("Using PIT calibration value"); return Ok(tsc_pit_min); } // The alternative source failed, use the PIT calibration value if tsc_ref_min == u64::MAX { - kwarn!("Unable to calibrate against HPET/PMTIMER, using PIT calibration value"); + warn!("Unable to calibrate against HPET/PMTIMER, using PIT calibration value"); return Ok(tsc_pit_min); } // The calibration values differ too much. In doubt, we use // the PIT value as we know that there are PMTIMERs around // running at double speed. At least we let the user know: - kwarn!( + warn!( "PIT calibration deviates from {}: tsc_pit_min={}, tsc_ref_min={}", if hpet { "HPET" } else { "PMTIMER" }, tsc_pit_min, tsc_ref_min ); - kinfo!("Using PIT calibration value"); + info!("Using PIT calibration value"); return Ok(tsc_pit_min); } @@ -326,7 +326,7 @@ impl TSCManager { } } - kwarn!("TSCManager: Failed to read reference value, tsc delta too high"); + warn!("TSCManager: Failed to read reference value, tsc delta too high"); return (u64::MAX, ref_ret); } diff --git a/kernel/src/arch/x86_64/init/mod.rs b/kernel/src/arch/x86_64/init/mod.rs index 6f392d3a1..c1ae2d8d7 100644 --- a/kernel/src/arch/x86_64/init/mod.rs +++ b/kernel/src/arch/x86_64/init/mod.rs @@ -1,5 +1,6 @@ use core::sync::atomic::{compiler_fence, Ordering}; +use log::debug; use system_error::SystemError; use x86::dtables::DescriptorTablePointer; @@ -7,7 +8,6 @@ use crate::{ arch::{interrupt::trap::arch_trap_init, process::table::TSSManager}, driver::clocksource::acpi_pm::init_acpi_pm_clocksource, init::init::start_kernel, - kdebug, mm::{MemoryManagementArch, PhysAddr}, }; @@ -35,6 +35,7 @@ extern "C" { } #[no_mangle] +#[allow(static_mut_refs)] unsafe extern "C" fn kernel_main( mb2_info: u64, mb2_magic: u64, @@ -66,16 +67,17 @@ unsafe extern "C" fn kernel_main( /// 在内存管理初始化之前的架构相关的早期初始化 #[inline(never)] +#[allow(static_mut_refs)] pub fn early_setup_arch() -> Result<(), SystemError> { let stack_start = unsafe { *(head_stack_start as *const u64) } as usize; - kdebug!("head_stack_start={:#x}\n", stack_start); + debug!("head_stack_start={:#x}\n", stack_start); unsafe { let gdt_vaddr = MMArch::phys_2_virt(PhysAddr::new(&GDT_Table as *const usize as usize)).unwrap(); let idt_vaddr = MMArch::phys_2_virt(PhysAddr::new(&IDT_Table as *const usize as usize)).unwrap(); - kdebug!("GDT_Table={:?}, IDT_Table={:?}\n", gdt_vaddr, idt_vaddr); + debug!("GDT_Table={:?}, IDT_Table={:?}\n", gdt_vaddr, idt_vaddr); } set_current_core_tss(stack_start, 0); @@ -107,10 +109,9 @@ pub fn setup_arch_post() -> Result<(), SystemError> { fn set_current_core_tss(stack_start: usize, ist0: usize) { let current_tss = unsafe { TSSManager::current_tss() }; - kdebug!( + debug!( "set_current_core_tss: stack_start={:#x}, ist0={:#x}\n", - stack_start, - ist0 + stack_start, ist0 ); current_tss.set_rsp(x86::Ring::Ring0, stack_start as u64); current_tss.set_ist(0, ist0 as u64); diff --git a/kernel/src/arch/x86_64/interrupt/entry.rs b/kernel/src/arch/x86_64/interrupt/entry.rs index da0921927..3d8637a31 100644 --- a/kernel/src/arch/x86_64/interrupt/entry.rs +++ b/kernel/src/arch/x86_64/interrupt/entry.rs @@ -564,6 +564,7 @@ pub unsafe fn set_system_trap_gate(irq: u32, ist: u8, vaddr: VirtAddr) { set_gate(idt_entry, 0xEF, ist, vaddr); } +#[allow(static_mut_refs)] unsafe fn get_idt_entry(irq: u32) -> &'static mut [u64] { assert!(irq < 256); let mut idt_vaddr = diff --git a/kernel/src/arch/x86_64/interrupt/ipi.rs b/kernel/src/arch/x86_64/interrupt/ipi.rs index 87cc785d9..78c6ae780 100644 --- a/kernel/src/arch/x86_64/interrupt/ipi.rs +++ b/kernel/src/arch/x86_64/interrupt/ipi.rs @@ -1,4 +1,5 @@ use alloc::sync::Arc; +use log::error; use system_error::SystemError; use x86::apic::ApicId; @@ -13,7 +14,6 @@ use crate::{ irqdesc::{irq_desc_manager, IrqDesc, IrqFlowHandler, IrqHandler}, HardwareIrqNumber, IrqNumber, }, - kerror, smp::cpu::ProcessorId, }; @@ -122,14 +122,14 @@ impl From for x86::apic::DestinationShorthand { #[inline(always)] pub fn send_ipi(kind: IpiKind, target: IpiTarget) { - // kdebug!("send_ipi: {:?} {:?}", kind, target); + // debug!("send_ipi: {:?} {:?}", kind, target); let ipi_vec = ArchIpiKind::from(kind).into(); let target = ArchIpiTarget::from(target); let shorthand: x86::apic::DestinationShorthand = target.into(); let destination: x86::apic::ApicId = target.into(); let icr = if CurrentApic.x2apic_enabled() { - // kdebug!("send_ipi: x2apic"); + // debug!("send_ipi: x2apic"); x86::apic::Icr::for_x2apic( ipi_vec, destination, @@ -141,7 +141,7 @@ pub fn send_ipi(kind: IpiKind, target: IpiTarget) { x86::apic::TriggerMode::Edge, ) } else { - // kdebug!("send_ipi: xapic"); + // debug!("send_ipi: xapic"); x86::apic::Icr::for_xapic( ipi_vec, destination, @@ -257,7 +257,7 @@ impl IrqFlowHandler for X86_64IpiIrqFlowHandler { CurrentApic.send_eoi(); } _ => { - kerror!("Unknown IPI: {}", irq.data()); + error!("Unknown IPI: {}", irq.data()); CurrentApic.send_eoi(); } } diff --git a/kernel/src/arch/x86_64/interrupt/mod.rs b/kernel/src/arch/x86_64/interrupt/mod.rs index ddd7db57d..0eb4a88a8 100644 --- a/kernel/src/arch/x86_64/interrupt/mod.rs +++ b/kernel/src/arch/x86_64/interrupt/mod.rs @@ -9,12 +9,12 @@ use core::{ sync::atomic::{compiler_fence, Ordering}, }; +use log::error; use system_error::SystemError; use crate::{ arch::CurrentIrqArch, exception::{InterruptArch, IrqFlags, IrqFlagsGuard, IrqNumber}, - kerror, }; use super::{ @@ -85,7 +85,7 @@ impl InterruptArch for X86_64InterruptArch { } fn ack_bad_irq(irq: IrqNumber) { - kerror!("Unexpected IRQ trap at vector {}", irq.data()); + error!("Unexpected IRQ trap at vector {}", irq.data()); CurrentApic.send_eoi(); } @@ -132,6 +132,12 @@ pub struct TrapFrame { pub ss: ::core::ffi::c_ulong, } +impl Default for TrapFrame { + fn default() -> Self { + Self::new() + } +} + impl TrapFrame { pub fn new() -> Self { Self { diff --git a/kernel/src/arch/x86_64/interrupt/msi.rs b/kernel/src/arch/x86_64/interrupt/msi.rs index 34d6c4207..84c73918b 100644 --- a/kernel/src/arch/x86_64/interrupt/msi.rs +++ b/kernel/src/arch/x86_64/interrupt/msi.rs @@ -26,6 +26,7 @@ pub struct X86MsiDataNormal { } #[derive(Debug)] +#[allow(dead_code)] pub struct X86MsiDataDmar { pub dmar_subhandle: u32, } diff --git a/kernel/src/arch/x86_64/interrupt/trap.rs b/kernel/src/arch/x86_64/interrupt/trap.rs index a75c73717..9cd31fa9b 100644 --- a/kernel/src/arch/x86_64/interrupt/trap.rs +++ b/kernel/src/arch/x86_64/interrupt/trap.rs @@ -1,9 +1,9 @@ +use log::{error, warn}; use system_error::SystemError; use crate::{ arch::{CurrentIrqArch, MMArch}, exception::InterruptArch, - kerror, kwarn, mm::VirtAddr, process::ProcessManager, smp::core::smp_get_processor_id, @@ -112,7 +112,7 @@ pub fn arch_trap_init() -> Result<(), SystemError> { /// 处理除法错误 0 #DE #[no_mangle] unsafe extern "C" fn do_divide_error(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_divide_error(0), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -126,7 +126,7 @@ unsafe extern "C" fn do_divide_error(regs: &'static TrapFrame, error_code: u64) /// 处理调试异常 1 #DB #[no_mangle] unsafe extern "C" fn do_debug(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_debug(1), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -140,7 +140,7 @@ unsafe extern "C" fn do_debug(regs: &'static TrapFrame, error_code: u64) { /// 处理NMI中断 2 NMI #[no_mangle] unsafe extern "C" fn do_nmi(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_nmi(2), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -154,7 +154,7 @@ unsafe extern "C" fn do_nmi(regs: &'static TrapFrame, error_code: u64) { /// 处理断点异常 3 #BP #[no_mangle] unsafe extern "C" fn do_int3(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_int3(3), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -168,7 +168,7 @@ unsafe extern "C" fn do_int3(regs: &'static TrapFrame, error_code: u64) { /// 处理溢出异常 4 #OF #[no_mangle] unsafe extern "C" fn do_overflow(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_overflow(4), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -182,7 +182,7 @@ unsafe extern "C" fn do_overflow(regs: &'static TrapFrame, error_code: u64) { /// 处理BOUND指令检查异常 5 #BR #[no_mangle] unsafe extern "C" fn do_bounds(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_bounds(5), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -196,7 +196,7 @@ unsafe extern "C" fn do_bounds(regs: &'static TrapFrame, error_code: u64) { /// 处理未定义操作码异常 6 #UD #[no_mangle] unsafe extern "C" fn do_undefined_opcode(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_undefined_opcode(6), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -210,7 +210,7 @@ unsafe extern "C" fn do_undefined_opcode(regs: &'static TrapFrame, error_code: u /// 处理设备不可用异常(FPU不存在) 7 #NM #[no_mangle] unsafe extern "C" fn do_dev_not_avaliable(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_dev_not_avaliable(7), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -224,7 +224,7 @@ unsafe extern "C" fn do_dev_not_avaliable(regs: &'static TrapFrame, error_code: /// 处理双重错误 8 #DF #[no_mangle] unsafe extern "C" fn do_double_fault(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_double_fault(8), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -238,7 +238,7 @@ unsafe extern "C" fn do_double_fault(regs: &'static TrapFrame, error_code: u64) /// 处理协处理器段越界 9 #MF #[no_mangle] unsafe extern "C" fn do_coprocessor_segment_overrun(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_coprocessor_segment_overrun(9), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -272,7 +272,7 @@ unsafe extern "C" fn do_invalid_TSS(regs: &'static TrapFrame, error_code: u64) { ERR_MSG_4 }; - kerror!( + error!( "do_invalid_TSS(10), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}\n{}{}", error_code, regs.rsp, @@ -288,7 +288,7 @@ unsafe extern "C" fn do_invalid_TSS(regs: &'static TrapFrame, error_code: u64) { /// 处理段不存在 11 #NP #[no_mangle] unsafe extern "C" fn do_segment_not_exists(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_segment_not_exists(11), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -302,7 +302,7 @@ unsafe extern "C" fn do_segment_not_exists(regs: &'static TrapFrame, error_code: /// 处理栈段错误 12 #SS #[no_mangle] unsafe extern "C" fn do_stack_segment_fault(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_stack_segment_fault(12), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -343,7 +343,7 @@ unsafe extern "C" fn do_general_protection(regs: &'static TrapFrame, error_code: } else { "" }; - kerror!( + error!( "do_general_protection(13), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t rflags: {:#x}\t CPU: {}, \tpid: {:?} {}{}{} Segment Selector Index: {:#x}\n @@ -363,7 +363,7 @@ Segment Selector Index: {:#x}\n /// 处理页错误 14 #PF #[no_mangle] unsafe extern "C" fn do_page_fault(regs: &'static TrapFrame, error_code: u64) { - // kerror!( + // error!( // "do_page_fault(14), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}, \nFault Address: {:#x}", // error_code, // regs.rsp, @@ -401,7 +401,7 @@ unsafe extern "C" fn do_page_fault(regs: &'static TrapFrame, error_code: u64) { // panic!("Page Fault"); CurrentIrqArch::interrupt_disable(); let address = x86::controlregs::cr2(); - // crate::kinfo!( + // log::info!( // "fault address: {:#x}, error_code: {:#b}, pid: {}\n", // address, // error_code, @@ -421,7 +421,7 @@ unsafe extern "C" fn do_page_fault(regs: &'static TrapFrame, error_code: u64) { /// 处理x87 FPU错误 16 #MF #[no_mangle] unsafe extern "C" fn do_x87_FPU_error(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_x87_FPU_error(16), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -435,7 +435,7 @@ unsafe extern "C" fn do_x87_FPU_error(regs: &'static TrapFrame, error_code: u64) /// 处理对齐检查 17 #AC #[no_mangle] unsafe extern "C" fn do_alignment_check(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_alignment_check(17), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -449,7 +449,7 @@ unsafe extern "C" fn do_alignment_check(regs: &'static TrapFrame, error_code: u6 /// 处理机器检查 18 #MC #[no_mangle] unsafe extern "C" fn do_machine_check(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_machine_check(18), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -463,7 +463,7 @@ unsafe extern "C" fn do_machine_check(regs: &'static TrapFrame, error_code: u64) /// 处理SIMD异常 19 #XM #[no_mangle] unsafe extern "C" fn do_SIMD_exception(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_SIMD_exception(19), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -477,7 +477,7 @@ unsafe extern "C" fn do_SIMD_exception(regs: &'static TrapFrame, error_code: u64 /// 处理虚拟化异常 20 #VE #[no_mangle] unsafe extern "C" fn do_virtualization_exception(regs: &'static TrapFrame, error_code: u64) { - kerror!( + error!( "do_virtualization_exception(20), \tError code: {:#x},\trsp: {:#x},\trip: {:#x},\t CPU: {}, \tpid: {:?}", error_code, regs.rsp, @@ -490,5 +490,5 @@ unsafe extern "C" fn do_virtualization_exception(regs: &'static TrapFrame, error #[no_mangle] unsafe extern "C" fn ignore_int_handler(_regs: &'static TrapFrame, _error_code: u64) { - kwarn!("Unknown interrupt."); + warn!("Unknown interrupt."); } diff --git a/kernel/src/arch/x86_64/ipc/signal.rs b/kernel/src/arch/x86_64/ipc/signal.rs index af4c60269..c9d0af99e 100644 --- a/kernel/src/arch/x86_64/ipc/signal.rs +++ b/kernel/src/arch/x86_64/ipc/signal.rs @@ -1,5 +1,6 @@ use core::{ffi::c_void, intrinsics::unlikely, mem::size_of}; +use log::error; use system_error::SystemError; use crate::{ @@ -14,7 +15,6 @@ use crate::{ signal::set_current_sig_blocked, signal_types::{SaHandlerType, SigInfo, Sigaction, SigactionType, SignalArch}, }, - kerror, mm::MemoryManagementArch, process::ProcessManager, sched::{schedule, SchedMode}, @@ -86,7 +86,7 @@ impl From for Signal { let ret: Signal = unsafe { core::mem::transmute(value) }; return ret; } else { - kerror!("Try to convert an invalid number to Signal"); + error!("Try to convert an invalid number to Signal"); return Signal::INVALID; } } @@ -101,7 +101,7 @@ impl From for usize { impl From for Signal { fn from(value: i32) -> Self { if value < 0 { - kerror!("Try to convert an invalid number to Signal"); + error!("Try to convert an invalid number to Signal"); return Signal::INVALID; } else { return Self::from(value as usize); @@ -145,7 +145,7 @@ impl Signal { pub fn handle_default(&self) { match self { Signal::INVALID => { - kerror!("attempting to handler an Invalid"); + error!("attempting to handler an Invalid"); } Signal::SIGHUP => sig_terminate(*self), Signal::SIGINT => sig_terminate(*self), @@ -396,6 +396,7 @@ impl SigContext { } } /// @brief 信号处理备用栈的信息 +#[allow(dead_code)] #[derive(Debug, Clone, Copy)] pub struct SigStack { pub sp: *mut c_void, @@ -461,7 +462,7 @@ impl SignalArch for X86_64SignalArch { match sigaction.action() { SigactionType::SaHandler(action_type) => match action_type { SaHandlerType::Error => { - kerror!("Trying to handle a Sigerror on Process:{:?}", pcb.pid()); + error!("Trying to handle a Sigerror on Process:{:?}", pcb.pid()); return; } SaHandlerType::Default => { @@ -488,7 +489,7 @@ impl SignalArch for X86_64SignalArch { let res: Result = handle_signal(sig_number, &mut sigaction, &info.unwrap(), &oldset, frame); if res.is_err() { - kerror!( + error!( "Error occurred when handling signal: {}, pid={:?}, errcode={:?}", sig_number as i32, ProcessManager::current_pcb().pid(), @@ -502,7 +503,7 @@ impl SignalArch for X86_64SignalArch { // 如果当前的rsp不来自用户态,则认为产生了错误(或被SROP攻击) if UserBufferWriter::new(frame, size_of::(), true).is_err() { - kerror!("rsp doesn't from user level"); + error!("rsp doesn't from user level"); let _r = Syscall::kill(ProcessManager::current_pcb().pid(), Signal::SIGSEGV as i32) .map_err(|e| e.to_posix_errno()); return trap_frame.rax; @@ -511,7 +512,7 @@ impl SignalArch for X86_64SignalArch { set_current_sig_blocked(&mut sigmask); // 从用户栈恢复sigcontext if !unsafe { &mut (*frame).context }.restore_sigcontext(trap_frame) { - kerror!("unable to restore sigcontext"); + error!("unable to restore sigcontext"); let _r = Syscall::kill(ProcessManager::current_pcb().pid(), Signal::SIGSEGV as i32) .map_err(|e| e.to_posix_errno()); // 如果这里返回 err 值的话会丢失上一个系统调用的返回值 @@ -569,7 +570,7 @@ fn setup_frame( sig.handle_default(); return Ok(0); } else { - kerror!("attempting to execute a signal handler from kernel"); + error!("attempting to execute a signal handler from kernel"); sig.handle_default(); return Err(SystemError::EINVAL); } @@ -578,7 +579,7 @@ fn setup_frame( if sigaction.flags().contains(SigFlags::SA_RESTORER) { ret_code_ptr = sigaction.restorer().unwrap().data() as *mut c_void; } else { - kerror!( + error!( "pid-{:?} forgot to set SA_FLAG_RESTORER for signal {:?}", ProcessManager::current_pcb().pid(), sig as i32 @@ -588,12 +589,12 @@ fn setup_frame( Signal::SIGSEGV as i32, ); if r.is_err() { - kerror!("In setup_sigcontext: generate SIGSEGV signal failed"); + error!("In setup_sigcontext: generate SIGSEGV signal failed"); } return Err(SystemError::EINVAL); } if sigaction.restorer().is_none() { - kerror!( + error!( "restorer in process:{:?} is not defined", ProcessManager::current_pcb().pid() ); @@ -611,12 +612,12 @@ fn setup_frame( }, SigactionType::SaSigaction(_) => { //TODO 这里应该是可以恢复栈的,等后续来做 - kerror!("trying to recover from sigaction type instead of handler"); + error!("trying to recover from sigaction type instead of handler"); return Err(SystemError::EINVAL); } } let frame: *mut SigFrame = get_stack(trap_frame, size_of::()); - // kdebug!("frame=0x{:016x}", frame as usize); + // debug!("frame=0x{:016x}", frame as usize); // 要求这个frame的地址位于用户空间,因此进行校验 let r: Result, SystemError> = UserBufferWriter::new(frame, size_of::(), true); @@ -625,9 +626,9 @@ fn setup_frame( // todo: 生成一个sigsegv let r = Syscall::kill(ProcessManager::current_pcb().pid(), Signal::SIGSEGV as i32); if r.is_err() { - kerror!("In setup frame: generate SIGSEGV signal failed"); + error!("In setup frame: generate SIGSEGV signal failed"); } - kerror!("In setup frame: access check failed"); + error!("In setup frame: access check failed"); return Err(SystemError::EFAULT); } @@ -636,7 +637,7 @@ fn setup_frame( .map_err(|e| -> SystemError { let r = Syscall::kill(ProcessManager::current_pcb().pid(), Signal::SIGSEGV as i32); if r.is_err() { - kerror!("In copy_siginfo_to_user: generate SIGSEGV signal failed"); + error!("In copy_siginfo_to_user: generate SIGSEGV signal failed"); } return e; })?; @@ -650,7 +651,7 @@ fn setup_frame( .map_err(|e: SystemError| -> SystemError { let r = Syscall::kill(ProcessManager::current_pcb().pid(), Signal::SIGSEGV as i32); if r.is_err() { - kerror!("In setup_sigcontext: generate SIGSEGV signal failed"); + error!("In setup_sigcontext: generate SIGSEGV signal failed"); } return e; })? @@ -707,7 +708,7 @@ fn sig_terminate_dump(sig: Signal) { fn sig_stop(sig: Signal) { let guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_stop().unwrap_or_else(|e| { - kerror!( + error!( "sleep error :{:?},failed to sleep process :{:?}, with signal :{:?}", e, ProcessManager::current_pcb(), @@ -721,7 +722,7 @@ fn sig_stop(sig: Signal) { /// 信号默认处理函数——继续进程 fn sig_continue(sig: Signal) { ProcessManager::wakeup_stop(&ProcessManager::current_pcb()).unwrap_or_else(|_| { - kerror!( + error!( "Failed to wake up process pid = {:?} with signal :{:?}", ProcessManager::current_pcb().pid(), sig diff --git a/kernel/src/arch/x86_64/kvm/mod.rs b/kernel/src/arch/x86_64/kvm/mod.rs index c9b8b19aa..d5f6c07aa 100644 --- a/kernel/src/arch/x86_64/kvm/mod.rs +++ b/kernel/src/arch/x86_64/kvm/mod.rs @@ -2,13 +2,10 @@ use crate::arch::kvm::vmx::vmcs::VmcsFields; use crate::arch::kvm::vmx::vmx_asm_wrapper::{vmx_vmlaunch, vmx_vmread}; use crate::libs::mutex::Mutex; use crate::virt::kvm::vm; -use crate::{ - kdebug, - kerror, - // libs::spinlock::{SpinLock, SpinLockGuard}, -}; + use alloc::sync::Arc; use core::arch::asm; +use log::{debug, error}; use raw_cpuid::CpuId; use system_error::SystemError; // use crate::virt::kvm::guest_code; @@ -54,7 +51,7 @@ impl X86_64KVMArch { #[deny(clippy::match_single_binding)] pub fn kvm_arch_dev_ioctl(cmd: u32, _arg: usize) -> Result { - kerror!("unknown kvm ioctl cmd: {}", cmd); + error!("unknown kvm ioctl cmd: {}", cmd); return Err(SystemError::EINVAL); } @@ -74,7 +71,7 @@ impl X86_64KVMArch { Ok(_) => {} Err(e) => { let vmx_err = vmx_vmread(VmcsFields::VMEXIT_INSTR_ERR as u32).unwrap(); - kdebug!("vmlaunch failed: {:?}", vmx_err); + debug!("vmlaunch failed: {:?}", vmx_err); return Err(e); } } @@ -103,12 +100,12 @@ impl X86_64KVMArch { #[no_mangle] pub extern "C" fn guest_code() { - kdebug!("guest_code"); + debug!("guest_code"); loop { unsafe { asm!("mov rax, 0", "mov rcx, 0", "cpuid"); } unsafe { asm!("nop") }; - kdebug!("guest_code"); + debug!("guest_code"); } } diff --git a/kernel/src/arch/x86_64/kvm/vmx/ept.rs b/kernel/src/arch/x86_64/kvm/vmx/ept.rs index 032231902..838c1a159 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/ept.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/ept.rs @@ -1,7 +1,7 @@ use crate::arch::mm::LockedFrameAllocator; use crate::arch::mm::PageMapper; use crate::arch::MMArch; -use crate::mm::page::PageFlags; +use crate::mm::page::EntryFlags; use crate::mm::{PageTableKind, PhysAddr, VirtAddr}; use crate::smp::core::smp_get_processor_id; use crate::smp::cpu::AtomicProcessorId; @@ -92,7 +92,7 @@ impl EptMapper { &mut self, gpa: u64, hpa: u64, - flags: PageFlags, + flags: EntryFlags, ) -> Result<(), SystemError> { if self.readonly { return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); diff --git a/kernel/src/arch/x86_64/kvm/vmx/mmu.rs b/kernel/src/arch/x86_64/kvm/vmx/mmu.rs index 2c03c2383..c05ef9bb5 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/mmu.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/mmu.rs @@ -1,11 +1,11 @@ use crate::{ arch::kvm::vmx::ept::EptMapper, - kdebug, libs::mutex::Mutex, - mm::{page::PageFlags, syscall::ProtFlags}, + mm::{page::EntryFlags, syscall::ProtFlags}, virt::kvm::host_mem::{__gfn_to_pfn, kvm_vcpu_gfn_to_memslot, PAGE_MASK, PAGE_SHIFT}, }; use bitfield_struct::bitfield; +use log::debug; use system_error::SystemError; use super::{ @@ -105,7 +105,7 @@ fn tdp_page_fault( error_code: u32, prefault: bool, ) -> Result<(), SystemError> { - kdebug!("tdp_page_fault"); + debug!("tdp_page_fault"); let gfn = gpa >> PAGE_SHIFT; // 物理地址右移12位得到物理页框号(相对于虚拟机而言) // 分配缓存池,为了避免在运行时分配空间失败,这里提前分配/填充足额的空间 mmu_topup_memory_caches(vcpu)?; @@ -211,14 +211,14 @@ pub fn __direct_map( pfn: u64, _prefault: bool, ) -> Result { - kdebug!("gpa={}, pfn={}, root_hpa={:x}", gpa, pfn, vcpu.mmu.root_hpa); + debug!("gpa={}, pfn={}, root_hpa={:x}", gpa, pfn, vcpu.mmu.root_hpa); // 判断vcpu.mmu.root_hpa是否有效 if vcpu.mmu.root_hpa == 0 { return Err(SystemError::KVM_HVA_ERR_BAD); } // 把gpa映射到hpa let mut ept_mapper = EptMapper::lock(); - let page_flags = PageFlags::from_prot_flags(ProtFlags::from_bits_truncate(0x7_u64), false); + let page_flags = EntryFlags::from_prot_flags(ProtFlags::from_bits_truncate(0x7_u64), false); unsafe { assert!(ept_mapper.walk(gpa, pfn << PAGE_SHIFT, page_flags).is_ok()); } diff --git a/kernel/src/arch/x86_64/kvm/vmx/vcpu.rs b/kernel/src/arch/x86_64/kvm/vmx/vcpu.rs index b718f5f6f..ae6f99baf 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/vcpu.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/vcpu.rs @@ -9,14 +9,15 @@ use crate::arch::kvm::vmx::{VcpuRegIndex, X86_CR0}; use crate::arch::mm::{LockedFrameAllocator, PageMapper}; use crate::arch::x86_64::mm::X86_64MMArch; use crate::arch::MMArch; -use crate::kdebug; -use crate::mm::{phys_2_virt, VirtAddr}; + use crate::mm::{MemoryManagementArch, PageTableKind}; +use crate::mm::{PhysAddr, VirtAddr}; use crate::virt::kvm::vcpu::Vcpu; use crate::virt::kvm::vm::Vm; use alloc::alloc::Global; use alloc::boxed::Box; use core::slice; +use log::debug; use raw_cpuid::CpuId; use system_error::SystemError; use x86; @@ -41,6 +42,7 @@ pub struct MSRBitmap { pub data: [u8; PAGE_SIZE], } +#[allow(dead_code)] #[derive(Debug)] pub struct VcpuData { /// The virtual and physical address of the Vmxon naturally aligned 4-KByte region of memory @@ -72,6 +74,7 @@ pub enum VcpuState { Act = 2, } +#[allow(dead_code)] #[derive(Debug)] pub struct VmxVcpu { pub vcpu_id: u32, @@ -132,13 +135,13 @@ impl VcpuData { // Get the Virtual Machine Control Structure revision identifier (VMCS revision ID) // (Intel Manual: 25.11.5 VMXON Region) let revision_id = unsafe { (msr::rdmsr(msr::IA32_VMX_BASIC) as u32) & 0x7FFF_FFFF }; - kdebug!("[+] VMXON Region Virtual Address: {:p}", self.vmxon_region); - kdebug!( + debug!("[+] VMXON Region Virtual Address: {:p}", self.vmxon_region); + debug!( "[+] VMXON Region Physical Addresss: 0x{:x}", self.vmxon_region_physical_address ); - kdebug!("[+] VMCS Region Virtual Address: {:p}", self.vmcs_region); - kdebug!( + debug!("[+] VMCS Region Virtual Address: {:p}", self.vmcs_region); + debug!( "[+] VMCS Region Physical Address1: 0x{:x}", self.vmcs_region_physical_address ); @@ -150,7 +153,7 @@ impl VcpuData { impl VmxVcpu { pub fn new(vcpu_id: u32, parent_vm: Vm) -> Result { - kdebug!("Creating processor {}", vcpu_id); + debug!("Creating processor {}", vcpu_id); let instance = Self { vcpu_id, vcpu_ctx: VcpuContextFrame { @@ -251,8 +254,8 @@ impl VmxVcpu { self.vcpu_ctx.regs[VcpuRegIndex::Rsp as usize] as u64, )?; vmx_vmwrite(VmcsFields::GUEST_RIP as u32, self.vcpu_ctx.rip as u64)?; - kdebug!("vmcs init guest rip: {:#x}", self.vcpu_ctx.rip as u64); - kdebug!( + debug!("vmcs init guest rip: {:#x}", self.vcpu_ctx.rip as u64); + debug!( "vmcs init guest rsp: {:#x}", self.vcpu_ctx.regs[VcpuRegIndex::Rsp as usize] as u64 ); @@ -317,13 +320,13 @@ impl VmxVcpu { )?; vmx_vmwrite( VmcsFields::HOST_GDTR_BASE as u32, - pseudo_descriptpr.base.to_bits() as u64, + pseudo_descriptpr.base as usize as u64, )?; vmx_vmwrite(VmcsFields::HOST_IDTR_BASE as u32, unsafe { let mut pseudo_descriptpr: x86::dtables::DescriptorTablePointer = Default::default(); x86::dtables::sidt(&mut pseudo_descriptpr); - pseudo_descriptpr.base.to_bits() as u64 + pseudo_descriptpr.base as usize as u64 })?; // fast entry into the kernel @@ -338,7 +341,7 @@ impl VmxVcpu { })?; // vmx_vmwrite(VmcsFields::HOST_RIP as u32, vmx_return as *const () as u64)?; - // kdebug!("vmcs init host rip: {:#x}", vmx_return as *const () as u64); + // debug!("vmcs init host rip: {:#x}", vmx_return as *const () as u64); Ok(()) } @@ -388,7 +391,7 @@ impl VmxVcpu { } fn kvm_mmu_load(&mut self) -> Result<(), SystemError> { - kdebug!("kvm_mmu_load!"); + debug!("kvm_mmu_load!"); // 申请并创建新的页表 let mapper: crate::mm::page::PageMapper = unsafe { PageMapper::create(PageTableKind::EPT, LockedFrameAllocator) @@ -399,7 +402,7 @@ impl VmxVcpu { let set_eptp_fn = self.mmu.set_eptp.unwrap(); set_eptp_fn(ept_root_hpa.data() as u64)?; self.mmu.root_hpa = ept_root_hpa.data() as u64; - kdebug!("ept_root_hpa:{:x}!", ept_root_hpa.data() as u64); + debug!("ept_root_hpa:{:x}!", ept_root_hpa.data() as u64); return Ok(()); } @@ -415,33 +418,33 @@ impl Vcpu for VmxVcpu { fn virtualize_cpu(&mut self) -> Result<(), SystemError> { match has_intel_vmx_support() { Ok(_) => { - kdebug!("[+] CPU supports Intel VMX"); + debug!("[+] CPU supports Intel VMX"); } Err(e) => { - kdebug!("[-] CPU does not support Intel VMX: {:?}", e); + debug!("[-] CPU does not support Intel VMX: {:?}", e); return Err(SystemError::ENOSYS); } }; match enable_vmx_operation() { Ok(_) => { - kdebug!("[+] Enabling Virtual Machine Extensions (VMX)"); + debug!("[+] Enabling Virtual Machine Extensions (VMX)"); } Err(_) => { - kdebug!("[-] VMX operation is not supported on this processor."); + debug!("[-] VMX operation is not supported on this processor."); return Err(SystemError::ENOSYS); } } vmxon(self.data.vmxon_region_physical_address)?; - kdebug!("[+] VMXON successful!"); + debug!("[+] VMXON successful!"); vmx_vmclear(self.data.vmcs_region_physical_address)?; vmx_vmptrld(self.data.vmcs_region_physical_address)?; - kdebug!("[+] VMPTRLD successful!"); + debug!("[+] VMPTRLD successful!"); self.vmcs_init().expect("vncs_init fail"); - kdebug!("[+] VMCS init!"); - // kdebug!("vmcs init host rip: {:#x}", vmx_return as *const () as u64); - // kdebug!("vmcs init host rsp: {:#x}", x86::bits64::registers::rsp()); + debug!("[+] VMCS init!"); + // debug!("vmcs init host rip: {:#x}", vmx_return as *const () as u64); + // debug!("vmcs init host rsp: {:#x}", x86::bits64::registers::rsp()); // vmx_vmwrite(VmcsFields::HOST_RSP as u32, x86::bits64::registers::rsp())?; // vmx_vmwrite(VmcsFields::HOST_RIP as u32, vmx_return as *const () as u64)?; // vmx_vmwrite(VmcsFields::HOST_RSP as u32, x86::bits64::registers::rsp())?; @@ -473,14 +476,9 @@ pub fn get_segment_base(gdt_base: *const u64, gdt_size: u16, segment_selector: u let base_mid = (descriptor & 0x0000_00FF_0000_0000) >> 16; let base_low = (descriptor & 0x0000_0000_FFFF_0000) >> 16; let segment_base = (base_high | base_mid | base_low) & 0xFFFFFFFF; - let virtaddr = phys_2_virt(segment_base.try_into().unwrap()) - .try_into() - .unwrap(); - kdebug!( - "segment_base={:x}", - phys_2_virt(segment_base.try_into().unwrap()) - ); - return virtaddr; + let virtaddr = unsafe { MMArch::phys_2_virt(PhysAddr::new(segment_base as usize)).unwrap() }; + + return virtaddr.data() as u64; } // FIXME: may have bug @@ -536,7 +534,7 @@ pub fn adjust_vmx_exit_controls() -> u32 { pub fn adjust_vmx_pinbased_controls() -> u32 { let mut controls: u32 = 16; adjust_vmx_controls(0, 0, msr::IA32_VMX_TRUE_PINBASED_CTLS, &mut controls); - // kdebug!("adjust_vmx_pinbased_controls: {:x}", controls); + // debug!("adjust_vmx_pinbased_controls: {:x}", controls); return controls; } @@ -593,11 +591,11 @@ pub fn enable_vmx_operation() -> Result<(), SystemError> { unsafe { controlregs::cr4_write(cr4) }; set_lock_bit()?; - kdebug!("[+] Lock bit set via IA32_FEATURE_CONTROL"); + debug!("[+] Lock bit set via IA32_FEATURE_CONTROL"); set_cr0_bits(); - kdebug!("[+] Mandatory bits in CR0 set/cleared"); + debug!("[+] Mandatory bits in CR0 set/cleared"); set_cr4_bits(); - kdebug!("[+] Mandatory bits in CR4 set/cleared"); + debug!("[+] Mandatory bits in CR4 set/cleared"); Ok(()) } diff --git a/kernel/src/arch/x86_64/kvm/vmx/vmexit.rs b/kernel/src/arch/x86_64/kvm/vmx/vmexit.rs index 32fb33727..b95d51dfb 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/vmexit.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/vmexit.rs @@ -1,8 +1,9 @@ use super::vmcs::{VmcsFields, VmxExitReason}; use super::vmx_asm_wrapper::{vmx_vmread, vmx_vmwrite}; -use crate::kdebug; + use crate::virt::kvm::vm; use core::arch::asm; +use log::debug; use system_error::SystemError; use x86::vmx::vmcs::ro::GUEST_PHYSICAL_ADDR_FULL; @@ -147,7 +148,7 @@ pub struct GuestCpuContext { #[no_mangle] pub extern "C" fn vmx_return() { - kdebug!("vmx_return!"); + debug!("vmx_return!"); unsafe { save_rpg() }; vmexit_handler(); // XMM registers are vector registers. They're renamed onto the FP/SIMD register file @@ -181,14 +182,14 @@ pub extern "C" fn vmx_return() { #[no_mangle] extern "C" fn vmexit_handler() { // let guest_cpu_context = unsafe { guest_cpu_context_ptr.as_mut().unwrap() }; - // kdebug!("guest_cpu_context_ptr={:p}",guest_cpu_context_ptr); - kdebug!("vmexit handler!"); + // debug!("guest_cpu_context_ptr={:p}",guest_cpu_context_ptr); + debug!("vmexit handler!"); let exit_reason = vmx_vmread(VmcsFields::VMEXIT_EXIT_REASON as u32).unwrap() as u32; let exit_basic_reason = exit_reason & 0x0000_ffff; let guest_rip = vmx_vmread(VmcsFields::GUEST_RIP as u32).unwrap(); // let guest_rsp = vmx_vmread(VmcsFields::GUEST_RSP as u32).unwrap(); - kdebug!("guest_rip={:x}", guest_rip); + debug!("guest_rip={:x}", guest_rip); let _guest_rflags = vmx_vmread(VmcsFields::GUEST_RFLAGS as u32).unwrap(); match VmxExitReason::from(exit_basic_reason as i32) { @@ -205,28 +206,28 @@ extern "C" fn vmexit_handler() { | VmxExitReason::VMFUNC | VmxExitReason::INVEPT | VmxExitReason::INVVPID => { - kdebug!("vmexit handler: vmx instruction!"); + debug!("vmexit handler: vmx instruction!"); vmexit_vmx_instruction_executed().expect("previledge instruction handle error"); } VmxExitReason::CPUID => { - kdebug!("vmexit handler: cpuid instruction!"); + debug!("vmexit handler: cpuid instruction!"); // vmexit_cpuid_handler(guest_cpu_context); adjust_rip(guest_rip).unwrap(); } VmxExitReason::RDMSR => { - kdebug!("vmexit handler: rdmsr instruction!"); + debug!("vmexit handler: rdmsr instruction!"); adjust_rip(guest_rip).unwrap(); } VmxExitReason::WRMSR => { - kdebug!("vmexit handler: wrmsr instruction!"); + debug!("vmexit handler: wrmsr instruction!"); adjust_rip(guest_rip).unwrap(); } VmxExitReason::TRIPLE_FAULT => { - kdebug!("vmexit handler: triple fault!"); + debug!("vmexit handler: triple fault!"); adjust_rip(guest_rip).unwrap(); } VmxExitReason::EPT_VIOLATION => { - kdebug!("vmexit handler: ept violation!"); + debug!("vmexit handler: ept violation!"); let gpa = vmx_vmread(GUEST_PHYSICAL_ADDR_FULL).unwrap(); let exit_qualification = vmx_vmread(VmcsFields::VMEXIT_QUALIFICATION as u32).unwrap(); /* It is a write fault? */ @@ -244,17 +245,17 @@ extern "C" fn vmexit_handler() { .expect("ept page fault error"); } _ => { - kdebug!( + debug!( "vmexit handler: unhandled vmexit reason: {}!", exit_basic_reason ); let info = vmx_vmread(VmcsFields::VMEXIT_INSTR_LEN as u32).unwrap() as u32; - kdebug!("vmexit handler: VMEXIT_INSTR_LEN: {}!", info); + debug!("vmexit handler: VMEXIT_INSTR_LEN: {}!", info); let info = vmx_vmread(VmcsFields::VMEXIT_INSTR_INFO as u32).unwrap() as u32; - kdebug!("vmexit handler: VMEXIT_INSTR_INFO: {}!", info); + debug!("vmexit handler: VMEXIT_INSTR_INFO: {}!", info); let info = vmx_vmread(VmcsFields::CTRL_EXPECTION_BITMAP as u32).unwrap() as u32; - kdebug!("vmexit handler: CTRL_EXPECTION_BITMAP: {}!", info); + debug!("vmexit handler: CTRL_EXPECTION_BITMAP: {}!", info); adjust_rip(guest_rip).unwrap(); // panic!(); diff --git a/kernel/src/arch/x86_64/kvm/vmx/vmx_asm_wrapper.rs b/kernel/src/arch/x86_64/kvm/vmx/vmx_asm_wrapper.rs index 449127eae..0540846a3 100644 --- a/kernel/src/arch/x86_64/kvm/vmx/vmx_asm_wrapper.rs +++ b/kernel/src/arch/x86_64/kvm/vmx/vmx_asm_wrapper.rs @@ -1,6 +1,7 @@ use super::vmcs::VmcsFields; -use crate::kdebug; + use core::arch::asm; +use log::debug; use system_error::SystemError; use x86; /// Enable VMX operation. @@ -8,7 +9,7 @@ pub fn vmxon(vmxon_pa: u64) -> Result<(), SystemError> { match unsafe { x86::bits64::vmx::vmxon(vmxon_pa) } { Ok(_) => Ok(()), Err(e) => { - kdebug!("vmxon fail: {:?}", e); + debug!("vmxon fail: {:?}", e); Err(SystemError::EVMXONFailed) } } @@ -27,8 +28,8 @@ pub fn vmx_vmwrite(vmcs_field: u32, value: u64) -> Result<(), SystemError> { match unsafe { x86::bits64::vmx::vmwrite(vmcs_field, value) } { Ok(_) => Ok(()), Err(e) => { - kdebug!("vmx_write fail: {:?}", e); - kdebug!("vmcs_field: {:x}", vmcs_field); + debug!("vmx_write fail: {:?}", e); + debug!("vmcs_field: {:x}", vmcs_field); Err(SystemError::EVMWRITEFailed) } } @@ -39,7 +40,7 @@ pub fn vmx_vmread(vmcs_field: u32) -> Result { match unsafe { x86::bits64::vmx::vmread(vmcs_field) } { Ok(value) => Ok(value), Err(e) => { - kdebug!("vmx_read fail: {:?}", e); + debug!("vmx_read fail: {:?}", e); Err(SystemError::EVMREADFailed) } } @@ -63,10 +64,10 @@ pub fn vmx_vmlaunch() -> Result<(), SystemError> { "push rsi", "push rdi", "vmwrite {0:r}, rsp", - "lea rax, 1f[rip]", + "lea rax, 2f[rip]", "vmwrite {1:r}, rax", "vmlaunch", - "1:", + "2:", "pop rdi", "pop rsi", "pop rdx", @@ -82,7 +83,7 @@ pub fn vmx_vmlaunch() -> Result<(), SystemError> { // match unsafe { x86::bits64::vmx::vmlaunch() } { // Ok(_) => Ok(()), // Err(e) => { - // kdebug!("vmx_launch fail: {:?}", e); + // debug!("vmx_launch fail: {:?}", e); // Err(SystemError::EVMLAUNCHFailed) // }, // } diff --git a/kernel/src/arch/x86_64/mm/fault.rs b/kernel/src/arch/x86_64/mm/fault.rs index 02f00cbd6..8d7c3346d 100644 --- a/kernel/src/arch/x86_64/mm/fault.rs +++ b/kernel/src/arch/x86_64/mm/fault.rs @@ -4,6 +4,7 @@ use core::{ }; use alloc::sync::Arc; +use log::error; use x86::{bits64::rflags::RFlags, controlregs::Cr4}; use crate::{ @@ -13,7 +14,6 @@ use crate::{ CurrentIrqArch, MMArch, }, exception::InterruptArch, - kerror, mm::{ fault::{FaultFlags, PageFaultHandler, PageFaultMessage}, ucontext::{AddressSpace, LockedVMA}, @@ -28,7 +28,7 @@ pub type PageMapper = impl X86_64MMArch { pub fn vma_access_error(vma: Arc, error_code: X86PfErrorCode) -> bool { - let vm_flags = *vma.lock().vm_flags(); + let vm_flags = *vma.lock_irqsave().vm_flags(); let foreign = false; if error_code.contains(X86PfErrorCode::X86_PF_PK) { return true; @@ -74,27 +74,27 @@ impl X86_64MMArch { if let Some(entry) = mapper.get_entry(address, 0) { if entry.present() { if !entry.flags().has_execute() { - kerror!("kernel tried to execute NX-protected page - exploit attempt?"); + error!("kernel tried to execute NX-protected page - exploit attempt?"); } else if mapper.table().phys().data() & MMArch::ENTRY_FLAG_USER != 0 && unsafe { x86::controlregs::cr4().contains(Cr4::CR4_ENABLE_SMEP) } { - kerror!("unable to execute userspace code (SMEP?)"); + error!("unable to execute userspace code (SMEP?)"); } } } if address.data() < X86_64MMArch::PAGE_SIZE && !regs.is_from_user() { - kerror!( + error!( "BUG: kernel NULL pointer dereference, address: {:#x}", address.data() ); } else { - kerror!( + error!( "BUG: unable to handle page fault for address: {:#x}", address.data() ); } - kerror!( + error!( "#PF: {} {} in {} mode\n", if error_code.contains(X86PfErrorCode::X86_PF_USER) { "user" @@ -114,7 +114,7 @@ impl X86_64MMArch { "kernel" } ); - kerror!( + error!( "#PF: error_code({:#04x}) - {}\n", error_code, if !error_code.contains(X86PfErrorCode::X86_PF_PROT) { @@ -223,7 +223,7 @@ impl X86_64MMArch { } let current_address_space: Arc = AddressSpace::current().unwrap(); - let mut space_guard = current_address_space.write(); + let mut space_guard = current_address_space.write_irqsave(); let mut fault; loop { let vma = space_guard.mappings.find_nearest(address); @@ -236,7 +236,7 @@ impl X86_64MMArch { address.data(), ) }); - let guard = vma.lock(); + let guard = vma.lock_irqsave(); let region = *guard.region(); let vm_flags = *guard.vm_flags(); drop(guard); @@ -269,11 +269,9 @@ impl X86_64MMArch { ); } let mapper = &mut space_guard.user_mapper.utable; + let message = PageFaultMessage::new(vma.clone(), address, flags, mapper); - fault = PageFaultHandler::handle_mm_fault( - PageFaultMessage::new(vma.clone(), address, flags), - mapper, - ); + fault = PageFaultHandler::handle_mm_fault(message); if fault.contains(VmFaultReason::VM_FAULT_COMPLETED) { return; diff --git a/kernel/src/arch/x86_64/mm/mod.rs b/kernel/src/arch/x86_64/mm/mod.rs index bfc491eff..f5c5badc6 100644 --- a/kernel/src/arch/x86_64/mm/mod.rs +++ b/kernel/src/arch/x86_64/mm/mod.rs @@ -6,6 +6,7 @@ pub mod pkru; use alloc::sync::Arc; use alloc::vec::Vec; use hashbrown::HashSet; +use log::{debug, info, warn}; use x86::time::rdtsc; use x86_64::registers::model_specific::EferFlags; @@ -27,9 +28,9 @@ use crate::{ }; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{PageEntry, PageFlags, PAGE_1G_SHIFT}; -use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr}; -use crate::{kdebug, kinfo, kwarn}; +use crate::mm::page::{EntryFlags, PageEntry, PAGE_1G_SHIFT}; +use crate::mm::{MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr, VmFlags}; + use system_error::SystemError; use core::arch::asm; @@ -159,8 +160,8 @@ impl MemoryManagementArch for X86_64MMArch { // 初始化物理内存区域(从multiboot2中获取) Self::init_memory_area_from_multiboot2().expect("init memory area failed"); - kdebug!("bootstrap info: {:?}", unsafe { BOOTSTRAP_MM_INFO }); - kdebug!("phys[0]=virt[0x{:x}]", unsafe { + debug!("bootstrap info: {:?}", unsafe { BOOTSTRAP_MM_INFO }); + debug!("phys[0]=virt[0x{:x}]", unsafe { MMArch::phys_2_virt(PhysAddr::new(0)).unwrap().data() }); @@ -325,6 +326,93 @@ impl MemoryManagementArch for X86_64MMArch { } pkru::pkru_allows_pkey(pkru::vma_pkey(vma), write) } + + const PROTECTION_MAP: [EntryFlags; 16] = protection_map(); + + const PAGE_NONE: usize = + Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_ACCESSED | Self::ENTRY_FLAG_GLOBAL; + + const PAGE_SHARED: usize = Self::ENTRY_FLAG_PRESENT + | Self::ENTRY_FLAG_READWRITE + | Self::ENTRY_FLAG_USER + | Self::ENTRY_FLAG_ACCESSED + | Self::ENTRY_FLAG_NO_EXEC; + + const PAGE_SHARED_EXEC: usize = Self::ENTRY_FLAG_PRESENT + | Self::ENTRY_FLAG_READWRITE + | Self::ENTRY_FLAG_USER + | Self::ENTRY_FLAG_ACCESSED; + + const PAGE_COPY_NOEXEC: usize = Self::ENTRY_FLAG_PRESENT + | Self::ENTRY_FLAG_USER + | Self::ENTRY_FLAG_ACCESSED + | Self::ENTRY_FLAG_NO_EXEC; + + const PAGE_COPY_EXEC: usize = + Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_USER | Self::ENTRY_FLAG_ACCESSED; + + const PAGE_COPY: usize = Self::ENTRY_FLAG_PRESENT + | Self::ENTRY_FLAG_USER + | Self::ENTRY_FLAG_ACCESSED + | Self::ENTRY_FLAG_NO_EXEC; + + const PAGE_READONLY: usize = Self::ENTRY_FLAG_PRESENT + | Self::ENTRY_FLAG_USER + | Self::ENTRY_FLAG_ACCESSED + | Self::ENTRY_FLAG_NO_EXEC; + + const PAGE_READONLY_EXEC: usize = + Self::ENTRY_FLAG_PRESENT | Self::ENTRY_FLAG_USER | Self::ENTRY_FLAG_ACCESSED; + + const PAGE_READ: usize = 0; + const PAGE_READ_EXEC: usize = 0; + const PAGE_WRITE: usize = 0; + const PAGE_WRITE_EXEC: usize = 0; + const PAGE_EXEC: usize = 0; +} + +/// 获取保护标志的映射表 +/// +/// +/// ## 返回值 +/// - `[usize; 16]`: 长度为16的映射表 +const fn protection_map() -> [EntryFlags; 16] { + let mut map = [unsafe { EntryFlags::from_data(0) }; 16]; + unsafe { + map[VmFlags::VM_NONE.bits()] = EntryFlags::from_data(MMArch::PAGE_NONE); + map[VmFlags::VM_READ.bits()] = EntryFlags::from_data(MMArch::PAGE_READONLY); + map[VmFlags::VM_WRITE.bits()] = EntryFlags::from_data(MMArch::PAGE_COPY); + map[VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_COPY); + map[VmFlags::VM_EXEC.bits()] = EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = + EntryFlags::from_data(MMArch::PAGE_COPY_EXEC); + map[VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_COPY_EXEC); + map[VmFlags::VM_SHARED.bits()] = EntryFlags::from_data(MMArch::PAGE_NONE); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_READONLY); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits()] = + EntryFlags::from_data(MMArch::PAGE_SHARED); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_WRITE.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_SHARED); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits()] = + EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_READ.bits()] = + EntryFlags::from_data(MMArch::PAGE_READONLY_EXEC); + map[VmFlags::VM_SHARED.bits() | VmFlags::VM_EXEC.bits() | VmFlags::VM_WRITE.bits()] = + EntryFlags::from_data(MMArch::PAGE_SHARED_EXEC); + map[VmFlags::VM_SHARED.bits() + | VmFlags::VM_EXEC.bits() + | VmFlags::VM_WRITE.bits() + | VmFlags::VM_READ.bits()] = EntryFlags::from_data(MMArch::PAGE_SHARED_EXEC); + } + // if X86_64MMArch::is_xd_reserved() { + // map.iter_mut().for_each(|x| *x &= !Self::ENTRY_FLAG_NO_EXEC) + // } + map } impl X86_64MMArch { @@ -382,18 +470,16 @@ impl X86_64MMArch { info_entry.len as usize, ) .unwrap_or_else(|e| { - kwarn!( + warn!( "Failed to add memory block: base={:#x}, size={:#x}, error={:?}", - info_entry.addr, - info_entry.len, - e + info_entry.addr, info_entry.len, e ); }); areas_count += 1; } } send_to_default_serial8250_port("init_memory_area_from_multiboot2 end\n\0".as_bytes()); - kinfo!("Total memory size: {} MB, total areas from multiboot2: {mb2_count}, valid areas: {areas_count}", total_mem_size / 1024 / 1024); + info!("Total memory size: {} MB, total areas from multiboot2: {mb2_count}, valid areas: {areas_count}", total_mem_size / 1024 / 1024); return Ok(areas_count); } @@ -402,7 +488,7 @@ impl X86_64MMArch { let efer: EferFlags = x86_64::registers::model_specific::Efer::read(); if !efer.contains(EferFlags::NO_EXECUTE_ENABLE) { // NO_EXECUTE_ENABLE是false,那么就设置xd_reserved为true - kdebug!("NO_EXECUTE_ENABLE is false, set XD_RESERVED to true"); + debug!("NO_EXECUTE_ENABLE is false, set XD_RESERVED to true"); XD_RESERVED.store(true, Ordering::Relaxed); } compiler_fence(Ordering::SeqCst); @@ -438,7 +524,7 @@ unsafe fn allocator_init() { .reserve_block(PhysAddr::new(0), phy_offset.data()) .expect("Failed to reserve block"); let mut bump_allocator = BumpAllocator::::new(phy_offset.data()); - kdebug!( + debug!( "BumpAllocator created, offset={:?}", bump_allocator.offset() ); @@ -459,7 +545,7 @@ unsafe fn allocator_init() { ) .expect("Failed to create page mapper"); new_page_table = mapper.table().phys(); - kdebug!("PageMapper created"); + debug!("PageMapper created"); // 取消最开始时候,在head.S中指定的映射(暂时不刷新TLB) { @@ -471,12 +557,12 @@ unsafe fn allocator_init() { .expect("Failed to empty page table entry"); } } - kdebug!("Successfully emptied page table"); + debug!("Successfully emptied page table"); let total_num = mem_block_manager().total_initial_memory_regions(); for i in 0..total_num { let area = mem_block_manager().get_initial_memory_region(i).unwrap(); - // kdebug!("area: base={:?}, size={:#x}, end={:?}", area.base, area.size, area.base + area.size); + // debug!("area: base={:?}, size={:#x}, end={:?}", area.base, area.size, area.base + area.size); for i in 0..((area.size + MMArch::PAGE_SIZE - 1) / MMArch::PAGE_SIZE) { let paddr = area.base.add(i * MMArch::PAGE_SIZE); let vaddr = unsafe { MMArch::phys_2_virt(paddr) }.unwrap(); @@ -494,7 +580,7 @@ unsafe fn allocator_init() { unsafe { INITIAL_CR3_VALUE = new_page_table; } - kdebug!( + debug!( "After mapping all physical memory, DragonOS used: {} KB", bump_allocator.offset() / 1024 ); @@ -503,7 +589,7 @@ unsafe fn allocator_init() { let buddy_allocator = unsafe { BuddyAllocator::::new(bump_allocator).unwrap() }; // 设置全局的页帧分配器 unsafe { set_inner_allocator(buddy_allocator) }; - kinfo!("Successfully initialized buddy allocator"); + info!("Successfully initialized buddy allocator"); // 关闭显示输出 scm_disable_put_to_window(); @@ -511,7 +597,7 @@ unsafe fn allocator_init() { { let mut binding = INNER_ALLOCATOR.lock(); let mut allocator_guard = binding.as_mut().unwrap(); - kdebug!("To enable new page table."); + debug!("To enable new page table."); compiler_fence(Ordering::SeqCst); let mapper = crate::mm::page::PageMapper::::new( PageTableKind::Kernel, @@ -521,9 +607,9 @@ unsafe fn allocator_init() { compiler_fence(Ordering::SeqCst); mapper.make_current(); compiler_fence(Ordering::SeqCst); - kdebug!("New page table enabled"); + debug!("New page table enabled"); } - kdebug!("Successfully enabled new page table"); + debug!("Successfully enabled new page table"); } #[no_mangle] @@ -536,7 +622,7 @@ pub fn test_buddy() { const TOTAL_SIZE: usize = 200 * 1024 * 1024; for i in 0..10 { - kdebug!("Test buddy, round: {i}"); + debug!("Test buddy, round: {i}"); // 存放申请的内存块 let mut v: Vec<(PhysAddr, PageFrameCount)> = Vec::with_capacity(60 * 1024); // 存放已经申请的内存块的地址(用于检查重复) @@ -601,14 +687,14 @@ pub fn test_buddy() { } } - kdebug!( + debug!( "Allocated {} MB memory, release: {} MB, no release: {} bytes", allocated / 1024 / 1024, free_count / 1024 / 1024, (allocated - free_count) ); - kdebug!("Now, to release buddy memory"); + debug!("Now, to release buddy memory"); // 释放所有的内存 for (paddr, allocated_frame_count) in v { unsafe { LockedFrameAllocator.free(paddr, allocated_frame_count) }; @@ -616,7 +702,7 @@ pub fn test_buddy() { free_count += allocated_frame_count.data() * MMArch::PAGE_SIZE; } - kdebug!("release done!, allocated: {allocated}, free_count: {free_count}"); + debug!("release done!, allocated: {allocated}, free_count: {free_count}"); } } @@ -651,17 +737,17 @@ impl FrameAllocator for LockedFrameAllocator { } /// 获取内核地址默认的页面标志 -pub unsafe fn kernel_page_flags(virt: VirtAddr) -> PageFlags { +pub unsafe fn kernel_page_flags(virt: VirtAddr) -> EntryFlags { let info: X86_64MMBootstrapInfo = BOOTSTRAP_MM_INFO.unwrap(); if virt.data() >= info.kernel_code_start && virt.data() < info.kernel_code_end { // Remap kernel code execute - return PageFlags::new().set_execute(true).set_write(true); + return EntryFlags::new().set_execute(true).set_write(true); } else if virt.data() >= info.kernel_data_end && virt.data() < info.kernel_rodata_end { // Remap kernel rodata read only - return PageFlags::new().set_execute(true); + return EntryFlags::new().set_execute(true); } else { - return PageFlags::new().set_write(true).set_execute(true); + return EntryFlags::new().set_write(true).set_execute(true); } } diff --git a/kernel/src/arch/x86_64/mm/pkru.rs b/kernel/src/arch/x86_64/mm/pkru.rs index f467f8d19..c40f5f0fa 100644 --- a/kernel/src/arch/x86_64/mm/pkru.rs +++ b/kernel/src/arch/x86_64/mm/pkru.rs @@ -16,8 +16,8 @@ const PKEY_MASK: usize = 1 << 32 | 1 << 33 | 1 << 34 | 1 << 35; /// ## 返回值 /// - `u16`: vma的protection_key pub fn vma_pkey(vma: Arc) -> u16 { - let guard = vma.lock(); - ((guard.vm_flags().bits() & PKEY_MASK as u64) >> VM_PKEY_SHIFT) as u16 + let guard = vma.lock_irqsave(); + ((guard.vm_flags().bits() & PKEY_MASK) >> VM_PKEY_SHIFT) as u16 } // TODO pkru实现参考:https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/include/asm/pkru.h diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index b2069f087..ff95ab1d1 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -30,6 +30,7 @@ pub use interrupt::X86_64InterruptArch as CurrentIrqArch; pub use crate::arch::asm::pio::X86_64PortIOArch as CurrentPortIOArch; pub use kvm::X86_64KVMArch as KVMArch; +#[allow(unused_imports)] pub use crate::arch::ipc::signal::X86_64SignalArch as CurrentSignalArch; pub use crate::arch::time::X86_64TimeArch as CurrentTimeArch; diff --git a/kernel/src/arch/x86_64/pci/pci.rs b/kernel/src/arch/x86_64/pci/pci.rs index ce47e1ffb..427d6c88b 100644 --- a/kernel/src/arch/x86_64/pci/pci.rs +++ b/kernel/src/arch/x86_64/pci/pci.rs @@ -2,18 +2,40 @@ use crate::arch::TraitPciArch; use crate::driver::acpi::acpi_manager; use crate::driver::pci::ecam::{pci_ecam_root_info_manager, EcamRootInfo}; use crate::driver::pci::pci::{ - pci_init, BusDeviceFunction, PciAddr, PciError, PORT_PCI_CONFIG_ADDRESS, PORT_PCI_CONFIG_DATA, + pci_init, BusDeviceFunction, PciAddr, PciCam, PciError, PORT_PCI_CONFIG_ADDRESS, + PORT_PCI_CONFIG_DATA, }; -use crate::include::bindings::bindings::{io_in32, io_out32}; +use crate::driver::pci::root::{pci_root_manager, PciRoot}; +use crate::include::bindings::bindings::{io_in32, io_in8, io_out32}; use crate::init::initcall::INITCALL_SUBSYS; -use crate::kerror; use crate::mm::PhysAddr; use acpi::mcfg::Mcfg; +use log::warn; use system_error::SystemError; use unified_init::macros::unified_init; pub struct X86_64PciArch; + +impl X86_64PciArch { + /// # 在早期引导阶段直接访问PCI配置空间的函数 + /// 参考:https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/pci/early.c?fi=read_pci_config_byte#19 + fn read_config_early(bus: u8, slot: u8, func: u8, offset: u8) -> u8 { + unsafe { + io_out32( + PORT_PCI_CONFIG_ADDRESS, + 0x80000000 + | ((bus as u32) << 16) + | ((slot as u32) << 11) + | ((func as u32) << 8) + | offset as u32, + ); + } + let value = unsafe { io_in8(PORT_PCI_CONFIG_DATA + (offset & 3) as u16) }; + return value; + } +} + impl TraitPciArch for X86_64PciArch { fn read_config(bus_device_function: &BusDeviceFunction, offset: u8) -> u32 { // 构造pci配置空间地址 @@ -50,8 +72,18 @@ impl TraitPciArch for X86_64PciArch { #[unified_init(INITCALL_SUBSYS)] fn x86_64_pci_init() -> Result<(), SystemError> { - if let Err(e) = discover_ecam_root() { - kerror!("x86_64_pci_init(): discover_ecam_root error: {:?}", e); + if discover_ecam_root().is_err() { + // ecam初始化失败,使用portio访问pci配置空间 + // 参考:https://code.dragonos.org.cn/xref/linux-6.6.21/arch/x86/pci/broadcom_bus.c#27 + let bus_begin = X86_64PciArch::read_config_early(0, 0, 0, 0x44); + let bus_end = X86_64PciArch::read_config_early(0, 0, 0, 0x45); + + if !pci_root_manager().has_root(bus_begin as u16) { + let root = PciRoot::new(None, PciCam::Portiocam, bus_begin, bus_end); + pci_root_manager().add_pci_root(root.unwrap()); + } else { + warn!("x86_64_pci_init(): pci_root_manager {}", bus_begin); + } } pci_init(); diff --git a/kernel/src/arch/x86_64/process/idle.rs b/kernel/src/arch/x86_64/process/idle.rs index 2ce5f5091..79dfb8a59 100644 --- a/kernel/src/arch/x86_64/process/idle.rs +++ b/kernel/src/arch/x86_64/process/idle.rs @@ -1,9 +1,10 @@ use core::hint::spin_loop; +use log::error; + use crate::{ arch::CurrentIrqArch, exception::InterruptArch, - kBUG, process::{ProcessFlags, ProcessManager}, sched::{SchedMode, __schedule}, }; @@ -21,7 +22,7 @@ impl ProcessManager { x86::halt(); } } else { - kBUG!("Idle process should not be scheduled with IRQs disabled."); + error!("Idle process should not be scheduled with IRQs disabled."); spin_loop(); } } diff --git a/kernel/src/arch/x86_64/process/kthread.rs b/kernel/src/arch/x86_64/process/kthread.rs index 1c0517d2a..58f6df1f8 100644 --- a/kernel/src/arch/x86_64/process/kthread.rs +++ b/kernel/src/arch/x86_64/process/kthread.rs @@ -42,9 +42,8 @@ impl KernelThreadMechanism { frame.rip = kernel_thread_bootstrap_stage1 as usize as u64; // fork失败的话,子线程不会执行。否则将导致内存安全问题。 - let pid = ProcessManager::fork(&frame, clone_flags).map_err(|e| { + let pid = ProcessManager::fork(&frame, clone_flags).inspect_err(|_e| { unsafe { KernelThreadCreateInfo::parse_unsafe_arc_ptr(create_info) }; - e })?; ProcessManager::find(pid) diff --git a/kernel/src/arch/x86_64/process/mod.rs b/kernel/src/arch/x86_64/process/mod.rs index 49685c223..c4382cdc7 100644 --- a/kernel/src/arch/x86_64/process/mod.rs +++ b/kernel/src/arch/x86_64/process/mod.rs @@ -8,13 +8,13 @@ use core::{ use alloc::sync::{Arc, Weak}; use kdepends::memoffset::offset_of; +use log::{error, warn}; use system_error::SystemError; use x86::{controlregs::Cr4, segmentation::SegmentSelector}; use crate::{ arch::process::table::TSSManager, exception::InterruptArch, - kerror, kwarn, libs::spinlock::SpinLockGuard, mm::VirtAddr, process::{ @@ -167,7 +167,7 @@ impl ArchPCBInfo { // 清空浮点寄存器 pub fn clear_fp_state(&mut self) { if unlikely(self.fp_state.is_none()) { - kwarn!("fp_state is none"); + warn!("fp_state is none"); return; } @@ -275,7 +275,7 @@ impl ProcessControlBlock { // 从内核栈的最低地址处取出pcb的地址 let p = stack_base.data() as *const *const ProcessControlBlock; if unlikely((unsafe { *p }).is_null()) { - kerror!("p={:p}", p); + error!("p={:p}", p); panic!("current_pcb is null"); } unsafe { @@ -406,7 +406,7 @@ impl ProcessManager { ); PROCESS_SWITCH_RESULT.as_mut().unwrap().get_mut().prev_pcb = Some(prev); PROCESS_SWITCH_RESULT.as_mut().unwrap().get_mut().next_pcb = Some(next); - // kdebug!("switch tss ok"); + // debug!("switch tss ok"); compiler_fence(Ordering::SeqCst); // 正式切换上下文 switch_to_inner(prev_arch, next_arch); @@ -515,7 +515,7 @@ pub unsafe fn arch_switch_to_user(trap_frame: TrapFrame) -> ! { let trap_frame_vaddr = VirtAddr::new( current_pcb.kernel_stack().stack_max_address().data() - core::mem::size_of::(), ); - // kdebug!("trap_frame_vaddr: {:?}", trap_frame_vaddr); + // debug!("trap_frame_vaddr: {:?}", trap_frame_vaddr); assert!( (x86::current::registers::rsp() as usize) < trap_frame_vaddr.data(), diff --git a/kernel/src/arch/x86_64/process/syscall.rs b/kernel/src/arch/x86_64/process/syscall.rs index 84448893f..7fe4944cf 100644 --- a/kernel/src/arch/x86_64/process/syscall.rs +++ b/kernel/src/arch/x86_64/process/syscall.rs @@ -1,4 +1,4 @@ -use alloc::{string::String, sync::Arc, vec::Vec}; +use alloc::{ffi::CString, string::String, sync::Arc, vec::Vec}; use system_error::SystemError; use crate::{ @@ -19,14 +19,14 @@ use crate::{ impl Syscall { pub fn do_execve( path: String, - argv: Vec, - envp: Vec, + argv: Vec, + envp: Vec, regs: &mut TrapFrame, ) -> Result<(), SystemError> { // 关中断,防止在设置地址空间的时候,发生中断,然后进调度器,出现错误。 let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; let pcb = ProcessManager::current_pcb(); - // crate::kdebug!( + // log::debug!( // "pid: {:?} do_execve: path: {:?}, argv: {:?}, envp: {:?}\n", // pcb.pid(), // path, @@ -55,23 +55,27 @@ impl Syscall { AddressSpace::is_current(&address_space), "Failed to set address space" ); - // kdebug!("Switch to new address space"); + // debug!("Switch to new address space"); // 切换到新的用户地址空间 unsafe { address_space.read().user_mapper.utable.make_current() }; drop(old_address_space); drop(irq_guard); - // kdebug!("to load binary file"); + // debug!("to load binary file"); let mut param = ExecParam::new(path.as_str(), address_space.clone(), ExecParamFlags::EXEC)?; // 加载可执行文件 let load_result = load_binary_file(&mut param)?; - // kdebug!("load binary file done"); - // kdebug!("argv: {:?}, envp: {:?}", argv, envp); + // debug!("load binary file done"); + // debug!("argv: {:?}, envp: {:?}", argv, envp); param.init_info_mut().args = argv; param.init_info_mut().envs = envp; + // 生成16字节随机数 + // TODO 暂时设为0 + param.init_info_mut().rand_num = [0u8; 16]; + // 把proc_init_info写到用户栈上 let mut ustack_message = unsafe { address_space @@ -82,19 +86,13 @@ impl Syscall { }; let (user_sp, argv_ptr) = unsafe { param - .init_info() - .push_at( - // address_space - // .write() - // .user_stack_mut() - // .expect("No user stack found"), - &mut ustack_message, - ) + .init_info_mut() + .push_at(&mut ustack_message) .expect("Failed to push proc_init_info to user stack") }; address_space.write().user_stack = Some(ustack_message); - // kdebug!("write proc_init_info to user stack done"); + // debug!("write proc_init_info to user stack done"); // (兼容旧版libc)把argv的指针写到寄存器内 // TODO: 改写旧版libc,不再需要这个兼容 @@ -116,9 +114,9 @@ impl Syscall { drop(param); - // kdebug!("regs: {:?}\n", regs); + // debug!("regs: {:?}\n", regs); - // crate::kdebug!( + // crate::debug!( // "tmp_rs_execve: done, load_result.entry_point()={:?}", // load_result.entry_point() // ); diff --git a/kernel/src/arch/x86_64/process/table.rs b/kernel/src/arch/x86_64/process/table.rs index e28c922c7..cbe5d7878 100644 --- a/kernel/src/arch/x86_64/process/table.rs +++ b/kernel/src/arch/x86_64/process/table.rs @@ -59,6 +59,7 @@ impl TSSManager { x86::task::load_tr(selector); } + #[allow(static_mut_refs)] unsafe fn set_tss_descriptor(index: u16, vaddr: VirtAddr) { const LIMIT: u64 = 103; let gdt_vaddr = VirtAddr::new(&GDT_Table as *const _ as usize); diff --git a/kernel/src/arch/x86_64/smp/mod.rs b/kernel/src/arch/x86_64/smp/mod.rs index 450464864..2eaa5fc74 100644 --- a/kernel/src/arch/x86_64/smp/mod.rs +++ b/kernel/src/arch/x86_64/smp/mod.rs @@ -5,12 +5,12 @@ use core::{ }; use kdepends::memoffset::offset_of; +use log::debug; use system_error::SystemError; use crate::{ arch::{mm::LowAddressRemapping, process::table::TSSManager, MMArch}, exception::InterruptArch, - kdebug, libs::{cpumask::CpuMask, rwlock::RwLock}, mm::{percpu::PerCpu, MemoryManagementArch, PhysAddr, VirtAddr, IDLE_PROCESS_ADDRESS_SPACE}, process::ProcessManager, @@ -77,7 +77,7 @@ unsafe extern "sysv64" fn smp_init_switch_stack(st: &ApStartStackInfo) -> ! { unsafe extern "C" fn smp_ap_start_stage1() -> ! { let id = smp_get_processor_id(); - kdebug!("smp_ap_start_stage1: id: {}\n", id.data()); + debug!("smp_ap_start_stage1: id: {}\n", id.data()); let current_idle = ProcessManager::idle_pcb()[smp_get_processor_id().data() as usize].clone(); let tss = TSSManager::current_tss(); @@ -187,7 +187,7 @@ fn print_cpus(s: &str, mask: &CpuMask) { v.push(cpu.data()); } - kdebug!("{s}: cpus: {v:?}\n"); + debug!("{s}: cpus: {v:?}\n"); } pub struct X86_64SMPArch; @@ -259,6 +259,7 @@ impl X86_64SMPArch { } impl SmpCpuManager { + #[allow(static_mut_refs)] pub fn arch_init(_boot_cpu: ProcessorId) { assert!(smp_get_processor_id().data() == 0); // 写入APU_START_CR3,这个值会在AP处理器启动时设置到CR3寄存器 diff --git a/kernel/src/arch/x86_64/syscall/mod.rs b/kernel/src/arch/x86_64/syscall/mod.rs index f5b8eb156..4ed274c23 100644 --- a/kernel/src/arch/x86_64/syscall/mod.rs +++ b/kernel/src/arch/x86_64/syscall/mod.rs @@ -11,6 +11,7 @@ use crate::{ process::ProcessManager, syscall::{Syscall, SYS_SCHED}, }; +use log::debug; use system_error::SystemError; use super::{ @@ -52,7 +53,7 @@ macro_rules! syscall_return { if $show { let pid = ProcessManager::current_pcb().pid(); - crate::kdebug!("syscall return:pid={:?},ret= {:?}\n", pid, ret as isize); + debug!("syscall return:pid={:?},ret= {:?}\n", pid, ret as isize); } unsafe { @@ -94,7 +95,7 @@ pub extern "sysv64" fn syscall_handler(frame: &mut TrapFrame) { // }; if show { - crate::kdebug!("syscall: pid: {:?}, num={:?}\n", pid, syscall_num); + debug!("syscall: pid: {:?}, num={:?}\n", pid, syscall_num); } // Arch specific syscall @@ -126,7 +127,7 @@ pub extern "sysv64" fn syscall_handler(frame: &mut TrapFrame) { /// 系统调用初始化 pub fn arch_syscall_init() -> Result<(), SystemError> { - // kinfo!("arch_syscall_init\n"); + // info!("arch_syscall_init\n"); unsafe { set_system_trap_gate(0x80, 0, VirtAddr::new(syscall_int as usize)) }; // 系统调用门 unsafe { init_syscall_64() }; return Ok(()); diff --git a/kernel/src/arch/x86_64/x86_64-unknown-none.json b/kernel/src/arch/x86_64/x86_64-unknown-none.json index a24b98788..fbc13a4a2 100644 --- a/kernel/src/arch/x86_64/x86_64-unknown-none.json +++ b/kernel/src/arch/x86_64/x86_64-unknown-none.json @@ -1,6 +1,6 @@ { "llvm-target": "x86_64-unknown-none", - "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", "arch": "x86_64", "target-endian": "little", "target-pointer-width": "64", diff --git a/kernel/src/debug/klog/mm.rs b/kernel/src/debug/klog/mm.rs index 619c5fa8f..ec3100261 100644 --- a/kernel/src/debug/klog/mm.rs +++ b/kernel/src/debug/klog/mm.rs @@ -1,17 +1,10 @@ extern crate klog_types; -use core::{ - intrinsics::unlikely, - sync::atomic::{compiler_fence, Ordering}, -}; +use core::sync::atomic::{compiler_fence, Ordering}; use klog_types::{AllocatorLog, AllocatorLogType, LogSource, MMLogChannel}; -use crate::{ - arch::CurrentTimeArch, - process::{Pid, ProcessManager}, - time::TimeArch, -}; +use crate::{arch::CurrentTimeArch, process::Pid, time::TimeArch}; /// 全局的内存分配器日志通道 /// @@ -31,13 +24,14 @@ static __MM_DEBUG_LOG_IDA: ida::IdAllocator = ida::IdAllocator::new(1, usize::MA /// /// - `log_type`:日志类型 /// - `source`:日志来源 -pub fn mm_debug_log(log_type: AllocatorLogType, source: LogSource) { - let pid = if unlikely(!ProcessManager::initialized()) { - Some(Pid::new(0)) - } else { - Some(ProcessManager::current_pcb().pid()) - }; - MMDebugLogManager::log(log_type, source, pid); +pub fn mm_debug_log(_log_type: AllocatorLogType, _source: LogSource) { + // todo: 由于目前底层的thingbuf存在卡死的问题,因此这里暂时注释掉。 + // let pid = if unlikely(!ProcessManager::initialized()) { + // Some(Pid::new(0)) + // } else { + // Some(ProcessManager::current_pcb().pid()) + // }; + // MMDebugLogManager::log(log_type, source, pid); } #[derive(Debug)] @@ -54,6 +48,7 @@ impl MMDebugLogManager { /// - `log_type`:日志类型 /// - `source`:日志来源 /// - `pid`:日志来源的pid + #[allow(dead_code)] pub fn log(log_type: AllocatorLogType, source: LogSource, pid: Option) { let id = __MM_DEBUG_LOG_IDA.alloc().unwrap(); let log = AllocatorLog::new( diff --git a/kernel/src/driver/acpi/bus.rs b/kernel/src/driver/acpi/bus.rs index 46869d11e..f91c27847 100644 --- a/kernel/src/driver/acpi/bus.rs +++ b/kernel/src/driver/acpi/bus.rs @@ -111,6 +111,7 @@ impl Bus for AcpiBus { /// /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/acpi/acpi_bus.h#364 +#[allow(unused)] pub trait AcpiDevice: Device {} /// Acpi驱动应当实现的trait @@ -120,4 +121,5 @@ pub trait AcpiDevice: Device {} /// todo: 仿照linux的acpi_driver去设计这个trait /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/acpi/acpi_bus.h#163 +#[allow(unused)] pub trait AcpiDriver: Driver {} diff --git a/kernel/src/driver/acpi/mod.rs b/kernel/src/driver/acpi/mod.rs index 2d2f644cc..8525f5f17 100644 --- a/kernel/src/driver/acpi/mod.rs +++ b/kernel/src/driver/acpi/mod.rs @@ -2,11 +2,11 @@ use core::{fmt::Debug, hint::spin_loop, ptr::NonNull}; use acpi::{AcpiHandler, AcpiTables, PlatformInfo}; use alloc::{string::ToString, sync::Arc}; +use log::{error, info}; use crate::{ arch::MMArch, driver::base::firmware::sys_firmware_kset, - kinfo, libs::align::{page_align_down, page_align_up, AlignedBox}, mm::{ mmio_buddy::{mmio_pool, MMIOSpaceGuard}, @@ -57,7 +57,7 @@ impl AcpiManager { /// /// https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/acpi/bus.c#1390 pub fn init(&self, rsdp_vaddr1: u64, rsdp_vaddr2: u64) -> Result<(), SystemError> { - kinfo!("Initializing Acpi Manager..."); + info!("Initializing Acpi Manager..."); // 初始化`/sys/firmware/acpi`的kset let kset = KSet::new("acpi".to_string()); @@ -67,7 +67,7 @@ impl AcpiManager { } self.map_tables(rsdp_vaddr1, rsdp_vaddr2)?; self.bus_init()?; - kinfo!("Acpi Manager initialized."); + info!("Acpi Manager initialized."); return Ok(()); } @@ -95,7 +95,7 @@ impl AcpiManager { } // 如果rsdpv1和rsdpv2都无法获取到acpi_table,说明有问题,打印报错信息后进入死循环 Err(e2) => { - kerror!("acpi_init(): failed to parse acpi tables, error: (rsdpv1: {:?}) or (rsdpv2: {:?})", e1, e2); + error!("acpi_init(): failed to parse acpi tables, error: (rsdpv1: {:?}) or (rsdpv2: {:?})", e1, e2); Self::drop_rsdp_tmp_box(); loop { spin_loop(); @@ -161,7 +161,7 @@ impl AcpiManager { pub fn platform_info(&self) -> Option> { let r = self.tables()?.platform_info(); if let Err(ref e) = r { - kerror!( + error!( "AcpiManager::platform_info(): failed to get platform info, error: {:?}", e ); diff --git a/kernel/src/driver/acpi/pmtmr.rs b/kernel/src/driver/acpi/pmtmr.rs index aec175376..37d292839 100644 --- a/kernel/src/driver/acpi/pmtmr.rs +++ b/kernel/src/driver/acpi/pmtmr.rs @@ -12,7 +12,7 @@ pub const ACPI_PM_MASK: u64 = 0xffffff; pub fn acpi_pm_read_early() -> u32 { use crate::driver::clocksource::acpi_pm::{acpi_pm_read_verified, PMTMR_IO_PORT}; use core::sync::atomic::Ordering; - let port = unsafe { PMTMR_IO_PORT.load(Ordering::SeqCst) }; + let port = PMTMR_IO_PORT.load(Ordering::SeqCst); // 如果端口为零直接返回 if port == 0 { diff --git a/kernel/src/driver/acpi/sysfs.rs b/kernel/src/driver/acpi/sysfs.rs index 73e8278f0..34bc04b25 100644 --- a/kernel/src/driver/acpi/sysfs.rs +++ b/kernel/src/driver/acpi/sysfs.rs @@ -18,6 +18,7 @@ use alloc::{ sync::Arc, vec::Vec, }; +use log::{debug, error, warn}; use system_error::SystemError; use super::{acpi_kset, AcpiManager}; @@ -109,7 +110,7 @@ impl AcpiManager { let tables = self.tables().unwrap(); let headers = tables.headers(); for header in headers { - kdebug!("ACPI header: {:?}", header); + debug!("ACPI header: {:?}", header); let attr = AttrAcpiTable::new(&header)?; acpi_table_attr_list().write().push(attr); self.acpi_table_data_init(&header)?; @@ -172,7 +173,7 @@ impl AttrAcpiTable { // 将当前实例的序号加1 r.instance += 1; if r.instance > ACPI_MAX_TABLE_INSTANCES as isize { - kwarn!("too many table instances. name: {}", r.name); + warn!("too many table instances. name: {}", r.name); return Err(SystemError::ERANGE); } @@ -289,10 +290,9 @@ impl BinAttribute for AttrAcpiTable { ($name: ident, $tables: expr) => { define_struct!($name); let table = $tables.find_entire_table::<$name>().map_err(|e| { - kwarn!( + warn!( "AttrAcpiTable::read(): failed to find table. name: {}, error: {:?}", - self.name, - e + self.name, e ); SystemError::ENODEV })?; @@ -500,7 +500,7 @@ impl BinAttribute for AttrAcpiTable { } _ => { - kerror!("AttrAcpiTable::read(): unknown table. name: {}", self.name); + error!("AttrAcpiTable::read(): unknown table. name: {}", self.name); return Err(SystemError::ENODEV); } }; diff --git a/kernel/src/driver/base/block/block_device.rs b/kernel/src/driver/base/block/block_device.rs index 37fd975a9..729157a62 100644 --- a/kernel/src/driver/base/block/block_device.rs +++ b/kernel/src/driver/base/block/block_device.rs @@ -1,23 +1,21 @@ /// 引入Module -use crate::{ - driver::{ - base::{ - device::{ - device_number::{DeviceNumber, Major}, - Device, DeviceError, IdTable, BLOCKDEVS, - }, - map::{ - DeviceStruct, DEV_MAJOR_DYN_END, DEV_MAJOR_DYN_EXT_END, DEV_MAJOR_DYN_EXT_START, - DEV_MAJOR_HASH_SIZE, DEV_MAJOR_MAX, - }, +use crate::driver::{ + base::{ + device::{ + device_number::{DeviceNumber, Major}, + Device, DeviceError, IdTable, BLOCKDEVS, + }, + map::{ + DeviceStruct, DEV_MAJOR_DYN_END, DEV_MAJOR_DYN_EXT_END, DEV_MAJOR_DYN_EXT_START, + DEV_MAJOR_HASH_SIZE, DEV_MAJOR_MAX, }, - block::cache::{cached_block_device::BlockCache, BlockCacheError, BLOCK_SIZE}, }, - kerror, + block::cache::{cached_block_device::BlockCache, BlockCacheError, BLOCK_SIZE}, }; use alloc::{sync::Arc, vec::Vec}; use core::any::Any; +use log::error; use system_error::SystemError; use super::disk_info::Partition; @@ -475,7 +473,7 @@ impl BlockDeviceOps { let mut major = device_number.major(); let baseminor = device_number.minor(); if major >= DEV_MAJOR_MAX { - kerror!( + error!( "DEV {} major requested {:?} is greater than the maximum {}\n", name, major, @@ -483,7 +481,7 @@ impl BlockDeviceOps { ); } if minorct > DeviceNumber::MINOR_MASK + 1 - baseminor { - kerror!("DEV {} minor range requested ({}-{}) is out of range of maximum range ({}-{}) for a single major\n", + error!("DEV {} minor range requested ({}-{}) is out of range of maximum range ({}-{}) for a single major\n", name, baseminor, baseminor + minorct - 1, 0, DeviceNumber::MINOR_MASK); } let blockdev = DeviceStruct::new(DeviceNumber::new(major, baseminor), minorct, name); @@ -549,7 +547,7 @@ impl BlockDeviceOps { #[allow(dead_code)] pub fn bdev_add(_bdev: Arc, id_table: IdTable) -> Result<(), DeviceError> { if id_table.device_number().data() == 0 { - kerror!("Device number can't be 0!\n"); + error!("Device number can't be 0!\n"); } todo!("bdev_add") // return device_manager().add_device(bdev.id_table(), bdev.device()); diff --git a/kernel/src/driver/base/char/mod.rs b/kernel/src/driver/base/char/mod.rs index cf9483c34..090abef18 100644 --- a/kernel/src/driver/base/char/mod.rs +++ b/kernel/src/driver/base/char/mod.rs @@ -1,6 +1,6 @@ use alloc::sync::Arc; +use log::error; -use crate::kerror; use system_error::SystemError; use super::{ @@ -129,7 +129,7 @@ impl CharDevOps { let mut major = device_number.major(); let baseminor = device_number.minor(); if major >= DEV_MAJOR_MAX { - kerror!( + error!( "DEV {} major requested {:?} is greater than the maximum {}\n", name, major, @@ -137,7 +137,7 @@ impl CharDevOps { ); } if minorct > DeviceNumber::MINOR_MASK + 1 - baseminor { - kerror!("DEV {} minor range requested ({}-{}) is out of range of maximum range ({}-{}) for a single major\n", + error!("DEV {} minor range requested ({}-{}) is out of range of maximum range ({}-{}) for a single major\n", name, baseminor, baseminor + minorct - 1, 0, DeviceNumber::MINOR_MASK); } let chardev = DeviceStruct::new(DeviceNumber::new(major, baseminor), minorct, name); @@ -207,7 +207,7 @@ impl CharDevOps { range: usize, ) -> Result<(), SystemError> { if id_table.device_number().data() == 0 { - kerror!("Device number can't be 0!\n"); + error!("Device number can't be 0!\n"); } device_manager().add_device(cdev.clone())?; kobj_map( diff --git a/kernel/src/driver/base/device/bus.rs b/kernel/src/driver/base/device/bus.rs index a94b1fae3..b7e70f893 100644 --- a/kernel/src/driver/base/device/bus.rs +++ b/kernel/src/driver/base/device/bus.rs @@ -25,6 +25,7 @@ use alloc::{ use core::{ffi::CStr, fmt::Debug, intrinsics::unlikely}; use hashbrown::HashMap; use intertrait::cast::CastArc; +use log::{debug, error, info}; use system_error::SystemError; /// `/sys/bus`的kset @@ -296,7 +297,7 @@ impl BusManager { .bus() .and_then(|bus| bus.upgrade()) .ok_or(SystemError::EINVAL)?; - kdebug!("bus '{}' add driver '{}'", bus.name(), driver.name()); + debug!("bus '{}' add driver '{}'", bus.name(), driver.name()); driver.set_kobj_type(Some(&BusDriverKType)); let kobj = driver.clone() as Arc; @@ -314,7 +315,7 @@ impl BusManager { driver_manager() .add_groups(driver, bus.drv_groups()) .map_err(|e| { - kerror!( + error!( "BusManager::add_driver: driver '{:?}' add_groups failed, err: '{:?}", driver.name(), e @@ -326,7 +327,7 @@ impl BusManager { if !driver.suppress_bind_attrs() { self.add_bind_files(driver) .map_err(|e| { - kerror!( + error!( "BusManager::add_driver: driver '{:?}' add_bind_files failed, err: '{:?}", driver.name(), e @@ -477,9 +478,8 @@ impl BusManager { driver_manager() .create_attr_file(driver, &DriverAttrBind) - .map_err(|e| { + .inspect_err(|_e| { driver_manager().remove_attr_file(driver, &DriverAttrUnbind); - e })?; return Ok(()); @@ -580,7 +580,7 @@ pub fn bus_add_device(dev: &Arc) -> Result<(), SystemError> { /// /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/base/bus.c?fi=bus_probe_device#478 pub fn bus_probe_device(dev: &Arc) { - kinfo!("bus_probe_device: dev: {:?}", dev.name()); + info!("bus_probe_device: dev: {:?}", dev.name()); bus_manager().probe_device(dev); } @@ -746,7 +746,7 @@ impl Attribute for DriverAttrUnbind { fn store(&self, kobj: Arc, buf: &[u8]) -> Result { let driver = kobj.cast::().map_err(|kobj| { - kerror!( + error!( "Intertrait casting not implemented for kobj: {}", kobj.name() ); @@ -795,7 +795,7 @@ impl Attribute for DriverAttrBind { */ fn store(&self, kobj: Arc, buf: &[u8]) -> Result { let driver = kobj.cast::().map_err(|kobj| { - kerror!( + error!( "Intertrait casting not implemented for kobj: {}", kobj.name() ); diff --git a/kernel/src/driver/base/device/dd.rs b/kernel/src/driver/base/device/dd.rs index 7adb840bc..358c51950 100644 --- a/kernel/src/driver/base/device/dd.rs +++ b/kernel/src/driver/base/device/dd.rs @@ -2,6 +2,7 @@ use core::intrinsics::unlikely; use alloc::{string::ToString, sync::Arc}; use intertrait::cast::CastArc; +use log::{debug, error, warn}; use crate::{ driver::base::kobject::KObject, @@ -59,20 +60,20 @@ impl DeviceManager { ) -> Result { if unlikely(allow_async) { // todo!("do_device_attach: allow_async") - kwarn!("do_device_attach: allow_async is true, but currently not supported"); + warn!("do_device_attach: allow_async is true, but currently not supported"); } if dev.is_dead() { return Ok(false); } - kwarn!("do_device_attach: dev: '{}'", dev.name()); + warn!("do_device_attach: dev: '{}'", dev.name()); let mut do_async = false; let mut r = Ok(false); if dev.driver().is_some() { if self.device_is_bound(dev) { - kdebug!( + debug!( "do_device_attach: device '{}' is already bound.", dev.name() ); @@ -86,7 +87,7 @@ impl DeviceManager { return Ok(false); } } else { - kdebug!("do_device_attach: device '{}' is not bound.", dev.name()); + debug!("do_device_attach: device '{}' is not bound.", dev.name()); let bus = dev .bus() .and_then(|bus| bus.upgrade()) @@ -116,7 +117,7 @@ impl DeviceManager { // try them. do_async = true; - kdebug!( + debug!( "do_device_attach: try scheduling asynchronous probe for device: {}", dev.name() ); @@ -141,6 +142,7 @@ impl DeviceManager { /// - Ok(true): 匹配成功 /// - Ok(false): 没有匹配成功 /// - Err(SystemError): 匹配过程中出现意外错误,没有匹配成功 + /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/base/dd.c#899 fn do_device_attach_driver( &self, @@ -153,7 +155,7 @@ impl DeviceManager { if let Err(e) = r { // 如果不是ENOSYS,则总线出错 if e != SystemError::ENOSYS { - kdebug!( + debug!( "do_device_attach_driver: bus.match_device() failed, dev: '{}', err: {:?}", data.dev.name(), e @@ -215,7 +217,7 @@ impl DeviceManager { } if let Err(e) = r.as_ref() { - kerror!( + error!( "device_bind_driver: driver_sysfs_add failed, dev: '{}', err: {:?}", dev.name(), e @@ -401,7 +403,7 @@ impl DriverManager { device.set_driver(Some(Arc::downgrade(driver))); self.add_to_sysfs(device).map_err(|e| { - kerror!( + error!( "really_probe: add_to_sysfs failed, dev: '{}', err: {:?}", device.name(), e @@ -412,7 +414,7 @@ impl DriverManager { })?; self.call_driver_probe(device, driver).map_err(|e| { - kerror!( + error!( "really_probe: call_driver_probe failed, dev: '{}', err: {:?}", device.name(), e @@ -427,7 +429,7 @@ impl DriverManager { device_manager() .add_groups(device, driver.dev_groups()) .map_err(|e| { - kerror!( + error!( "really_probe: add_groups failed, dev: '{}', err: {:?}", device.name(), e @@ -443,7 +445,7 @@ impl DriverManager { device_manager() .create_file(device, &DeviceAttrStateSynced) .map_err(|e| { - kerror!( + error!( "really_probe: create_file failed, dev: '{}', err: {:?}", device.name(), e @@ -483,17 +485,15 @@ impl DriverManager { sysfs_instance() .create_link(Some(&device_kobj), &driver_kobj, "driver".to_string()) - .map_err(|e| { + .inspect_err(|_e| { fail_rm_dev_link(); - e })?; device_manager() .create_file(device, &DeviceAttrCoredump) - .map_err(|e| { + .inspect_err(|_e| { sysfs_instance().remove_link(&device_kobj, "driver".to_string()); fail_rm_dev_link(); - e })?; return Ok(()); @@ -515,7 +515,7 @@ impl DriverManager { .ok_or(SystemError::EINVAL)?; let r = bus.probe(device); if r == Err(SystemError::ENOSYS) { - kerror!( + error!( "call_driver_probe: bus.probe() failed, dev: '{}', err: {:?}", device.name(), r @@ -530,7 +530,7 @@ impl DriverManager { let err = r.unwrap_err(); match err { SystemError::ENODEV | SystemError::ENXIO => { - kdebug!( + debug!( "driver'{}': probe of {} rejects match {:?}", driver.name(), device.name(), @@ -539,7 +539,7 @@ impl DriverManager { } _ => { - kwarn!( + warn!( "driver'{}': probe of {} failed with error {:?}", driver.name(), device.name(), @@ -555,7 +555,7 @@ impl DriverManager { /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/base/dd.c#393 fn driver_bound(&self, device: &Arc) { if self.driver_is_bound(device) { - kwarn!("driver_bound: device '{}' is already bound.", device.name()); + warn!("driver_bound: device '{}' is already bound.", device.name()); return; } @@ -600,7 +600,7 @@ impl Attribute for DeviceAttrStateSynced { fn show(&self, kobj: Arc, buf: &mut [u8]) -> Result { let dev = kobj.cast::().map_err(|kobj| { - kerror!( + error!( "Intertrait casting not implemented for kobj: {}", kobj.name() ); @@ -635,7 +635,7 @@ impl Attribute for DeviceAttrCoredump { fn store(&self, kobj: Arc, buf: &[u8]) -> Result { let dev = kobj.cast::().map_err(|kobj| { - kerror!( + error!( "Intertrait casting not implemented for kobj: {}", kobj.name() ); diff --git a/kernel/src/driver/base/device/driver.rs b/kernel/src/driver/base/device/driver.rs index 0df615e12..c2f77a598 100644 --- a/kernel/src/driver/base/device/driver.rs +++ b/kernel/src/driver/base/device/driver.rs @@ -15,6 +15,7 @@ use alloc::{ vec::Vec, }; use core::fmt::Debug; +use log::error; use system_error::SystemError; /// @brief: Driver error @@ -193,7 +194,7 @@ impl DriverManager { /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/base/driver.c#222 pub fn register(&self, driver: Arc) -> Result<(), SystemError> { let bus = driver.bus().and_then(|bus| bus.upgrade()).ok_or_else(|| { - kerror!( + error!( "DriverManager::register() failed: driver.bus() is None. Driver: '{:?}'", driver.name() ); @@ -203,7 +204,7 @@ impl DriverManager { let drv_name = driver.name(); let other = bus.find_driver_by_name(&drv_name); if other.is_some() { - kerror!( + error!( "DriverManager::register() failed: driver '{}' already registered", drv_name ); @@ -212,10 +213,10 @@ impl DriverManager { bus_manager().add_driver(&driver)?; - self.add_groups(&driver, driver.groups()).map_err(|e| { - bus_manager().remove_driver(&driver); - e - })?; + self.add_groups(&driver, driver.groups()) + .inspect_err(|_e| { + bus_manager().remove_driver(&driver); + })?; // todo: 发送uevent diff --git a/kernel/src/driver/base/device/init.rs b/kernel/src/driver/base/device/init.rs index 7138d6448..62f5e8004 100644 --- a/kernel/src/driver/base/device/init.rs +++ b/kernel/src/driver/base/device/init.rs @@ -1,16 +1,13 @@ use alloc::{string::ToString, sync::Arc}; +use log::info; -use crate::{ - driver::base::{ - device::{ - set_sys_dev_block_kset, set_sys_dev_char_kset, set_sys_devices_virtual_kset, - sys_dev_kset, sys_devices_kset, DeviceManager, DEVICES_KSET_INSTANCE, DEVICE_MANAGER, - DEV_KSET_INSTANCE, - }, - kobject::KObject, - kset::KSet, +use crate::driver::base::{ + device::{ + set_sys_dev_block_kset, set_sys_dev_char_kset, set_sys_devices_virtual_kset, sys_dev_kset, + sys_devices_kset, DeviceManager, DEVICES_KSET_INSTANCE, DEVICE_MANAGER, DEV_KSET_INSTANCE, }, - kinfo, + kobject::KObject, + kset::KSet, }; use system_error::SystemError; @@ -54,7 +51,7 @@ pub fn devices_init() -> Result<(), SystemError> { // 创建 `/sys/dev/block` 目录 { - // kdebug!("create /sys/dev/block"); + // debug!("create /sys/dev/block"); let dev_kset = sys_dev_kset(); let dev_block_kset = KSet::new("block".to_string()); let parent = dev_kset.clone() as Arc; @@ -69,7 +66,7 @@ pub fn devices_init() -> Result<(), SystemError> { // 创建 `/sys/dev/char` 目录 { - // kdebug!("create /sys/dev/char"); + // debug!("create /sys/dev/char"); let dev_kset = sys_dev_kset(); let dev_char_kset = KSet::new("char".to_string()); let parent = dev_kset.clone() as Arc; @@ -82,7 +79,7 @@ pub fn devices_init() -> Result<(), SystemError> { unsafe { set_sys_dev_char_kset(dev_char_kset) }; } - kinfo!("devices init success"); + info!("devices init success"); return Ok(()); } diff --git a/kernel/src/driver/base/device/mod.rs b/kernel/src/driver/base/device/mod.rs index 2281d124c..8803b02b7 100644 --- a/kernel/src/driver/base/device/mod.rs +++ b/kernel/src/driver/base/device/mod.rs @@ -3,6 +3,7 @@ use alloc::{ sync::{Arc, Weak}, }; use intertrait::cast::CastArc; +use log::{error, warn}; use crate::{ driver::{ @@ -75,7 +76,7 @@ static mut DEVICES_VIRTUAL_KSET_INSTANCE: Option> = None; /// 获取`/sys/devices`的kset实例 #[inline(always)] -pub(super) fn sys_devices_kset() -> Arc { +pub fn sys_devices_kset() -> Arc { unsafe { DEVICES_KSET_INSTANCE.as_ref().unwrap().clone() } } @@ -139,7 +140,7 @@ pub trait Device: KObject { /// 设备释放时的回调函数 fn release(&self) { let name = self.name(); - kwarn!( + warn!( "device {} does not have a release() function, it is broken and must be fixed.", name ); @@ -287,6 +288,7 @@ pub enum DeviceType { Intc, PlatformDev, Char, + Pci, } /// @brief: 设备标识符类型 @@ -481,7 +483,7 @@ impl DeviceManager { let actual_parent = self.get_device_parent(&device, current_parent)?; if let Some(actual_parent) = actual_parent { - // kdebug!( + // debug!( // "device '{}' parent is '{}', strong_count: {}", // device.name().to_string(), // actual_parent.name(), @@ -491,7 +493,7 @@ impl DeviceManager { } KObjectManager::add_kobj(device.clone() as Arc, None).map_err(|e| { - kerror!("add device '{:?}' failed: {:?}", device.name(), e); + error!("add device '{:?}' failed: {:?}", device.name(), e); e })?; @@ -551,10 +553,10 @@ impl DeviceManager { device: &Arc, current_parent: Option>, ) -> Result>, SystemError> { - // kdebug!("get_device_parent() device:{:?}", device.name()); + // debug!("get_device_parent() device:{:?}", device.name()); if device.class().is_some() { let parent_kobj: Arc; - // kdebug!("current_parent:{:?}", current_parent); + // debug!("current_parent:{:?}", current_parent); if let Some(cp) = current_parent { if cp.class().is_some() { return Ok(Some(cp.clone() as Arc)); @@ -644,18 +646,16 @@ impl DeviceManager { let parent_kobj = parent.clone() as Arc; sysfs_instance() .create_link(Some(&dev_kobj), &parent_kobj, "device".to_string()) - .map_err(|e| { + .inspect_err(|_e| { err_remove_subsystem(&dev_kobj); - e })?; } sysfs_instance() .create_link(Some(&subsys_kobj), &dev_kobj, dev.name()) - .map_err(|e| { + .inspect_err(|_e| { err_remove_device(&dev_kobj); err_remove_subsystem(&dev_kobj); - e })?; return Ok(()); @@ -693,18 +693,16 @@ impl DeviceManager { // 添加kobj_type的属性文件 if let Some(kobj_type) = dev.kobj_type() { self.add_groups(dev, kobj_type.attribute_groups().unwrap_or(&[])) - .map_err(|e| { + .inspect_err(|_e| { err_remove_class_groups(dev); - e })?; } // 添加设备本身的属性文件 self.add_groups(dev, dev.attribute_groups().unwrap_or(&[])) - .map_err(|e| { + .inspect_err(|_e| { err_remove_kobj_type_groups(dev); err_remove_class_groups(dev); - e })?; return Ok(()); @@ -755,7 +753,7 @@ impl DeviceManager { attr.mode().contains(ModeType::S_IRUGO) && (!attr.support().contains(SysFSOpsSupport::ATTR_SHOW)), ) { - kwarn!( + warn!( "Attribute '{}': read permission without 'show'", attr.name() ); @@ -764,7 +762,7 @@ impl DeviceManager { attr.mode().contains(ModeType::S_IWUGO) && (!attr.support().contains(SysFSOpsSupport::ATTR_STORE)), ) { - kwarn!( + warn!( "Attribute '{}': write permission without 'store'", attr.name() ); @@ -806,7 +804,7 @@ impl DeviceManager { /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/base/core.c?fi=device_links_force_bind#1226 pub fn device_links_force_bind(&self, _dev: &Arc) { - kwarn!("device_links_force_bind not implemented"); + warn!("device_links_force_bind not implemented"); } /// 把device对象的一些结构进行默认初始化 @@ -871,7 +869,7 @@ impl Attribute for DeviceAttrDev { fn show(&self, kobj: Arc, buf: &mut [u8]) -> Result { let dev = kobj.cast::().map_err(|kobj| { - kerror!( + error!( "Intertrait casting not implemented for kobj: {}", kobj.name() ); diff --git a/kernel/src/driver/base/kobject.rs b/kernel/src/driver/base/kobject.rs index a8659c44d..73e3c7eeb 100644 --- a/kernel/src/driver/base/kobject.rs +++ b/kernel/src/driver/base/kobject.rs @@ -6,13 +6,13 @@ use alloc::{ }; use driver_base_macros::get_weak_or_clear; use intertrait::CastFromSync; +use log::{debug, error}; use crate::{ filesystem::{ kernfs::KernFSInode, sysfs::{sysfs_instance, Attribute, AttributeGroup, SysFSOps, SysFSOpsSupport}, }, - kerror, libs::{ casting::DowncastArc, rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, @@ -213,7 +213,7 @@ impl KObjectManager { } kobj.set_parent(None); if e == SystemError::EEXIST { - kerror!("KObjectManager::add_kobj() failed with error: {e:?}, kobj:{kobj:?}"); + error!("KObjectManager::add_kobj() failed with error: {e:?}, kobj:{kobj:?}"); } return Err(e); @@ -269,7 +269,7 @@ pub struct DynamicKObjKType; impl KObjType for DynamicKObjKType { fn release(&self, kobj: Arc) { - kdebug!("DynamicKObjKType::release() kobj:{:?}", kobj.name()); + debug!("DynamicKObjKType::release() kobj:{:?}", kobj.name()); } fn sysfs_ops(&self) -> Option<&dyn SysFSOps> { diff --git a/kernel/src/driver/base/platform/platform_device.rs b/kernel/src/driver/base/platform/platform_device.rs index 50701f5c9..32163e009 100644 --- a/kernel/src/driver/base/platform/platform_device.rs +++ b/kernel/src/driver/base/platform/platform_device.rs @@ -64,6 +64,7 @@ pub trait PlatformDevice: Device { /// @brief: 判断设备是否初始化 /// @parameter: None /// @return: 如果已经初始化,返回true,否则,返回false + #[allow(dead_code)] fn is_initialized(&self) -> bool; /// @brief: 设置设备状态 diff --git a/kernel/src/driver/base/platform/platform_driver.rs b/kernel/src/driver/base/platform/platform_driver.rs index b9cda0f75..d0d786863 100644 --- a/kernel/src/driver/base/platform/platform_driver.rs +++ b/kernel/src/driver/base/platform/platform_driver.rs @@ -16,6 +16,7 @@ use super::{platform_bus, platform_device::PlatformDevice}; /// /// 应当在所有实现这个trait的结构体上方,添加 `#[cast_to([sync] PlatformDriver)]`, /// 否则运行时将报错“该对象不是PlatformDriver” +#[allow(dead_code)] pub trait PlatformDriver: Driver { /// 检测设备是否能绑定到这个驱动 /// diff --git a/kernel/src/driver/base/platform/subsys.rs b/kernel/src/driver/base/platform/subsys.rs index 26eb3d888..57e5c2583 100644 --- a/kernel/src/driver/base/platform/subsys.rs +++ b/kernel/src/driver/base/platform/subsys.rs @@ -3,6 +3,7 @@ use alloc::{ sync::{Arc, Weak}, }; use intertrait::cast::CastArc; +use log::error; use super::{platform_device::PlatformDevice, platform_driver::PlatformDriver}; use crate::{ @@ -58,12 +59,12 @@ impl Bus for PlatformBus { fn probe(&self, device: &Arc) -> Result<(), SystemError> { let drv = device.driver().ok_or(SystemError::EINVAL)?; let pdrv = drv.cast::().map_err(|_|{ - kerror!("PlatformBus::probe() failed: device.driver() is not a PlatformDriver. Device: '{:?}'", device.name()); + error!("PlatformBus::probe() failed: device.driver() is not a PlatformDriver. Device: '{:?}'", device.name()); SystemError::EINVAL })?; let pdev = device.clone().cast::().map_err(|_| { - kerror!( + error!( "PlatformBus::probe() failed: device is not a PlatformDevice. Device: '{:?}'", device.name() ); diff --git a/kernel/src/driver/block/cache/cached_block_device.rs b/kernel/src/driver/block/cache/cached_block_device.rs index 29443042f..9d7f2020f 100644 --- a/kernel/src/driver/block/cache/cached_block_device.rs +++ b/kernel/src/driver/block/cache/cached_block_device.rs @@ -1,5 +1,6 @@ use alloc::{boxed::Box, vec::Vec}; use hashbrown::HashMap; +use log::debug; use crate::{driver::base::block::block_device::BlockId, libs::rwlock::RwLock}; @@ -15,6 +16,7 @@ static mut CMAPPER: Option = None; /// 该结构体向外提供BlockCache服务 pub struct BlockCache; +#[allow(static_mut_refs)] unsafe fn mapper() -> Result<&'static mut LockedCacheMapper, BlockCacheError> { unsafe { match &mut CMAPPER { @@ -24,6 +26,7 @@ unsafe fn mapper() -> Result<&'static mut LockedCacheMapper, BlockCacheError> { }; } +#[allow(static_mut_refs)] unsafe fn space() -> Result<&'static mut LockedCacheSpace, BlockCacheError> { unsafe { match &mut CSPACE { @@ -41,7 +44,7 @@ impl BlockCache { CSPACE = Some(LockedCacheSpace::new(CacheSpace::new())); CMAPPER = Some(LockedCacheMapper::new(CacheMapper::new())); } - kdebug!("BlockCache Initialized!"); + debug!("BlockCache Initialized!"); } /// # 函数的功能 /// 使用blockcache进行对块设备进行连续块的读操作 diff --git a/kernel/src/driver/block/virtio_blk.rs b/kernel/src/driver/block/virtio_blk.rs index 75fe897eb..2475e9f93 100644 --- a/kernel/src/driver/block/virtio_blk.rs +++ b/kernel/src/driver/block/virtio_blk.rs @@ -5,6 +5,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::{debug, error}; use system_error::SystemError; use unified_init::macros::unified_init; use virtio_drivers::device::blk::VirtIOBlk; @@ -63,7 +64,7 @@ pub fn virtio_blk_0() -> Option> { pub fn virtio_blk(transport: VirtIOTransport, dev_id: Arc) { let device = VirtIOBlkDevice::new(transport, dev_id); if let Some(device) = device { - kdebug!("VirtIOBlkDevice '{:?}' created", device.dev_id); + debug!("VirtIOBlkDevice '{:?}' created", device.dev_id); virtio_device_manager() .device_add(device.clone() as Arc) .expect("Add virtio blk failed"); @@ -89,7 +90,7 @@ impl VirtIOBlkDevice { let irq = transport.irq().map(|irq| IrqNumber::new(irq.data())); let device_inner = VirtIOBlk::::new(transport); if let Err(e) = device_inner { - kerror!("VirtIOBlkDevice '{dev_id:?}' create failed: {:?}", e); + error!("VirtIOBlkDevice '{dev_id:?}' create failed: {:?}", e); return None; } @@ -134,10 +135,9 @@ impl BlockDevice for VirtIOBlkDevice { .device_inner .read_blocks(lba_id_start, &mut buf[..count * LBA_SIZE]) .map_err(|e| { - kerror!( + error!( "VirtIOBlkDevice '{:?}' read_at_sync failed: {:?}", - self.dev_id, - e + self.dev_id, e ); SystemError::EIO })?; @@ -416,7 +416,7 @@ impl VirtIODriver for VirtIOBlkDriver { .arc_any() .downcast::() .map_err(|_| { - kerror!( + error!( "VirtIOBlkDriver::probe() failed: device is not a VirtIO block device. Device: '{:?}'", device.name() ); diff --git a/kernel/src/driver/clocksource/acpi_pm.rs b/kernel/src/driver/clocksource/acpi_pm.rs index 00265698e..e5b7d7a58 100644 --- a/kernel/src/driver/clocksource/acpi_pm.rs +++ b/kernel/src/driver/clocksource/acpi_pm.rs @@ -15,12 +15,13 @@ use acpi::fadt::Fadt; use alloc::sync::{Arc, Weak}; use core::intrinsics::unlikely; use core::sync::atomic::{AtomicU32, Ordering}; +use log::info; use system_error::SystemError; // 参考:https://code.dragonos.org.cn/xref/linux-6.6.21/drivers/clocksource/acpi_pm.c /// acpi_pmtmr所在的I/O端口 -pub static mut PMTMR_IO_PORT: AtomicU32 = AtomicU32::new(0); +pub static PMTMR_IO_PORT: AtomicU32 = AtomicU32::new(0); /// # 读取acpi_pmtmr当前值,并对齐进行掩码操作 #[inline(always)] @@ -87,8 +88,10 @@ impl Acpipm { max_idle_ns: Default::default(), flags: ClocksourceFlags::CLOCK_SOURCE_IS_CONTINUOUS, watchdog_last: CycleNum::new(0), + cs_last: CycleNum::new(0), uncertainty_margin: 0, maxadj: 0, + cycle_last: CycleNum::new(0), }; let acpi_pm = Arc::new(Acpipm(SpinLock::new(InnerAcpipm { data, @@ -116,14 +119,18 @@ impl Clocksource for Acpipm { fn update_clocksource_data(&self, data: ClocksourceData) -> Result<(), SystemError> { let d = &mut self.0.lock_irqsave().data; - d.set_flags(data.flags); - d.set_mask(data.mask); - d.set_max_idle_ns(data.max_idle_ns); - d.set_mult(data.mult); d.set_name(data.name); d.set_rating(data.rating); + d.set_mask(data.mask); + d.set_mult(data.mult); d.set_shift(data.shift); + d.set_max_idle_ns(data.max_idle_ns); + d.set_flags(data.flags); d.watchdog_last = data.watchdog_last; + d.cs_last = data.cs_last; + d.set_uncertainty_margin(data.uncertainty_margin); + d.set_maxadj(data.maxadj); + d.cycle_last = data.cycle_last; return Ok(()); } } @@ -170,6 +177,8 @@ const PMTMR_EXPECTED_RATE: u64 = #[cfg(not(target_arch = "x86_64"))] #[allow(dead_code)] fn verify_pmtmr_rate() -> bool { + use log::info; + let mut count: u32 = 0; mach_prepare_counter(); @@ -179,7 +188,7 @@ fn verify_pmtmr_rate() -> bool { let delta = (value2 - value1) & ACPI_PM_MASK; if (delta < (PMTMR_EXPECTED_RATE * 19) / 20) || (delta > (PMTMR_EXPECTED_RATE * 21) / 20) { - kinfo!( + info!( "PM Timer running at invalid rate: {}", 100 * delta / PMTMR_EXPECTED_RATE ); @@ -206,12 +215,13 @@ fn find_acpi_pm_clock() -> Result<(), SystemError> { let pm_timer_block = fadt.pm_timer_block().map_err(|_| SystemError::ENODEV)?; let pm_timer_block = pm_timer_block.ok_or(SystemError::ENODEV)?; let pmtmr_addr = pm_timer_block.address; - unsafe { - PMTMR_IO_PORT.store(pmtmr_addr as u32, Ordering::SeqCst); - } - kinfo!("apic_pmtmr I/O port: {}", unsafe { + + PMTMR_IO_PORT.store(pmtmr_addr as u32, Ordering::SeqCst); + + info!( + "apic_pmtmr I/O port: {}", PMTMR_IO_PORT.load(Ordering::SeqCst) - }); + ); return Ok(()); } @@ -222,18 +232,19 @@ fn find_acpi_pm_clock() -> Result<(), SystemError> { #[allow(dead_code)] pub fn init_acpi_pm_clocksource() -> Result<(), SystemError> { let acpi_pm = Acpipm::new(); - unsafe { - CLOCKSOURCE_ACPI_PM = Some(acpi_pm); - } // 解析fadt find_acpi_pm_clock()?; // 检查pmtmr_io_port是否被设置 - if unsafe { PMTMR_IO_PORT.load(Ordering::SeqCst) } == 0 { + if PMTMR_IO_PORT.load(Ordering::SeqCst) == 0 { return Err(SystemError::ENODEV); } + unsafe { + CLOCKSOURCE_ACPI_PM = Some(acpi_pm); + } + // 验证ACPI PM Timer作为时钟源的稳定性和一致性 for j in 0..ACPI_PM_MONOTONIC_CHECKS { let mut cnt = 100 * j; @@ -255,30 +266,28 @@ pub fn init_acpi_pm_clocksource() -> Result<(), SystemError> { if (value2 < value1) && (value2 < 0xfff) { break; } - kinfo!("PM Timer had inconsistens results: {} {}", value1, value2); - unsafe { - PMTMR_IO_PORT.store(0, Ordering::SeqCst); - } + info!("PM Timer had inconsistens results: {} {}", value1, value2); + + PMTMR_IO_PORT.store(0, Ordering::SeqCst); + return Err(SystemError::EINVAL); } if i == ACPI_PM_READ_CHECKS { - kinfo!("PM Timer failed consistency check: {}", value1); - unsafe { - PMTMR_IO_PORT.store(0, Ordering::SeqCst); - } + info!("PM Timer failed consistency check: {}", value1); + + PMTMR_IO_PORT.store(0, Ordering::SeqCst); + return Err(SystemError::EINVAL); } } // 检查ACPI PM Timer的频率是否正确 if !verify_pmtmr_rate() { - unsafe { - PMTMR_IO_PORT.store(0, Ordering::SeqCst); - } + PMTMR_IO_PORT.store(0, Ordering::SeqCst); } // 检查TSC时钟源的监视器是否被禁用,如果被禁用则将时钟源的标志设置为CLOCK_SOURCE_MUST_VERIFY - // 没有实现clocksource_selecet_watchdog函数,所以这里设置为false + // 是因为jiffies精度小于acpi pm,所以不需要被jiffies监视 let tsc_clocksource_watchdog_disabled = false; if tsc_clocksource_watchdog_disabled { clocksource_acpi_pm().0.lock_irqsave().data.flags |= @@ -287,13 +296,13 @@ pub fn init_acpi_pm_clocksource() -> Result<(), SystemError> { // 注册ACPI PM Timer let acpi_pmtmr = clocksource_acpi_pm() as Arc; - match acpi_pmtmr.register(100, PMTMR_TICKS_PER_SEC as u32) { + match acpi_pmtmr.register(1, PMTMR_TICKS_PER_SEC as u32) { Ok(_) => { - kinfo!("ACPI PM Timer registered as clocksource sccessfully"); + info!("ACPI PM Timer registered as clocksource sccessfully"); return Ok(()); } Err(_) => { - kinfo!("ACPI PM Timer init registered failed"); + info!("ACPI PM Timer init registered failed"); return Err(SystemError::ENOSYS); } }; diff --git a/kernel/src/driver/clocksource/timer_riscv.rs b/kernel/src/driver/clocksource/timer_riscv.rs index e5481f4ec..fad652ef4 100644 --- a/kernel/src/driver/clocksource/timer_riscv.rs +++ b/kernel/src/driver/clocksource/timer_riscv.rs @@ -20,12 +20,9 @@ use crate::{ }, libs::spinlock::SpinLock, mm::percpu::PerCpu, - process::ProcessManager, smp::core::smp_get_processor_id, time::{ - clocksource::HZ, - jiffies::NSEC_PER_JIFFY, - timer::{try_raise_timer_softirq, update_timer_jiffies}, + clocksource::HZ, tick_common::tick_handle_periodic, timer::try_raise_timer_softirq, TimeArch, }, }; @@ -37,25 +34,18 @@ static SBI_TIMER_INIT_BMP: SpinLock Result<(), SystemError> { // 更新下一次中断时间 - // kdebug!( + // debug!( // "riscv_sbi_timer: handle_irq: cpu_id: {}, time: {}", // smp_get_processor_id().data(), // CurrentTimeArch::get_cycles() as u64 // ); - ProcessManager::update_process_times(trap_frame.is_from_user()); - Self::update_nsec_passed_and_walltime(); + tick_handle_periodic(trap_frame); + compiler_fence(Ordering::SeqCst); sbi_rt::set_timer(CurrentTimeArch::get_cycles() as u64 + unsafe { INTERVAL_CNT } as u64); Ok(()) } @@ -68,30 +58,6 @@ impl RiscVSbiTimer { fn disable() { unsafe { riscv::register::sie::clear_stimer() }; } - - fn update_nsec_passed_and_walltime() { - if smp_get_processor_id().data() != 0 { - return; - } - - let cycles = CurrentTimeArch::get_cycles() as u64; - let nsec_passed = - CurrentTimeArch::cycles2ns((cycles - unsafe { HART0_LAST_UPDATED }) as usize); - unsafe { - HART0_LAST_UPDATED = cycles; - HART0_NSEC_PASSED += nsec_passed; - } - - let jiffies = unsafe { HART0_NSEC_PASSED } / NSEC_PER_JIFFY as usize; - unsafe { HART0_NSEC_PASSED %= NSEC_PER_JIFFY as usize }; - - update_timer_jiffies( - jiffies as u64, - (jiffies * NSEC_PER_JIFFY as usize / 1000) as i64, - ); - try_raise_timer_softirq(); - compiler_fence(Ordering::SeqCst); - } } /// riscv 初始化本地调度时钟源 diff --git a/kernel/src/driver/disk/ahci/ahcidisk.rs b/kernel/src/driver/disk/ahci/ahcidisk.rs index 4aaaa7993..2bbb15141 100644 --- a/kernel/src/driver/disk/ahci/ahcidisk.rs +++ b/kernel/src/driver/disk/ahci/ahcidisk.rs @@ -1,4 +1,5 @@ -use super::{_port, hba::HbaCmdTable, virt_2_phys}; +use super::{_port, hba::HbaCmdTable}; +use crate::arch::MMArch; use crate::driver::base::block::block_device::{BlockDevice, BlockId}; use crate::driver::base::block::disk_info::Partition; use crate::driver::base::class::Class; @@ -13,16 +14,14 @@ use crate::driver::disk::ahci::HBA_PxIS_TFES; use crate::filesystem::kernfs::KernFSInode; use crate::filesystem::mbr::MbrDiskPartionTable; +use crate::driver::disk::ahci::hba::{ + FisRegH2D, FisType, HbaCmdHeader, ATA_CMD_READ_DMA_EXT, ATA_CMD_WRITE_DMA_EXT, ATA_DEV_BUSY, + ATA_DEV_DRQ, +}; use crate::libs::rwlock::{RwLockReadGuard, RwLockWriteGuard}; use crate::libs::spinlock::SpinLock; -use crate::mm::{phys_2_virt, verify_area, VirtAddr}; -use crate::{ - driver::disk::ahci::hba::{ - FisRegH2D, FisType, HbaCmdHeader, ATA_CMD_READ_DMA_EXT, ATA_CMD_WRITE_DMA_EXT, - ATA_DEV_BUSY, ATA_DEV_DRQ, - }, - kerror, -}; +use crate::mm::{verify_area, MemoryManagementArch, PhysAddr, VirtAddr}; +use log::error; use system_error::SystemError; use alloc::sync::Weak; @@ -70,7 +69,7 @@ impl AhciDisk { compiler_fence(Ordering::SeqCst); let check_length = ((count - 1) >> 4) + 1; // prdt length if count * 512 > buf.len() || check_length > 8_usize { - kerror!("ahci read: e2big"); + error!("ahci read: e2big"); // 不可能的操作 return Err(SystemError::E2BIG); } else if count == 0 { @@ -88,9 +87,11 @@ impl AhciDisk { #[allow(unused_unsafe)] let cmdheader: &mut HbaCmdHeader = unsafe { - (phys_2_virt( + (MMArch::phys_2_virt(PhysAddr::new( volatile_read!(port.clb) as usize + slot as usize * size_of::(), - ) as *mut HbaCmdHeader) + )) + .unwrap() + .data() as *mut HbaCmdHeader) .as_mut() .unwrap() }; @@ -120,7 +121,9 @@ impl AhciDisk { #[allow(unused_unsafe)] let cmdtbl = unsafe { - (phys_2_virt(volatile_read!(cmdheader.ctba) as usize) as *mut HbaCmdTable) + (MMArch::phys_2_virt(PhysAddr::new(volatile_read!(cmdheader.ctba) as usize)) + .unwrap() + .data() as *mut HbaCmdTable) .as_mut() .unwrap() // 必须使用 as_mut ,得到的才是原来的变量 }; @@ -130,11 +133,14 @@ impl AhciDisk { // 清空整个table的旧数据 write_bytes(cmdtbl, 0, 1); } - // kdebug!("cmdheader.prdtl={}", volatile_read!(cmdheader.prdtl)); + // debug!("cmdheader.prdtl={}", volatile_read!(cmdheader.prdtl)); // 8K bytes (16 sectors) per PRDT for i in 0..((volatile_read!(cmdheader.prdtl) - 1) as usize) { - volatile_write!(cmdtbl.prdt_entry[i].dba, virt_2_phys(buf_ptr) as u64); + volatile_write!( + cmdtbl.prdt_entry[i].dba, + MMArch::virt_2_phys(VirtAddr::new(buf_ptr)).unwrap().data() as u64 + ); cmdtbl.prdt_entry[i].dbc = 8 * 1024 - 1; volatile_set_bit!(cmdtbl.prdt_entry[i].dbc, 1 << 31, true); // 允许中断 prdt_entry.i buf_ptr += 8 * 1024; @@ -143,7 +149,10 @@ impl AhciDisk { // Last entry let las = (volatile_read!(cmdheader.prdtl) - 1) as usize; - volatile_write!(cmdtbl.prdt_entry[las].dba, virt_2_phys(buf_ptr) as u64); + volatile_write!( + cmdtbl.prdt_entry[las].dba, + MMArch::virt_2_phys(VirtAddr::new(buf_ptr)).unwrap().data() as u64 + ); cmdtbl.prdt_entry[las].dbc = ((tmp_count << 9) - 1) as u32; // 数据长度 volatile_set_bit!(cmdtbl.prdt_entry[las].dbc, 1 << 31, true); // 允许中断 @@ -181,25 +190,24 @@ impl AhciDisk { } if spin_count == SPIN_LIMIT { - kerror!("Port is hung"); + error!("Port is hung"); return Err(SystemError::EIO); } volatile_set_bit!(port.ci, 1 << slot, true); // Issue command - // kdebug!("To wait ahci read complete."); + // debug!("To wait ahci read complete."); // 等待操作完成 loop { if (volatile_read!(port.ci) & (1 << slot)) == 0 { break; } if (volatile_read!(port.is) & HBA_PxIS_TFES) > 0 { - kerror!("Read disk error"); + error!("Read disk error"); return Err(SystemError::EIO); } } - - if kbuf.is_some() { - buf.copy_from_slice(kbuf.as_ref().unwrap()); + if let Some(kbuf) = &kbuf { + buf.copy_from_slice(kbuf); } compiler_fence(Ordering::SeqCst); @@ -236,9 +244,11 @@ impl AhciDisk { compiler_fence(Ordering::SeqCst); #[allow(unused_unsafe)] let cmdheader: &mut HbaCmdHeader = unsafe { - (phys_2_virt( + (MMArch::phys_2_virt(PhysAddr::new( volatile_read!(port.clb) as usize + slot as usize * size_of::(), - ) as *mut HbaCmdHeader) + )) + .unwrap() + .data() as *mut HbaCmdHeader) .as_mut() .unwrap() }; @@ -275,7 +285,9 @@ impl AhciDisk { #[allow(unused_unsafe)] let cmdtbl = unsafe { - (phys_2_virt(volatile_read!(cmdheader.ctba) as usize) as *mut HbaCmdTable) + (MMArch::phys_2_virt(PhysAddr::new(volatile_read!(cmdheader.ctba) as usize)) + .unwrap() + .data() as *mut HbaCmdTable) .as_mut() .unwrap() }; @@ -289,7 +301,10 @@ impl AhciDisk { // 8K bytes (16 sectors) per PRDT for i in 0..((volatile_read!(cmdheader.prdtl) - 1) as usize) { - volatile_write!(cmdtbl.prdt_entry[i].dba, virt_2_phys(buf_ptr) as u64); + volatile_write!( + cmdtbl.prdt_entry[i].dba, + MMArch::virt_2_phys(VirtAddr::new(buf_ptr)).unwrap().data() as u64 + ); volatile_write_bit!(cmdtbl.prdt_entry[i].dbc, (1 << 22) - 1, 8 * 1024 - 1); // 数据长度 volatile_set_bit!(cmdtbl.prdt_entry[i].dbc, 1 << 31, true); // 允许中断 buf_ptr += 8 * 1024; @@ -298,7 +313,10 @@ impl AhciDisk { // Last entry let las = (volatile_read!(cmdheader.prdtl) - 1) as usize; - volatile_write!(cmdtbl.prdt_entry[las].dba, virt_2_phys(buf_ptr) as u64); + volatile_write!( + cmdtbl.prdt_entry[las].dba, + MMArch::virt_2_phys(VirtAddr::new(buf_ptr)).unwrap().data() as u64 + ); volatile_set_bit!(cmdtbl.prdt_entry[las].dbc, 1 << 31, true); // 允许中断 volatile_write_bit!( cmdtbl.prdt_entry[las].dbc, @@ -336,7 +354,7 @@ impl AhciDisk { break; } if (volatile_read!(port.is) & HBA_PxIS_TFES) > 0 { - kerror!("Write disk error"); + error!("Write disk error"); return Err(SystemError::EIO); } } diff --git a/kernel/src/driver/disk/ahci/hba.rs b/kernel/src/driver/disk/ahci/hba.rs index 2aa504405..aa324e654 100644 --- a/kernel/src/driver/disk/ahci/hba.rs +++ b/kernel/src/driver/disk/ahci/hba.rs @@ -2,7 +2,8 @@ use core::{intrinsics::size_of, ptr}; use core::sync::atomic::compiler_fence; -use crate::mm::phys_2_virt; +use crate::arch::MMArch; +use crate::mm::{MemoryManagementArch, PhysAddr}; /// 文件说明: 实现了 AHCI 中的控制器 HBA 的相关行为 @@ -43,6 +44,7 @@ pub enum HbaPortType { /// 声明了 HBA 的所有属性 #[repr(packed)] +#[allow(dead_code)] pub struct HbaPort { pub clb: u64, // 0x00, command list base address, 1K-byte aligned pub fb: u64, // 0x08, FIS base address, 256-byte aligned @@ -65,6 +67,7 @@ pub struct HbaPort { /// 全称 HBA Memory Register,是HBA的寄存器在内存中的映射 #[repr(packed)] +#[allow(dead_code)] pub struct HbaMem { pub cap: u32, // 0x00, Host capability pub ghc: u32, // 0x04, Global host control @@ -94,6 +97,7 @@ pub struct HbaPrdtEntry { /// HAB Command Table /// 每个 Port 一个 Table,主机和设备的交互都靠这个数据结构 #[repr(packed)] +#[allow(dead_code)] pub struct HbaCmdTable { // 0x00 pub cfis: [u8; 64], // Command FIS @@ -195,7 +199,13 @@ impl HbaPort { unsafe { compiler_fence(core::sync::atomic::Ordering::SeqCst); - ptr::write_bytes(phys_2_virt(clb as usize) as *mut u64, 0, 1024); + ptr::write_bytes( + MMArch::phys_2_virt(PhysAddr::new(clb as usize)) + .unwrap() + .data() as *mut u64, + 0, + 1024, + ); } // 赋值 fis base address @@ -204,20 +214,36 @@ impl HbaPort { volatile_write!(self.fb, fb); unsafe { compiler_fence(core::sync::atomic::Ordering::SeqCst); - ptr::write_bytes(phys_2_virt(fb as usize) as *mut u64, 0, 256); + ptr::write_bytes( + MMArch::phys_2_virt(PhysAddr::new(fb as usize)) + .unwrap() + .data() as *mut u64, + 0, + 256, + ); } // 赋值 command table base address // Command table offset: 40K + 8K*portno // Command table size = 256*32 = 8K per port - let mut cmdheaders = phys_2_virt(clb as usize) as *mut u64 as *mut HbaCmdHeader; + let mut cmdheaders = unsafe { + MMArch::phys_2_virt(PhysAddr::new(clb as usize)) + .unwrap() + .data() + } as *mut u64 as *mut HbaCmdHeader; for ctbas_value in ctbas.iter().take(32) { volatile_write!((*cmdheaders).prdtl, 0); // 一开始没有询问,prdtl = 0(预留了8个PRDT项的空间) volatile_write!((*cmdheaders).ctba, *ctbas_value); // 这里限制了 prdtl <= 8, 所以一共用了256bytes,如果需要修改,可以修改这里 compiler_fence(core::sync::atomic::Ordering::SeqCst); unsafe { - ptr::write_bytes(phys_2_virt(*ctbas_value as usize) as *mut u64, 0, 256); + ptr::write_bytes( + MMArch::phys_2_virt(PhysAddr::new(*ctbas_value as usize)) + .unwrap() + .data() as *mut u64, + 0, + 256, + ); } cmdheaders = (cmdheaders as usize + size_of::()) as *mut HbaCmdHeader; } @@ -262,6 +288,7 @@ pub enum FisType { } #[repr(packed)] +#[allow(dead_code)] pub struct FisRegH2D { // DWORD 0 pub fis_type: u8, // FIS_TYPE_REG_H2D diff --git a/kernel/src/driver/disk/ahci/mod.rs b/kernel/src/driver/disk/ahci/mod.rs index 778fc9a49..c72256efc 100644 --- a/kernel/src/driver/disk/ahci/mod.rs +++ b/kernel/src/driver/disk/ahci/mod.rs @@ -3,6 +3,7 @@ pub mod ahci_inode; pub mod ahcidisk; pub mod hba; +use crate::arch::MMArch; use crate::driver::base::block::disk_info::BLK_GF_AHCI; use crate::driver::block::cache::cached_block_device::BlockCache; // 依赖的rust工具包 @@ -10,21 +11,19 @@ use crate::driver::pci::pci::{ get_pci_device_structure_mut, PciDeviceStructure, PCI_DEVICE_LINKEDLIST, }; use crate::filesystem::devfs::devfs_register; -use crate::kerror; + +use crate::driver::disk::ahci::{ + ahcidisk::LockedAhciDisk, + hba::HbaMem, + hba::{HbaPort, HbaPortType}, +}; use crate::libs::rwlock::RwLockWriteGuard; use crate::libs::spinlock::{SpinLock, SpinLockGuard}; -use crate::mm::virt_2_phys; -use crate::{ - driver::disk::ahci::{ - ahcidisk::LockedAhciDisk, - hba::HbaMem, - hba::{HbaPort, HbaPortType}, - }, - kdebug, -}; +use crate::mm::{MemoryManagementArch, VirtAddr}; use ahci_inode::LockedAhciInode; use alloc::{boxed::Box, collections::LinkedList, format, string::String, sync::Arc, vec::Vec}; use core::sync::atomic::compiler_fence; +use log::{debug, error}; use system_error::SystemError; // 仅module内可见 全局数据区 hbr_port, disks @@ -90,22 +89,34 @@ pub fn ahci_init() -> Result<(), SystemError> { let tp = hba_mem_port.check_type(); match tp { HbaPortType::None => { - kdebug!(" Find a None type Disk."); + debug!(" Find a None type Disk."); } HbaPortType::Unknown(err) => { - kdebug!(" Find a Unknown({:?}) type Disk.", err); + debug!(" Find a Unknown({:?}) type Disk.", err); } _ => { - kdebug!(" Find a {:?} type Disk.", tp); + debug!(" Find a {:?} type Disk.", tp); // 计算地址 - let fb = virt_2_phys(ahci_port_base_vaddr + (32 << 10) + (j << 8)); - let clb = virt_2_phys(ahci_port_base_vaddr + (j << 10)); + let fb = unsafe { + MMArch::virt_2_phys(VirtAddr::new( + ahci_port_base_vaddr + (32 << 10) + (j << 8), + )) + } + .unwrap() + .data(); + let clb = unsafe { + MMArch::virt_2_phys(VirtAddr::new(ahci_port_base_vaddr + (j << 10))) + .unwrap() + .data() + }; let ctbas = (0..32) - .map(|x| { - virt_2_phys( + .map(|x| unsafe { + MMArch::virt_2_phys(VirtAddr::new( ahci_port_base_vaddr + (40 << 10) + (j << 13) + (x << 8), - ) as u64 + )) + .unwrap() + .data() as u64 }) .collect::>(); @@ -122,7 +133,7 @@ pub fn ahci_init() -> Result<(), SystemError> { )?); id += 1; // ID 从0开始 - kdebug!("start register ahci device"); + debug!("start register ahci device"); // 挂载到devfs上面去 let ret = devfs_register( @@ -130,7 +141,7 @@ pub fn ahci_init() -> Result<(), SystemError> { LockedAhciInode::new(disks_list.last().unwrap().clone()), ); if let Err(err) = ret { - kerror!( + error!( "Ahci_{} ctrl = {}, port = {} failed to register, error code = {:?}", id, hba_mem_index as u8, diff --git a/kernel/src/driver/firmware/efi/fdt.rs b/kernel/src/driver/firmware/efi/fdt.rs index 52ad8bb75..4276dfce5 100644 --- a/kernel/src/driver/firmware/efi/fdt.rs +++ b/kernel/src/driver/firmware/efi/fdt.rs @@ -3,6 +3,7 @@ use core::fmt::Debug; use fdt::Fdt; +use log::error; use system_error::SystemError; use crate::init::boot_params; @@ -117,7 +118,7 @@ impl EFIManager { ) } .map_err(|e| { - kerror!("failed to parse fdt, err={:?}", e); + error!("failed to parse fdt, err={:?}", e); SystemError::EINVAL })?; @@ -145,7 +146,7 @@ impl EFIManager { self.do_get_fdt_prop(prop_type, &prop, &mut ret) .unwrap_or_else(|e| { - kerror!("Failed to get fdt prop: {prop_type:?}, error: {e:?}"); + error!("Failed to get fdt prop: {prop_type:?}, error: {e:?}"); }) } } diff --git a/kernel/src/driver/firmware/efi/init.rs b/kernel/src/driver/firmware/efi/init.rs index c425a8872..33a89213b 100644 --- a/kernel/src/driver/firmware/efi/init.rs +++ b/kernel/src/driver/firmware/efi/init.rs @@ -1,5 +1,6 @@ use core::{hint::spin_loop, intrinsics::unlikely, mem::size_of}; +use log::{error, info, warn}; use system_error::SystemError; use uefi_raw::table::boot::{MemoryAttribute, MemoryType}; @@ -21,17 +22,17 @@ use super::efi_manager; #[allow(dead_code)] #[inline(never)] pub fn efi_init() { - kinfo!("Initializing efi..."); + info!("Initializing efi..."); let data_from_fdt = efi_manager() .get_fdt_params() .expect("Failed to get fdt params"); if data_from_fdt.systable.is_none() { - kerror!("Failed to get systable from fdt"); + error!("Failed to get systable from fdt"); return; } - // kdebug!("to map memory table"); + // debug!("to map memory table"); // 映射mmap table if efi_manager().memmap_init_early(&data_from_fdt).is_err() { @@ -39,23 +40,23 @@ pub fn efi_init() { // 那么 UEFI memory map 就是我们拥有的关于内存的唯一描述, // 所以如果我们无法访问它,那么继续进行下去就没有什么意义了 - kerror!("Failed to initialize early memory map"); + error!("Failed to initialize early memory map"); loop { spin_loop(); } } - // kdebug!("NNNN"); - // kwarn!("BBBB, e:{:?}", SystemError::EINVAL); + // debug!("NNNN"); + // warn!("BBBB, e:{:?}", SystemError::EINVAL); let desc_version = efi_manager().desc_version(); if unlikely(desc_version != 1) { - kwarn!("Unexpected EFI memory map version: {}", desc_version); + warn!("Unexpected EFI memory map version: {}", desc_version); } let r = uefi_init(PhysAddr::new(data_from_fdt.systable.unwrap() as usize)); if let Err(e) = r { - kerror!("Failed to initialize UEFI: {:?}", e); + error!("Failed to initialize UEFI: {:?}", e); efi_manager().efi_memmap_unmap(); return; } @@ -97,7 +98,7 @@ pub fn efi_init() { // todo: Initialize screen info - kinfo!("UEFI init done!"); + info!("UEFI init done!"); } fn efi_find_mirror() { @@ -117,7 +118,7 @@ fn efi_find_mirror() { } if mirror_size > 0 { - kinfo!( + info!( "Memory: {}M/{}M mirrored memory", mirror_size >> 20, total_size >> 20 @@ -133,7 +134,7 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> { let err_unmap_systable = |st_vaddr: VirtAddr| { EarlyIoRemap::unmap(st_vaddr) .map_err(|e| { - kerror!("Failed to unmap system table: {e:?}"); + error!("Failed to unmap system table: {e:?}"); }) .ok(); }; @@ -143,7 +144,7 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> { let st_size = size_of::(); let st_vaddr = EarlyIoRemap::map_not_aligned(system_table, st_size, true).map_err(|e| { - kwarn!("Unable to map EFI system table, e:{e:?}"); + warn!("Unable to map EFI system table, e:{e:?}"); e })?; @@ -167,9 +168,8 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> { let st_ptr = st_vaddr.data() as *const uefi_raw::table::system::SystemTable; efi_manager() .check_system_table_header(unsafe { &st_ptr.as_ref().unwrap().header }, 2) - .map_err(|e| { + .inspect_err(|_| { err_unmap_systable(st_vaddr); - e })?; let st_ref = unsafe { st_ptr.as_ref().unwrap() }; @@ -195,7 +195,7 @@ fn uefi_init(system_table: PhysAddr) -> Result<(), SystemError> { true, ) .map_err(|e| { - kwarn!("Unable to map EFI configuration table, e:{e:?}"); + warn!("Unable to map EFI configuration table, e:{e:?}"); err_unmap_systable(st_vaddr); e })?; @@ -265,11 +265,11 @@ fn reserve_memory_regions() { let phys_start = page_align_down(md.phys_start as usize); let size = (page_count << (MMArch::PAGE_SHIFT as u64)) as usize; - // kdebug!("Reserve memory region: {:#x}-{:#x}({:#x}), is_memory: {}, is_usable_memory:{}, type: {:?}, att: {:?}", phys_start, phys_start + size, page_count, md.is_memory(), md.is_usable_memory(), md.ty, md.att); + // debug!("Reserve memory region: {:#x}-{:#x}({:#x}), is_memory: {}, is_usable_memory:{}, type: {:?}, att: {:?}", phys_start, phys_start + size, page_count, md.is_memory(), md.is_usable_memory(), md.ty, md.att); if md.is_memory() { open_firmware_fdt_driver().early_init_dt_add_memory(phys_start as u64, size as u64); if !md.is_usable_memory() { - // kdebug!( + // debug!( // "Marking non-usable memory as nomap: {:#x}-{:#x}", // phys_start, // phys_start + size diff --git a/kernel/src/driver/firmware/efi/memmap.rs b/kernel/src/driver/firmware/efi/memmap.rs index 1bfea15f8..c213d853b 100644 --- a/kernel/src/driver/firmware/efi/memmap.rs +++ b/kernel/src/driver/firmware/efi/memmap.rs @@ -1,5 +1,6 @@ use core::{intrinsics::unlikely, mem::size_of}; +use log::error; use system_error::SystemError; use crate::{ @@ -110,7 +111,7 @@ impl EFIManager { let offset = paddr.data() - page_align_down(paddr.data()); let map_size = data.mmap_size.unwrap() as usize + offset; - // kdebug!("do_efi_memmap_init: map_size={map_size:#x}"); + // debug!("do_efi_memmap_init: map_size={map_size:#x}"); // 映射内存 let mut vaddr = EarlyIoRemap::map( @@ -130,7 +131,7 @@ impl EFIManager { } if inner_guard.mmap.vaddr.is_none() { - kerror!("Cannot map the EFI memory map!"); + error!("Cannot map the EFI memory map!"); return Err(SystemError::ENOMEM); } diff --git a/kernel/src/driver/firmware/efi/mod.rs b/kernel/src/driver/firmware/efi/mod.rs index a07d4df26..4d0f9e3bf 100644 --- a/kernel/src/driver/firmware/efi/mod.rs +++ b/kernel/src/driver/firmware/efi/mod.rs @@ -1,3 +1,4 @@ +use log::{error, warn}; use system_error::SystemError; use crate::{ @@ -90,15 +91,14 @@ impl EFIManager { min_major: u16, ) -> Result<(), SystemError> { if header.signature != uefi_raw::table::system::SystemTable::SIGNATURE { - kerror!("System table signature mismatch!"); + error!("System table signature mismatch!"); return Err(SystemError::EINVAL); } if header.revision.major() < min_major { - kwarn!( + warn!( "System table version: {:?}, expected {}.00 or greater!", - header.revision, - min_major + header.revision, min_major ); } diff --git a/kernel/src/driver/firmware/efi/tables.rs b/kernel/src/driver/firmware/efi/tables.rs index bab605005..a6e9c3448 100644 --- a/kernel/src/driver/firmware/efi/tables.rs +++ b/kernel/src/driver/firmware/efi/tables.rs @@ -1,6 +1,7 @@ use core::{ffi::CStr, mem::size_of}; use hashbrown::Equivalent; +use log::{debug, error, info, warn}; use system_error::SystemError; use uefi_raw::table::{ boot::{MemoryAttribute, MemoryType}, @@ -66,16 +67,16 @@ impl EFIManager { } EarlyIoRemap::unmap(fw_ptr).map_err(|e|{ - kerror!("report systable header: failed to unmap systable header, fw_ptr: {fw_ptr:?}, err: {e:?}"); + error!("report systable header: failed to unmap systable header, fw_ptr: {fw_ptr:?}, err: {e:?}"); e }).ok(); } else { - kwarn!("report systable header: failed to map systable header, err: {fw_ptr:?}"); + warn!("report systable header: failed to map systable header, err: {fw_ptr:?}"); } let s = CStr::from_bytes_with_nul(&tmp_buf) .unwrap_or_else(|_| CStr::from_bytes_with_nul(b"Unknown\0").unwrap()); - kinfo!("EFI version: {:?}, vendor: {:?}", header.revision, s); + info!("EFI version: {:?}, vendor: {:?}", header.revision, s); } /// 解析EFI config table @@ -86,7 +87,7 @@ impl EFIManager { if let Some(r) = parser.match_table(table) { // 有匹配结果 if let Err(e) = r { - kwarn!( + warn!( "Failed to parse cfg table: '{}', err: {e:?}", parser.table.name() ); @@ -97,7 +98,7 @@ impl EFIManager { } if !flag { - kwarn!("Cannot find parser for guid: {:?}", table.vendor_guid); + warn!("Cannot find parser for guid: {:?}", table.vendor_guid); } } @@ -107,7 +108,7 @@ impl EFIManager { while !prev_paddr.is_null() { let vaddr = EarlyIoRemap::map_not_aligned(prev_paddr, MMArch::PAGE_SIZE, true) .map_err(|e| { - kerror!( + error!( "Failed to map UEFI memreserve table, paddr: {prev_paddr:?}, err: {e:?}" ); @@ -129,7 +130,7 @@ impl EFIManager { + size_of::() * psize, ) .map_err(|e| { - kerror!("Failed to reserve block, paddr: {prev_paddr:?}, err: {e:?}"); + error!("Failed to reserve block, paddr: {prev_paddr:?}, err: {e:?}"); EarlyIoRemap::unmap(vaddr).unwrap(); e })?; @@ -146,7 +147,7 @@ impl EFIManager { mem_block_manager() .reserve_block(PhysAddr::new(entry.base), entry.size) .map_err(|e| { - kerror!("Failed to reserve block, paddr: {prev_paddr:?}, err: {e:?}"); + error!("Failed to reserve block, paddr: {prev_paddr:?}, err: {e:?}"); EarlyIoRemap::unmap(vaddr).unwrap(); e })?; @@ -344,7 +345,7 @@ impl MatchTable for MatchTableMemReserve { ) -> Result<(), SystemError> { efi_manager().inner.write_irqsave().memreserve_table_paddr = Some(PhysAddr::new(table_raw.vendor_table as usize)); - kdebug!( + debug!( "memreserve_table_paddr: {:#x}", table_raw.vendor_table as usize ); @@ -374,7 +375,7 @@ impl MatchTable for MatchTableEsrt { ) -> Result<(), SystemError> { efi_manager().inner.write_irqsave().esrt_table_paddr = Some(PhysAddr::new(table_raw.vendor_table as usize)); - kdebug!("esrt_table_paddr: {:#x}", table_raw.vendor_table as usize); + debug!("esrt_table_paddr: {:#x}", table_raw.vendor_table as usize); return Ok(()); } } diff --git a/kernel/src/driver/input/ps2_dev/ps2_device.rs b/kernel/src/driver/input/ps2_dev/ps2_device.rs index 26050c7d5..a8a00d90d 100644 --- a/kernel/src/driver/input/ps2_dev/ps2_device.rs +++ b/kernel/src/driver/input/ps2_dev/ps2_device.rs @@ -1,4 +1,5 @@ use crate::driver::{base::device::Device, input::serio::serio_device::SerioDevice}; // todo: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/libps2.h#33 +#[allow(unused)] pub trait Ps2Device: Device + SerioDevice {} diff --git a/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs b/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs index e99f683d8..8da1c9fd3 100644 --- a/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs +++ b/kernel/src/driver/input/ps2_mouse/ps_mouse_device.rs @@ -6,6 +6,7 @@ use alloc::{ vec::Vec, }; use kdepends::ringbuffer::{AllocRingBuffer, RingBuffer}; +use log::{debug, error}; use system_error::SystemError; use crate::{ @@ -229,7 +230,7 @@ impl Ps2MouseDevice { self.send_command_to_ps2mouse(PsMouseCommand::EnablePacketStreaming) .map_err(|e| { - kerror!("ps2 mouse init error: {:?}", e); + error!("ps2 mouse init error: {:?}", e); e })?; self.read_data_port().ok(); @@ -313,7 +314,7 @@ impl Ps2MouseDevice { guard.current_state.y = self.get_y_movement(packet, flags); } - // kdebug!( + // debug!( // "Ps2MouseDevice packet : flags:{}, x:{}, y:{}\n", // guard.current_state.flags.bits, // guard.current_state.x, @@ -386,6 +387,7 @@ impl Ps2MouseDevice { Ok(()) } + #[allow(dead_code)] fn wait_for_read(&self) -> Result<(), SystemError> { let timeout = 100_000; for _ in 0..timeout { @@ -664,7 +666,7 @@ impl IndexNode for Ps2MouseDevice { impl Ps2Device for Ps2MouseDevice {} pub fn rs_ps2_mouse_device_init(parent: Arc) -> Result<(), SystemError> { - kdebug!("ps2_mouse_device initializing..."); + debug!("ps2_mouse_device initializing..."); let psmouse = Arc::new(Ps2MouseDevice::new()); device_manager().device_default_initialize(&(psmouse.clone() as Arc)); @@ -672,7 +674,7 @@ pub fn rs_ps2_mouse_device_init(parent: Arc) -> Result<(), SystemEr serio_device_manager().register_port(psmouse.clone() as Arc)?; devfs_register(&psmouse.name(), psmouse.clone()).map_err(|e| { - kerror!( + error!( "register psmouse device '{}' to devfs failed: {:?}", psmouse.name(), e diff --git a/kernel/src/driver/input/ps2_mouse/ps_mouse_driver.rs b/kernel/src/driver/input/ps2_mouse/ps_mouse_driver.rs index a9b76c338..baedcd4ed 100644 --- a/kernel/src/driver/input/ps2_mouse/ps_mouse_driver.rs +++ b/kernel/src/driver/input/ps2_mouse/ps_mouse_driver.rs @@ -3,6 +3,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::debug; use system_error::SystemError; use unified_init::macros::unified_init; @@ -276,10 +277,10 @@ impl SerioDriver for Ps2MouseDriver { #[unified_init(INITCALL_DEVICE)] fn ps2_mouse_driver_init() -> Result<(), SystemError> { - kdebug!("Ps2_mouse_drive initializing..."); + debug!("Ps2_mouse_drive initializing..."); let driver = Ps2MouseDriver::new(); serio_driver_manager().register(driver.clone())?; unsafe { PS2_MOUSE_DRIVER = Some(driver) }; - kdebug!("Ps2_mouse_drive initialized!"); + debug!("Ps2_mouse_drive initialized!"); return Ok(()); } diff --git a/kernel/src/driver/input/serio/i8042/mod.rs b/kernel/src/driver/input/serio/i8042/mod.rs index 31a2b1de5..a65453ac3 100644 --- a/kernel/src/driver/input/serio/i8042/mod.rs +++ b/kernel/src/driver/input/serio/i8042/mod.rs @@ -1,4 +1,5 @@ use alloc::sync::Arc; +use log::debug; use system_error::SystemError; use unified_init::macros::unified_init; @@ -33,7 +34,7 @@ pub fn i8042_platform_device() -> Arc { // TODO: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/input/serio/i8042.c#1612 #[unified_init(INITCALL_DEVICE)] pub fn i8042_init() -> Result<(), SystemError> { - kdebug!("i8042 initializing..."); + debug!("i8042 initializing..."); let i8042_device = Arc::new(I8042PlatformDevice::new()); device_manager().device_default_initialize(&(i8042_device.clone() as Arc)); platform_device_manager().device_add(i8042_device.clone() as Arc)?; @@ -46,14 +47,16 @@ pub fn i8042_init() -> Result<(), SystemError> { Ok(()) } -// TODO: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/input/serio/i8042.c#441 +/// TODO: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/input/serio/i8042.c#441 +#[allow(dead_code)] pub fn i8042_start(_serio: &Arc) -> Result<(), SystemError> { - todo!() + todo!("i8042_start") } -// TODO: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/input/serio/i8042.c#471 +/// TODO: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/input/serio/i8042.c#471 +#[allow(dead_code)] pub fn i8042_stop(_serio: &Arc) -> Result<(), SystemError> { - todo!() + todo!("i8042_stop") } /// # 函数的功能 diff --git a/kernel/src/driver/input/serio/serio_device.rs b/kernel/src/driver/input/serio/serio_device.rs index cb610ab21..a2041f429 100644 --- a/kernel/src/driver/input/serio/serio_device.rs +++ b/kernel/src/driver/input/serio/serio_device.rs @@ -8,6 +8,7 @@ use super::serio_bus; /// 串行设备,实现该trait的设备实例挂载在serio总线上,同时应该实现Device trait /// /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/serio.h#20 +#[allow(dead_code)] pub trait SerioDevice: Device { /// # 函数功能 /// diff --git a/kernel/src/driver/input/serio/serio_driver.rs b/kernel/src/driver/input/serio/serio_driver.rs index 8cc3c572d..c076a5c44 100644 --- a/kernel/src/driver/input/serio/serio_driver.rs +++ b/kernel/src/driver/input/serio/serio_driver.rs @@ -11,6 +11,7 @@ use super::{serio_bus, serio_device::SerioDevice}; /// 实现该trait的设备驱动实例应挂载在serio总线上,同时应该实现Driver trait /// /// 参考: https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/serio.h#67 +#[allow(dead_code)] pub trait SerioDriver: Driver { // 写入时唤醒设备 fn write_wakeup(&self, device: &Arc) -> Result<(), SystemError>; diff --git a/kernel/src/driver/input/serio/subsys.rs b/kernel/src/driver/input/serio/subsys.rs index d3291a855..afdd9349f 100644 --- a/kernel/src/driver/input/serio/subsys.rs +++ b/kernel/src/driver/input/serio/subsys.rs @@ -3,6 +3,7 @@ use alloc::{ sync::{Arc, Weak}, }; use intertrait::cast::CastArc; +use log::error; use system_error::SystemError; use crate::{ @@ -59,7 +60,7 @@ impl Bus for SerioBus { fn probe(&self, device: &Arc) -> Result<(), SystemError> { let drv = device.driver().ok_or(SystemError::EINVAL)?; let pdrv = drv.cast::().map_err(|_| { - kerror!( + error!( "SerioBus::probe() failed: device.driver() is not a SerioDriver. Device: '{:?}'", device.name() ); @@ -67,7 +68,7 @@ impl Bus for SerioBus { })?; let pdev = device.clone().cast::().map_err(|_| { - kerror!( + error!( "SerioBus::probe() failed: device is not a SerioDevice. Device: '{:?}'", device.name() ); diff --git a/kernel/src/driver/irqchip/riscv_intc.rs b/kernel/src/driver/irqchip/riscv_intc.rs index 2e83dbb3f..e14d25ad2 100644 --- a/kernel/src/driver/irqchip/riscv_intc.rs +++ b/kernel/src/driver/irqchip/riscv_intc.rs @@ -1,4 +1,5 @@ use alloc::{string::ToString, sync::Arc}; +use log::error; use system_error::SystemError; use crate::{ @@ -155,7 +156,7 @@ pub unsafe fn riscv_intc_init() -> Result<(), SystemError> { RiscvIntcChip::IRQ_SIZE, ) .ok_or_else(|| { - kerror!("Failed to create riscv-intc domain"); + error!("Failed to create riscv-intc domain"); SystemError::ENXIO })?; @@ -197,7 +198,7 @@ pub fn riscv_intc_assicate_irq(hwirq: HardwareIrqNumber) -> Option { irq_domain_manager() .domain_associate( riscv_intc_domain().as_ref().or_else(|| { - kerror!("riscv_intc_domain is None"); + error!("riscv_intc_domain is None"); None })?, virq, diff --git a/kernel/src/driver/irqchip/riscv_sifive_plic.rs b/kernel/src/driver/irqchip/riscv_sifive_plic.rs index 83e26916a..05bc52f0b 100644 --- a/kernel/src/driver/irqchip/riscv_sifive_plic.rs +++ b/kernel/src/driver/irqchip/riscv_sifive_plic.rs @@ -25,6 +25,7 @@ use alloc::{ }; use bitmap::AllocBitmap; use fdt::node::FdtNode; +use log::{debug, warn}; use system_error::SystemError; use crate::{ @@ -187,7 +188,7 @@ impl PlicHandler { fn plic_irq_toggle(cpumask: &CpuMask, irq_data: &Arc, enable: bool) { cpumask.iter_cpu().for_each(|cpu| { - kdebug!("plic: irq_toggle: cpu: {cpu:?}"); + debug!("plic: irq_toggle: cpu: {cpu:?}"); let handler = unsafe { plic_handlers().force_get(cpu) }; handler.toggle(irq_data.hardware_irq(), enable); }); @@ -240,7 +241,7 @@ impl IrqChip for PlicIrqChip { "SiFive PLIC" } fn irq_enable(&self, irq_data: &Arc) -> Result<(), SystemError> { - // kwarn!("plic: irq_enable"); + // warn!("plic: irq_enable"); let common_data = irq_data.common_data(); let inner_guard = common_data.inner(); let mask = inner_guard.effective_affinity(); @@ -252,7 +253,7 @@ impl IrqChip for PlicIrqChip { } fn irq_unmask(&self, irq_data: &Arc) -> Result<(), SystemError> { - // kwarn!("plic: irq_unmask"); + // warn!("plic: irq_unmask"); let chip_data = irq_data .chip_info_read_irqsave() @@ -300,7 +301,7 @@ impl IrqChip for PlicIrqChip { } fn irq_disable(&self, irq_data: &Arc) { - kdebug!("plic: irq_disable"); + debug!("plic: irq_disable"); let common_data = irq_data.common_data(); let inner_guard = common_data.inner(); let mask = inner_guard.effective_affinity(); @@ -321,7 +322,7 @@ impl IrqChip for PlicIrqChip { handler.toggle(irq_data.hardware_irq(), false); } else { - // kdebug!("plic: irq_eoi: hwirq: {:?}", irq_data.hardware_irq()); + // debug!("plic: irq_eoi: hwirq: {:?}", irq_data.hardware_irq()); unsafe { write_volatile( (handler.inner().hart_base + PlicIrqChip::CONTEXT_CLAIM).data() as *mut u32, @@ -423,7 +424,7 @@ pub fn riscv_sifive_plic_init() -> Result<(), SystemError> { }); for node in all_plics { if let Err(e) = do_riscv_sifive_plic_init(&node) { - kwarn!("Failed to init SiFive PLIC: node: {node:?} {e:?}"); + warn!("Failed to init SiFive PLIC: node: {node:?} {e:?}"); } } @@ -457,7 +458,7 @@ fn do_riscv_sifive_plic_init(fdt_node: &FdtNode) -> Result<(), SystemError> { .ok_or(SystemError::EINVAL)? .as_usize() .ok_or(SystemError::EINVAL)?; - kdebug!( + debug!( "plic: node: {}, irq_num: {irq_num}, paddr: {paddr:?}, size: {size}", fdt_node.name ); @@ -465,7 +466,7 @@ fn do_riscv_sifive_plic_init(fdt_node: &FdtNode) -> Result<(), SystemError> { .interrupts_extended() .ok_or(SystemError::EINVAL)? .count(); - kdebug!("plic: nr_contexts: {nr_contexts}"); + debug!("plic: nr_contexts: {nr_contexts}"); let irq_domain = irq_domain_manager() .create_and_add_linear( @@ -474,7 +475,7 @@ fn do_riscv_sifive_plic_init(fdt_node: &FdtNode) -> Result<(), SystemError> { (irq_num + 1) as u32, ) .ok_or(SystemError::EINVAL)?; - // kdebug!("plic: irq_domain: {irq_domain:?}"); + // debug!("plic: irq_domain: {irq_domain:?}"); let priv_data = PlicChipData::new( Arc::downgrade(&irq_domain), @@ -506,13 +507,13 @@ fn do_riscv_sifive_plic_init(fdt_node: &FdtNode) -> Result<(), SystemError> { let cpu = ProcessorId::new(i as u32); let handler = unsafe { plic_handlers().force_get(cpu) }; if handler.present() { - kwarn!("plic: handler {i} already present."); + warn!("plic: handler {i} already present."); handler.set_threshold(PlicIrqChip::PLIC_ENABLE_THRESHOLD); loop_done_setup(handler); continue; } - kdebug!("plic: setup lmask {cpu:?}."); + debug!("plic: setup lmask {cpu:?}."); priv_data.lmask().set(cpu, true); let mut handler_inner = handler.inner(); handler_inner.hart_base = @@ -560,7 +561,7 @@ fn associate_irq_with_plic_domain( let irq = irq as u32; let virq = IrqNumber::new(irq); let hwirq = HardwareIrqNumber::new(irq); - kdebug!("plic: associate irq: {irq}, virq: {virq:?}, hwirq: {hwirq:?}"); + debug!("plic: associate irq: {irq}, virq: {virq:?}, hwirq: {hwirq:?}"); irq_domain_manager() .domain_associate(irq_domain, virq, hwirq) .ok(); @@ -583,7 +584,7 @@ impl IrqDomainOps for PlicIrqDomainOps { hwirq: HardwareIrqNumber, virq: IrqNumber, ) -> Result<(), SystemError> { - // kdebug!("plic: map: virq: {virq:?}, hwirq: {hwirq:?}"); + // debug!("plic: map: virq: {virq:?}, hwirq: {hwirq:?}"); let chip_data = irq_domain.host_data().ok_or(SystemError::EINVAL)?; let plic_chip_data = chip_data @@ -613,7 +614,7 @@ impl IrqDomainOps for PlicIrqDomainOps { _irq_data: &Arc, _reserve: bool, ) -> Result<(), SystemError> { - kwarn!("plic: activate"); + warn!("plic: activate"); loop {} } @@ -622,7 +623,7 @@ impl IrqDomainOps for PlicIrqDomainOps { /// 处理PLIC中断 pub(super) fn do_plic_irq(trap_frame: &mut TrapFrame) { - // kdebug!("plic: do_plic_irq"); + // debug!("plic: do_plic_irq"); let handler = plic_handlers().get(); let priv_data = handler.priv_data(); @@ -648,11 +649,11 @@ pub(super) fn do_plic_irq(trap_frame: &mut TrapFrame) { if claim == 0 { break; } - kdebug!("plic: claim: {claim:?}"); + debug!("plic: claim: {claim:?}"); let hwirq = HardwareIrqNumber::new(claim); if let Err(e) = GenericIrqHandler::handle_domain_irq(domain.clone(), hwirq, trap_frame) { - kwarn!("plic: can't find mapping for hwirq {hwirq:?}, {e:?}"); + warn!("plic: can't find mapping for hwirq {hwirq:?}, {e:?}"); } } } diff --git a/kernel/src/driver/net/dma.rs b/kernel/src/driver/net/dma.rs index 11fcf6229..f8c06b74e 100644 --- a/kernel/src/driver/net/dma.rs +++ b/kernel/src/driver/net/dma.rs @@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags; use crate::arch::MMArch; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, PageFlags}; +use crate::mm::page::{page_manager_lock_irqsave, EntryFlags}; use crate::mm::{ allocator::page_frame::{ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, @@ -25,7 +25,7 @@ pub fn dma_alloc(pages: usize) -> (usize, NonNull) { // 清空这块区域,防止出现脏数据 core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); - let dma_flags: PageFlags = PageFlags::mmio_flags(); + let dma_flags: EntryFlags = EntryFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let kernel_mapper = kernel_mapper.as_mut().unwrap(); diff --git a/kernel/src/driver/net/e1000e/e1000e.rs b/kernel/src/driver/net/e1000e/e1000e.rs index c2a27806d..a144b1386 100644 --- a/kernel/src/driver/net/e1000e/e1000e.rs +++ b/kernel/src/driver/net/e1000e/e1000e.rs @@ -9,6 +9,7 @@ use core::mem::size_of; use core::ptr::NonNull; use core::slice::{from_raw_parts, from_raw_parts_mut}; use core::sync::atomic::{compiler_fence, Ordering}; +use log::{debug, info}; use super::e1000e_driver::e1000e_driver_init; use crate::driver::base::device::DeviceId; @@ -23,8 +24,6 @@ use crate::exception::IrqNumber; use crate::libs::volatile::{ReadOnly, Volatile, WriteOnly}; -use crate::{kdebug, kinfo}; - const PAGE_SIZE: usize = 4096; const NETWORK_CLASS: u8 = 0x2; const ETHERNET_SUBCLASS: u8 = 0x0; @@ -284,7 +283,7 @@ impl E1000EDevice { volwrite!(general_regs, ctrl, ctrl | E1000E_CTRL_SLU); } let status = unsafe { volread!(general_regs, status) }; - kdebug!("Status: {status:#X}"); + debug!("Status: {status:#X}"); // 读取设备的mac地址 // Read mac address @@ -442,7 +441,7 @@ impl E1000EDevice { buffer.set_length(desc.len as usize); rdt = index; unsafe { volwrite!(self.receive_regs, rdt0, rdt as u32) }; - // kdebug!("e1000e: receive packet"); + // debug!("e1000e: receive packet"); return Some(buffer); } @@ -559,7 +558,7 @@ impl Drop for E1000EDevice { fn drop(&mut self) { // 释放已分配的所有dma页 // free all dma pages we have allocated - kdebug!("droping..."); + debug!("droping..."); let recv_ring_length = PAGE_SIZE / size_of::(); let trans_ring_length = PAGE_SIZE / size_of::(); unsafe { @@ -590,10 +589,10 @@ impl Drop for E1000EDevice { pub fn e1000e_init() { match e1000e_probe() { Ok(_code) => { - kinfo!("Successfully init e1000e device!"); + info!("Successfully init e1000e device!"); } Err(_error) => { - kinfo!("Error occurred!"); + info!("Error occurred!"); } } } @@ -610,7 +609,7 @@ pub fn e1000e_probe() -> Result { if header.vendor_id == 0x8086 { // intel if E1000E_DEVICE_ID.contains(&header.device_id) { - kdebug!( + debug!( "Detected e1000e PCI device with device id {:#x}", header.device_id ); @@ -781,7 +780,8 @@ const E1000E_TXD_CMD_EOP: u8 = 1 << 0; const E1000E_TXD_CMD_IFCS: u8 = 1 << 1; const E1000E_TXD_CMD_RS: u8 = 1 << 3; -// E1000E驱动初始化过程中可能的错误 +/// E1000E驱动初始化过程中可能的错误 +#[allow(dead_code)] pub enum E1000EPciError { // 获取到错误类型的BAR(IO BAR) // An IO BAR was provided rather than a memory BAR. diff --git a/kernel/src/driver/net/e1000e/e1000e_driver.rs b/kernel/src/driver/net/e1000e/e1000e_driver.rs index 92ff60c45..fbac2f834 100644 --- a/kernel/src/driver/net/e1000e/e1000e_driver.rs +++ b/kernel/src/driver/net/e1000e/e1000e_driver.rs @@ -10,7 +10,6 @@ use crate::{ }, net::NetDevice, }, - kinfo, libs::spinlock::SpinLock, net::{generate_iface_id, NET_DEVICES}, time::Instant, @@ -24,6 +23,7 @@ use core::{ fmt::Debug, ops::{Deref, DerefMut}, }; +use log::info; use smoltcp::{ phy, wire::{self, HardwareAddress}, @@ -367,5 +367,5 @@ pub fn e1000e_driver_init(device: E1000EDevice) { NET_DEVICES .write_irqsave() .insert(iface.nic_id(), iface.clone()); - kinfo!("e1000e driver init successfully!\tMAC: [{}]", mac); + info!("e1000e driver init successfully!\tMAC: [{}]", mac); } diff --git a/kernel/src/driver/net/loopback.rs b/kernel/src/driver/net/loopback.rs new file mode 100644 index 000000000..32e4f07bb --- /dev/null +++ b/kernel/src/driver/net/loopback.rs @@ -0,0 +1,483 @@ +use crate::arch::rand::rand; +use crate::driver::base::class::Class; +use crate::driver::base::device::bus::Bus; +use crate::driver::base::device::driver::Driver; +use crate::driver::base::device::{Device, DeviceType, IdTable}; +use crate::driver::base::kobject::{KObjType, KObject, KObjectState}; +use crate::init::initcall::INITCALL_DEVICE; +use crate::libs::spinlock::SpinLock; +use crate::net::{generate_iface_id, NET_DEVICES}; +use crate::time::Instant; +use alloc::collections::VecDeque; +use alloc::fmt::Debug; +use alloc::string::{String, ToString}; +use alloc::sync::{Arc, Weak}; +use alloc::vec::Vec; +use core::cell::UnsafeCell; +use core::ops::{Deref, DerefMut}; +use smoltcp::wire::HardwareAddress; +use smoltcp::{ + phy::{self}, + wire::{IpAddress, IpCidr}, +}; +use system_error::SystemError; +use unified_init::macros::unified_init; + +use super::NetDevice; + +const DEVICE_NAME: &str = "loopback"; + +/// ## 环回接收令牌 +/// 用于储存lo网卡接收到的数据 +pub struct LoopbackRxToken { + buffer: Vec, +} + +impl phy::RxToken for LoopbackRxToken { + /// ## 实现Rxtoken的consume函数 + /// 接受一个函数 `f`,并在 `self.buffer` 上调用它。 + /// + /// ## 参数 + /// - mut self :一个可变的 `LoopbackRxToken` 实例。 + /// - f :接受一个可变的 u8 切片,并返回类型 `R` 的结果。 + /// + /// ## 返回值 + /// 返回函数 `f` 在 `self.buffer` 上的调用结果。 + fn consume(mut self, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + f(self.buffer.as_mut_slice()) + } +} + +/// ## 环回发送令牌 +/// 返回驱动用于操作lo设备 +pub struct LoopbackTxToken { + driver: LoopbackDriver, +} + +impl phy::TxToken for LoopbackTxToken { + /// ## 实现TxToken的consume函数 + /// 向lo的队列推入待发送的数据报,实现环回 + /// + /// ## 参数 + /// - self + /// - len:数据包的长度 + /// - f:接受一个可变的 u8 切片,并返回类型 `R` 的结果。 + /// + /// ## 返回值 + /// 返回f对数据包操纵的结果 + fn consume(self, len: usize, f: F) -> R + where + F: FnOnce(&mut [u8]) -> R, + { + let mut buffer = vec![0; len]; + let result = f(buffer.as_mut_slice()); + let mut device = self.driver.inner.lock(); + device.loopback_transmit(buffer); + result + } +} + +/// ## Loopback设备 +/// 成员是一个队列,用来存放接受到的数据包。 +/// 当使用lo发送数据包时,不会把数据包传到link层,而是直接发送到该队列,实现环回。 +pub struct Loopback { + //回环设备的缓冲区,接受的数据包会存放在这里,发送的数据包也会发送到这里,实现环回 + queue: VecDeque>, +} + +impl Loopback { + /// ## Loopback创建函数 + /// 创建lo设备 + pub fn new() -> Self { + let queue = VecDeque::new(); + Loopback { queue } + } + /// ## Loopback处理接受到的数据包函数 + /// Loopback接受到数据后会调用这个函数来弹出接收的数据,返回给协议栈 + /// + /// ## 参数 + /// - &mut self :自身可变引用 + /// + /// ## 返回值 + /// - queue的头部数据包 + pub fn loopback_receive(&mut self) -> Vec { + let buffer = self.queue.pop_front(); + match buffer { + Some(buffer) => { + //debug!("lo receive:{:?}", buffer); + return buffer; + } + None => { + return Vec::new(); + } + } + } + /// ## Loopback发送数据包的函数 + /// Loopback发送数据包给自己的接收队列,实现环回 + /// + /// ## 参数 + /// - &mut self:自身可变引用 + /// - buffer:需要发送的数据包 + pub fn loopback_transmit(&mut self, buffer: Vec) { + //debug!("lo transmit!"); + self.queue.push_back(buffer) + } +} + +/// ## driver的包裹器 +/// 为实现获得不可变引用的Interface的内部可变性,故为Driver提供UnsafeCell包裹器 +/// +/// 参考virtio_net.rs +struct LoopbackDriverWapper(UnsafeCell); +unsafe impl Send for LoopbackDriverWapper {} +unsafe impl Sync for LoopbackDriverWapper {} + +/// ## deref 方法返回一个指向 `LoopbackDriver` 的引用。 +impl Deref for LoopbackDriverWapper { + type Target = LoopbackDriver; + fn deref(&self) -> &Self::Target { + unsafe { &*self.0.get() } + } +} +/// ## `deref_mut` 方法返回一个指向可变 `LoopbackDriver` 的引用。 +impl DerefMut for LoopbackDriverWapper { + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { &mut *self.0.get() } + } +} + +impl LoopbackDriverWapper { + /// ## force_get_mut返回一个指向可变 `LoopbackDriver` 的引用。 + #[allow(clippy::mut_from_ref)] + #[allow(clippy::mut_from_ref)] + fn force_get_mut(&self) -> &mut LoopbackDriver { + unsafe { &mut *self.0.get() } + } +} + +/// ## Loopback驱动 +/// 负责操作Loopback设备实现基本的网卡功能 +pub struct LoopbackDriver { + pub inner: Arc>, +} + +impl LoopbackDriver { + /// ## LoopbackDriver创建函数 + pub fn new() -> Self { + let inner = Arc::new(SpinLock::new(Loopback::new())); + LoopbackDriver { inner } + } +} + +impl Clone for LoopbackDriver { + fn clone(&self) -> Self { + LoopbackDriver { + inner: self.inner.clone(), + } + } +} + +impl phy::Device for LoopbackDriver { + type RxToken<'a> = LoopbackRxToken where Self: 'a; + type TxToken<'a> = LoopbackTxToken where Self: 'a; + /// ## 返回设备的物理层特性。 + /// lo设备的最大传输单元为65535,最大突发大小为1,传输介质默认为Ethernet + fn capabilities(&self) -> phy::DeviceCapabilities { + let mut result = phy::DeviceCapabilities::default(); + result.max_transmission_unit = 65535; + result.max_burst_size = Some(1); + result.medium = smoltcp::phy::Medium::Ethernet; + return result; + } + /// ## Loopback驱动处理接受数据事件 + /// 驱动调用Loopback的receive函数,处理buffer封装成(rx,tx)返回给上层 + /// + /// ## 参数 + /// - `&mut self` :自身可变引用 + /// - `_timestamp` + /// + /// ## 返回值 + /// - None: 如果接收队列为空,返回 `None`,以通知上层没有可以接收的包 + /// - Option::Some((rx, tx)):如果接收队列不为空,返回 `Some`,其中包含一个接收令牌 `rx` 和一个发送令牌 `tx` + fn receive( + &mut self, + _timestamp: smoltcp::time::Instant, + ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { + let buffer = self.inner.lock().loopback_receive(); + //receive队列为为空,返回NONE值以通知上层没有可以receive的包 + if buffer.is_empty() { + return Option::None; + } + let rx = LoopbackRxToken { buffer }; + let tx = LoopbackTxToken { + driver: self.clone(), + }; + return Option::Some((rx, tx)); + } + /// ## Loopback驱动处理发送数据包事件 + /// Loopback驱动在需要发送数据时会调用这个函数来获取一个发送令牌。 + /// + /// ## 参数 + /// - `&mut self` :自身可变引用 + /// - `_timestamp` + /// + /// ## 返回值 + /// - 返回一个 `Some`,其中包含一个发送令牌,该令牌包含一个对自身的克隆引用 + fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { + Some(LoopbackTxToken { + driver: self.clone(), + }) + } +} + +/// ## LoopbackInterface结构 +/// 封装驱动包裹器和iface,设置接口名称 +pub struct LoopbackInterface { + driver: LoopbackDriverWapper, + iface_id: usize, + iface: SpinLock, + name: String, +} + +impl LoopbackInterface { + /// ## `new` 是一个公共函数,用于创建一个新的 `LoopbackInterface` 实例。 + /// 生成一个新的接口 ID。创建一个新的接口配置,设置其硬件地址和随机种子,使用接口配置和驱动器创建一个新的 `smoltcp::iface::Interface` 实例。 + /// 设置接口的 IP 地址为 127.0.0.1。 + /// 创建一个新的 `LoopbackDriverWapper` 实例,包装驱动器。 + /// 创建一个新的 `LoopbackInterface` 实例,包含驱动器、接口 ID、接口和名称,并将其封装在一个 `Arc` 中。 + /// ## 参数 + /// - `driver`:一个 `LoopbackDriver` 实例,用于驱动网络环回操作。 + /// + /// ## 返回值 + /// 返回一个 `Arc`,即一个指向新创建的 `LoopbackInterface` 实例的智能指针。 + pub fn new(mut driver: LoopbackDriver) -> Arc { + let iface_id = generate_iface_id(); + let mut iface_config = smoltcp::iface::Config::new(HardwareAddress::Ethernet( + smoltcp::wire::EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]), + )); + iface_config.random_seed = rand() as u64; + + let mut iface = + smoltcp::iface::Interface::new(iface_config, &mut driver, Instant::now().into()); + //设置网卡地址为127.0.0.1 + iface.update_ip_addrs(|ip_addrs| { + ip_addrs + .push(IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)) + .unwrap(); + }); + let driver = LoopbackDriverWapper(UnsafeCell::new(driver)); + Arc::new(LoopbackInterface { + driver, + iface_id, + iface: SpinLock::new(iface), + name: "lo".to_string(), + }) + } +} + +impl Debug for LoopbackInterface { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("LoopbackInterface") + .field("iface_id", &self.iface_id) + .field("iface", &"smtoltcp::iface::Interface") + .field("name", &self.name) + .finish() + } +} +//TODO: 向sysfs注册lo设备 +impl KObject for LoopbackInterface { + fn as_any_ref(&self) -> &dyn core::any::Any { + self + } + + fn set_inode(&self, _inode: Option>) { + todo!() + } + + fn inode(&self) -> Option> { + todo!() + } + + fn parent(&self) -> Option> { + todo!() + } + + fn set_parent(&self, _parent: Option>) { + todo!() + } + + fn kset(&self) -> Option> { + todo!() + } + + fn set_kset(&self, _kset: Option>) { + todo!() + } + + fn kobj_type(&self) -> Option<&'static dyn crate::driver::base::kobject::KObjType> { + todo!() + } + + fn name(&self) -> String { + self.name.clone() + } + + fn set_name(&self, _name: String) { + todo!() + } + + fn kobj_state( + &self, + ) -> crate::libs::rwlock::RwLockReadGuard { + todo!() + } + + fn kobj_state_mut( + &self, + ) -> crate::libs::rwlock::RwLockWriteGuard { + todo!() + } + + fn set_kobj_state(&self, _state: KObjectState) { + todo!() + } + + fn set_kobj_type(&self, _ktype: Option<&'static dyn KObjType>) { + todo!() + } +} + +impl Device for LoopbackInterface { + fn dev_type(&self) -> DeviceType { + DeviceType::Net + } + + fn id_table(&self) -> IdTable { + IdTable::new(DEVICE_NAME.to_string(), None) + } + + fn set_bus(&self, _bus: Option>) { + todo!() + } + + fn set_class(&self, _class: Option>) { + todo!() + } + + fn driver(&self) -> Option> { + todo!() + } + + fn set_driver(&self, _driver: Option>) { + todo!() + } + + fn is_dead(&self) -> bool { + todo!() + } + + fn can_match(&self) -> bool { + todo!() + } + + fn set_can_match(&self, _can_match: bool) { + todo!() + } + + fn state_synced(&self) -> bool { + true + } +} + +impl NetDevice for LoopbackInterface { + /// 由于lo网卡设备不是实际的物理设备,其mac地址需要手动设置为一个默认值,这里默认为0200000001 + fn mac(&self) -> smoltcp::wire::EthernetAddress { + let mac = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01]; + smoltcp::wire::EthernetAddress(mac) + } + + #[inline] + fn nic_id(&self) -> usize { + self.iface_id + } + + #[inline] + fn name(&self) -> String { + self.name.clone() + } + /// ## `update_ip_addrs` 用于更新接口的 IP 地址。 + /// + /// ## 参数 + /// - `&self` :自身引用 + /// - `ip_addrs` :一个包含 `smoltcp::wire::IpCidr` 的切片,表示要设置的 IP 地址和子网掩码 + /// + /// ## 返回值 + /// - 如果 `ip_addrs` 的长度不为 1,返回 `Err(SystemError::EINVAL)`,表示输入参数无效 + /// - 如果更新成功,返回 `Ok(())` + fn update_ip_addrs( + &self, + ip_addrs: &[smoltcp::wire::IpCidr], + ) -> Result<(), system_error::SystemError> { + if ip_addrs.len() != 1 { + return Err(SystemError::EINVAL); + } + + self.iface.lock().update_ip_addrs(|addrs| { + let dest = addrs.iter_mut().next(); + + if let Some(dest) = dest { + *dest = ip_addrs[0]; + } else { + addrs.push(ip_addrs[0]).expect("Push ipCidr failed: full"); + } + }); + return Ok(()); + } + /// ## `poll` 用于轮询接口的状态。 + /// + /// ## 参数 + /// - `&self` :自身引用 + /// - `sockets` :一个可变引用到 `smoltcp::iface::SocketSet`,表示要轮询的套接字集 + /// + /// ## 返回值 + /// - 如果轮询成功,返回 `Ok(())` + /// - 如果轮询失败,返回 `Err(SystemError::EAGAIN_OR_EWOULDBLOCK)`,表示需要再次尝试或者操作会阻塞 + fn poll(&self, sockets: &mut smoltcp::iface::SocketSet) -> Result<(), SystemError> { + let timestamp: smoltcp::time::Instant = Instant::now().into(); + let mut guard = self.iface.lock(); + let poll_res = guard.poll(timestamp, self.driver.force_get_mut(), sockets); + if poll_res { + return Ok(()); + } + return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); + } + + #[inline(always)] + fn inner_iface(&self) -> &SpinLock { + return &self.iface; + } +} + +pub fn loopback_probe() { + loopback_driver_init(); +} +/// ## lo网卡设备初始化函数 +/// 创建驱动和iface,初始化一个lo网卡,添加到全局NET_DEVICES中 +pub fn loopback_driver_init() { + let driver = LoopbackDriver::new(); + let iface = LoopbackInterface::new(driver); + + NET_DEVICES + .write_irqsave() + .insert(iface.iface_id, iface.clone()); +} + +/// ## lo网卡设备的注册函数 +#[unified_init(INITCALL_DEVICE)] +pub fn loopback_init() -> Result<(), SystemError> { + loopback_probe(); + return Ok(()); +} diff --git a/kernel/src/driver/net/mod.rs b/kernel/src/driver/net/mod.rs index ce1a149ba..18e5a3fe1 100644 --- a/kernel/src/driver/net/mod.rs +++ b/kernel/src/driver/net/mod.rs @@ -11,8 +11,9 @@ use system_error::SystemError; mod dma; pub mod e1000e; pub mod irq_handle; +pub mod loopback; pub mod virtio_net; - +#[allow(dead_code)] pub trait NetDevice: Device { /// @brief 获取网卡的MAC地址 fn mac(&self) -> EthernetAddress; diff --git a/kernel/src/driver/net/virtio_net.rs b/kernel/src/driver/net/virtio_net.rs index 1bc1f6a6d..58551e4a9 100644 --- a/kernel/src/driver/net/virtio_net.rs +++ b/kernel/src/driver/net/virtio_net.rs @@ -10,6 +10,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::{debug, error}; use smoltcp::{iface, phy, wire}; use unified_init::macros::unified_init; use virtio_drivers::device::net::VirtIONet; @@ -39,7 +40,6 @@ use crate::{ exception::{irqdesc::IrqReturn, IrqNumber}, filesystem::kernfs::KernFSInode, init::initcall::INITCALL_POSTCORE, - kerror, libs::{ rwlock::{RwLockReadGuard, RwLockWriteGuard}, spinlock::{SpinLock, SpinLockGuard}, @@ -357,12 +357,12 @@ impl phy::Device for VirtIONicDeviceInner { } fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { - // kdebug!("VirtioNet: transmit"); + // debug!("VirtioNet: transmit"); if self.inner.lock_irqsave().can_send() { - // kdebug!("VirtioNet: can send"); + // debug!("VirtioNet: can send"); return Some(VirtioNetToken::new(self.clone(), None)); } else { - // kdebug!("VirtioNet: can not send"); + // debug!("VirtioNet: can not send"); return None; } } @@ -418,14 +418,14 @@ pub fn virtio_net(transport: VirtIOTransport, dev_id: Arc) { match VirtIONet::::new(transport, 4096) { Ok(net) => net, Err(_) => { - kerror!("VirtIONet init failed"); + error!("VirtIONet init failed"); return; } }; let mac = wire::EthernetAddress::from_bytes(&driver_net.mac_address()); let dev_inner = VirtIONicDeviceInner::new(driver_net); let iface = VirtioInterface::new(dev_inner, dev_id); - kdebug!("To add virtio net: {}, mac: {}", iface.device_name(), mac); + debug!("To add virtio net: {}, mac: {}", iface.device_name(), mac); virtio_device_manager() .device_add(iface.clone() as Arc) .expect("Add virtio net failed"); @@ -471,7 +471,7 @@ impl NetDevice for VirtioInterface { let mut guard = self.iface.lock(); let poll_res = guard.poll(timestamp, self.device_inner.force_get_mut(), sockets); // todo: notify!!! - // kdebug!("Virtio Interface poll:{poll_res}"); + // debug!("Virtio Interface poll:{poll_res}"); if poll_res { return Ok(()); } @@ -596,7 +596,7 @@ impl VirtIODriver for VirtIONetDriver { .arc_any() .downcast::() .map_err(|_| { - kerror!( + error!( "VirtIONetDriver::probe() failed: device is not a VirtioInterface. Device: '{:?}'", device.name() ); diff --git a/kernel/src/driver/open_firmware/fdt.rs b/kernel/src/driver/open_firmware/fdt.rs index 2189ef5a3..aff55017d 100644 --- a/kernel/src/driver/open_firmware/fdt.rs +++ b/kernel/src/driver/open_firmware/fdt.rs @@ -4,6 +4,7 @@ use fdt::{ node::{FdtNode, NodeProperty}, Fdt, }; +use log::{debug, error, warn}; use system_error::SystemError; use crate::{ @@ -79,7 +80,7 @@ impl OpenFirmwareFdtDriver { let fdt_vaddr = boot_params().read().fdt().ok_or(SystemError::ENODEV)?; let fdt: Fdt<'_> = unsafe { fdt::Fdt::from_ptr(fdt_vaddr.as_ptr()).map_err(|e| { - kerror!("failed to parse fdt, err={:?}", e); + error!("failed to parse fdt, err={:?}", e); SystemError::EINVAL }) }?; @@ -91,7 +92,7 @@ impl OpenFirmwareFdtDriver { .expect("Failed to scan fdt root node."); self.early_init_scan_chosen(fdt).unwrap_or_else(|_| { - kwarn!("No `chosen` node found"); + warn!("No `chosen` node found"); }); self.early_init_scan_memory(fdt); @@ -106,13 +107,13 @@ impl OpenFirmwareFdtDriver { if let Some(prop) = node.property("#size-cells") { guard.root_size_cells = prop.as_usize().unwrap() as u32; - // kdebug!("fdt_root_size_cells={}", guard.root_size_cells); + // debug!("fdt_root_size_cells={}", guard.root_size_cells); } if let Some(prop) = node.property("#address-cells") { guard.root_addr_cells = prop.as_usize().unwrap() as u32; - // kdebug!("fdt_root_addr_cells={}", guard.root_addr_cells); + // debug!("fdt_root_addr_cells={}", guard.root_addr_cells); } return Ok(()); @@ -144,7 +145,7 @@ impl OpenFirmwareFdtDriver { // TODO: 拼接内核自定义的command line参数 - kdebug!("Command line: {}", boot_params().read().boot_cmdline_str()); + debug!("Command line: {}", boot_params().read().boot_cmdline_str()); return Ok(()); } @@ -194,7 +195,7 @@ impl OpenFirmwareFdtDriver { continue; } - kdebug!("Found memory: base={:#x}, size={:#x}", base, size); + debug!("Found memory: base={:#x}, size={:#x}", base, size); self.early_init_dt_add_memory(base, size); found_memory = true; } @@ -205,7 +206,7 @@ impl OpenFirmwareFdtDriver { #[cfg(target_arch = "x86_64")] pub fn early_init_dt_add_memory(&self, _base: u64, _size: u64) { - kBUG!("x86_64 should not call early_init_dt_add_memory"); + panic!("x86_64 should not call early_init_dt_add_memory"); } #[cfg(not(target_arch = "x86_64"))] @@ -220,7 +221,7 @@ impl OpenFirmwareFdtDriver { let mut size = size as usize; if size < (MMArch::PAGE_SIZE - (base & (!MMArch::PAGE_MASK))) { - kwarn!("Ignoring memory block {:#x}-{:#x}", base, base + size); + warn!("Ignoring memory block {:#x}-{:#x}", base, base + size); } if PhysAddr::new(base).check_aligned(MMArch::PAGE_SIZE) == false { @@ -231,11 +232,11 @@ impl OpenFirmwareFdtDriver { size = page_align_down(size); if base > MemBlockManager::MAX_MEMBLOCK_ADDR.data() { - kwarn!("Ignoring memory block {:#x}-{:#x}", base, base + size); + warn!("Ignoring memory block {:#x}-{:#x}", base, base + size); } if base + size - 1 > MemBlockManager::MAX_MEMBLOCK_ADDR.data() { - kwarn!( + warn!( "Ignoring memory range {:#x}-{:#x}", MemBlockManager::MAX_MEMBLOCK_ADDR.data() + 1, base + size @@ -244,13 +245,13 @@ impl OpenFirmwareFdtDriver { } if base + size < MemBlockManager::MIN_MEMBLOCK_ADDR.data() { - kwarn!("Ignoring memory range {:#x}-{:#x}", base, base + size); + warn!("Ignoring memory range {:#x}-{:#x}", base, base + size); return; } if base < MemBlockManager::MIN_MEMBLOCK_ADDR.data() { { - kwarn!( + warn!( "Ignoring memory range {:#x}-{:#x}", base, MemBlockManager::MIN_MEMBLOCK_ADDR.data() @@ -312,7 +313,7 @@ impl OpenFirmwareFdtDriver { if node.size() != 0 { let address = PhysAddr::new(node.address() as usize); let size = node.size(); - kdebug!("Reserve memory: {:?}-{:?}", address, address + size); + debug!("Reserve memory: {:?}-{:?}", address, address + size); mem_block_manager().reserve_block(address, size).unwrap(); } } @@ -406,7 +407,7 @@ fn reserved_mem_reserve_reg(node: &FdtNode<'_, '_>) -> Result<(), SystemError> { let mut reg_size = reg.value.len(); if reg_size > 0 && reg_size % t_len != 0 { - kerror!( + error!( "Reserved memory: invalid reg property in '{}', skipping node.", node.name ); @@ -431,18 +432,14 @@ fn reserved_mem_reserve_reg(node: &FdtNode<'_, '_>) -> Result<(), SystemError> { .early_init_dt_reserve_memory(PhysAddr::new(base as usize), size as usize, nomap) .is_ok() { - kdebug!( + debug!( "Reserved memory: base={:#x}, size={:#x}, nomap={}", - base, - size, - nomap + base, size, nomap ); } else { - kerror!( + error!( "Failed to reserve memory: base={:#x}, size={:#x}, nomap={}", - base, - size, - nomap + base, size, nomap ); } @@ -471,8 +468,7 @@ fn read_cell(reg_value: &[u8], base_index: usize, cells: usize) -> (u64, usize) 1 => { return ( u32::from_be_bytes(reg_value[base_index..base_index + 4].try_into().unwrap()) - .try_into() - .unwrap(), + .into(), next_base_index, ); } diff --git a/kernel/src/driver/pci/attr.rs b/kernel/src/driver/pci/attr.rs new file mode 100644 index 000000000..6d59122be --- /dev/null +++ b/kernel/src/driver/pci/attr.rs @@ -0,0 +1,160 @@ +use alloc::sync::Arc; +use intertrait::cast::CastArc; +use log::warn; +use system_error::SystemError; + +use crate::{ + driver::base::kobject::KObject, + filesystem::{ + sysfs::{ + file::sysfs_emit_str, Attribute, AttributeGroup, SysFSOpsSupport, SYSFS_ATTR_MODE_RO, + }, + vfs::syscall::ModeType, + }, +}; + +use super::device::PciDevice; +#[derive(Debug)] +pub struct BasicPciReadOnlyAttrs; + +impl AttributeGroup for BasicPciReadOnlyAttrs { + fn name(&self) -> Option<&str> { + None + } + + fn attrs(&self) -> &[&'static dyn Attribute] { + &[&Vendor, &DeviceID, &SubsystemVendor, &SubsystemDevice] + } + + fn is_visible( + &self, + _kobj: Arc, + attr: &'static dyn Attribute, + ) -> Option { + return Some(attr.mode()); + } +} + +#[derive(Debug)] +pub struct Vendor; + +impl Attribute for Vendor { + fn mode(&self) -> ModeType { + SYSFS_ATTR_MODE_RO + } + + fn name(&self) -> &str { + "vendor" + } + + fn show(&self, _kobj: Arc, _buf: &mut [u8]) -> Result { + let dev = _kobj + .cast::() + .map_err(|e: Arc| { + warn!("device:{:?} is not a pci device!", e); + SystemError::EINVAL + })?; + return sysfs_emit_str(_buf, &format!("0x{:04x}", dev.vendor())); + } + + fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { + todo!() + } + + fn support(&self) -> SysFSOpsSupport { + SysFSOpsSupport::ATTR_SHOW + } +} + +#[derive(Debug)] +pub struct DeviceID; + +impl Attribute for DeviceID { + fn mode(&self) -> ModeType { + SYSFS_ATTR_MODE_RO + } + + fn name(&self) -> &str { + "device" + } + + fn show(&self, _kobj: Arc, _buf: &mut [u8]) -> Result { + let dev = _kobj + .cast::() + .map_err(|e: Arc| { + warn!("device:{:?} is not a pci device!", e); + SystemError::EINVAL + })?; + return sysfs_emit_str(_buf, &format!("0x{:04x}", dev.device_id())); + } + + fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { + todo!() + } + + fn support(&self) -> SysFSOpsSupport { + SysFSOpsSupport::ATTR_SHOW + } +} + +#[derive(Debug)] +pub struct SubsystemVendor; + +impl Attribute for SubsystemVendor { + fn mode(&self) -> ModeType { + SYSFS_ATTR_MODE_RO + } + + fn name(&self) -> &str { + "subsystem_vendor" + } + + fn show(&self, _kobj: Arc, _buf: &mut [u8]) -> Result { + let dev = _kobj + .cast::() + .map_err(|e: Arc| { + warn!("device:{:?} is not a pci device!", e); + SystemError::EINVAL + })?; + return sysfs_emit_str(_buf, &format!("0x{:04x}", dev.subsystem_vendor())); + } + + fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { + todo!() + } + + fn support(&self) -> SysFSOpsSupport { + SysFSOpsSupport::ATTR_SHOW + } +} + +#[derive(Debug)] +pub struct SubsystemDevice; + +impl Attribute for SubsystemDevice { + fn mode(&self) -> ModeType { + SYSFS_ATTR_MODE_RO + } + + fn name(&self) -> &str { + "subsystem_device" + } + + fn show(&self, _kobj: Arc, _buf: &mut [u8]) -> Result { + let dev = _kobj + .cast::() + .map_err(|e: Arc| { + warn!("device:{:?} is not a pci device!", e); + SystemError::EINVAL + })?; + return sysfs_emit_str(_buf, &format!("0x{:04x}", dev.subsystem_device())); + } + + fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { + todo!() + } + + fn support(&self) -> SysFSOpsSupport { + SysFSOpsSupport::ATTR_SHOW + } +} diff --git a/kernel/src/driver/pci/dev_id.rs b/kernel/src/driver/pci/dev_id.rs new file mode 100644 index 000000000..f79904179 --- /dev/null +++ b/kernel/src/driver/pci/dev_id.rs @@ -0,0 +1,109 @@ +use alloc::sync::Arc; + +use super::device::PciDevice; +const PCI_ANY_ID: u32 = 0xffff_ffff; + +/// # 结构功能 +/// 该结构用于驱动和设备之间的识别,驱动会有一个支持的设备ID列表,而设备会自带一个ID,如果设备的ID在驱动的支持列表中,则驱动和设备就可以识别了 +/// 见https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/mod_devicetable.h#43 +#[derive(Debug, Copy, Clone)] +pub struct PciDeviceID { + vendor: u32, + device_id: u32, + subvendor: u32, + subdevice: u32, + class: u32, + class_mask: u32, + _driver_data: u64, + _override_only: u32, + /// 可能有些设备的识别方式比较特殊,那么可以通过设置该字段进行自定义的识别方式,只需要在PciSpecifiedData枚举中加入一个类型即可 + /// 若该字段不为None,则优先使用special_data进行识别; + /// 该字段是为了增加灵活性 + special_data: Option, +} + +impl PciDeviceID { + #[allow(dead_code)] + pub fn set_special(&mut self, data: PciSpecifiedData) { + self.special_data = Some(data); + } + + pub fn dummpy() -> Self { + return Self { + vendor: PCI_ANY_ID, + device_id: PCI_ANY_ID, + subvendor: PCI_ANY_ID, + subdevice: PCI_ANY_ID, + class: PCI_ANY_ID, + class_mask: PCI_ANY_ID, + _driver_data: 0, + _override_only: PCI_ANY_ID, + special_data: None, + }; + } + pub fn match_dev(&self, dev: &Arc) -> bool { + if let Some(d_data) = &dev.dynid().special_data { + return d_data.match_dev(self.special_data); + } + if let Some(s_data) = &self.special_data { + return s_data.match_dev(dev.dynid().special_data); + } else { + let d_id = dev.dynid(); + return self.general_match(d_id); + } + } + + /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/pci/pci.h?fi=pci_match_one_device#195 + pub fn general_match(&self, id: PciDeviceID) -> bool { + if (self.vendor == id.vendor() || self.vendor == PCI_ANY_ID) + && (self.device_id == id.device_id() || self.device_id == PCI_ANY_ID) + && (self.subvendor == id.subvendor() || self.subvendor == PCI_ANY_ID) + && (self.subdevice == id.subdevice() || self.subdevice == PCI_ANY_ID) + && self.class_check(&id) + { + return true; + } + return false; + } + + pub fn class_check(&self, id: &Self) -> bool { + return (self.class ^ id.class()) & self.class_mask == 0; + } + + pub fn vendor(&self) -> u32 { + self.vendor + } + + pub fn device_id(&self) -> u32 { + self.device_id + } + + pub fn subvendor(&self) -> u32 { + self.subvendor + } + + pub fn subdevice(&self) -> u32 { + self.subdevice + } + + pub fn class(&self) -> u32 { + self.class + } + + pub fn _class_mask(&self) -> u32 { + self.class_mask + } +} + +#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)] +pub enum PciSpecifiedData {} + +impl PciSpecifiedData { + pub fn match_dev(&self, data: Option) -> bool { + if let Some(data) = data { + return *self == data; + } else { + return false; + } + } +} diff --git a/kernel/src/driver/pci/device.rs b/kernel/src/driver/pci/device.rs new file mode 100644 index 000000000..2d139f52a --- /dev/null +++ b/kernel/src/driver/pci/device.rs @@ -0,0 +1,219 @@ +use alloc::{ + string::{String, ToString}, + sync::{Arc, Weak}, +}; +use system_error::SystemError; + +use crate::{ + driver::base::{ + device::{ + bus::Bus, device_manager, driver::Driver, Device, DeviceCommonData, DeviceType, IdTable, + }, + kobject::{KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState}, + kset::KSet, + }, + filesystem::kernfs::KernFSInode, + libs::{rwlock::RwLockWriteGuard, spinlock::SpinLock}, +}; + +use super::{ + dev_id::PciDeviceID, + subsys::{pci_bus, pci_bus_device}, +}; + +/// # 结构功能 +/// 该结构为Pci设备的管理器,使用该结构可以将pci设备添加到sysfs中 +pub struct PciDeviceManager; + +pub fn pci_device_manager() -> &'static PciDeviceManager { + &PciDeviceManager +} + +impl PciDeviceManager { + /// #函数的功能 + /// 将pci设备注册到sysfs中 + /// + /// ## 参数: + /// - 'pci_dev':需要添加的pci设备 + /// + /// ## 返回值: + /// - OK(()) :表示成功 + /// - Err(e) :失败原因 + pub fn device_add(&self, pci_dev: Arc) -> Result<(), SystemError> { + // pci设备一般放置在/sys/device/pci:xxxx下 + if pci_dev.parent().is_none() { + pci_dev.set_parent(Some(Arc::downgrade( + &(pci_bus_device() as Arc), + ))); + } + // 设置设备的总线 + pci_dev.set_bus(Some(Arc::downgrade(&(pci_bus() as Arc)))); + // 对设备进行默认的初始化 + device_manager().device_default_initialize(&(pci_dev.clone() as Arc)); + // 使用设备管理器注册设备,当设备被注册后,会根据它的总线字段,在对应的总线上扫描驱动,并尝试进行匹配 + let r = device_manager().add_device(pci_dev.clone() as Arc); + + if r.is_ok() { + //todo:这里可能还要处理一些设置成功后设备状态的变化 + return Ok(()); + } else { + //todo:这里可能有一些添加失败的处理 + return r; + } + } +} + +/// #trait功能 +/// 要进入sysfs的Pci设备应当实现的trait +pub trait PciDevice: Device { + /// # 函数的功能 + /// 返回本设备的PciDeviceID,该ID用于driver和device之间的匹配 + /// + /// ## 返回值 + /// - 'PciDeviceID' :本设备的PciDeviceID + fn dynid(&self) -> PciDeviceID; + + /// # 函数的功能 + /// 返回本设备的供应商(vendor)ID + /// + /// ## 返回值 + /// - u16 :表示供应商ID + fn vendor(&self) -> u16; + fn device_id(&self) -> u16; + fn subsystem_vendor(&self) -> u16; + fn subsystem_device(&self) -> u16; +} + +/// #结构功能 +/// 由于Pci总线本身就属于一个设备,故该结构代表Pci总线(控制器)本身 +/// 它对应/sys/device/pci +#[derive(Debug)] +#[cast_to([sync] Device)] +pub struct PciBusDevice { + // inner: SpinLock, + device_data: SpinLock, + kobj_data: SpinLock, + kobj_state: LockedKObjectState, + name: String, +} + +impl PciBusDevice { + pub fn new(parent: Option>) -> Arc { + let common_device = DeviceCommonData::default(); + let common_kobj = KObjectCommonData::default(); + let bus_device = Self { + device_data: SpinLock::new(common_device), + kobj_data: SpinLock::new(common_kobj), + kobj_state: LockedKObjectState::new(None), + name: "pci".to_string(), + }; + bus_device.set_parent(parent); + return Arc::new(bus_device); + } +} + +impl KObject for PciBusDevice { + fn as_any_ref(&self) -> &dyn core::any::Any { + self + } + + fn parent(&self) -> Option> { + self.kobj_data.lock().parent.clone() + } + + fn inode(&self) -> Option> { + self.kobj_data.lock().kern_inode.clone() + } + + fn set_inode(&self, inode: Option>) { + self.kobj_data.lock().kern_inode = inode; + } + + fn kobj_type(&self) -> Option<&'static dyn KObjType> { + self.kobj_data.lock().kobj_type + } + + fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { + self.kobj_data.lock().kobj_type = ktype + } + + fn kset(&self) -> Option> { + self.kobj_data.lock().kset.clone() + } + + fn kobj_state( + &self, + ) -> crate::libs::rwlock::RwLockReadGuard { + self.kobj_state.read() + } + + fn kobj_state_mut(&self) -> RwLockWriteGuard { + self.kobj_state.write() + } + + fn set_kobj_state(&self, state: KObjectState) { + *self.kobj_state.write() = state; + } + + fn name(&self) -> String { + self.name.clone() + } + + fn set_name(&self, _name: String) { + //do nothing; it's not supposed to change this struct's name + } + + fn set_kset(&self, kset: Option>) { + self.kobj_data.lock().kset = kset; + } + + fn set_parent(&self, parent: Option>) { + self.kobj_data.lock().parent = parent; + } +} + +impl Device for PciBusDevice { + fn dev_type(&self) -> DeviceType { + return DeviceType::Bus; + } + + fn id_table(&self) -> IdTable { + IdTable::new("pci".to_string(), None) + } + + fn bus(&self) -> Option> { + self.device_data.lock().bus.clone() + } + + fn set_bus(&self, bus: Option>) { + self.device_data.lock().bus = bus + } + + fn driver(&self) -> Option> { + self.device_data.lock().driver.clone()?.upgrade() + } + + fn is_dead(&self) -> bool { + false + } + + fn set_driver(&self, driver: Option>) { + self.device_data.lock().driver = driver; + } + + fn can_match(&self) -> bool { + todo!() + } + + fn set_can_match(&self, _can_match: bool) { + todo!() + } + + fn set_class(&self, _class: Option>) { + todo!() + } + + fn state_synced(&self) -> bool { + todo!() + } +} diff --git a/kernel/src/driver/pci/driver.rs b/kernel/src/driver/pci/driver.rs new file mode 100644 index 000000000..0643f2cba --- /dev/null +++ b/kernel/src/driver/pci/driver.rs @@ -0,0 +1,84 @@ +use alloc::{sync::Arc, vec::Vec}; +use system_error::SystemError; + +use crate::driver::base::device::{ + bus::Bus, + driver::{driver_manager, Driver}, +}; + +use super::{dev_id::PciDeviceID, device::PciDevice, subsys::pci_bus}; + +/// # trait功能 +/// Pci驱动应该实现的trait +/// +/// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/pci.h#907 +#[allow(dead_code)] +pub trait PciDriver: Driver { + /// # 函数的功能 + /// 对设备进行probe操作 + /// + /// ## 参数: + /// - 'device' :要进行probe的设备 + /// - 'id' :设备的ID(暂时不清楚为什么需要这个,依Linux实现是有ID的) + /// + /// ## 返回值: + /// - Ok:probe成功 + /// - Err:probe失败 + fn probe(&self, device: &Arc, id: &PciDeviceID) -> Result<(), SystemError>; + fn remove(&self, device: &Arc) -> Result<(), SystemError>; + fn shutdown(&self, device: &Arc) -> Result<(), SystemError>; + fn suspend(&self, device: &Arc) -> Result<(), SystemError>; + fn resume(&self, device: &Arc) -> Result<(), SystemError>; + /// # 函数的功能 + /// 向驱动中加入一个PciDeviceID,表示该驱动可以支持该ID的设备 + /// + /// ## 参数: + /// - 'id' :要添加的ID + /// + /// ## 返回值: + /// - 'Ok':添加成功 + /// - 'Err':添加失败 + fn add_dynid(&mut self, id: PciDeviceID) -> Result<(), SystemError>; + /// # 函数的功能 + /// 每个Pci驱动都应该持有一个支持ID的列表,并通过该函数进行访问 + /// + /// ## 返回值: + /// - 'Some(Vec)': 支持ID的列表 + /// - 'None':未能获取列表 + fn locked_dynid_list(&self) -> Option>>; + /// # 函数的功能 + /// 检测当前驱动是否支持目标设备 + /// + /// ## 参数: + /// - 'dev' :要检测的设备 + /// + /// ## 返回值: + /// - 'Some(Arc)': 如果支持,则返回支持的ID + /// - 'None': 不支持的设备 + fn match_dev(&self, dev: &Arc) -> Option> { + for i in self.locked_dynid_list()?.iter() { + if i.match_dev(dev) { + return Some(i.clone()); + } + } + return None; + } +} + +pub struct PciDriverManager; + +pub fn pci_driver_manager() -> &'static PciDriverManager { + &PciDriverManager +} + +impl PciDriverManager { + pub fn register(&self, driver: Arc) -> Result<(), SystemError> { + driver.set_bus(Some(Arc::downgrade(&(pci_bus() as Arc)))); + return driver_manager().register(driver as Arc); + } + + #[allow(dead_code)] + pub fn unregister(&self, driver: &Arc) { + driver_manager().unregister(&(driver.clone() as Arc)); + } +} diff --git a/kernel/src/driver/pci/ecam.rs b/kernel/src/driver/pci/ecam.rs index de1529c9c..e5f1f86c2 100644 --- a/kernel/src/driver/pci/ecam.rs +++ b/kernel/src/driver/pci/ecam.rs @@ -1,3 +1,5 @@ +use log::{error, warn}; + use crate::mm::PhysAddr; use super::{ @@ -11,27 +13,32 @@ pub fn pci_ecam_root_info_manager() -> &'static EcamRootInfoManager { } /// Ecam pci root info -#[derive(Clone, Copy)] +#[derive(Clone, Debug, Copy)] pub struct EcamRootInfo { - pub segement_group_number: SegmentGroupNumber, + /// 段组号 + pub segment_group_number: SegmentGroupNumber, + /// 该分组中的最小bus pub bus_begin: u8, + /// 该分组中的最大bus pub bus_end: u8, + /// 物理基地址 pub physical_address_base: PhysAddr, } impl EcamRootInfo { pub fn new( - segement_group_number: SegmentGroupNumber, + segment_group_number: SegmentGroupNumber, bus_begin: u8, bus_end: u8, physical_address_base: PhysAddr, ) -> Self { - Self { - segement_group_number, + let ecam_root_info = Self { + segment_group_number, bus_begin, bus_end, physical_address_base, - } + }; + return ecam_root_info; } } @@ -46,25 +53,24 @@ impl EcamRootInfoManager { /// /// - `ecam_root_info`: EcamRootInfo - 要添加的EcamRootInfo实例 pub fn add_ecam_root_info(&self, ecam_root_info: EcamRootInfo) { - if !pci_root_manager().has_root(ecam_root_info.segement_group_number) { + if !pci_root_manager().has_root(ecam_root_info.segment_group_number) { let root = PciRoot::new( - ecam_root_info.segement_group_number, + Some(ecam_root_info), PciCam::Ecam, - ecam_root_info.physical_address_base, ecam_root_info.bus_begin, ecam_root_info.bus_end, ); if let Err(err) = root { - kerror!("add_ecam_root_info(): failed to create PciRoot: {:?}", err); + error!("add_ecam_root_info(): failed to create PciRoot: {:?}", err); return; } pci_root_manager().add_pci_root(root.unwrap()); } else { - kwarn!( + warn!( "add_ecam_root_info(): root {} already exists", - ecam_root_info.segement_group_number + ecam_root_info.segment_group_number ); } } diff --git a/kernel/src/driver/pci/mod.rs b/kernel/src/driver/pci/mod.rs index 9973b1225..d443a4123 100644 --- a/kernel/src/driver/pci/mod.rs +++ b/kernel/src/driver/pci/mod.rs @@ -1,5 +1,12 @@ +pub mod attr; +pub mod dev_id; +pub mod device; +pub mod driver; pub mod ecam; #[allow(clippy::module_inception)] pub mod pci; pub mod pci_irq; +pub mod raw_device; pub mod root; +pub mod subsys; +pub mod test; diff --git a/kernel/src/driver/pci/pci.rs b/kernel/src/driver/pci/pci.rs index 0f37e59ec..b339529f9 100644 --- a/kernel/src/driver/pci/pci.rs +++ b/kernel/src/driver/pci/pci.rs @@ -1,20 +1,26 @@ #![allow(dead_code)] // 目前仅支持单主桥单Segment +use super::device::pci_device_manager; use super::pci_irq::{IrqType, PciIrqError}; +use super::raw_device::PciGeneralDevice; use super::root::{pci_root_0, PciRoot}; + use crate::arch::{PciArch, TraitPciArch}; +use crate::driver::pci::subsys::pci_bus_subsys_init; use crate::exception::IrqNumber; use crate::libs::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::mm::mmio_buddy::{mmio_pool, MMIOSpaceGuard}; use crate::mm::VirtAddr; -use crate::{kdebug, kerror, kinfo, kwarn}; + +use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; use alloc::{boxed::Box, collections::LinkedList}; use bitflags::bitflags; +use log::{debug, error, info, warn}; use core::{ convert::TryFrom, @@ -633,6 +639,8 @@ impl PciDeviceStructure for PciDeviceStructurePciToCardbusBridge { /// 用于访问PCI设备的功能配置空间的一组机制。 #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum PciCam { + /// PortIO配置访问机制 + Portiocam, /// PCI内存映射配置访问机制 /// /// 为每个设备功能提供256字节的配置空间访问。 @@ -647,6 +655,7 @@ impl PciCam { /// Returns the total size in bytes of the memory-mapped region. pub const fn size(self) -> u32 { match self { + Self::Portiocam => 0x100000, Self::MmioCam => 0x1000000, Self::Ecam => 0x10000000, } @@ -716,11 +725,18 @@ fn pci_read_header( }; match HeaderType::from(header_type & 0x7f) { HeaderType::Standard => { - let general_device = pci_read_general_device_header(header, &bus_device_function); - let box_general_device = Box::new(general_device); + let general_device: PciDeviceStructureGeneralDevice = + pci_read_general_device_header(header, &bus_device_function); + let box_general_device = Box::new(general_device.clone()); let box_general_device_clone = box_general_device.clone(); if add_to_list { PCI_DEVICE_LINKEDLIST.add(box_general_device); + //这里实际上不应该使用clone,因为raw是用于sysfs的结构,但是实际上pci设备是在PCI_DEVICE_LINKEDLIST链表上的, + //这就导致sysfs呈现的对pci设备的操控接口实际上操控的是pci设备描述符是一个副本 + //但是无奈这里没有使用Arc + //todo:修改pci设备描述符在静态链表中存在的方式,并修改这里的clone操作 + let raw = PciGeneralDevice::from(&general_device); + let _ = pci_device_manager().device_add(Arc::new(raw)); } Ok(box_general_device_clone) } @@ -950,7 +966,7 @@ fn pci_read_pci_to_cardbus_bridge_header( /// @brief 检查所有bus上的设备并将其加入链表 /// @return 成功返回ok(),失败返回失败原因 fn pci_check_all_buses() -> Result { - kinfo!("Checking all devices in PCI bus..."); + info!("Checking all devices in PCI bus..."); let busdevicefunction = BusDeviceFunction { bus: 0, device: 0, @@ -969,7 +985,7 @@ fn pci_check_all_buses() -> Result { /// @brief 检查特定设备并将其加入链表 /// @return 成功返回ok(),失败返回失败原因 fn pci_check_function(busdevicefunction: BusDeviceFunction) -> Result { - //kdebug!("PCI check function {}", busdevicefunction.function); + //debug!("PCI check function {}", busdevicefunction.function); let header = match pci_read_header(busdevicefunction, true) { Ok(header) => header, Err(PciError::GetWrongHeader) => { @@ -995,7 +1011,7 @@ fn pci_check_function(busdevicefunction: BusDeviceFunction) -> Result Result { - //kdebug!("PCI check device {}", device); + //debug!("PCI check device {}", device); let busdevicefunction = BusDeviceFunction { bus, device, @@ -1014,10 +1030,9 @@ fn pci_check_device(bus: u8, device: u8) -> Result { pci_check_function(busdevicefunction)?; let common_header = header.common_header(); if common_header.header_type & 0x80 != 0 { - kdebug!( + debug!( "Detected multi func device in bus{},device{}", - busdevicefunction.bus, - busdevicefunction.device + busdevicefunction.bus, busdevicefunction.device ); // 这是一个多function的设备,因此查询剩余的function for function in 1..8 { @@ -1034,7 +1049,7 @@ fn pci_check_device(bus: u8, device: u8) -> Result { /// @brief 检查该bus上的设备并将其加入链表 /// @return 成功返回ok(),失败返回失败原因 fn pci_check_bus(bus: u8) -> Result { - //kdebug!("PCI check bus {}", bus); + //debug!("PCI check bus {}", bus); for device in 0..32 { pci_check_device(bus, device)?; } @@ -1044,12 +1059,13 @@ fn pci_check_bus(bus: u8) -> Result { /// pci初始化函数 #[inline(never)] pub fn pci_init() { - kinfo!("Initializing PCI bus..."); + info!("Initializing PCI bus..."); + pci_bus_subsys_init().expect("Failed to init pci bus subsystem"); if let Err(e) = pci_check_all_buses() { - kerror!("pci init failed when checking bus because of error: {}", e); + error!("pci init failed when checking bus because of error: {}", e); return; } - kinfo!( + info!( "Total pci device and function num = {}", PCI_DEVICE_LINKEDLIST.num() ); @@ -1058,39 +1074,34 @@ pub fn pci_init() { let common_header = box_pci_device.common_header(); match box_pci_device.header_type() { HeaderType::Standard if common_header.status & 0x10 != 0 => { - kinfo!("Found pci standard device with class code ={} subclass={} status={:#x} cap_pointer={:#x} vendor={:#x}, device id={:#x},bdf={}", common_header.class_code, common_header.subclass, common_header.status, box_pci_device.as_standard_device().unwrap().capabilities_pointer,common_header.vendor_id, common_header.device_id,common_header.bus_device_function); + info!("Found pci standard device with class code ={} subclass={} status={:#x} cap_pointer={:#x} vendor={:#x}, device id={:#x},bdf={}", common_header.class_code, common_header.subclass, common_header.status, box_pci_device.as_standard_device().unwrap().capabilities_pointer,common_header.vendor_id, common_header.device_id,common_header.bus_device_function); } HeaderType::Standard => { - kinfo!( + info!( "Found pci standard device with class code ={} subclass={} status={:#x} ", - common_header.class_code, - common_header.subclass, - common_header.status + common_header.class_code, common_header.subclass, common_header.status ); } HeaderType::PciPciBridge if common_header.status & 0x10 != 0 => { - kinfo!("Found pci-to-pci bridge device with class code ={} subclass={} status={:#x} cap_pointer={:#x}", common_header.class_code, common_header.subclass, common_header.status, box_pci_device.as_pci_to_pci_bridge_device().unwrap().capability_pointer); + info!("Found pci-to-pci bridge device with class code ={} subclass={} status={:#x} cap_pointer={:#x}", common_header.class_code, common_header.subclass, common_header.status, box_pci_device.as_pci_to_pci_bridge_device().unwrap().capability_pointer); } HeaderType::PciPciBridge => { - kinfo!( + info!( "Found pci-to-pci bridge device with class code ={} subclass={} status={:#x} ", - common_header.class_code, - common_header.subclass, - common_header.status + common_header.class_code, common_header.subclass, common_header.status ); } HeaderType::PciCardbusBridge => { - kinfo!( + info!( "Found pcicardbus bridge device with class code ={} subclass={} status={:#x} ", - common_header.class_code, - common_header.subclass, - common_header.status + common_header.class_code, common_header.subclass, common_header.status ); } HeaderType::Unrecognised(_) => {} } } - kinfo!("PCI bus initialized."); + + info!("PCI bus initialized."); } /// An identifier for a PCI bus, device and function. @@ -1114,6 +1125,19 @@ impl BusDeviceFunction { self.device < 32 && self.function < 8 } } + +impl From for String { + /// # 函数的功能 + /// 这里提供一个由BusDeviceFunction到dddd:bb:vv.f字符串的转换函数,主要用于转换成设备的名称(pci设备的名称一般是诸如0000:00:00.1这种) + fn from(value: BusDeviceFunction) -> Self { + //需要注意,这里的0000应该是所谓的“域号”(Domain ID),但是尚不知道是如何获得的,故硬编码在这里 + //todo:实现域号的获取 + format!( + "0000:{:02x}:{:02x}.{}", + value.bus, value.device, value.function + ) + } +} ///实现BusDeviceFunction的Display trait,使其可以直接输出 impl Display for BusDeviceFunction { fn fmt(&self, f: &mut Formatter) -> fmt::Result { @@ -1313,7 +1337,7 @@ pub fn pci_bar_init( // A wrapping add is necessary to correctly handle the case of unused BARs, which read back // as 0, and should be treated as size 0. let size = (!(size_mask & 0xfffffff0)).wrapping_add(1); - //kdebug!("bar_orig:{:#x},size: {:#x}", bar_orig,size); + //debug!("bar_orig:{:#x},size: {:#x}", bar_orig,size); // Restore the original value. pci_root_0().write_config( bus_device_function, @@ -1353,7 +1377,7 @@ pub fn pci_bar_init( .create_mmio(size_want) .map_err(|_| PciError::CreateMmioError)?; space_guard = Arc::new(tmp); - //kdebug!("Pci bar init: mmio space: {space_guard:?}, paddr={paddr:?}, size_want={size_want}"); + //debug!("Pci bar init: mmio space: {space_guard:?}, paddr={paddr:?}, size_want={size_want}"); assert!( space_guard.map_phys(paddr, size_want).is_ok(), "pci_bar_init: map_phys failed" @@ -1389,7 +1413,7 @@ pub fn pci_bar_init( _ => {} } } - //kdebug!("pci_device_bar:{}", device_bar); + //debug!("pci_device_bar:{}", device_bar); return Ok(device_bar); } @@ -1427,7 +1451,7 @@ impl Iterator for CapabilityIterator { self.next_capability_offset = if next_offset == 0 { None } else if next_offset < 64 || next_offset & 0x3 != 0 { - kwarn!("Invalid next capability offset {:#04x}", next_offset); + warn!("Invalid next capability offset {:#04x}", next_offset); None } else { Some(next_offset) @@ -1475,7 +1499,7 @@ impl<'a> Iterator for ExternalCapabilityIterator<'a> { self.next_capability_offset = if next_offset == 0 { None } else if next_offset < 0x100 || next_offset & 0x3 != 0 { - kwarn!("Invalid next capability offset {:#04x}", next_offset); + warn!("Invalid next capability offset {:#04x}", next_offset); None } else { Some(next_offset) diff --git a/kernel/src/driver/pci/pci_irq.rs b/kernel/src/driver/pci/pci_irq.rs index 2246f0c9c..e31082dd1 100644 --- a/kernel/src/driver/pci/pci_irq.rs +++ b/kernel/src/driver/pci/pci_irq.rs @@ -6,6 +6,7 @@ use core::ptr::NonNull; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; +use log::error; use system_error::SystemError; use super::pci::{PciDeviceStructure, PciDeviceStructureGeneralDevice, PciError}; @@ -385,7 +386,7 @@ pub trait PciInterrupt: PciDeviceStructure { } Err(_) => { - kerror!( + error!( "Failed to request pci irq {} for device {}", irq_num.data(), &common_msg.irq_name @@ -548,7 +549,7 @@ pub trait PciInterrupt: PciDeviceStructure { } Err(_) => { - kerror!( + error!( "Failed to request pci irq {} for device {}", irq_num.data(), &common_msg.irq_name diff --git a/kernel/src/driver/pci/raw_device.rs b/kernel/src/driver/pci/raw_device.rs new file mode 100644 index 000000000..c2be0700e --- /dev/null +++ b/kernel/src/driver/pci/raw_device.rs @@ -0,0 +1,194 @@ +use core::any::Any; + +use alloc::{ + string::{String, ToString}, + sync::{Arc, Weak}, +}; + +use crate::{ + driver::base::{ + class::Class, + device::{bus::Bus, driver::Driver, Device, DeviceCommonData, DeviceType, IdTable}, + kobject::{KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState}, + kset::KSet, + }, + filesystem::{kernfs::KernFSInode, sysfs::AttributeGroup}, + libs::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, +}; + +use super::{ + attr::BasicPciReadOnlyAttrs, dev_id::PciDeviceID, device::PciDevice, + pci::PciDeviceStructureGeneralDevice, +}; +#[derive(Debug)] +#[cast_to([sync] Device)] +#[cast_to([sync] PciDevice)] +pub struct PciGeneralDevice { + device_data: RwLock, + kobj_data: RwLock, + name: RwLock>, + kobj_state: LockedKObjectState, + dev_id: PciDeviceID, + header: Arc, +} + +impl From<&PciDeviceStructureGeneralDevice> for PciGeneralDevice { + fn from(value: &PciDeviceStructureGeneralDevice) -> Self { + let value = Arc::new(value.clone()); + let name: String = value.common_header.bus_device_function.into(); + let kobj_state = LockedKObjectState::new(None); + let common_dev = RwLock::new(DeviceCommonData::default()); + let common_kobj = RwLock::new(KObjectCommonData::default()); + let dev_id = PciDeviceID::dummpy(); + + // dev_id.set_special(PciSpecifiedData::Virtio()); + let res = Self { + device_data: common_dev, + kobj_data: common_kobj, + kobj_state, + dev_id, + header: value, + name: RwLock::new(None), + }; + res.set_name(name); + res + } +} + +impl PciDevice for PciGeneralDevice { + fn dynid(&self) -> PciDeviceID { + self.dev_id + } + + fn vendor(&self) -> u16 { + self.header.common_header.vendor_id + } + + fn device_id(&self) -> u16 { + self.header.common_header.device_id + } + + fn subsystem_vendor(&self) -> u16 { + self.header.subsystem_vendor_id + } + + fn subsystem_device(&self) -> u16 { + self.header.subsystem_id + } +} + +impl Device for PciGeneralDevice { + fn attribute_groups(&self) -> Option<&'static [&'static dyn AttributeGroup]> { + Some(&[&BasicPciReadOnlyAttrs]) + } + + fn bus(&self) -> Option> { + self.device_data.read().bus.clone() + } + + fn class(&self) -> Option> { + let mut guard = self.device_data.write(); + let r = guard.class.clone()?.upgrade(); + if r.is_none() { + guard.class = None; + } + + return r; + } + + fn driver(&self) -> Option> { + self.device_data.read().driver.clone()?.upgrade() + } + + fn dev_type(&self) -> DeviceType { + DeviceType::Pci + } + + fn id_table(&self) -> IdTable { + IdTable::new("testPci".to_string(), None) + } + + fn can_match(&self) -> bool { + true + } + + fn is_dead(&self) -> bool { + false + } + + fn set_bus(&self, bus: Option>) { + self.device_data.write().bus = bus; + } + + fn set_can_match(&self, _can_match: bool) {} + + fn set_class(&self, class: Option>) { + self.device_data.write().class = class; + } + + fn set_driver(&self, driver: Option>) { + self.device_data.write().driver = driver + } + + fn state_synced(&self) -> bool { + true + } +} + +impl KObject for PciGeneralDevice { + fn as_any_ref(&self) -> &dyn Any { + self + } + + fn set_inode(&self, inode: Option>) { + self.kobj_data.write().kern_inode = inode; + } + + fn inode(&self) -> Option> { + self.kobj_data.read().kern_inode.clone() + } + + fn parent(&self) -> Option> { + self.kobj_data.read().parent.clone() + } + + fn set_parent(&self, parent: Option>) { + self.kobj_data.write().parent = parent; + } + + fn kset(&self) -> Option> { + self.kobj_data.read().kset.clone() + } + + fn set_kset(&self, kset: Option>) { + self.kobj_data.write().kset = kset; + } + + fn kobj_type(&self) -> Option<&'static dyn KObjType> { + self.kobj_data.read().kobj_type + } + + fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { + self.kobj_data.write().kobj_type = ktype; + } + + fn name(&self) -> String { + self.name.read().clone().unwrap() + } + + fn set_name(&self, name: String) { + *self.name.write() = Some(name); + } + + fn kobj_state(&self) -> RwLockReadGuard { + self.kobj_state.read() + } + + fn kobj_state_mut(&self) -> RwLockWriteGuard { + self.kobj_state.write() + } + + fn set_kobj_state(&self, state: KObjectState) { + *self.kobj_state.write() = state; + } +} diff --git a/kernel/src/driver/pci/root.rs b/kernel/src/driver/pci/root.rs index 2d7928847..c74c6cf9b 100644 --- a/kernel/src/driver/pci/root.rs +++ b/kernel/src/driver/pci/root.rs @@ -4,16 +4,17 @@ use alloc::sync::Arc; use hashbrown::HashMap; use crate::{ + arch::{PciArch, TraitPciArch}, libs::spinlock::{SpinLock, SpinLockGuard}, mm::{ mmio_buddy::{mmio_pool, MMIOSpaceGuard}, page::PAGE_2M_SIZE, - PhysAddr, }, }; -use super::pci::{ - BusDeviceFunction, ExternalCapabilityIterator, PciCam, PciError, SegmentGroupNumber, +use super::{ + ecam::EcamRootInfo, + pci::{BusDeviceFunction, ExternalCapabilityIterator, PciCam, PciError, SegmentGroupNumber}, }; lazy_static! { @@ -28,13 +29,14 @@ pub fn pci_root_manager() -> &'static PciRootManager { /// 代表一个PCI segement greoup. #[derive(Clone, Debug)] pub struct PciRoot { - pub physical_address_base: PhysAddr, //物理地址,acpi获取 - pub mmio_guard: Option>, //映射后的虚拟地址,为方便访问数据这里转化成指针 - pub segment_group_number: SegmentGroupNumber, //segement greoup的id - pub bus_begin: u8, //该分组中的最小bus - pub bus_end: u8, //该分组中的最大bus + pub ecam_root_info: Option, + pub mmio_guard: Option>, //映射后的虚拟地址,为方便访问数据这里转化成指针 /// 配置空间访问机制 pub cam: PciCam, + /// bus起始位置 + pub bus_begin: u8, + /// bus结束位置 + pub bus_end: u8, } ///线程间共享需要,该结构体只需要在初始化时写入数据,无需读写锁保证线程安全 @@ -43,11 +45,15 @@ unsafe impl Sync for PciRoot {} ///实现PciRoot的Display trait,自定义输出 impl core::fmt::Display for PciRoot { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - write!( - f, - "PCI Root with segement:{}, bus begin at {}, bus end at {}, physical address at {:?},mapped at {:?}", - self.segment_group_number, self.bus_begin, self.bus_end, self.physical_address_base, self.mmio_guard - ) + if let Some(ecam_root_info) = &self.ecam_root_info { + write!( + f, + "PCI Eacm Root with segment:{}, bus begin at {}, bus end at {}, physical address at {:?},mapped at {:?}", + ecam_root_info.segment_group_number, ecam_root_info.bus_begin, ecam_root_info.bus_end, ecam_root_info.physical_address_base, self.mmio_guard + ) + } else { + write!(f, "PCI Root cam is {:?}", self.cam,) + } } } @@ -69,29 +75,30 @@ impl PciRoot { /// /// - 成功执行后,结构体的内部状态将被初始化为包含映射后的虚拟地址。 pub fn new( - segment_group_number: SegmentGroupNumber, + ecam_root_info: Option, cam: PciCam, - phys_base: PhysAddr, bus_begin: u8, bus_end: u8, ) -> Result, PciError> { - assert_eq!(cam, PciCam::Ecam); let mut pci_root = Self { - physical_address_base: phys_base, + ecam_root_info, mmio_guard: None, - segment_group_number, + cam, bus_begin, bus_end, - cam, }; - pci_root.map()?; + + if ecam_root_info.is_some() { + pci_root.map()?; + } Ok(Arc::new(pci_root)) } - /// @brief 完成物理地址到虚拟地址的映射,并将虚拟地址加入mmio_base变量 - /// @return 返回错误或Ok(0) + + /// # 完成物理地址到虚拟地址的映射,并将虚拟地址加入mmio_base变量 + /// ## return 返回错误或Ok(0) fn map(&mut self) -> Result { - //kdebug!("bus_begin={},bus_end={}", self.bus_begin,self.bus_end); + //debug!("bus_begin={},bus_end={}", self.bus_begin,self.bus_end); let bus_number = (self.bus_end - self.bus_begin) as u32 + 1; let bus_number_double = (bus_number - 1) / 2 + 1; //一个bus占据1MB空间,计算全部bus占据空间相对于2MB空间的个数 @@ -104,7 +111,7 @@ impl PciRoot { self.mmio_guard = Some(space_guard.clone()); assert!(space_guard - .map_phys(self.physical_address_base, size) + .map_phys(self.ecam_root_info.unwrap().physical_address_base, size) .is_ok()); } return Ok(0); @@ -129,11 +136,12 @@ impl PciRoot { /// - 此函数计算出的地址需要是字对齐的(即地址与0x3对齐)。如果不是,将panic。 fn cam_offset(&self, bus_device_function: BusDeviceFunction, register_offset: u16) -> u32 { assert!(bus_device_function.valid()); - let bdf = ((bus_device_function.bus - self.bus_begin) as u32) << 8 + let bdf = ((bus_device_function.bus - self.ecam_root_info.unwrap().bus_begin) as u32) << 8 | (bus_device_function.device as u32) << 3 | bus_device_function.function as u32; let address = bdf << match self.cam { + PciCam::Portiocam => 4, PciCam::MmioCam => 8, PciCam::Ecam => 12, } | register_offset as u32; @@ -141,6 +149,7 @@ impl PciRoot { assert!(address & 0x3 == 0); address } + /// # read_config - 通过bus_device_function和offset读取相应位置寄存器的值(32位) /// /// 此函数用于通过指定的bus_device_function和register_offset读取PCI设备中相应位置的寄存器值。 @@ -154,12 +163,16 @@ impl PciRoot { /// /// - `u32`: 寄存器读值结果 pub fn read_config(&self, bus_device_function: BusDeviceFunction, register_offset: u16) -> u32 { - let address = self.cam_offset(bus_device_function, register_offset); - unsafe { - // Right shift to convert from byte offset to word offset. - ((self.mmio_guard.as_ref().unwrap().vaddr().data() as *mut u32) - .add((address >> 2) as usize)) - .read_volatile() + if self.ecam_root_info.is_some() { + let address = self.cam_offset(bus_device_function, register_offset); + unsafe { + // Right shift to convert from byte offset to word offset. + ((self.mmio_guard.as_ref().unwrap().vaddr().data() as *mut u32) + .add((address >> 2) as usize)) + .read_volatile() + } + } else { + PciArch::read_config(&bus_device_function, register_offset as u8) } } @@ -178,16 +191,21 @@ impl PciRoot { register_offset: u16, data: u32, ) { - let address = self.cam_offset(bus_device_function, register_offset); - // Safe because both the `mmio_base` and the address offset are properly aligned, and the - // resulting pointer is within the MMIO range of the CAM. - unsafe { - // Right shift to convert from byte offset to word offset. - ((self.mmio_guard.as_ref().unwrap().vaddr().data() as *mut u32) - .add((address >> 2) as usize)) - .write_volatile(data) + if self.ecam_root_info.is_some() { + let address = self.cam_offset(bus_device_function, register_offset); + // Safe because both the `mmio_base` and the address offset are properly aligned, and the + // resulting pointer is within the MMIO range of the CAM. + unsafe { + // Right shift to convert from byte offset to word offset. + ((self.mmio_guard.as_ref().unwrap().vaddr().data() as *mut u32) + .add((address >> 2) as usize)) + .write_volatile(data) + } + } else { + PciArch::write_config(&bus_device_function, register_offset as u8, data); } } + /// 返回迭代器,遍历pcie设备的external_capabilities #[allow(dead_code)] pub fn external_capabilities( @@ -233,9 +251,14 @@ impl PciRootManager { /// - `pci_root`: Arc,要添加的PciRoot的Arc指针 pub fn add_pci_root(&self, pci_root: Arc) { let mut inner = self.inner.lock(); - inner - .pci_root - .insert(pci_root.segment_group_number, pci_root); + + if let Some(ecam_root_info) = pci_root.ecam_root_info { + inner + .pci_root + .insert(ecam_root_info.segment_group_number, pci_root); + } else { + inner.pci_root.insert(pci_root.bus_begin as u16, pci_root); + } } /// # 检查是否存在PciRoot - 检查PciRootManager中是否存在指定segment_group_number的PciRoot diff --git a/kernel/src/driver/pci/subsys.rs b/kernel/src/driver/pci/subsys.rs new file mode 100644 index 000000000..c2e5ee393 --- /dev/null +++ b/kernel/src/driver/pci/subsys.rs @@ -0,0 +1,191 @@ +use alloc::{ + string::{String, ToString}, + sync::{Arc, Weak}, +}; +use intertrait::cast::CastArc; +use log::error; +use system_error::SystemError; + +use crate::{ + driver::base::{ + device::{ + bus::{bus_register, Bus}, + device_register, + driver::Driver, + sys_devices_kset, Device, + }, + kobject::KObject, + subsys::SubSysPrivate, + }, + filesystem::sysfs::AttributeGroup, +}; + +use super::{ + device::{PciBusDevice, PciDevice}, + driver::PciDriver, + test::pt_init, +}; + +static mut PCI_BUS_DEVICE: Option> = None; +static mut PCI_BUS: Option> = None; + +pub(super) fn set_pci_bus_device(device: Arc) { + unsafe { + PCI_BUS_DEVICE = Some(device); + } +} + +pub(super) fn set_pci_bus(bus: Arc) { + unsafe { + PCI_BUS = Some(bus); + } +} + +pub fn pci_bus_device() -> Arc { + unsafe { + return PCI_BUS_DEVICE.clone().unwrap(); + } +} + +pub fn pci_bus() -> Arc { + unsafe { + return PCI_BUS.clone().unwrap(); + } +} + +/// # 结构功能 +/// 该结构为Pci总线,由于总线也属于设备,故设此结构; +/// 此结构对应/sys/bus/pci +#[derive(Debug)] +pub struct PciBus { + private: SubSysPrivate, +} + +impl PciBus { + pub fn new() -> Arc { + let w: Weak = Weak::new(); + let private = SubSysPrivate::new("pci".to_string(), Some(w), None, &[]); + let bus = Arc::new(Self { private }); + bus + } +} + +impl Bus for PciBus { + fn name(&self) -> String { + return "pci".to_string(); + } + + fn dev_name(&self) -> String { + return self.name(); + } + + fn dev_groups(&self) -> &'static [&'static dyn AttributeGroup] { + return &[&PciDeviceAttrGroup]; + } + + fn subsystem(&self) -> &SubSysPrivate { + return &self.private; + } + + fn probe(&self, device: &Arc) -> Result<(), SystemError> { + let drv = device.driver().ok_or(SystemError::EINVAL)?; + let pci_drv = drv.cast::().map_err(|_| { + error!( + "PciBus::probe() failed: device.driver() is not a PciDriver. Device: '{:?}'", + device.name() + ); + SystemError::EINVAL + })?; + let pci_dev = device.clone().cast::().map_err(|_| { + error!( + "PciBus::probe() failed: device is not a PciDevice. Device: '{:?}'", + device.name() + ); + SystemError::EINVAL + })?; + //见https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/pci/pci-driver.c#324 + let id = pci_drv.match_dev(&pci_dev).ok_or(SystemError::EINVAL)?; + pci_drv.probe(&pci_dev, &id) + } + + fn remove(&self, _device: &Arc) -> Result<(), SystemError> { + todo!() + } + + fn sync_state(&self, _device: &Arc) { + todo!() + } + + fn shutdown(&self, _device: &Arc) { + todo!() + } + + fn resume(&self, _device: &Arc) -> Result<(), SystemError> { + todo!() + } + + fn match_device( + &self, + device: &Arc, + driver: &Arc, + ) -> Result { + //首先将设备和驱动映射为pci设备和pci驱动 + let pci_driver = driver.clone().cast::().map_err(|_| { + return SystemError::EINVAL; + })?; + let pci_dev = device.clone().cast::().map_err(|_| { + return SystemError::EINVAL; + })?; + //pci_driver需要实现一个match_dev函数,即driver需要识别是否支持给定的pci设备 + //这是主要的match方式 + if pci_driver.match_dev(&pci_dev).is_some() { + return Ok(true); + } + + //todo:这里似乎需要一个driver_override_only的支持,但是目前不清楚driver_override_only 的用途,故暂时参考platform总线的match方法 + //override_only相关代码在 https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/pci/pci-driver.c#159 + if let Some(driver_id_table) = driver.id_table() { + if driver_id_table.name().eq(&pci_dev.name()) { + return Ok(true); + } + }; + return Ok(pci_dev.name().eq(&pci_driver.name())); + } +} + +#[derive(Debug)] +pub struct PciDeviceAttrGroup; + +impl AttributeGroup for PciDeviceAttrGroup { + fn name(&self) -> Option<&str> { + return None; + } + + fn attrs(&self) -> &[&'static dyn crate::filesystem::sysfs::Attribute] { + return &[]; + } + + fn is_visible( + &self, + _kobj: Arc, + attr: &'static dyn crate::filesystem::sysfs::Attribute, + ) -> Option { + return Some(attr.mode()); + } +} + +pub(super) fn pci_bus_subsys_init() -> Result<(), SystemError> { + let pci_bus_device: Arc = PciBusDevice::new(Some(Arc::downgrade( + &(sys_devices_kset() as Arc), + ))); + + set_pci_bus_device(pci_bus_device.clone()); + + device_register(pci_bus_device.clone())?; + let pci_bus = PciBus::new(); + + set_pci_bus(pci_bus.clone()); + let r = bus_register(pci_bus.clone() as Arc); + pt_init()?; + return r; +} diff --git a/kernel/src/driver/pci/test/mod.rs b/kernel/src/driver/pci/test/mod.rs new file mode 100644 index 000000000..4f0da79f2 --- /dev/null +++ b/kernel/src/driver/pci/test/mod.rs @@ -0,0 +1,30 @@ +use alloc::sync::Arc; +use system_error::SystemError; + +use self::{pt_device::TestDevice, pt_driver::TestDriver}; + +use super::{ + dev_id::PciDeviceID, + device::pci_device_manager, + driver::{pci_driver_manager, PciDriver}, +}; + +pub mod pt_device; +pub mod pt_driver; + +static mut TEST_DRIVER: Option> = None; +static mut TEST_DEVICE: Option> = None; +pub fn pt_init() -> Result<(), SystemError> { + let tdev = Arc::new(TestDevice::new()); + let mut drv = TestDriver::new(); + drv.add_dynid(PciDeviceID::dummpy())?; + let tdrv = Arc::new(drv); + + let _ = pci_device_manager().device_add(tdev.clone()); + let _ = pci_driver_manager().register(tdrv.clone()); + unsafe { + TEST_DEVICE = Some(tdev); + TEST_DRIVER = Some(tdrv); + } + Ok(()) +} diff --git a/kernel/src/driver/pci/test/pt_device.rs b/kernel/src/driver/pci/test/pt_device.rs new file mode 100644 index 000000000..65d1a05b0 --- /dev/null +++ b/kernel/src/driver/pci/test/pt_device.rs @@ -0,0 +1,236 @@ +use core::any::Any; + +use alloc::{ + string::{String, ToString}, + sync::{Arc, Weak}, +}; +use system_error::SystemError; + +use crate::{ + driver::{ + base::{ + class::Class, + device::{bus::Bus, driver::Driver, Device, DeviceCommonData, DeviceType, IdTable}, + kobject::{KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState}, + kset::KSet, + }, + pci::{dev_id::PciDeviceID, device::PciDevice}, + }, + filesystem::{ + kernfs::KernFSInode, + sysfs::{ + file::sysfs_emit_str, Attribute, AttributeGroup, SysFSOpsSupport, SYSFS_ATTR_MODE_RO, + }, + vfs::syscall::ModeType, + }, + libs::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, +}; +#[derive(Debug)] +#[cast_to([sync] Device)] +#[cast_to([sync] PciDevice)] +/// # 结构功能 +/// 这是一个测试用的PciDevice,也可以作为新PciDevice的参考 +/// 它需要实现KObject PciDevice Device这些接口 +/// 并通过函数pci_device_manager().device_add()来将设备进行接入 +pub struct TestDevice { + device_data: RwLock, + kobj_data: RwLock, + kobj_state: LockedKObjectState, +} + +impl TestDevice { + pub fn new() -> Self { + let common_dev = RwLock::new(DeviceCommonData::default()); + let common_kobj = RwLock::new(KObjectCommonData::default()); + Self { + device_data: common_dev, + kobj_data: common_kobj, + kobj_state: LockedKObjectState::new(None), + } + } +} + +impl PciDevice for TestDevice { + fn dynid(&self) -> PciDeviceID { + PciDeviceID::dummpy() + } + + fn vendor(&self) -> u16 { + return 0xffff; + } + + fn device_id(&self) -> u16 { + return 0xffff; + } + + fn subsystem_vendor(&self) -> u16 { + return 0xffff; + } + + fn subsystem_device(&self) -> u16 { + return 0xffff; + } +} + +impl Device for TestDevice { + fn attribute_groups(&self) -> Option<&'static [&'static dyn AttributeGroup]> { + Some(&[&HelloAttr]) + } + + fn bus(&self) -> Option> { + self.device_data.read().bus.clone() + } + + fn class(&self) -> Option> { + let mut guard = self.device_data.write(); + let r = guard.class.clone()?.upgrade(); + if r.is_none() { + guard.class = None; + } + + return r; + } + + fn driver(&self) -> Option> { + self.device_data.read().driver.clone()?.upgrade() + } + + fn dev_type(&self) -> DeviceType { + DeviceType::Pci + } + + fn id_table(&self) -> IdTable { + IdTable::new("testPci".to_string(), None) + } + + fn can_match(&self) -> bool { + true + } + + fn is_dead(&self) -> bool { + false + } + + fn set_bus(&self, bus: Option>) { + self.device_data.write().bus = bus + } + + fn set_can_match(&self, _can_match: bool) { + //todo + } + + fn set_class(&self, class: Option>) { + self.device_data.write().class = class + } + + fn set_driver(&self, driver: Option>) { + self.device_data.write().driver = driver + } + + fn state_synced(&self) -> bool { + true + } +} + +impl KObject for TestDevice { + fn as_any_ref(&self) -> &dyn Any { + self + } + + fn set_inode(&self, inode: Option>) { + self.kobj_data.write().kern_inode = inode; + } + + fn inode(&self) -> Option> { + self.kobj_data.read().kern_inode.clone() + } + + fn parent(&self) -> Option> { + self.kobj_data.read().parent.clone() + } + + fn set_parent(&self, parent: Option>) { + self.kobj_data.write().parent = parent; + } + + fn kset(&self) -> Option> { + self.kobj_data.read().kset.clone() + } + + fn set_kset(&self, kset: Option>) { + self.kobj_data.write().kset = kset; + } + + fn kobj_type(&self) -> Option<&'static dyn KObjType> { + self.kobj_data.read().kobj_type + } + + fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { + self.kobj_data.write().kobj_type = ktype; + } + + fn name(&self) -> String { + "PciTest".to_string() + } + + fn set_name(&self, _name: String) { + // do nothing + } + + fn kobj_state(&self) -> RwLockReadGuard { + self.kobj_state.read() + } + + fn kobj_state_mut(&self) -> RwLockWriteGuard { + self.kobj_state.write() + } + + fn set_kobj_state(&self, state: KObjectState) { + *self.kobj_state.write() = state; + } +} + +#[derive(Debug)] +pub struct HelloAttr; + +impl AttributeGroup for HelloAttr { + fn name(&self) -> Option<&str> { + return Some("TestAttr"); + } + + fn attrs(&self) -> &[&'static dyn Attribute] { + &[&Hello] + } + + fn is_visible( + &self, + _kobj: Arc, + attr: &'static dyn Attribute, + ) -> Option { + return Some(attr.mode()); + } +} +#[derive(Debug)] +pub struct Hello; + +impl Attribute for Hello { + fn mode(&self) -> ModeType { + SYSFS_ATTR_MODE_RO + } + + fn name(&self) -> &str { + "Hello" + } + + fn show(&self, _kobj: Arc, _buf: &mut [u8]) -> Result { + return sysfs_emit_str(_buf, "Hello Pci"); + } + + fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { + todo!() + } + + fn support(&self) -> SysFSOpsSupport { + SysFSOpsSupport::ATTR_SHOW + } +} diff --git a/kernel/src/driver/pci/test/pt_driver.rs b/kernel/src/driver/pci/test/pt_driver.rs new file mode 100644 index 000000000..65affee28 --- /dev/null +++ b/kernel/src/driver/pci/test/pt_driver.rs @@ -0,0 +1,171 @@ +use alloc::{ + string::{String, ToString}, + sync::{Arc, Weak}, + vec::Vec, +}; + +use crate::{ + driver::{ + base::{ + device::{ + bus::Bus, + driver::{Driver, DriverCommonData}, + Device, IdTable, + }, + kobject::{KObjType, KObject, KObjectCommonData, KObjectState, LockedKObjectState}, + kset::KSet, + }, + pci::{dev_id::PciDeviceID, device::PciDevice, driver::PciDriver}, + }, + filesystem::kernfs::KernFSInode, + libs::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, +}; +#[derive(Debug)] +#[cast_to([sync] PciDriver)] +pub struct TestDriver { + driver_data: RwLock, + kobj_data: RwLock, + kobj_state: LockedKObjectState, + pub locked_dynid_list: RwLock>>, +} + +/// # 结构功能 +/// 本结构体是测试用的驱动,目前暂时保留,否则将出现大量dead code +/// 在编写了实际的pci驱动后,可将该驱动删除 +impl TestDriver { + pub fn new() -> Self { + Self { + driver_data: RwLock::new(DriverCommonData::default()), + kobj_data: RwLock::new(KObjectCommonData::default()), + kobj_state: LockedKObjectState::new(None), + locked_dynid_list: RwLock::new(vec![]), + } + } +} + +impl PciDriver for TestDriver { + fn add_dynid(&mut self, id: PciDeviceID) -> Result<(), system_error::SystemError> { + let id = Arc::new(id); + self.locked_dynid_list.write().push(id); + Ok(()) + } + + fn locked_dynid_list(&self) -> Option>> { + Some(self.locked_dynid_list.read().clone()) + } + + fn probe( + &self, + _device: &Arc, + _id: &PciDeviceID, + ) -> Result<(), system_error::SystemError> { + Ok(()) + } + + fn remove(&self, _device: &Arc) -> Result<(), system_error::SystemError> { + Ok(()) + } + + fn resume(&self, _device: &Arc) -> Result<(), system_error::SystemError> { + Ok(()) + } + + fn shutdown(&self, _device: &Arc) -> Result<(), system_error::SystemError> { + Ok(()) + } + + fn suspend(&self, _device: &Arc) -> Result<(), system_error::SystemError> { + Ok(()) + } +} + +impl Driver for TestDriver { + fn id_table(&self) -> Option { + Some(IdTable::new("PciTestDriver".to_string(), None)) + } + + fn devices(&self) -> Vec> { + self.driver_data.read().devices.clone() + } + + fn add_device(&self, device: Arc) { + let mut guard = self.driver_data.write(); + // check if the device is already in the list + if guard.devices.iter().any(|dev| Arc::ptr_eq(dev, &device)) { + return; + } + + guard.devices.push(device); + } + + fn delete_device(&self, device: &Arc) { + let mut guard = self.driver_data.write(); + guard.devices.retain(|dev| !Arc::ptr_eq(dev, device)); + } + + fn set_bus(&self, bus: Option>) { + self.driver_data.write().bus = bus; + } + + fn bus(&self) -> Option> { + self.driver_data.read().bus.clone() + } +} + +impl KObject for TestDriver { + fn as_any_ref(&self) -> &dyn core::any::Any { + self + } + + fn set_inode(&self, inode: Option>) { + self.kobj_data.write().kern_inode = inode; + } + + fn inode(&self) -> Option> { + self.kobj_data.read().kern_inode.clone() + } + + fn parent(&self) -> Option> { + self.kobj_data.read().parent.clone() + } + + fn set_parent(&self, parent: Option>) { + self.kobj_data.write().parent = parent; + } + + fn kset(&self) -> Option> { + self.kobj_data.read().kset.clone() + } + + fn set_kset(&self, kset: Option>) { + self.kobj_data.write().kset = kset; + } + + fn kobj_type(&self) -> Option<&'static dyn KObjType> { + self.kobj_data.read().kobj_type + } + + fn set_kobj_type(&self, ktype: Option<&'static dyn KObjType>) { + self.kobj_data.write().kobj_type = ktype; + } + + fn name(&self) -> String { + "PciTestDriver".to_string() + } + + fn set_name(&self, _name: String) { + // do nothing + } + + fn kobj_state(&self) -> RwLockReadGuard { + self.kobj_state.read() + } + + fn kobj_state_mut(&self) -> RwLockWriteGuard { + self.kobj_state.write() + } + + fn set_kobj_state(&self, state: KObjectState) { + *self.kobj_state.write() = state; + } +} diff --git a/kernel/src/driver/rtc/class.rs b/kernel/src/driver/rtc/class.rs index 12184e561..d10f11cff 100644 --- a/kernel/src/driver/rtc/class.rs +++ b/kernel/src/driver/rtc/class.rs @@ -2,6 +2,7 @@ use alloc::{ string::ToString, sync::{Arc, Weak}, }; +use log::info; use system_error::SystemError; use unified_init::macros::unified_init; @@ -100,7 +101,7 @@ fn rtc_hctosys(dev: &Arc) { let r = do_settimeofday64(timespec64); dev.set_hc2sys_result(r); - kinfo!( + info!( "Setting system clock to {} {} UTC ({})", time.date_string(), time.time_string(), diff --git a/kernel/src/driver/rtc/mod.rs b/kernel/src/driver/rtc/mod.rs index 7031c0ba3..80f99d5b0 100644 --- a/kernel/src/driver/rtc/mod.rs +++ b/kernel/src/driver/rtc/mod.rs @@ -47,6 +47,7 @@ pub trait RtcDevice: Device { fn class_ops(&self) -> &'static dyn RtcClassOps; } +#[allow(dead_code)] pub trait RtcClassOps: Send + Sync + Debug { fn read_time(&self, dev: &Arc) -> Result; fn set_time(&self, dev: &Arc, time: &RtcTime) -> Result<(), SystemError>; diff --git a/kernel/src/driver/serial/mod.rs b/kernel/src/driver/serial/mod.rs index 53af56d85..77bc956ff 100644 --- a/kernel/src/driver/serial/mod.rs +++ b/kernel/src/driver/serial/mod.rs @@ -9,6 +9,7 @@ use self::serial8250::serial8250_manager; pub mod serial8250; +#[allow(dead_code)] pub trait UartDriver: Debug + Send + Sync { fn device_number(&self) -> DeviceNumber; @@ -21,6 +22,7 @@ pub trait UartDriver: Debug + Send + Sync { /// 串口端口应当实现的trait /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/serial_core.h#428 +#[allow(dead_code)] pub trait UartPort { fn iobase(&self) -> Option { None diff --git a/kernel/src/driver/serial/serial8250/mod.rs b/kernel/src/driver/serial/serial8250/mod.rs index 48c82c07f..f12fe63c8 100644 --- a/kernel/src/driver/serial/serial8250/mod.rs +++ b/kernel/src/driver/serial/serial8250/mod.rs @@ -8,6 +8,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::error; use system_error::SystemError; use crate::{ @@ -140,6 +141,7 @@ impl Serial8250Manager { } /// 所有的8250串口设备都应该实现的trait +#[allow(dead_code)] trait Serial8250Port: UartPort { fn device(&self) -> Option> { None @@ -410,7 +412,7 @@ impl PlatformDriver for Serial8250ISADriver { .arc_any() .downcast::() .map_err(|_| { - kerror!("Serial8250ISADriver::probe: device is not a Serial8250ISADevices"); + error!("Serial8250ISADriver::probe: device is not a Serial8250ISADevices"); SystemError::EINVAL })?; isa_dev.set_driver(Some(self.self_ref.clone())); diff --git a/kernel/src/driver/serial/serial8250/serial8250_pio.rs b/kernel/src/driver/serial/serial8250/serial8250_pio.rs index fbf909ce7..54760ad9a 100644 --- a/kernel/src/driver/serial/serial8250/serial8250_pio.rs +++ b/kernel/src/driver/serial/serial8250/serial8250_pio.rs @@ -20,6 +20,7 @@ static mut PIO_PORTS: [Option; 8] = [None, None, None, None, None, None, None, None]; impl Serial8250Manager { + #[allow(static_mut_refs)] pub(super) fn bind_pio_ports( &self, uart_driver: &Arc, @@ -251,6 +252,7 @@ impl Serial8250PIOPortInner { Self { device: None } } + #[allow(dead_code)] pub fn device(&self) -> Option> { if let Some(device) = self.device.as_ref() { return device.upgrade(); diff --git a/kernel/src/driver/tty/console.rs b/kernel/src/driver/tty/console.rs index a9064ccba..f127900d4 100644 --- a/kernel/src/driver/tty/console.rs +++ b/kernel/src/driver/tty/console.rs @@ -10,6 +10,7 @@ pub trait ConsoleSwitch: Sync + Send { fn con_init(&self, vc_data: &mut VirtualConsoleData, init: bool) -> Result<(), SystemError>; /// 进行释放等系列操作,目前未使用 + #[allow(dead_code)] fn con_deinit(&self) -> Result<(), SystemError>; /// ## 清空console的一片区域 diff --git a/kernel/src/driver/tty/tty_core.rs b/kernel/src/driver/tty/tty_core.rs index ecd7ee0c1..90da3be6a 100644 --- a/kernel/src/driver/tty/tty_core.rs +++ b/kernel/src/driver/tty/tty_core.rs @@ -7,7 +7,6 @@ use alloc::{ collections::LinkedList, string::String, sync::{Arc, Weak}, - vec::Vec, }; use system_error::SystemError; @@ -277,14 +276,6 @@ pub struct TtyContorlInfo { pub packet: bool, } -#[derive(Debug, Default)] -pub struct TtyCoreWriteData { - /// 写缓冲区 - pub write_buf: Vec, - /// 写入数量 - pub write_cnt: usize, -} - #[derive(Debug, Default)] pub struct TtyFlowState { /// 表示流控是否被停止 @@ -465,9 +456,6 @@ impl TtyCoreData { } } -/// TTY 核心接口,不同的tty需要各自实现这个trait -pub trait TtyCoreFuncs: Debug + Send + Sync {} - impl TtyOperation for TtyCore { #[inline] fn open(&self, tty: &TtyCoreData) -> Result<(), SystemError> { diff --git a/kernel/src/driver/tty/tty_device.rs b/kernel/src/driver/tty/tty_device.rs index e2458607c..56a053b40 100644 --- a/kernel/src/driver/tty/tty_device.rs +++ b/kernel/src/driver/tty/tty_device.rs @@ -34,7 +34,7 @@ use crate::{ spinlock::SpinLockGuard, }, mm::VirtAddr, - net::event_poll::{EPollItem, EventPoll}, + net::event_poll::{EPollItem, KernelIoctlData}, process::ProcessManager, syscall::user_access::{UserBufferReader, UserBufferWriter}, }; @@ -308,6 +308,35 @@ impl IndexNode for TtyDevice { Ok(()) } + fn kernel_ioctl( + &self, + arg: Arc, + data: &FilePrivateData, + ) -> Result { + let epitem = arg + .arc_any() + .downcast::() + .map_err(|_| SystemError::EFAULT)?; + + let _ = UserBufferReader::new( + &epitem as *const Arc, + core::mem::size_of::>(), + false, + )?; + + let (tty, _) = if let FilePrivateData::Tty(tty_priv) = data { + (tty_priv.tty(), tty_priv.mode) + } else { + return Err(SystemError::EIO); + }; + + let core = tty.core(); + + core.add_epitem(epitem.clone()); + + return Ok(0); + } + fn ioctl(&self, cmd: u32, arg: usize, data: &FilePrivateData) -> Result { let (tty, _) = if let FilePrivateData::Tty(tty_priv) = data { (tty_priv.tty(), tty_priv.mode) @@ -326,20 +355,6 @@ impl IndexNode for TtyDevice { todo!() } } - EventPoll::ADD_EPOLLITEM => { - let _ = UserBufferReader::new( - arg as *const Arc, - core::mem::size_of::>(), - false, - )?; - let epitem = unsafe { &*(arg as *const Arc) }; - - let core = tty.core(); - - core.add_epitem(epitem.clone()); - - return Ok(0); - } _ => {} } diff --git a/kernel/src/driver/tty/tty_driver.rs b/kernel/src/driver/tty/tty_driver.rs index 532250678..6acb4cbde 100644 --- a/kernel/src/driver/tty/tty_driver.rs +++ b/kernel/src/driver/tty/tty_driver.rs @@ -6,6 +6,7 @@ use alloc::{ vec::Vec, }; use hashbrown::HashMap; +use log::warn; use system_error::SystemError; use crate::{ @@ -284,7 +285,7 @@ impl TtyDriver { Some(tty) => { // TODO: 暂时这么写,因为还没写TtyPort if tty.core().port().is_none() { - kwarn!("{} port is None", tty.core().name()); + warn!("{} port is None", tty.core().name()); } else if tty.core().port().unwrap().state() == TtyPortState::KOPENED { return Err(SystemError::EBUSY); } diff --git a/kernel/src/driver/tty/tty_ldisc/ntty.rs b/kernel/src/driver/tty/tty_ldisc/ntty.rs index 9253a0e50..4f2958064 100644 --- a/kernel/src/driver/tty/tty_ldisc/ntty.rs +++ b/kernel/src/driver/tty/tty_ldisc/ntty.rs @@ -388,9 +388,9 @@ impl NTtyData { continue; } - if self.char_map.get(c as usize).unwrap() { + if ((c as usize) < self.char_map.size()) && self.char_map.get(c as usize).unwrap() { // 特殊字符 - self.receive_special_char(c, tty.clone(), lookahead_done) + self.receive_special_char(c, tty.clone(), lookahead_done); } else { self.receive_char(c, tty.clone()); } @@ -1344,7 +1344,7 @@ impl NTtyData { tty.write(core, &[8], 1)?; } if tty.put_char(tty.core(), b' ').is_err() { - tty.write(core, &[b' '], 1)?; + tty.write(core, b" ", 1)?; } self.cursor_column -= 1; space -= 1; @@ -1357,7 +1357,7 @@ impl NTtyData { } if tty.put_char(tty.core(), b'^').is_err() { - tty.write(core, &[b'^'], 1)?; + tty.write(core, b"^", 1)?; } if tty.put_char(tty.core(), ch ^ 0o100).is_err() { diff --git a/kernel/src/driver/tty/virtual_terminal/virtual_console.rs b/kernel/src/driver/tty/virtual_terminal/virtual_console.rs index 3fc94b323..992659c4b 100644 --- a/kernel/src/driver/tty/virtual_terminal/virtual_console.rs +++ b/kernel/src/driver/tty/virtual_terminal/virtual_console.rs @@ -5,6 +5,7 @@ use alloc::{ vec::Vec, }; use bitmap::{traits::BitMapOps, StaticBitmap}; +use log::warn; use crate::{ driver::{ @@ -131,7 +132,6 @@ pub struct VirtualConsoleData { pub utf_char: u32, /// 构建utf时需要的参数,表示目前接收了多少个字节的数据来构建utf字符 pub npar: u32, - /// pub par: [u32; NPAR], /// 字符转换表 用于将输入字符映射到特定的字符 @@ -936,7 +936,7 @@ impl VirtualConsoleData { 'c' => { if self.par[0] == 0 { - kwarn!("respone ID todo"); + warn!("respone ID todo"); } return; } @@ -1612,7 +1612,7 @@ impl VirtualConsoleData { tc |= ((attr as u32) << 8) & (!himask as u32); - // kwarn!( + // warn!( // "ch {} pos {} x {} y {} cols {}", // c as u8 as char, // self.pos, @@ -1785,6 +1785,7 @@ impl VirtualConsoleData { draw.size = 0; } + #[allow(clippy::manual_rotate)] fn build_attr( &self, color: u8, diff --git a/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs b/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs index d0a3040e6..4a193f284 100644 --- a/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs +++ b/kernel/src/driver/video/fbdev/base/fbcon/framebuffer_console.rs @@ -1,4 +1,5 @@ use alloc::{sync::Arc, vec::Vec}; +use log::warn; use system_error::SystemError; use crate::{ @@ -200,7 +201,7 @@ impl ConsoleSwitch for BlittingFbConsole { vc_data.font.height = font.height; vc_data.font.count = font.char_count; } else { - kwarn!("The frontend Framebuffer is not implemented"); + warn!("The frontend Framebuffer is not implemented"); } vc_data.color_mode = fb.color_depth() != 1; diff --git a/kernel/src/driver/video/fbdev/base/fbcon/mod.rs b/kernel/src/driver/video/fbdev/base/fbcon/mod.rs index 3d95e85a4..ac6e50cfb 100644 --- a/kernel/src/driver/video/fbdev/base/fbcon/mod.rs +++ b/kernel/src/driver/video/fbdev/base/fbcon/mod.rs @@ -3,6 +3,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::warn; use system_error::SystemError; use crate::{ @@ -165,7 +166,7 @@ impl KObject for FbConsoleDevice { fn set_name(&self, _name: String) { // 不允许修改 - kwarn!("fbcon name can not be changed"); + warn!("fbcon name can not be changed"); } fn kobj_state(&self) -> RwLockReadGuard { @@ -199,7 +200,7 @@ impl Device for FbConsoleDevice { fn set_class(&self, _class: Option>) { // 不允许修改 - kwarn!("fbcon's class can not be changed"); + warn!("fbcon's class can not be changed"); } fn class(&self) -> Option> { @@ -279,13 +280,13 @@ impl Attribute for AttrRotate { /// https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/video/fbdev/core/fbcon.c#3226 fn show(&self, _kobj: Arc, buf: &mut [u8]) -> Result { - kwarn!("fbcon rotate show not implemented"); + warn!("fbcon rotate show not implemented"); return sysfs_emit_str(buf, "0\n"); } /// https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/video/fbdev/core/fbcon.c#3182 fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - kwarn!("fbcon rotate store not implemented"); + warn!("fbcon rotate store not implemented"); return Err(SystemError::ENOSYS); } } @@ -308,7 +309,7 @@ impl Attribute for AttrRotateAll { /// https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/video/fbdev/core/fbcon.c#3204 fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - kwarn!("fbcon rotate_all store not implemented"); + warn!("fbcon rotate_all store not implemented"); return Err(SystemError::ENOSYS); } } @@ -340,12 +341,12 @@ impl Attribute for AttrCursorBlink { } #[derive(Debug, Default)] +#[allow(dead_code)] pub struct FrameBufferConsoleData { /// 光标闪烁间隔 pub cursor_blink_jiffies: i64, /// 是否刷新光标 pub cursor_flash: bool, - /// pub display: FbConsoleDisplay, /// 光标状态 pub cursor_state: FbCursor, diff --git a/kernel/src/driver/video/fbdev/base/fbmem.rs b/kernel/src/driver/video/fbdev/base/fbmem.rs index bb404ab30..8031fc6d3 100644 --- a/kernel/src/driver/video/fbdev/base/fbmem.rs +++ b/kernel/src/driver/video/fbdev/base/fbmem.rs @@ -6,6 +6,7 @@ use alloc::{ vec::Vec, }; +use log::error; use system_error::SystemError; use unified_init::macros::unified_init; @@ -152,7 +153,7 @@ impl FrameBufferManager { device_manager().add_device(fb_device.clone() as Arc)?; // 添加到devfs devfs_register(&fb_device.name(), fb_device.clone()).map_err(|e| { - kerror!( + error!( "register fb device '{}' to devfs failed: {:?}", fb_device.name(), e diff --git a/kernel/src/driver/video/fbdev/base/fbsysfs.rs b/kernel/src/driver/video/fbdev/base/fbsysfs.rs index 0020ec1d9..7b6025703 100644 --- a/kernel/src/driver/video/fbdev/base/fbsysfs.rs +++ b/kernel/src/driver/video/fbdev/base/fbsysfs.rs @@ -1,4 +1,5 @@ use alloc::sync::Arc; +use log::warn; use system_error::SystemError; use crate::{ @@ -85,7 +86,7 @@ impl Attribute for AttrBitsPerPixel { } fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - kwarn!("attr bits_per_pixel store not implemented"); + warn!("attr bits_per_pixel store not implemented"); return Err(SystemError::ENOSYS); } @@ -116,7 +117,7 @@ impl Attribute for AttrBlank { // todo: https://code.dragonos.org.cn/xref/linux-6.1.9/drivers/video/fbdev/core/fbsysfs.c#309 fn store(&self, _kobj: Arc, _buf: &[u8]) -> Result { - kwarn!("attr blank store not implemented"); + warn!("attr blank store not implemented"); return Err(SystemError::ENOSYS); } } diff --git a/kernel/src/driver/video/fbdev/base/mod.rs b/kernel/src/driver/video/fbdev/base/mod.rs index e20b08711..a266b3635 100644 --- a/kernel/src/driver/video/fbdev/base/mod.rs +++ b/kernel/src/driver/video/fbdev/base/mod.rs @@ -301,6 +301,7 @@ impl FrameBufferInfoData { } /// 帧缓冲区信息 +#[allow(dead_code)] pub trait FrameBufferInfo: FrameBufferOps { fn framebuffer_info_data(&self) -> &RwLock; @@ -341,7 +342,7 @@ pub trait FrameBufferInfo: FrameBufferOps { // && var.green.offset == var.blue.offset // && var.green.offset == var.red.offset // { - // kerror!("return {}", var.green.length); + // error!("return {}", var.green.length); // return var.green.length; // } else { // return var.green.length + var.blue.length + var.red.length; @@ -377,6 +378,7 @@ pub trait FrameBufferInfo: FrameBufferOps { /// 帧缓冲区操作 /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/fb.h#237 +#[allow(dead_code)] pub trait FrameBufferOps { fn fb_open(&self, user: bool); fn fb_release(&self, user: bool); @@ -1063,6 +1065,7 @@ pub enum FbAccel { // Add other accelerators here } +#[allow(dead_code)] #[derive(Debug, Copy, Clone)] pub struct BootTimeScreenInfo { pub origin_x: u8, diff --git a/kernel/src/driver/video/fbdev/vesafb.rs b/kernel/src/driver/video/fbdev/vesafb.rs index 2472973d7..a5c486964 100644 --- a/kernel/src/driver/video/fbdev/vesafb.rs +++ b/kernel/src/driver/video/fbdev/vesafb.rs @@ -9,6 +9,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::{info, warn}; use system_error::SystemError; use unified_init::macros::unified_init; @@ -390,7 +391,7 @@ impl FrameBufferOps for VesaFb { /// ## 填充矩形 fn fb_fillrect(&self, rect: super::base::FillRectData) -> Result<(), SystemError> { - // kwarn!("rect {rect:?}"); + // warn!("rect {rect:?}"); let boot_param = boot_params().read(); let screen_base = boot_param @@ -1006,7 +1007,7 @@ fn vesa_fb_device_init() -> Result<(), SystemError> { static INIT: Once = Once::new(); INIT.call_once(|| { - kinfo!("vesa fb device init"); + info!("vesa fb device init"); let device = Arc::new(VesaFb::new()); let mut fb_fix = VESAFB_FIX_INFO.write_irqsave(); @@ -1068,7 +1069,7 @@ fn vesa_fb_device_init() -> Result<(), SystemError> { // 加入全局fb表 let mut guard = FRAME_BUFFER_SET.write(); if guard.get(device.fb_id().data() as usize).unwrap().is_some() { - kwarn!( + warn!( "vesa_fb_device_init: There is already an element {:?} in the FRAME_BUFFER_SET", device.fb_id() ); diff --git a/kernel/src/driver/video/mod.rs b/kernel/src/driver/video/mod.rs index 6a8c406d9..e9a705140 100644 --- a/kernel/src/driver/video/mod.rs +++ b/kernel/src/driver/video/mod.rs @@ -3,7 +3,6 @@ use core::sync::atomic::{AtomicBool, Ordering}; use crate::{ arch::MMArch, init::boot_params, - kinfo, libs::{ align::page_align_up, lib_ui::screen_manager::{ScmBuffer, ScmBufferFlag, ScmBufferInfo}, @@ -11,12 +10,13 @@ use crate::{ spinlock::SpinLock, }, mm::{ - allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::PageFlags, + allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, page::EntryFlags, MemoryManagementArch, }, time::timer::{Timer, TimerFunction}, }; use alloc::{boxed::Box, sync::Arc}; +use log::info; use system_error::SystemError; pub mod console; @@ -78,7 +78,7 @@ impl VideoRefreshManager { * 将帧缓存区映射到地址SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE处 */ fn init_frame_buffer(&self) { - kinfo!("Re-mapping VBE frame buffer..."); + info!("Re-mapping VBE frame buffer..."); let buf_vaddr = boot_params() .read_irqsave() .screen_info @@ -95,7 +95,7 @@ impl VideoRefreshManager { let count = PageFrameCount::new( page_align_up(frame_buffer_info_guard.buf_size()) / MMArch::PAGE_SIZE, ); - let page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); + let page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); let mut kernel_mapper = KernelMapper::lock(); let mut kernel_mapper = kernel_mapper.as_mut(); @@ -115,7 +115,7 @@ impl VideoRefreshManager { } } - kinfo!("VBE frame buffer successfully Re-mapped!"); + info!("VBE frame buffer successfully Re-mapped!"); } /** diff --git a/kernel/src/driver/virtio/irq.rs b/kernel/src/driver/virtio/irq.rs index 70f823ab1..aa727670e 100644 --- a/kernel/src/driver/virtio/irq.rs +++ b/kernel/src/driver/virtio/irq.rs @@ -1,5 +1,6 @@ use alloc::sync::Arc; use hashbrown::HashMap; +use log::warn; use system_error::SystemError; use unified_init::macros::unified_init; @@ -120,7 +121,7 @@ impl IrqHandler for DefaultVirtioIrqHandler { return dev.handle_irq(irq); } else { // 未绑定具体设备,因此无法处理中断 - kwarn!("No device found for IRQ: {:?}", irq); + warn!("No device found for IRQ: {:?}", irq); return Ok(IrqReturn::NotHandled); } } diff --git a/kernel/src/driver/virtio/mmio.rs b/kernel/src/driver/virtio/mmio.rs index 027a70bd4..815cce58d 100644 --- a/kernel/src/driver/virtio/mmio.rs +++ b/kernel/src/driver/virtio/mmio.rs @@ -1,4 +1,5 @@ use fdt::node::FdtNode; +use log::error; use system_error::SystemError; use crate::driver::{ @@ -9,7 +10,7 @@ use super::{transport::VirtIOTransport, virtio::virtio_device_init}; pub(super) fn virtio_probe_mmio() { if let Err(e) = do_probe_virtio_mmio() { - kerror!("virtio_probe_mmio failed: {:?}", e); + error!("virtio_probe_mmio failed: {:?}", e); } } diff --git a/kernel/src/driver/virtio/mod.rs b/kernel/src/driver/virtio/mod.rs index 9498dcb25..024d44074 100644 --- a/kernel/src/driver/virtio/mod.rs +++ b/kernel/src/driver/virtio/mod.rs @@ -18,6 +18,7 @@ pub mod virtio_impl; /// virtio 设备厂商ID pub const VIRTIO_VENDOR_ID: u16 = 0x1af4; +#[allow(dead_code)] pub trait VirtIODevice: Device { fn handle_irq(&self, _irq: IrqNumber) -> Result; diff --git a/kernel/src/driver/virtio/sysfs.rs b/kernel/src/driver/virtio/sysfs.rs index 526a88ade..dc1d0ea1e 100644 --- a/kernel/src/driver/virtio/sysfs.rs +++ b/kernel/src/driver/virtio/sysfs.rs @@ -4,6 +4,7 @@ use alloc::{ }; use ida::IdAllocator; use intertrait::cast::CastArc; +use log::error; use system_error::SystemError; use unified_init::macros::unified_init; @@ -78,7 +79,7 @@ impl Bus for VirtIOBus { fn probe(&self, device: &Arc) -> Result<(), SystemError> { let drv = device.driver().ok_or(SystemError::EINVAL)?; let virtio_drv = drv.cast::().map_err(|_| { - kerror!( + error!( "VirtIOBus::probe() failed: device.driver() is not a VirtioDriver. Device: '{:?}'", device.name() ); @@ -86,7 +87,7 @@ impl Bus for VirtIOBus { })?; let virtio_dev = device.clone().cast::().map_err(|_| { - kerror!( + error!( "VirtIOBus::probe() failed: device is not a VirtIODevice. Device: '{:?}'", device.name() ); @@ -167,7 +168,7 @@ impl VirtIODeviceManager { let drv = dev.driver().ok_or(SystemError::EINVAL)?; let virtio_drv = drv.cast::().map_err(|_| { - kerror!( + error!( "VirtIODeviceManager::device_add() failed: device.driver() is not a VirtioDriver. Device: '{:?}'", dev.name() ); @@ -199,7 +200,7 @@ impl VirtIODeviceManager { IrqHandleFlags::IRQF_SHARED, Some(dev.dev_id().clone()), ) { - kerror!( + error!( "Failed to request irq for virtio device '{}': irq: {:?}, error {:?}", dev.device_name(), irq, @@ -211,7 +212,7 @@ impl VirtIODeviceManager { virtio_irq_manager() .register_device(dev.clone()) .map_err(|e| { - kerror!( + error!( "Failed to register virtio device's irq, dev: '{}', irq: {:?}, error {:?}", dev.device_name(), irq, @@ -302,7 +303,7 @@ impl Attribute for AttrDevice { fn show(&self, kobj: Arc, buf: &mut [u8]) -> Result { let dev = kobj.cast::().map_err(|_| { - kerror!("AttrDevice::show() failed: kobj is not a VirtIODevice"); + error!("AttrDevice::show() failed: kobj is not a VirtIODevice"); SystemError::EINVAL })?; let device_type_id = dev.device_type_id(); @@ -329,7 +330,7 @@ impl Attribute for AttrVendor { fn show(&self, kobj: Arc, buf: &mut [u8]) -> Result { let dev = kobj.cast::().map_err(|_| { - kerror!("AttrVendor::show() failed: kobj is not a VirtIODevice"); + error!("AttrVendor::show() failed: kobj is not a VirtIODevice"); SystemError::EINVAL })?; let vendor = dev.vendor(); diff --git a/kernel/src/driver/virtio/transport_mmio.rs b/kernel/src/driver/virtio/transport_mmio.rs index 435f90fc8..0ee3864d4 100644 --- a/kernel/src/driver/virtio/transport_mmio.rs +++ b/kernel/src/driver/virtio/transport_mmio.rs @@ -2,6 +2,7 @@ use core::ptr::NonNull; use alloc::sync::Arc; use fdt::node::FdtNode; +use log::info; use system_error::SystemError; use virtio_drivers::transport::{ mmio::{MmioTransport, VirtIOHeader}, @@ -54,7 +55,7 @@ impl VirtIOMmioTransport { match unsafe { MmioTransport::new(header) } { Ok(mmio_transport) => { - kinfo!( "Detected virtio MMIO device with vendor id {:#X}, device type {:?}, version {:?}, hw irq: {}", + info!( "Detected virtio MMIO device with vendor id {:#X}, device type {:?}, version {:?}, hw irq: {}", mmio_transport.vendor_id(), mmio_transport.device_type(), mmio_transport.version(), @@ -69,7 +70,7 @@ impl VirtIOMmioTransport { }) } Err(_) => { - // kwarn!("MmioTransport::new failed: {:?}", e); + // warn!("MmioTransport::new failed: {:?}", e); Err(SystemError::EINVAL) } } diff --git a/kernel/src/driver/virtio/transport_pci.rs b/kernel/src/driver/virtio/transport_pci.rs index 071f7edd0..88f4fcab8 100644 --- a/kernel/src/driver/virtio/transport_pci.rs +++ b/kernel/src/driver/virtio/transport_pci.rs @@ -211,7 +211,7 @@ impl PciTransport { notify_off_multiplier, )); } - //kdebug!("notify.offset={},notify.length={}",notify_cfg.offset,notify_cfg.length); + //debug!("notify.offset={},notify.length={}",notify_cfg.offset,notify_cfg.length); let notify_region = get_bar_region_slice::<_>(&device.standard_device_bar, ¬ify_cfg)?; let isr_status = get_bar_region::<_>( &device.standard_device_bar, @@ -532,7 +532,7 @@ fn get_bar_region( { return Err(VirtioPciError::BarOffsetOutOfRange); } - //kdebug!("Chossed bar ={},used={}",struct_info.bar,struct_info.offset + struct_info.length); + //debug!("Chossed bar ={},used={}",struct_info.bar,struct_info.offset + struct_info.length); let vaddr = (bar_info .virtual_address() .ok_or(VirtioPciError::BarGetVaddrFailed)?) diff --git a/kernel/src/driver/virtio/virtio.rs b/kernel/src/driver/virtio/virtio.rs index 4314edbe2..9253542ef 100644 --- a/kernel/src/driver/virtio/virtio.rs +++ b/kernel/src/driver/virtio/virtio.rs @@ -10,10 +10,11 @@ use crate::driver::pci::pci::{ }; use crate::driver::virtio::transport::VirtIOTransport; use crate::libs::rwlock::RwLockWriteGuard; -use crate::{kdebug, kerror, kwarn}; + use alloc::sync::Arc; use alloc::vec::Vec; use alloc::{boxed::Box, collections::LinkedList}; +use log::{debug, error, warn}; use virtio_drivers::transport::{DeviceType, Transport}; ///@brief 寻找并加载所有virtio设备的驱动(目前只有virtio-net,但其他virtio设备也可添加) @@ -32,7 +33,7 @@ fn virtio_probe_pci() { let dev_id = DeviceId::new(None, Some(format!("{dev_id}"))).unwrap(); match PciTransport::new::(virtio_device, dev_id.clone()) { Ok(mut transport) => { - kdebug!( + debug!( "Detected virtio PCI device with device type {:?}, features {:#018x}", transport.device_type(), transport.read_device_features(), @@ -41,7 +42,7 @@ fn virtio_probe_pci() { virtio_device_init(transport, dev_id); } Err(err) => { - kerror!("Pci transport create failed because of error: {}", err); + error!("Pci transport create failed because of error: {}", err); } } } @@ -52,14 +53,14 @@ pub(super) fn virtio_device_init(transport: VirtIOTransport, dev_id: Arc virtio_blk(transport, dev_id), DeviceType::GPU => { - kwarn!("Not support virtio_gpu device for now"); + warn!("Not support virtio_gpu device for now"); } DeviceType::Input => { - kwarn!("Not support virtio_input device for now"); + warn!("Not support virtio_input device for now"); } DeviceType::Network => virtio_net(transport, dev_id), t => { - kwarn!("Unrecognized virtio device: {:?}", t); + warn!("Unrecognized virtio device: {:?}", t); } } } diff --git a/kernel/src/driver/virtio/virtio_impl.rs b/kernel/src/driver/virtio/virtio_impl.rs index 92aa07cc1..0166b138b 100644 --- a/kernel/src/driver/virtio/virtio_impl.rs +++ b/kernel/src/driver/virtio/virtio_impl.rs @@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags; use crate::arch::MMArch; use crate::mm::kernel_mapper::KernelMapper; -use crate::mm::page::{page_manager_lock_irqsave, PageFlags}; +use crate::mm::page::{page_manager_lock_irqsave, EntryFlags}; use crate::mm::{ allocator::page_frame::{ allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame, @@ -32,7 +32,7 @@ unsafe impl Hal for HalImpl { // 清空这块区域,防止出现脏数据 core::ptr::write_bytes(virt.data() as *mut u8, 0, count.data() * MMArch::PAGE_SIZE); - let dma_flags: PageFlags = PageFlags::mmio_flags(); + let dma_flags: EntryFlags = EntryFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let kernel_mapper = kernel_mapper.as_mut().unwrap(); @@ -90,7 +90,7 @@ unsafe impl Hal for HalImpl { _direction: BufferDirection, ) -> virtio_drivers::PhysAddr { let vaddr = VirtAddr::new(buffer.as_ptr() as *mut u8 as usize); - //kdebug!("virt:{:x}", vaddr); + //debug!("virt:{:x}", vaddr); // Nothing to do, as the host already has access to all memory. return MMArch::virt_2_phys(vaddr).unwrap().data(); } diff --git a/kernel/src/exception/handle.rs b/kernel/src/exception/handle.rs index fcfea1e41..e39ab02ac 100644 --- a/kernel/src/exception/handle.rs +++ b/kernel/src/exception/handle.rs @@ -1,6 +1,7 @@ use core::{intrinsics::unlikely, ops::BitAnd}; use alloc::sync::Arc; +use log::{debug, error, warn}; use system_error::SystemError; use crate::{ @@ -100,7 +101,7 @@ impl IrqFlowHandler for EdgeIrqHandler { fn handle(&self, irq_desc: &Arc, _trap_frame: &mut TrapFrame) { let mut desc_inner_guard: SpinLockGuard<'_, InnerIrqDesc> = irq_desc.inner(); if !irq_may_run(&desc_inner_guard) { - // kdebug!("!irq_may_run"); + // debug!("!irq_may_run"); desc_inner_guard .internal_state_mut() .insert(IrqDescState::IRQS_PENDING); @@ -109,7 +110,7 @@ impl IrqFlowHandler for EdgeIrqHandler { } if desc_inner_guard.common_data().disabled() { - // kdebug!("desc_inner_guard.common_data().disabled()"); + // debug!("desc_inner_guard.common_data().disabled()"); desc_inner_guard .internal_state_mut() .insert(IrqDescState::IRQS_PENDING); @@ -123,7 +124,7 @@ impl IrqFlowHandler for EdgeIrqHandler { loop { if unlikely(desc_inner_guard.actions().is_empty()) { - kdebug!("no action for irq {}", irq_data.irq().data()); + debug!("no action for irq {}", irq_data.irq().data()); irq_manager().mask_irq(&irq_data); return; } @@ -136,12 +137,12 @@ impl IrqFlowHandler for EdgeIrqHandler { { let status = desc_inner_guard.common_data().status(); if !status.disabled() && status.masked() { - // kdebug!("re-enable irq"); + // debug!("re-enable irq"); irq_manager().unmask_irq(&desc_inner_guard); } } - // kdebug!("handle_irq_event"); + // debug!("handle_irq_event"); desc_inner_guard = handle_irq_event(irq_desc, desc_inner_guard); @@ -268,7 +269,7 @@ fn do_handle_irq_event(desc: &Arc) -> Result<(), SystemError> { for action in actions { let mut action_inner: SpinLockGuard<'_, InnerIrqAction> = action.inner(); - // kdebug!("do_handle_irq_event: action: {:?}", action_inner.name()); + // debug!("do_handle_irq_event: action: {:?}", action_inner.name()); let dynamic_data = action_inner .dev_id() .clone() @@ -314,17 +315,17 @@ fn cond_unmask_eoi_irq( && desc_inner_guard.common_data().masked() && desc_inner_guard.threads_oneshot() == 0 { - kdebug!( + debug!( "eoi unmask irq {}", desc_inner_guard.irq_data().irq().data() ); chip.irq_eoi(desc_inner_guard.irq_data()); unmask_irq(desc_inner_guard.irq_data()); } else if !chip.flags().contains(IrqChipFlags::IRQCHIP_EOI_THREADED) { - kdebug!("eoi irq {}", desc_inner_guard.irq_data().irq().data()); + debug!("eoi irq {}", desc_inner_guard.irq_data().irq().data()); chip.irq_eoi(desc_inner_guard.irq_data()); } else { - kwarn!( + warn!( "irq {} eoi failed", desc_inner_guard.irq_data().irq().data() ); @@ -340,7 +341,7 @@ fn warn_no_thread(irq: IrqNumber, action_inner: &mut SpinLockGuard<'_, InnerIrqA return; } - kwarn!( + warn!( "irq {}, device {} returned IRQ_WAKE_THREAD, but no threaded handler", irq.data(), action_inner.name() @@ -402,7 +403,7 @@ impl IrqFlowHandler for PerCpuDevIdIrqHandler { static ONCE: Once = Once::new(); ONCE.call_once(|| { - kerror!( + error!( "Spurious percpu irq {} on cpu {:?}, enabled: {}", irq.data(), cpu, diff --git a/kernel/src/exception/irqchip.rs b/kernel/src/exception/irqchip.rs index 93e2aa093..064994867 100644 --- a/kernel/src/exception/irqchip.rs +++ b/kernel/src/exception/irqchip.rs @@ -5,6 +5,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::warn; use system_error::SystemError; use crate::{ @@ -34,6 +35,7 @@ use super::{ }; /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irq.h#506 +#[allow(dead_code)] pub trait IrqChip: Sync + Send + Any + Debug { fn name(&self) -> &'static str; /// start up the interrupt (defaults to ->enable if ENOSYS) @@ -126,6 +128,7 @@ pub trait IrqChip: Sync + Send + Any + Debug { } /// enable/disable power management wake-on of an interrupt + #[allow(dead_code)] fn irq_set_wake(&self, _irq_data: &Arc, _on: bool) -> Result<(), SystemError> { Err(SystemError::ENOSYS) } @@ -280,6 +283,7 @@ struct InnerIrqChipGeneric { chip_types: Vec, } +#[allow(dead_code)] pub trait IrqChipGenericOps: Debug + Send + Sync { /// Alternate I/O accessor (defaults to readl if NULL) unsafe fn reg_readl(&self, addr: VirtAddr) -> u32; @@ -433,7 +437,7 @@ impl IrqManager { * 则放弃。 */ if unlikely(is_chained) { - kwarn!( + warn!( "Chained handler for irq {} is not supported", dt.irq().data() ); @@ -453,7 +457,7 @@ impl IrqManager { &no_irq_chip(), ), ) { - kwarn!("No irq chip for irq {}", desc_inner.irq_data().irq().data()); + warn!("No irq chip for irq {}", desc_inner.irq_data().irq().data()); return; } } @@ -572,7 +576,7 @@ impl IrqHandler for ChainedActionHandler { ) -> Result { static ONCE: Once = Once::new(); ONCE.call_once(|| { - kwarn!("Chained irq {} should not call an action.", irq.data()); + warn!("Chained irq {} should not call an action.", irq.data()); }); Ok(IrqReturn::NotHandled) diff --git a/kernel/src/exception/irqdomain.rs b/kernel/src/exception/irqdomain.rs index 92267aa17..021979dd8 100644 --- a/kernel/src/exception/irqdomain.rs +++ b/kernel/src/exception/irqdomain.rs @@ -6,6 +6,7 @@ use alloc::{ vec::Vec, }; use hashbrown::HashMap; +use log::{info, warn}; use system_error::SystemError; use crate::{ @@ -157,7 +158,7 @@ impl IrqDomainManager { ) { for i in 0..count { if let Err(e) = self.domain_associate(domain, first_irq + i, first_hwirq + i) { - kwarn!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), (first_hwirq + i).data(), (first_irq + i).data()); + warn!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), (first_hwirq + i).data(), (first_irq + i).data()); } } } @@ -172,7 +173,7 @@ impl IrqDomainManager { hwirq: HardwareIrqNumber, ) -> Result<(), SystemError> { if hwirq >= domain.revmap.read_irqsave().hwirq_max { - kwarn!( + warn!( "hwirq {} is out of range for domain {:?}", hwirq.data(), domain.name() @@ -182,12 +183,12 @@ impl IrqDomainManager { let irq_data = irq_desc_manager() .lookup(irq) .ok_or_else(|| { - kwarn!("irq_desc not found for irq {}", irq.data()); + warn!("irq_desc not found for irq {}", irq.data()); SystemError::EINVAL })? .irq_data(); if irq_data.domain().is_some() { - kwarn!( + warn!( "irq {} is already associated with domain {:?}", irq.data(), irq_data.domain().unwrap().name() @@ -203,7 +204,7 @@ impl IrqDomainManager { if let Err(e) = r { if e != SystemError::ENOSYS { if e != SystemError::EPERM { - kinfo!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), hwirq.data(), irq.data()); + info!("domain associate failed: {:?}, domain '{:?}' didn't like hwirq {} to virq {} mapping.", e, domain.name(), hwirq.data(), irq.data()); } let mut irq_data_guard = irq_data.inner(); irq_data_guard.set_domain(None); @@ -246,7 +247,7 @@ impl IrqDomainManager { /// 这是调用 domain_ops->activate 以编程中断控制器的第二步,以便中断实际上可以被传递。 pub fn activate_irq(&self, irq_data: &Arc, reserve: bool) -> Result<(), SystemError> { let mut r = Ok(()); - // kdebug!( + // debug!( // "activate_irq: irq_data.common_data().status().is_activated()={}", // irq_data.common_data().status().is_activated() // ); @@ -270,9 +271,9 @@ impl IrqDomainManager { let mut r = Ok(()); if let Some(irq_data) = irq_data { - // kdebug!("do_activate_irq: irq_data={:?}", irq_data); + // debug!("do_activate_irq: irq_data={:?}", irq_data); if let Some(domain) = irq_data.domain() { - // kdebug!("do_activate_irq: domain={:?}", domain.name()); + // debug!("do_activate_irq: domain={:?}", domain.name()); let parent_data = irq_data.parent_data().and_then(|x| x.upgrade()); if let Some(parent_data) = parent_data.clone() { r = self.do_activate_irq(Some(parent_data), reserve); @@ -643,6 +644,7 @@ pub enum IrqDomainBusToken { /// 参考 https://code.dragonos.org.cn/xref/linux-6.1.9/include/linux/irqdomain.h#107 pub trait IrqDomainOps: Debug + Send + Sync { /// 匹配一个中断控制器设备节点到一个主机。 + #[allow(dead_code)] fn match_node( &self, _irq_domain: &Arc, @@ -666,6 +668,7 @@ pub trait IrqDomainOps: Debug + Send + Sync { } /// 删除一个虚拟中断号与一个硬件中断号之间的映射。 + #[allow(dead_code)] fn unmap(&self, irq_domain: &Arc, virq: IrqNumber); fn activate( diff --git a/kernel/src/exception/manage.rs b/kernel/src/exception/manage.rs index 80a632498..85a47b336 100644 --- a/kernel/src/exception/manage.rs +++ b/kernel/src/exception/manage.rs @@ -2,6 +2,7 @@ use core::ops::{BitXor, Deref, DerefMut}; use alloc::{string::String, sync::Arc}; +use log::{debug, error, warn}; use system_error::SystemError; use crate::{ @@ -135,7 +136,7 @@ impl IrqManager { } let desc = irq_desc_manager().lookup(irq).ok_or(SystemError::EINVAL)?; if !desc.can_request() { - kwarn!("desc {} can not request", desc.irq().data()); + warn!("desc {} can not request", desc.irq().data()); return Err(SystemError::EINVAL); } @@ -155,7 +156,7 @@ impl IrqManager { *action_guard.flags_mut() = flags; *action_guard.dev_id_mut() = dev_id; drop(action_guard); - kdebug!("to inner_setup_irq"); + debug!("to inner_setup_irq"); return self.inner_setup_irq(irq, irqaction, desc); } @@ -215,7 +216,7 @@ impl IrqManager { .flags() .contains(IrqHandleFlags::IRQF_PROBE_SHARED) { - kerror!("Flags mismatch for irq {} (name: {}, flags: {:?}). old action name: {}, old flags: {:?}", irq.data(), action_guard.name(), action_guard.flags(), old_action_guard.name(), old_action_guard.flags()); + error!("Flags mismatch for irq {} (name: {}, flags: {:?}). old action name: {}, old flags: {:?}", irq.data(), action_guard.name(), action_guard.flags(), old_action_guard.name(), old_action_guard.flags()); } return err_out_unlock( SystemError::EBUSY, @@ -300,7 +301,7 @@ impl IrqManager { // 如果当前中断线上还没有irqaction, 则先为中断线申请资源 if desc.actions().is_empty() { if let Err(e) = self.irq_request_resources(desc.clone()) { - kerror!( + error!( "Failed to request resources for {} (irq {}) on irqchip {}, error {:?}", action_guard.name(), irq.data(), @@ -329,7 +330,7 @@ impl IrqManager { .internal_state() .contains(IrqDescState::IRQS_NMI) { - kerror!( + error!( "Invalid attempt to share NMI for {} (irq {}) on irqchip {}", action_guard.name(), irq.data(), @@ -370,7 +371,7 @@ impl IrqManager { || ((old_guard.flags().bitxor(*action_guard.flags())) .contains(IrqHandleFlags::IRQF_ONESHOT)) { - kdebug!( + debug!( "Flags mismatch for irq {} (name: {}, flags: {:?}). old action name: {}, old flags: {:?}", irq.data(), action_guard.name(), @@ -392,7 +393,7 @@ impl IrqManager { if *old_guard.flags() & IrqHandleFlags::IRQF_PERCPU != *action_guard.flags() & IrqHandleFlags::IRQF_PERCPU { - kdebug!( + debug!( "Per-cpu mismatch for irq {} (name: {}, flags: {:?})", irq.data(), action_guard.name(), @@ -429,7 +430,7 @@ impl IrqManager { // 因为我们不能确定这个中断实际上具有什么类型。 // 由于底层芯片实现可能会覆盖它们,所以类型标志并不可靠. - kerror!( + error!( "Requesting irq {} without a handler, and ONESHOT flags not set for irqaction: {}", irq.data(), action_guard.name() @@ -451,7 +452,7 @@ impl IrqManager { if let Err(e) = self.do_set_irq_trigger(desc.clone(), &mut desc_inner_guard, trigger_type) { - kdebug!( + debug!( "Failed to set trigger type for irq {} (name: {}, flags: {:?}), error {:?}", irq.data(), action_guard.name(), @@ -467,10 +468,10 @@ impl IrqManager { )); } } - kdebug!("to irq_activate"); + debug!("to irq_activate"); // 激活中断。这种激活必须独立于IRQ_NOAUTOEN进行*desc_inner_guard.internal_state_mut() |= IrqDescState::IRQS_NOREQUEST;uest. if let Err(e) = self.irq_activate(&desc, &mut desc_inner_guard) { - kdebug!( + debug!( "Failed to activate irq {} (name: {}, flags: {:?}), error {:?}", irq.data(), action_guard.name(), @@ -537,7 +538,7 @@ impl IrqManager { static mut WARNED: bool = false; if action_guard.flags().contains(IrqHandleFlags::IRQF_SHARED) && unsafe { !WARNED } { - kwarn!( + warn!( "Shared interrupt {} for {} requested but not auto enabled", irq.data(), action_guard.name() @@ -551,7 +552,7 @@ impl IrqManager { let new_trigger_type = action_guard.flags().trigger_type(); let old_trigger_type = desc_inner_guard.common_data().trigger_type(); if new_trigger_type != old_trigger_type { - kwarn!("Irq {} uses trigger type: {old_trigger_type:?}, but requested trigger type: {new_trigger_type:?}.", irq.data()); + warn!("Irq {} uses trigger type: {old_trigger_type:?}, but requested trigger type: {new_trigger_type:?}.", irq.data()); } } @@ -612,7 +613,7 @@ impl IrqManager { .thread_completion() .wait_for_completion() .map_err(|e| { - kwarn!( + warn!( "Failed to wait for irq thread ready for {} (irq {:?}), error {:?}", action.inner().name(), desc.irq_data().irq(), @@ -628,7 +629,7 @@ impl IrqManager { desc_inner_guard: &mut SpinLockGuard<'_, InnerIrqDesc>, resend: bool, ) -> Result<(), SystemError> { - kdebug!( + debug!( "irq_activate_and_startup: irq: {}, name: {:?}", desc.irq().data(), desc_inner_guard.name() @@ -659,7 +660,7 @@ impl IrqManager { resend: bool, force: bool, ) -> Result<(), SystemError> { - kdebug!( + debug!( "irq_startup: irq: {}, name: {:?}", desc_inner_guard.irq_data().irq().data(), desc_inner_guard.name() @@ -710,7 +711,7 @@ impl IrqManager { if resend { if let Err(e) = self.irq_check_and_resend(desc_inner_guard, false) { - kerror!( + error!( "Failed to check and resend irq {}, error {:?}", irq_data.irq().data(), e @@ -734,7 +735,7 @@ impl IrqManager { if e == SystemError::ENOSYS { self.unmask_irq(desc_inner_guard); } - kerror!( + error!( "Failed to enable irq {} (name: {:?}), error {:?}", desc_inner_guard.irq_data().irq().data(), desc_inner_guard.name(), @@ -930,7 +931,7 @@ impl IrqManager { let mut to_unmask = false; if !chip.can_set_flow_type() { - // kdebug!( + // debug!( // "No set_irq_type function for irq {}, chip {}", // desc_inner_guard.irq_data().irq().data(), // chip.name() @@ -979,7 +980,7 @@ impl IrqManager { ret = Ok(()); } else { - kerror!( + error!( "Failed to set irq {} trigger type to {:?} on irqchip {}, error {:?}", desc_inner_guard.irq_data().irq().data(), trigger_type, @@ -1059,7 +1060,7 @@ impl IrqManager { if let Err(e) = r { if e != SystemError::ENOSYS { - kerror!( + error!( "Failed to unmask irq {} on irqchip {}, error {:?}", desc_inner_guard.irq_data().irq().data(), desc_inner_guard @@ -1100,7 +1101,7 @@ impl IrqManager { /// /// 参考 https://code.dragonos.org.cn/xref/linux-6.6.21/kernel/irq/manage.c#2026 pub fn free_irq(&self, _irq: IrqNumber, _dev_id: Option>) { - kwarn!("Unimplemented free_irq"); + warn!("Unimplemented free_irq"); } } @@ -1138,7 +1139,7 @@ impl IrqHandler for IrqNestedPrimaryHandler { _static_data: Option<&dyn IrqHandlerData>, _dynamic_data: Option>, ) -> Result { - kwarn!("Primary handler called for nested irq {}", irq.data()); + warn!("Primary handler called for nested irq {}", irq.data()); return Ok(IrqReturn::NotHandled); } } diff --git a/kernel/src/exception/msi.rs b/kernel/src/exception/msi.rs index f521d117f..ef0993eb1 100644 --- a/kernel/src/exception/msi.rs +++ b/kernel/src/exception/msi.rs @@ -124,6 +124,7 @@ impl MsiDesc { } } +#[allow(dead_code)] pub trait MsiDescFunc: Debug + Send + Sync { /// Callback that may be called when the MSI message /// address or data changes. diff --git a/kernel/src/exception/softirq.rs b/kernel/src/exception/softirq.rs index 06e882e80..f7d9513a5 100644 --- a/kernel/src/exception/softirq.rs +++ b/kernel/src/exception/softirq.rs @@ -7,13 +7,13 @@ use core::{ }; use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use log::{debug, info}; use num_traits::FromPrimitive; use system_error::SystemError; use crate::{ arch::CurrentIrqArch, exception::InterruptArch, - kdebug, kinfo, libs::rwlock::RwLock, mm::percpu::{PerCpu, PerCpuVar}, process::ProcessManager, @@ -35,7 +35,7 @@ pub extern "C" fn rs_softirq_init() { #[inline(never)] pub fn softirq_init() -> Result<(), SystemError> { - kinfo!("Initializing softirq..."); + info!("Initializing softirq..."); unsafe { __SORTIRQ_VECTORS = Box::leak(Box::new(Softirq::new())); __CPU_PENDING = Some(Box::new( @@ -46,7 +46,7 @@ pub fn softirq_init() -> Result<(), SystemError> { cpu_pending[i as usize] = VecStatus::default(); } } - kinfo!("Softirq initialized."); + info!("Softirq initialized."); return Ok(()); } @@ -143,20 +143,20 @@ impl Softirq { softirq_num: SoftirqNumber, handler: Arc, ) -> Result { - // kdebug!("register_softirq softirq_num = {:?}", softirq_num as u64); + // debug!("register_softirq softirq_num = {:?}", softirq_num as u64); // let self = &mut SOFTIRQ_VECTORS.lock(); // 判断该软中断向量是否已经被注册 let mut table_guard = self.table.write_irqsave(); if table_guard[softirq_num as usize].is_some() { - // kdebug!("register_softirq failed"); + // debug!("register_softirq failed"); return Err(SystemError::EINVAL); } table_guard[softirq_num as usize] = Some(handler); drop(table_guard); - // kdebug!( + // debug!( // "register_softirq successfully, softirq_num = {:?}", // softirq_num as u64 // ); @@ -169,7 +169,7 @@ impl Softirq { /// @param irq_num 中断向量号码 #[allow(dead_code)] pub fn unregister_softirq(&self, softirq_num: SoftirqNumber) { - // kdebug!("unregister_softirq softirq_num = {:?}", softirq_num as u64); + // debug!("unregister_softirq softirq_num = {:?}", softirq_num as u64); let mut table_guard = self.table.write_irqsave(); // 将软中断向量清空 table_guard[softirq_num as usize] = None; @@ -219,7 +219,7 @@ impl Softirq { softirq_func.as_ref().unwrap().run(); if unlikely(prev_count != ProcessManager::current_pcb().preempt_count()) { - kdebug!( + debug!( "entered softirq {:?} with preempt_count {:?},exited with {:?}", i, prev_count, @@ -255,7 +255,7 @@ impl Softirq { compiler_fence(Ordering::SeqCst); drop(guard); - // kdebug!("raise_softirq exited"); + // debug!("raise_softirq exited"); } #[allow(dead_code)] diff --git a/kernel/src/exception/sysfs.rs b/kernel/src/exception/sysfs.rs index 390e65a9a..1e499bf56 100644 --- a/kernel/src/exception/sysfs.rs +++ b/kernel/src/exception/sysfs.rs @@ -1,4 +1,5 @@ use alloc::{string::ToString, sync::Arc}; +use log::warn; use system_error::SystemError; use unified_init::macros::unified_init; @@ -117,7 +118,7 @@ fn irq_sysfs_add(irq: &IrqNumber, desc: &Arc) { let kset = sys_kernel_irq_kset(); KObjectManager::add_kobj(desc.clone() as Arc, Some(kset)).unwrap_or_else(|e| { - kwarn!("Failed to add irq({irq:?}) kobject to sysfs: {:?}", e); + warn!("Failed to add irq({irq:?}) kobject to sysfs: {:?}", e); }); desc.mark_in_sysfs(); diff --git a/kernel/src/filesystem/devfs/mod.rs b/kernel/src/filesystem/devfs/mod.rs index 2a3ffd2e7..92f602a22 100644 --- a/kernel/src/filesystem/devfs/mod.rs +++ b/kernel/src/filesystem/devfs/mod.rs @@ -11,7 +11,6 @@ use super::vfs::{ }; use crate::{ driver::base::device::device_number::DeviceNumber, - kerror, kinfo, libs::{ once::Once, spinlock::{SpinLock, SpinLockGuard}, @@ -24,6 +23,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::{error, info}; use system_error::SystemError; const DEVFS_BLOCK_SIZE: u64 = 512; @@ -97,7 +97,7 @@ impl DevFS { .expect("DevFS: Failed to create /dev/block"); devfs.register_bultinin_device(); - // kdebug!("ls /dev: {:?}", root.list()); + // debug!("ls /dev: {:?}", root.list()); return devfs; } @@ -541,7 +541,7 @@ impl IndexNode for LockedDevFSInode { _buf: &mut [u8], _data: SpinLockGuard, ) -> Result { - kerror!("DevFS: read_at is not supported!"); + error!("DevFS: read_at is not supported!"); Err(SystemError::ENOSYS) } @@ -580,7 +580,7 @@ macro_rules! devfs_exact_ref { () => {{ let devfs_inode: Result, SystemError> = ROOT_INODE().find("dev"); if let Err(e) = devfs_inode { - kerror!("failed to get DevFS ref. errcode = {:?}", e); + error!("failed to get DevFS ref. errcode = {:?}", e); return Err(SystemError::ENOENT); } @@ -611,7 +611,7 @@ pub fn devfs_init() -> Result<(), SystemError> { static INIT: Once = Once::new(); let mut result = None; INIT.call_once(|| { - kinfo!("Initializing DevFS..."); + info!("Initializing DevFS..."); // 创建 devfs 实例 let devfs: Arc = DevFS::new(); // devfs 挂载 @@ -620,7 +620,7 @@ pub fn devfs_init() -> Result<(), SystemError> { .expect("Unabled to find /dev") .mount(devfs) .expect("Failed to mount at /dev"); - kinfo!("DevFS mounted."); + info!("DevFS mounted."); result = Some(Ok(())); }); diff --git a/kernel/src/filesystem/devpts/mod.rs b/kernel/src/filesystem/devpts/mod.rs index 1fc57f5c6..c019d81ac 100644 --- a/kernel/src/filesystem/devpts/mod.rs +++ b/kernel/src/filesystem/devpts/mod.rs @@ -7,6 +7,7 @@ use alloc::{ vec::Vec, }; use ida::IdAllocator; +use log::info; use system_error::SystemError; use unified_init::macros::unified_init; @@ -278,7 +279,7 @@ pub fn devpts_init() -> Result<(), SystemError> { let ptsfs: Arc = DevPtsFs::new(); do_mount_mkdir(ptsfs, "/dev/pts").expect("Failed to mount DevPtsFS"); - kinfo!("DevPtsFs mounted."); + info!("DevPtsFs mounted."); Ok(()) } diff --git a/kernel/src/filesystem/eventfd.rs b/kernel/src/filesystem/eventfd.rs new file mode 100644 index 000000000..b35f608af --- /dev/null +++ b/kernel/src/filesystem/eventfd.rs @@ -0,0 +1,268 @@ +use crate::filesystem::vfs::file::{File, FileMode}; +use crate::filesystem::vfs::syscall::ModeType; +use crate::filesystem::vfs::{FilePrivateData, FileSystem, FileType, IndexNode, Metadata}; +use crate::libs::spinlock::{SpinLock, SpinLockGuard}; +use crate::libs::wait_queue::WaitQueue; +use crate::net::event_poll::{EPollEventType, EPollItem, EventPoll, KernelIoctlData}; +use crate::process::ProcessManager; +use crate::syscall::Syscall; +use alloc::collections::LinkedList; +use alloc::string::String; +use alloc::sync::Arc; +use alloc::sync::Weak; +use alloc::vec::Vec; +use core::any::Any; +use ida::IdAllocator; +use system_error::SystemError; + +static EVENTFD_ID_ALLOCATOR: IdAllocator = IdAllocator::new(0, u32::MAX as usize); + +bitflags! { + pub struct EventFdFlags: u32{ + /// Provide semaphore-like semantics for reads from the new + /// file descriptor. + const EFD_SEMAPHORE = 0o1; + /// Set the close-on-exec (FD_CLOEXEC) flag on the new file + /// descriptor + const EFD_CLOEXEC = 0o2000000; + /// Set the O_NONBLOCK file status flag on the open file + /// description (see open(2)) referred to by the new file + /// descriptor + const EFD_NONBLOCK = 0o0004000; + } +} + +#[derive(Debug)] +pub struct EventFd { + count: u64, + flags: EventFdFlags, + #[allow(unused)] + id: u32, +} + +impl EventFd { + pub fn new(count: u64, flags: EventFdFlags, id: u32) -> Self { + EventFd { count, flags, id } + } +} + +#[derive(Debug)] +pub struct EventFdInode { + eventfd: SpinLock, + wait_queue: WaitQueue, + epitems: SpinLock>>, +} + +impl EventFdInode { + pub fn new(eventfd: EventFd) -> Self { + EventFdInode { + eventfd: SpinLock::new(eventfd), + wait_queue: WaitQueue::default(), + epitems: SpinLock::new(LinkedList::new()), + } + } + pub fn remove_epoll(&self, epoll: &Weak>) -> Result<(), SystemError> { + let is_remove = !self + .epitems + .lock_irqsave() + .extract_if(|x| x.epoll().ptr_eq(epoll)) + .collect::>() + .is_empty(); + + if is_remove { + return Ok(()); + } + + Err(SystemError::ENOENT) + } +} + +impl IndexNode for EventFdInode { + fn open( + &self, + _data: SpinLockGuard, + _mode: &FileMode, + ) -> Result<(), SystemError> { + Ok(()) + } + + fn close(&self, _data: SpinLockGuard) -> Result<(), SystemError> { + Ok(()) + } + + /// # 从 counter 里读取一个 8 字节的int值 + /// + /// 1. counter !=0 + /// - EFD_SEMAPHORE 如果没有被设置,从 eventfd read,会得到 counter,并将它归0 + /// - EFD_SEMAPHORE 如果被设置,从 eventfd read,会得到值 1,并将 counter - 1 + /// 2. counter == 0 + /// - EFD_NONBLOCK 如果被设置,那么会以 EAGAIN 的错失败 + /// - 否则 read 会被阻塞,直到为非0。 + fn read_at( + &self, + _offset: usize, + len: usize, + buf: &mut [u8], + data: SpinLockGuard, + ) -> Result { + if len < 8 { + return Err(SystemError::EINVAL); + } + let mut val = loop { + let val = self.eventfd.lock().count; + if val != 0 { + break val; + } + if self + .eventfd + .lock() + .flags + .contains(EventFdFlags::EFD_NONBLOCK) + { + return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); + } + self.wait_queue.sleep(); + }; + + let mut eventfd = self.eventfd.lock(); + if eventfd.flags.contains(EventFdFlags::EFD_SEMAPHORE) { + eventfd.count -= 1; + val = 1; + } else { + eventfd.count = 0; + } + let val_bytes = val.to_ne_bytes(); + buf[..8].copy_from_slice(&val_bytes); + + let pollflag = EPollEventType::from_bits_truncate(self.poll(&data)? as u32); + // 唤醒epoll中等待的进程 + EventPoll::wakeup_epoll(&self.epitems, pollflag)?; + + return Ok(8); + } + + /// # 把一个 8 字节的int值写入到 counter 里 + /// + /// - counter 最大值是 2^64 - 1 + /// - 如果写入时会发生溢出,则write会被阻塞 + /// - 如果 EFD_NONBLOCK 被设置,那么以 EAGAIN 失败 + /// - 以不合法的值写入时,会以 EINVAL 失败 + /// - 比如 0xffffffffffffffff 不合法 + /// - 比如 写入的值 size 小于8字节 + fn write_at( + &self, + _offset: usize, + len: usize, + buf: &[u8], + data: SpinLockGuard, + ) -> Result { + if len < 8 { + return Err(SystemError::EINVAL); + } + let val = u64::from_ne_bytes(buf[..8].try_into().unwrap()); + if val == u64::MAX { + return Err(SystemError::EINVAL); + } + loop { + let eventfd = self.eventfd.lock(); + if u64::MAX - eventfd.count > val { + break; + } + // block until a read() is performed on the + // file descriptor, or fails with the error EAGAIN if the + // file descriptor has been made nonblocking. + if eventfd.flags.contains(EventFdFlags::EFD_NONBLOCK) { + return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); + } + drop(eventfd); + self.wait_queue.sleep(); + } + let mut eventfd = self.eventfd.lock(); + eventfd.count += val; + self.wait_queue.wakeup_all(None); + + let pollflag = EPollEventType::from_bits_truncate(self.poll(&data)? as u32); + // 唤醒epoll中等待的进程 + EventPoll::wakeup_epoll(&self.epitems, pollflag)?; + return Ok(8); + } + + /// # 检查 eventfd 的状态 + /// + /// - 如果 counter 的值大于 0 ,那么 fd 的状态就是可读的 + /// - 如果能无阻塞地写入一个至少为 1 的值,那么 fd 的状态就是可写的 + fn poll(&self, _private_data: &FilePrivateData) -> Result { + let mut events = EPollEventType::empty(); + if self.eventfd.lock().count != 0 { + events |= EPollEventType::EPOLLIN | EPollEventType::EPOLLRDNORM; + } + if self.eventfd.lock().count != u64::MAX { + events |= EPollEventType::EPOLLOUT | EPollEventType::EPOLLWRNORM; + } + return Ok(events.bits() as usize); + } + + fn metadata(&self) -> Result { + let meta = Metadata { + mode: ModeType::from_bits_truncate(0o755), + file_type: FileType::File, + ..Default::default() + }; + Ok(meta) + } + + fn resize(&self, _len: usize) -> Result<(), SystemError> { + Ok(()) + } + fn kernel_ioctl( + &self, + arg: Arc, + _data: &FilePrivateData, + ) -> Result { + let epitem = arg + .arc_any() + .downcast::() + .map_err(|_| SystemError::EFAULT)?; + self.epitems.lock().push_back(epitem); + Ok(0) + } + fn fs(&self) -> Arc { + panic!("EventFd does not have a filesystem") + } + fn as_any_ref(&self) -> &dyn Any { + self + } + fn list(&self) -> Result, SystemError> { + Err(SystemError::EINVAL) + } +} + +impl Syscall { + /// # 创建一个 eventfd 文件描述符 + /// + /// ## 参数 + /// - `init_val`: u32: eventfd 的初始值 + /// - `flags`: u32: eventfd 的标志 + /// + /// ## 返回值 + /// - `Ok(usize)`: 成功创建的文件描述符 + /// - `Err(SystemError)`: 创建失败 + /// + /// See: https://man7.org/linux/man-pages/man2/eventfd2.2.html + pub fn sys_eventfd(init_val: u32, flags: u32) -> Result { + let flags = EventFdFlags::from_bits(flags).ok_or(SystemError::EINVAL)?; + let id = EVENTFD_ID_ALLOCATOR.alloc().ok_or(SystemError::ENOMEM)? as u32; + let eventfd = EventFd::new(init_val as u64, flags, id); + let inode = Arc::new(EventFdInode::new(eventfd)); + let filemode = if flags.contains(EventFdFlags::EFD_CLOEXEC) { + FileMode::O_RDWR | FileMode::O_CLOEXEC + } else { + FileMode::O_RDWR + }; + let file = File::new(inode, filemode)?; + let binding = ProcessManager::current_pcb().fd_table(); + let mut fd_table_guard = binding.write(); + let fd = fd_table_guard.alloc_fd(file, None).map(|x| x as usize); + return fd; + } +} diff --git a/kernel/src/filesystem/fat/bpb.rs b/kernel/src/filesystem/fat/bpb.rs index 1c7b8cc7c..2f8d6ec77 100644 --- a/kernel/src/filesystem/fat/bpb.rs +++ b/kernel/src/filesystem/fat/bpb.rs @@ -1,10 +1,10 @@ #![allow(dead_code)] use alloc::sync::Arc; +use log::error; use system_error::SystemError; use crate::{ driver::base::block::{block_device::LBA_SIZE, disk_info::Partition, SeekFrom}, - kerror, libs::vec_cursor::VecCursor, }; @@ -190,27 +190,27 @@ impl BiosParameterBlockFAT32 { /// @brief 验证BPB32的信息是否合法 fn validate(&self, bpb: &BiosParameterBlock) -> Result<(), SystemError> { if bpb.fat_size_16 != 0 { - kerror!("Invalid fat_size_16 value in BPB (should be zero for FAT32)"); + error!("Invalid fat_size_16 value in BPB (should be zero for FAT32)"); return Err(SystemError::EINVAL); } if bpb.root_entries_cnt != 0 { - kerror!("Invalid root_entries value in BPB (should be zero for FAT32)"); + error!("Invalid root_entries value in BPB (should be zero for FAT32)"); return Err(SystemError::EINVAL); } if bpb.total_sectors_16 != 0 { - kerror!("Invalid total_sectors_16 value in BPB (should be zero for FAT32)"); + error!("Invalid total_sectors_16 value in BPB (should be zero for FAT32)"); return Err(SystemError::EINVAL); } if self.fat_size_32 == 0 { - kerror!("Invalid fat_size_32 value in BPB (should be non-zero for FAT32)"); + error!("Invalid fat_size_32 value in BPB (should be non-zero for FAT32)"); return Err(SystemError::EINVAL); } if self.fs_version != 0 { - kerror!("Unknown FAT FS version"); + error!("Unknown FAT FS version"); return Err(SystemError::EINVAL); } @@ -315,28 +315,28 @@ impl BiosParameterBlock { pub fn validate(&self) -> Result<(), SystemError> { // 校验每扇区字节数是否合法 if self.bytes_per_sector.count_ones() != 1 { - kerror!("Invalid bytes per sector(not a power of 2)"); + error!("Invalid bytes per sector(not a power of 2)"); return Err(SystemError::EINVAL); } else if self.bytes_per_sector < 512 { - kerror!("Invalid bytes per sector (value < 512)"); + error!("Invalid bytes per sector (value < 512)"); return Err(SystemError::EINVAL); } else if self.bytes_per_sector > 4096 { - kerror!("Invalid bytes per sector (value > 4096)"); + error!("Invalid bytes per sector (value > 4096)"); return Err(SystemError::EINVAL); } if self.rsvd_sec_cnt < 1 { - kerror!("Invalid rsvd_sec_cnt value in BPB"); + error!("Invalid rsvd_sec_cnt value in BPB"); return Err(SystemError::EINVAL); } if self.num_fats == 0 { - kerror!("Invalid fats value in BPB"); + error!("Invalid fats value in BPB"); return Err(SystemError::EINVAL); } if (self.total_sectors_16 == 0) && (self.total_sectors_32 == 0) { - kerror!("Invalid BPB (total_sectors_16 or total_sectors_32 should be non-zero)"); + error!("Invalid BPB (total_sectors_16 or total_sectors_32 should be non-zero)"); return Err(SystemError::EINVAL); } @@ -367,7 +367,7 @@ impl BiosParameterBlock { // 总扇区数应当大于第一个数据扇区的扇区号 if total_sectors <= first_data_sector { - kerror!("Total sectors lesser than first data sector"); + error!("Total sectors lesser than first data sector"); return Err(SystemError::EINVAL); } diff --git a/kernel/src/filesystem/fat/entry.rs b/kernel/src/filesystem/fat/entry.rs index 322486aeb..cafb3dc4c 100644 --- a/kernel/src/filesystem/fat/entry.rs +++ b/kernel/src/filesystem/fat/entry.rs @@ -1,10 +1,10 @@ #![allow(dead_code)] use core::{cmp::min, intrinsics::unlikely}; +use log::{debug, warn}; use system_error::SystemError; use crate::{ driver::base::block::{block_device::LBA_SIZE, SeekFrom}, - kwarn, libs::vec_cursor::VecCursor, }; use alloc::{ @@ -265,7 +265,7 @@ impl FATFile { let last_cluster = if let Some(c) = fs.get_last_cluster(self.first_cluster) { c } else { - kwarn!("FAT: last cluster not found, File = {self:?}"); + warn!("FAT: last cluster not found, File = {self:?}"); return Err(SystemError::EINVAL); }; // 申请簇 @@ -450,7 +450,7 @@ impl FATDir { free += 1; if free == num_free { - // kdebug!("first_free = {first_free:?}, current_free = ({current_cluster:?}, {offset})"); + // debug!("first_free = {first_free:?}, current_free = ({current_cluster:?}, {offset})"); return Ok(first_free); } } @@ -472,7 +472,7 @@ impl FATDir { / fs.bytes_per_cluster(); let mut first_cluster = Cluster::default(); let mut prev_cluster = current_cluster; - // kdebug!( + // debug!( // "clusters_required={clusters_required}, prev_cluster={prev_cluster:?}, free ={free}" // ); // 申请簇 @@ -592,7 +592,7 @@ impl FATDir { pub fn create_dir(&self, name: &str, fs: &Arc) -> Result { let r: Result = self.check_existence(name, Some(true), fs.clone()); - // kdebug!("check existence ok"); + // debug!("check existence ok"); // 检查错误码,如果能够表明目录项已经存在,则返回-EEXIST if let Err(err_val) = r { if err_val == (SystemError::EISDIR) || err_val == (SystemError::ENOTDIR) { @@ -639,7 +639,7 @@ impl FATDir { dot_dot_entry.flush(fs, fs.cluster_bytes_offset(first_cluster) + offset)?; - // kdebug!("to create dentries"); + // debug!("to create dentries"); // 在当前目录下创建目标目录项 let res = self .create_dir_entries( @@ -652,7 +652,7 @@ impl FATDir { fs.clone(), ) .map(|e| e.to_dir())?; - // kdebug!("create dentries ok"); + // debug!("create dentries ok"); return res; } FATDirEntryOrShortName::DirEntry(_) => { @@ -732,7 +732,7 @@ impl FATDir { LongNameEntryGenerator::new(long_name, short_dentry.checksum()); let num_entries = long_name_gen.num_entries() as u64; - // kdebug!("to find free entries"); + // debug!("to find free entries"); let free_entries: Option<(Cluster, u64)> = self.find_free_entries(num_entries, fs.clone())?; // 目录项开始位置 @@ -1119,7 +1119,7 @@ impl LongDirEntry { | '^' | '#' | '&' => {} '+' | ',' | ';' | '=' | '[' | ']' | '.' | ' ' => {} _ => { - kdebug!("error char: {}", c); + debug!("error char: {}", c); return Err(SystemError::EILSEQ); } } @@ -1328,6 +1328,7 @@ impl ShortDirEntry { } /// @brief 计算短目录项的名称的校验和 + #[allow(clippy::manual_rotate)] fn checksum(&self) -> u8 { let mut result = 0; @@ -1574,7 +1575,7 @@ impl FATDirIter { } } } - // kdebug!("collect dentries done. long_name_entries={long_name_entries:?}"); + // debug!("collect dentries done. long_name_entries={long_name_entries:?}"); let dir_entry: Result = FATDirEntry::new( long_name_entries, ( @@ -1582,16 +1583,16 @@ impl FATDirIter { (self.current_cluster, self.offset), ), ); - // kdebug!("dir_entry={:?}", dir_entry); + // debug!("dir_entry={:?}", dir_entry); match dir_entry { Ok(d) => { - // kdebug!("dir_entry ok"); + // debug!("dir_entry ok"); self.offset += FATRawDirEntry::DIR_ENTRY_LEN; return Ok((self.current_cluster, self.offset, Some(d))); } Err(_) => { - // kdebug!("dir_entry err, e={}", e); + // debug!("dir_entry err, e={}", e); self.offset += FATRawDirEntry::DIR_ENTRY_LEN; } } @@ -2411,7 +2412,7 @@ pub fn get_raw_dir_entry( // let step1 = fs.get_in_partition_bytes_offset(in_disk_bytes_offset); // let step2 = fs.bytes_to_sector(step1); // let lba = fs.get_lba_from_offset(step2); - // kdebug!("step1={step1}, step2={step2}, lba={lba}"); + // debug!("step1={step1}, step2={step2}, lba={lba}"); let mut v: Vec = vec![0; LBA_SIZE]; fs.partition.disk().read_at(lba, 1, &mut v)?; diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index df6af8ce8..9e6fc0126 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -2,6 +2,7 @@ use alloc::string::ToString; use core::cmp::Ordering; use core::intrinsics::unlikely; use core::{any::Any, fmt::Debug}; +use log::error; use system_error::SystemError; use alloc::{ @@ -12,9 +13,12 @@ use alloc::{ }; use crate::driver::base::device::device_number::DeviceNumber; +use crate::filesystem::vfs::file::PageCache; use crate::filesystem::vfs::utils::DName; use crate::filesystem::vfs::{Magic, SpecialNodeData, SuperBlock}; use crate::ipc::pipe::LockedPipeInode; +use crate::mm::fault::{PageFaultHandler, PageFaultMessage}; +use crate::mm::VmFaultReason; use crate::{ driver::base::block::{block_device::LBA_SIZE, disk_info::Partition, SeekFrom}, filesystem::vfs::{ @@ -23,7 +27,6 @@ use crate::{ syscall::ModeType, FileSystem, FileType, IndexNode, InodeId, Metadata, }, - kerror, libs::{ spinlock::{SpinLock, SpinLockGuard}, vec_cursor::VecCursor, @@ -45,6 +48,7 @@ pub const MAX_FILE_SIZE: u64 = 0xffff_ffff; /// @brief 表示当前簇和上一个簇的关系的结构体 /// 定义这样一个结构体的原因是,FAT文件系统的文件中,前后两个簇具有关联关系。 +#[allow(dead_code)] #[derive(Debug, Clone, Copy, Default)] pub struct Cluster { pub cluster_num: u64, @@ -117,6 +121,9 @@ pub struct FATInode { /// 目录名 dname: DName, + + /// 页缓存 + page_cache: Option>, } impl FATInode { @@ -131,7 +138,7 @@ impl FATInode { self.metadata.size = d.size(&self.fs.upgrade().unwrap().clone()) as i64; } FATDirEntry::UnInit => { - kerror!("update_metadata: Uninitialized FATDirEntry: {:?}", self); + error!("update_metadata: Uninitialized FATDirEntry: {:?}", self); return; } }; @@ -214,8 +221,14 @@ impl LockedFATInode { }, special_node: None, dname, + page_cache: None, }))); + if !inode.0.lock().inode_type.is_dir() { + let page_cache = PageCache::new(Some(Arc::downgrade(&inode) as Weak)); + inode.0.lock().page_cache = Some(page_cache); + } + inode.0.lock().self_ref = Arc::downgrade(&inode); inode.0.lock().update_metadata(); @@ -270,6 +283,19 @@ impl FileSystem for FATFileSystem { FAT_MAX_NAMELEN, ) } + + unsafe fn fault(&self, pfm: &mut PageFaultMessage) -> VmFaultReason { + PageFaultHandler::filemap_fault(pfm) + } + + unsafe fn map_pages( + &self, + pfm: &mut PageFaultMessage, + start_pgoff: usize, + end_pgoff: usize, + ) -> VmFaultReason { + PageFaultHandler::filemap_map_pages(pfm, start_pgoff, end_pgoff) + } } impl FATFileSystem { @@ -309,7 +335,7 @@ impl FATFileSystem { match bpb.fat_type { FATType::FAT32(x) => x.fat_size_32 as u64, _ => { - kerror!("FAT12 and FAT16 volumes should have non-zero BPB_FATSz16"); + error!("FAT12 and FAT16 volumes should have non-zero BPB_FATSz16"); return Err(SystemError::EINVAL); } } @@ -347,6 +373,7 @@ impl FATFileSystem { }, special_node: None, dname: DName::default(), + page_cache: None, }))); let result: Arc = Arc::new(FATFileSystem { @@ -457,7 +484,7 @@ impl FATFileSystem { match entry { _n if (0x0ffffff7..=0x0fffffff).contains(¤t_cluster) => { // 当前簇号不是一个能被获得的簇(可能是文件系统出错了) - kerror!("FAT32 get fat entry: current cluster number [{}] is not an allocatable cluster number.", current_cluster); + error!("FAT32 get fat entry: current cluster number [{}] is not an allocatable cluster number.", current_cluster); FATEntry::Bad } 0 => FATEntry::Unused, @@ -613,7 +640,7 @@ impl FATFileSystem { // 如果这个空闲簇不是簇链的第一个簇,那么把当前簇跟前一个簇连上。 if let Some(prev_cluster) = prev_cluster { - // kdebug!("set entry, prev ={prev_cluster:?}, next = {free_cluster:?}"); + // debug!("set entry, prev ={prev_cluster:?}, next = {free_cluster:?}"); self.set_entry(prev_cluster, FATEntry::Next(free_cluster))?; } // 清空新获取的这个簇 @@ -642,12 +669,12 @@ impl FATFileSystem { self.set_entry(cluster, FATEntry::Unused)?; self.fs_info.0.lock().update_free_count_delta(1); // 安全选项:清空被释放的簇 - #[cfg(feature = "secure")] + #[cfg(feature = "fatfs-secure")] self.zero_cluster(cluster)?; return Ok(()); } else { // 不能释放坏簇 - kerror!("Bad clusters cannot be freed."); + error!("Bad clusters cannot be freed."); return Err(SystemError::EFAULT); } } @@ -1136,14 +1163,14 @@ impl FATFileSystem { } else { self.bpb.num_fats as u64 }; - // kdebug!("set entry, bound={bound}, fat_size={fat_size}"); + // debug!("set entry, bound={bound}, fat_size={fat_size}"); for i in 0..bound { // 当前操作的FAT表在磁盘上的字节偏移量 let f_offset: u64 = fat_part_bytes_offset + i * fat_size; let in_block_offset: u64 = self.get_in_block_offset(f_offset); let lba = self.get_lba_from_offset(self.bytes_to_sector(f_offset)); - // kdebug!("set entry, lba={lba}, in_block_offset={in_block_offset}"); + // debug!("set entry, lba={lba}, in_block_offset={in_block_offset}"); let mut v: Vec = vec![0; LBA_SIZE]; self.partition.disk().read_at(lba, 1, &mut v)?; @@ -1157,7 +1184,7 @@ impl FATFileSystem { && cluster.cluster_num >= 0x0ffffff7 && cluster.cluster_num <= 0x0fffffff { - kerror!( + error!( "FAT32: Reserved Cluster {:?} cannot be marked as free", cluster ); @@ -1175,7 +1202,7 @@ impl FATFileSystem { // 恢复保留位 raw_val |= old_bits; - // kdebug!("sent entry, raw_val={raw_val}"); + // debug!("sent entry, raw_val={raw_val}"); cursor.seek(SeekFrom::SeekSet(in_block_offset as i64))?; cursor.write_u32(raw_val)?; @@ -1206,7 +1233,7 @@ impl Drop for FATFileSystem { fn drop(&mut self) { let r = self.umount(); if r.is_err() { - kerror!( + error!( "Umount FAT filesystem failed: errno={:?}, FS detail:{self:?}", r.as_ref().unwrap_err() ); @@ -1257,7 +1284,7 @@ impl FATFsInfo { if fsinfo.is_valid() { return Ok(fsinfo); } else { - kerror!("Error occurred while parsing FATFsInfo."); + error!("Error occurred while parsing FATFsInfo."); return Err(SystemError::EINVAL); } } @@ -1395,7 +1422,7 @@ impl IndexNode for LockedFATInode { return Err(SystemError::EISDIR); } FATDirEntry::UnInit => { - kerror!("FATFS: param: Inode_type uninitialized."); + error!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } } @@ -1421,7 +1448,7 @@ impl IndexNode for LockedFATInode { return Err(SystemError::EISDIR); } FATDirEntry::UnInit => { - kerror!("FATFS: param: Inode_type uninitialized."); + error!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } } @@ -1455,7 +1482,7 @@ impl IndexNode for LockedFATInode { _ => return Err(SystemError::EINVAL), }, FATDirEntry::UnInit => { - kerror!("FATFS: param: Inode_type uninitialized."); + error!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } } @@ -1472,6 +1499,16 @@ impl IndexNode for LockedFATInode { fn metadata(&self) -> Result { return Ok(self.0.lock().metadata.clone()); } + fn set_metadata(&self, metadata: &Metadata) -> Result<(), SystemError> { + let inode = &mut self.0.lock(); + inode.metadata.atime = metadata.atime; + inode.metadata.mtime = metadata.mtime; + inode.metadata.ctime = metadata.ctime; + inode.metadata.mode = metadata.mode; + inode.metadata.uid = metadata.uid; + inode.metadata.gid = metadata.gid; + Ok(()) + } fn resize(&self, len: usize) -> Result<(), SystemError> { let mut guard: SpinLockGuard = self.0.lock(); let fs: &Arc = &guard.fs.upgrade().unwrap(); @@ -1509,7 +1546,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(_) => return Err(SystemError::ENOSYS), FATDirEntry::UnInit => { - kerror!("FATFS: param: Inode_type uninitialized."); + error!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } } @@ -1542,7 +1579,7 @@ impl IndexNode for LockedFATInode { // ====== 生成inode缓存,存入B树 let name = DName::from(ent.name().to_uppercase()); - // kdebug!("name={name}"); + // debug!("name={name}"); if !guard.children.contains_key(&name) && name.as_ref() != "." @@ -1562,7 +1599,7 @@ impl IndexNode for LockedFATInode { return Ok(ret); } FATDirEntry::UnInit => { - kerror!("FATFS: param: Inode_type uninitialized."); + error!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } } @@ -1608,7 +1645,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(d) => d, FATDirEntry::UnInit => { - kerror!("FATFS: param: Inode_type uninitialized."); + error!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } }; @@ -1635,7 +1672,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(d) => d, FATDirEntry::UnInit => { - kerror!("FATFS: param: Inode_type uninitialized."); + error!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } }; @@ -1683,7 +1720,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(d) => d, FATDirEntry::UnInit => { - kerror!("FATFS: param: Inode_type uninitialized."); + error!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } }; @@ -1712,7 +1749,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(d) => d, FATDirEntry::UnInit => { - kerror!("FATFS: param: Inode_type uninitialized."); + error!("FATFS: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } }; @@ -1722,7 +1759,7 @@ impl IndexNode for LockedFATInode { } FATDirEntry::Dir(d) => d, FATDirEntry::UnInit => { - kerror!("FATFA: param: Inode_type uninitialized."); + error!("FATFA: param: Inode_type uninitialized."); return Err(SystemError::EROFS); } }; @@ -1831,6 +1868,10 @@ impl IndexNode for LockedFATInode { .map(|item| item as Arc) .ok_or(SystemError::EINVAL) } + + fn page_cache(&self) -> Option> { + self.0.lock().page_cache.clone() + } } impl Default for FATFsInfo { diff --git a/kernel/src/filesystem/kernfs/mod.rs b/kernel/src/filesystem/kernfs/mod.rs index ff29cf2ac..91765e577 100644 --- a/kernel/src/filesystem/kernfs/mod.rs +++ b/kernel/src/filesystem/kernfs/mod.rs @@ -6,6 +6,7 @@ use alloc::{ vec::Vec, }; use hashbrown::HashMap; +use log::warn; use system_error::SystemError; use crate::{ @@ -345,7 +346,7 @@ impl IndexNode for KernFSInode { } if self.callback.is_none() { - kwarn!("kernfs: callback is none"); + warn!("kernfs: callback is none"); return Err(SystemError::ENOSYS); } @@ -590,7 +591,7 @@ impl KernFSInode { target: &Arc, target_absolute_path: String, ) -> Result, SystemError> { - // kdebug!("kernfs add link: name:{name}, target path={target_absolute_path}"); + // debug!("kernfs add link: name:{name}, target path={target_absolute_path}"); let inode = self.inner_create( name, KernInodeType::SymLink, diff --git a/kernel/src/filesystem/mbr.rs b/kernel/src/filesystem/mbr.rs index eb3af9af8..b3e900de3 100644 --- a/kernel/src/filesystem/mbr.rs +++ b/kernel/src/filesystem/mbr.rs @@ -4,6 +4,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::debug; use system_error::SystemError; use crate::{ @@ -54,7 +55,7 @@ impl MbrDiskPartitionTableEntry { #[repr(packed)] #[derive(Debug, Clone, Copy)] pub struct MbrDiskPartionTable { - pub reserved: [u8; 446], + pub _reserved: [u8; 446], pub dpte: [MbrDiskPartitionTableEntry; 4], // 磁盘分区表项 pub bs_trailsig: u16, } @@ -62,7 +63,7 @@ pub struct MbrDiskPartionTable { impl Default for MbrDiskPartionTable { fn default() -> Self { MbrDiskPartionTable { - reserved: [0; 446], + _reserved: [0; 446], dpte: [Default::default(); 4], bs_trailsig: Default::default(), } @@ -105,10 +106,10 @@ impl MbrDiskPartionTable { table.dpte[i].starting_lba = cursor.read_u32()?; table.dpte[i].total_sectors = cursor.read_u32()?; - kdebug!("dpte[{i}] = {:?}", table.dpte[i]); + debug!("dpte[{i}] = {:?}", table.dpte[i]); } table.bs_trailsig = cursor.read_u16()?; - // kdebug!("bs_trailsig = {}", unsafe { + // debug!("bs_trailsig = {}", unsafe { // read_unaligned(addr_of!(table.bs_trailsig)) // }); diff --git a/kernel/src/filesystem/mod.rs b/kernel/src/filesystem/mod.rs index 3af494b0b..90dcc51bf 100644 --- a/kernel/src/filesystem/mod.rs +++ b/kernel/src/filesystem/mod.rs @@ -1,5 +1,6 @@ pub mod devfs; pub mod devpts; +pub mod eventfd; pub mod fat; pub mod kernfs; pub mod mbr; diff --git a/kernel/src/filesystem/procfs/kmsg.rs b/kernel/src/filesystem/procfs/kmsg.rs index 79f78c0aa..c2181dc80 100644 --- a/kernel/src/filesystem/procfs/kmsg.rs +++ b/kernel/src/filesystem/procfs/kmsg.rs @@ -8,6 +8,7 @@ use alloc::{borrow::ToOwned, string::ToString, vec::Vec}; use kdepends::ringbuffer::{AllocRingBuffer, RingBuffer}; +use log::info; use system_error::SystemError; /// 缓冲区容量 @@ -18,13 +19,13 @@ pub static mut KMSG: Option> = None; /// 初始化KMSG pub fn kmsg_init() { - kinfo!("kmsg_init"); + info!("kmsg_init"); let kmsg = SpinLock::new(Kmsg::new()); compiler_fence(Ordering::SeqCst); unsafe { KMSG = Some(kmsg) }; compiler_fence(Ordering::SeqCst); - kinfo!("kmsg_init done"); + info!("kmsg_init done"); } /// 日志 diff --git a/kernel/src/filesystem/procfs/mod.rs b/kernel/src/filesystem/procfs/mod.rs index 07dcd1795..0defc3e0e 100644 --- a/kernel/src/filesystem/procfs/mod.rs +++ b/kernel/src/filesystem/procfs/mod.rs @@ -1,5 +1,6 @@ use core::intrinsics::size_of; +use ::log::{error, info}; use alloc::{ borrow::ToOwned, collections::BTreeMap, @@ -17,7 +18,6 @@ use crate::{ core::{generate_inode_id, ROOT_INODE}, FileType, }, - kerror, kinfo, libs::{ once::Once, rwlock::RwLock, @@ -149,7 +149,7 @@ impl ProcFSInode { let pcb = if let Some(pcb) = pcb { pcb } else { - kerror!( + error!( "ProcFS: Cannot find pcb for pid {:?} when opening its 'status' file.", pid ); @@ -157,7 +157,7 @@ impl ProcFSInode { }; // 传入数据 let pdata: &mut Vec = &mut pdata.data; - + // name pdata.append( &mut format!("Name:\t{}", pcb.basic().name()) .as_bytes() @@ -174,17 +174,32 @@ impl ProcFSInode { let priority = sched_info_guard.policy(); let vrtime = sched_info_guard.sched_entity.vruntime; + // State pdata.append(&mut format!("\nState:\t{:?}", state).as_bytes().to_owned()); + + // Tgid + pdata.append(&mut format!("\nTgid:\t{}", pcb.tgid().into()).into()); + + // pid pdata.append( &mut format!("\nPid:\t{}", pcb.pid().into()) .as_bytes() .to_owned(), ); + + // ppid pdata.append( &mut format!("\nPpid:\t{}", pcb.basic().ppid().into()) .as_bytes() .to_owned(), ); + + // fdsize + pdata.append(&mut format!("\nFDSize:\t{}", pcb.fd_table().read().fd_open_count()).into()); + + // kthread + pdata.append(&mut format!("\nKthread:\t{}", pcb.is_kthread() as usize).into()); + pdata.append(&mut format!("\ncpu_id:\t{}", cpu_id).as_bytes().to_owned()); pdata.append(&mut format!("\npriority:\t{:?}", priority).as_bytes().to_owned()); pdata.append( @@ -192,6 +207,7 @@ impl ProcFSInode { .as_bytes() .to_owned(), ); + pdata.append(&mut format!("\nvrtime:\t{}", vrtime).as_bytes().to_owned()); if let Some(user_vm) = pcb.basic().user_vm() { @@ -821,7 +837,7 @@ pub fn procfs_init() -> Result<(), SystemError> { static INIT: Once = Once::new(); let mut result = None; INIT.call_once(|| { - kinfo!("Initializing ProcFS..."); + info!("Initializing ProcFS..."); // 创建 procfs 实例 let procfs: Arc = ProcFS::new(); // procfs 挂载 @@ -830,7 +846,7 @@ pub fn procfs_init() -> Result<(), SystemError> { .expect("Unabled to find /proc") .mount(procfs) .expect("Failed to mount at /proc"); - kinfo!("ProcFS mounted."); + info!("ProcFS mounted."); result = Some(Ok(())); }); diff --git a/kernel/src/filesystem/procfs/syscall.rs b/kernel/src/filesystem/procfs/syscall.rs index 2c4aa0416..f80365cc0 100644 --- a/kernel/src/filesystem/procfs/syscall.rs +++ b/kernel/src/filesystem/procfs/syscall.rs @@ -1,5 +1,3 @@ -use core::usize; - use system_error::SystemError; use crate::syscall::Syscall; diff --git a/kernel/src/filesystem/ramfs/mod.rs b/kernel/src/filesystem/ramfs/mod.rs index c2435ff3e..5f1ec4864 100644 --- a/kernel/src/filesystem/ramfs/mod.rs +++ b/kernel/src/filesystem/ramfs/mod.rs @@ -25,6 +25,9 @@ use super::vfs::{ file::FilePrivateData, syscall::ModeType, utils::DName, FileSystem, FileSystemMaker, FsInfo, IndexNode, InodeId, Metadata, SpecialNodeData, }; + +use linkme::distributed_slice; + use super::vfs::{Magic, SuperBlock}; /// RamFS的inode名称的最大长度 diff --git a/kernel/src/filesystem/sysfs/file.rs b/kernel/src/filesystem/sysfs/file.rs index 01450a8ae..1d65be42b 100644 --- a/kernel/src/filesystem/sysfs/file.rs +++ b/kernel/src/filesystem/sysfs/file.rs @@ -4,6 +4,7 @@ use alloc::{ string::ToString, sync::{Arc, Weak}, }; +use log::warn; use system_error::SystemError; use crate::{ @@ -16,7 +17,6 @@ use crate::{ sysfs::{SysFSOps, SysFSOpsSupport}, vfs::{syscall::ModeType, PollStatus}, }, - kwarn, }; use super::{Attribute, BinAttribute, SysFS, SysFSKernPrivateData}; @@ -130,7 +130,7 @@ impl SysFS { drop(x); let sysfs_ops: &dyn SysFSOps = kobj.kobj_type().unwrap().sysfs_ops().ok_or_else(|| { - kwarn!("missing sysfs attribute operations for kobject: {kobj:?}"); + warn!("missing sysfs attribute operations for kobject: {kobj:?}"); SystemError::EINVAL })?; @@ -184,7 +184,7 @@ impl SysFS { if let Some(parent) = parent { let r = parent.remove(attr.name()); if unlikely(r.is_err()) { - kwarn!( + warn!( "failed to remove file '{}' from '{}'", attr.name(), kobj.name() @@ -220,7 +220,7 @@ impl SysFS { if let Some(parent) = parent { let r = parent.remove(attr.name()); if unlikely(r.is_err()) { - kwarn!( + warn!( "failed to remove file '{}' from '{}'", attr.name(), kobj.name() diff --git a/kernel/src/filesystem/sysfs/group.rs b/kernel/src/filesystem/sysfs/group.rs index fb45ccb9f..f1dff750c 100644 --- a/kernel/src/filesystem/sysfs/group.rs +++ b/kernel/src/filesystem/sysfs/group.rs @@ -1,6 +1,7 @@ use core::intrinsics::unlikely; use alloc::{string::ToString, sync::Arc}; +use log::{error, warn}; use system_error::SystemError; use crate::{ @@ -10,7 +11,6 @@ use crate::{ sysfs::{dir::SysKernDirPriv, sysfs_instance, SysFSKernPrivateData}, vfs::{syscall::ModeType, IndexNode}, }, - kwarn, libs::casting::DowncastArc, }; @@ -38,7 +38,7 @@ impl SysFS { continue; } if let Err(e) = self.do_create_group(kobj, group, update) { - kerror!( + error!( "Failed to create group '{}', err={e:?}", group.name().unwrap_or("") ); @@ -136,9 +136,8 @@ impl SysFS { if let Some(name) = group.name() { parent_inode = inode .find(name) - .map_err(|e| { - kwarn!("sysfs group '{name}' not found for kobject {kobj:?}"); - e + .inspect_err(|_e| { + warn!("sysfs group '{name}' not found for kobject {kobj:?}"); })? .downcast_arc() .unwrap(); @@ -189,7 +188,7 @@ impl SysFS { } if unlikely((mode.bits() & (!0o644)) != 0) { - kwarn!( + warn!( "Attribute '{name}' has invalid mode 0{mode:o}", name = attr.name(), mode = mode @@ -204,7 +203,7 @@ impl SysFS { } if let Err(e) = e { - kerror!( + error!( "Failed to create sysfs files for group '{}', err={e:?}", group.name().unwrap_or("") ); diff --git a/kernel/src/filesystem/sysfs/mod.rs b/kernel/src/filesystem/sysfs/mod.rs index 2c2dce136..97579f007 100644 --- a/kernel/src/filesystem/sysfs/mod.rs +++ b/kernel/src/filesystem/sysfs/mod.rs @@ -9,10 +9,10 @@ use super::{ use crate::{ driver::base::kobject::KObject, filesystem::vfs::ROOT_INODE, - kinfo, kwarn, libs::{casting::DowncastArc, once::Once}, }; use alloc::sync::Arc; +use log::{info, warn}; use system_error::SystemError; pub mod dir; @@ -34,7 +34,7 @@ pub fn sysfs_init() -> Result<(), SystemError> { static INIT: Once = Once::new(); let mut result = None; INIT.call_once(|| { - kinfo!("Initializing SysFS..."); + info!("Initializing SysFS..."); // 创建 sysfs 实例 // let sysfs: Arc = OldSysFS::new(); @@ -47,9 +47,9 @@ pub fn sysfs_init() -> Result<(), SystemError> { .expect("Unabled to find /sys") .mount(sysfs_instance().fs().clone()) .expect("Failed to mount at /sys"); - kinfo!("SysFS mounted."); + info!("SysFS mounted."); - // kdebug!("sys_bus_init result: {:?}", SYS_BUS_INODE().list()); + // debug!("sys_bus_init result: {:?}", SYS_BUS_INODE().list()); result = Some(Ok(())); }); @@ -227,6 +227,6 @@ impl SysFS { /// 警告:重复的sysfs entry pub(self) fn warn_duplicate(&self, parent: &Arc, name: &str) { let path = self.kernfs_path(parent); - kwarn!("duplicate sysfs entry: {path}/{name}"); + warn!("duplicate sysfs entry: {path}/{name}"); } } diff --git a/kernel/src/filesystem/sysfs/symlink.rs b/kernel/src/filesystem/sysfs/symlink.rs index f9fb27124..66e4b33e7 100644 --- a/kernel/src/filesystem/sysfs/symlink.rs +++ b/kernel/src/filesystem/sysfs/symlink.rs @@ -72,7 +72,7 @@ impl SysFS { let target_abs_path = "/sys".to_string() + &self.kernfs_path(&target_inode).to_owned(); // let current_path = self.kernfs_path(inode); - // kdebug!("sysfs: create link {} to {}", current_path, target_abs_path); + // debug!("sysfs: create link {} to {}", current_path, target_abs_path); let kn = inode.add_link(name.clone(), &target_inode, target_abs_path); if kn.is_ok() { diff --git a/kernel/src/filesystem/vfs/core.rs b/kernel/src/filesystem/vfs/core.rs index bc2d0ebba..5a1d55176 100644 --- a/kernel/src/filesystem/vfs/core.rs +++ b/kernel/src/filesystem/vfs/core.rs @@ -1,6 +1,7 @@ use core::{hint::spin_loop, sync::atomic::Ordering}; use alloc::sync::Arc; +use log::{error, info}; use system_error::SystemError; use crate::{ @@ -13,7 +14,6 @@ use crate::{ sysfs::sysfs_init, vfs::{mount::MountFS, syscall::ModeType, AtomicInodeId, FileSystem, FileType}, }, - kerror, kinfo, process::ProcessManager, }; @@ -67,7 +67,7 @@ pub fn vfs_init() -> Result<(), SystemError> { let root_entries = ROOT_INODE().list().expect("VFS init failed"); if !root_entries.is_empty() { - kinfo!("Successfully initialized VFS!"); + info!("Successfully initialized VFS!"); } return Ok(()); } @@ -75,7 +75,7 @@ pub fn vfs_init() -> Result<(), SystemError> { /// @brief 迁移伪文件系统的inode /// 请注意,为了避免删掉了伪文件系统内的信息,因此没有在原root inode那里调用unlink. fn migrate_virtual_filesystem(new_fs: Arc) -> Result<(), SystemError> { - kinfo!("VFS: Migrating filesystems..."); + info!("VFS: Migrating filesystems..."); let new_fs = MountFS::new(new_fs, None); // 获取新的根文件系统的根节点的引用 @@ -108,7 +108,7 @@ fn migrate_virtual_filesystem(new_fs: Arc) -> Result<(), SystemE drop(old_root_inode); } - kinfo!("VFS: Migrate filesystems done!"); + info!("VFS: Migrate filesystems done!"); return Ok(()); } @@ -131,7 +131,7 @@ fn root_partition() -> Arc { let virtio0 = crate::driver::block::virtio_blk::virtio_blk_0(); if virtio0.is_none() { - kerror!("Failed to get virtio_blk_0"); + error!("Failed to get virtio_blk_0"); loop { spin_loop(); } @@ -142,12 +142,12 @@ fn root_partition() -> Arc { } } pub fn mount_root_fs() -> Result<(), SystemError> { - kinfo!("Try to mount FAT32 as root fs..."); + info!("Try to mount FAT32 as root fs..."); let partiton: Arc = root_partition(); let fatfs: Result, SystemError> = FATFileSystem::new(partiton); if fatfs.is_err() { - kerror!( + error!( "Failed to initialize fatfs, code={:?}", fatfs.as_ref().err() ); @@ -158,12 +158,12 @@ pub fn mount_root_fs() -> Result<(), SystemError> { let fatfs: Arc = fatfs.unwrap(); let r = migrate_virtual_filesystem(fatfs); if r.is_err() { - kerror!("Failed to migrate virtual filesystem to FAT32!"); + error!("Failed to migrate virtual filesystem to FAT32!"); loop { spin_loop(); } } - kinfo!("Successfully migrate rootfs to FAT32!"); + info!("Successfully migrate rootfs to FAT32!"); return Ok(()); } @@ -174,14 +174,14 @@ pub fn do_mkdir_at( path: &str, mode: FileMode, ) -> Result, SystemError> { - // kdebug!("Call do mkdir at"); + // debug!("Call do mkdir at"); let (mut current_inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path.trim())?; let (name, parent) = rsplit_path(&path); if let Some(parent) = parent { current_inode = current_inode.lookup(parent)?; } - // kdebug!("mkdir at {:?}", current_inode.metadata()?.inode_id); + // debug!("mkdir at {:?}", current_inode.metadata()?.inode_id); return current_inode.mkdir(name, ModeType::from_bits_truncate(mode.bits())); } @@ -239,7 +239,7 @@ pub fn do_unlink_at(dirfd: i32, path: &str) -> Result { return Err(SystemError::EPERM); } - let (filename, parent_path) = rsplit_path(path); + let (filename, parent_path) = rsplit_path(&remain_path); // 查找父目录 let parent_inode: Arc = inode_begin .lookup_follow_symlink(parent_path.unwrap_or("/"), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index c867d4ca7..69994b73f 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -5,26 +5,29 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::error; use system_error::SystemError; +use xarray::XArray; +use super::{Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; +use crate::filesystem::eventfd::EventFdInode; use crate::{ + arch::MMArch, driver::{ base::{block::SeekFrom, device::DevicePrivateData}, tty::tty_device::TtyFilePrivateData, }, filesystem::procfs::ProcfsFilePrivateData, ipc::pipe::{LockedPipeInode, PipeFsPrivateData}, - kerror, libs::{rwlock::RwLock, spinlock::SpinLock}, + mm::{page::Page, MemoryManagementArch}, net::{ event_poll::{EPollItem, EPollPrivateData, EventPoll}, socket::SocketInode, }, - process::ProcessManager, + process::{cred::Cred, ProcessManager}, }; -use super::{Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData}; - /// 文件私有信息的枚举类型 #[derive(Debug, Clone)] #[allow(dead_code)] @@ -118,6 +121,66 @@ impl FileMode { return self.bits() & FileMode::O_ACCMODE.bits(); } } + +/// 页面缓存 +pub struct PageCache { + xarray: SpinLock>>, + inode: Option>, +} + +impl core::fmt::Debug for PageCache { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("PageCache") + .field( + "xarray", + &self + .xarray + .lock() + .range(0..((MMArch::PAGE_ADDRESS_SIZE >> MMArch::PAGE_SHIFT) as u64)) + .map(|(_, r)| (*r).clone()) + .collect::>>(), + ) + .finish() + } +} + +impl PageCache { + pub fn new(inode: Option>) -> Arc { + let page_cache = Self { + xarray: SpinLock::new(XArray::new()), + inode, + }; + Arc::new(page_cache) + } + + pub fn inode(&self) -> Option> { + self.inode.clone() + } + + pub fn add_page(&self, offset: usize, page: &Arc) { + let mut guard = self.xarray.lock(); + let mut cursor = guard.cursor_mut(offset as u64); + cursor.store(page.clone()); + } + + pub fn get_page(&self, offset: usize) -> Option> { + let mut guard = self.xarray.lock(); + let mut cursor = guard.cursor_mut(offset as u64); + let page = cursor.load().map(|r| (*r).clone()); + page + } + + pub fn remove_page(&self, offset: usize) { + let mut guard = self.xarray.lock(); + let mut cursor = guard.cursor_mut(offset as u64); + cursor.remove(); + } + + pub fn set_inode(&mut self, inode: Weak) { + self.inode = Some(inode) + } +} + /// @brief 抽象文件结构体 #[derive(Debug)] pub struct File { @@ -131,6 +194,8 @@ pub struct File { /// readdir时候用的,暂存的本次循环中,所有子目录项的名字的数组 readdir_subdirs_name: SpinLock>, pub private_data: SpinLock, + /// 文件的凭证 + cred: Cred, } impl File { @@ -154,6 +219,7 @@ impl File { file_type, readdir_subdirs_name: SpinLock::new(Vec::default()), private_data: SpinLock::new(FilePrivateData::default()), + cred: ProcessManager::current_pcb().cred(), }; f.inode.open(f.private_data.lock(), &mode)?; @@ -233,7 +299,14 @@ impl File { let len = self .inode - .read_at(offset, len, buf, self.private_data.lock())?; + .read_at(offset, len, buf, self.private_data.lock()) + .map_err(|e| { + if e == SystemError::ERESTARTSYS { + SystemError::EINTR + } else { + e + } + })?; if update_offset { self.offset @@ -258,11 +331,24 @@ impl File { // 如果文件指针已经超过了文件大小,则需要扩展文件大小 if offset > self.inode.metadata()?.size as usize { - self.inode.resize(offset)?; + self.inode.resize(offset).map_err(|e| { + if e == SystemError::ERESTARTSYS { + SystemError::EINTR + } else { + e + } + })?; } let len = self .inode - .write_at(offset, len, buf, self.private_data.lock())?; + .write_at(offset, len, buf, self.private_data.lock()) + .map_err(|e| { + if e == SystemError::ERESTARTSYS { + SystemError::EINTR + } else { + e + } + })?; if update_offset { self.offset @@ -349,7 +435,7 @@ impl File { *readdir_subdirs_name = inode.list()?; readdir_subdirs_name.sort(); } - // kdebug!("sub_entries={sub_entries:?}"); + // debug!("sub_entries={sub_entries:?}"); // 已经读到末尾 if offset == readdir_subdirs_name.len() { @@ -360,7 +446,7 @@ impl File { let sub_inode: Arc = match inode.find(name) { Ok(i) => i, Err(e) => { - kerror!( + error!( "Readdir error: Failed to find sub inode:{name:?}, file={self:?}, error={e:?}" ); return Err(e); @@ -408,6 +494,7 @@ impl File { file_type: self.file_type, readdir_subdirs_name: SpinLock::new(self.readdir_subdirs_name.lock().clone()), private_data: SpinLock::new(self.private_data.lock().clone()), + cred: self.cred.clone(), }; // 调用inode的open方法,让inode知道有新的文件打开了这个inode if self @@ -492,11 +579,7 @@ impl File { return inode.inner().lock().add_epoll(epitem); } _ => { - let r = self.inode.ioctl( - EventPoll::ADD_EPOLLITEM, - &epitem as *const Arc as usize, - &self.private_data.lock(), - ); + let r = self.inode.kernel_ioctl(epitem, &self.private_data.lock()); if r.is_err() { return Err(SystemError::ENOSYS); } @@ -513,9 +596,19 @@ impl File { let inode = self.inode.downcast_ref::().unwrap(); let mut socket = inode.inner(); - return socket.remove_epoll(epoll); + socket.remove_epoll(epoll) + } + FileType::Pipe => { + let inode = self.inode.downcast_ref::().unwrap(); + inode.inner().lock().remove_epoll(epoll) + } + _ => { + let inode = self + .inode + .downcast_ref::() + .ok_or(SystemError::ENOSYS)?; + inode.remove_epoll(epoll) } - _ => return Err(SystemError::ENOSYS), } } @@ -529,7 +622,7 @@ impl Drop for File { let r: Result<(), SystemError> = self.inode.close(self.private_data.lock()); // 打印错误信息 if r.is_err() { - kerror!( + error!( "pid: {:?} failed to close file: {:?}, errno={:?}", ProcessManager::current_pcb().pid(), self, @@ -545,7 +638,11 @@ pub struct FileDescriptorVec { /// 当前进程打开的文件描述符 fds: Vec>>, } - +impl Default for FileDescriptorVec { + fn default() -> Self { + Self::new() + } +} impl FileDescriptorVec { pub const PROCESS_MAX_FD: usize = 1024; @@ -573,6 +670,17 @@ impl FileDescriptorVec { return res; } + /// 返回 `已经打开的` 文件描述符的数量 + pub fn fd_open_count(&self) -> usize { + let mut size = 0; + for fd in &self.fds { + if fd.is_some() { + size += 1; + } + } + return size; + } + /// @brief 判断文件描述符序号是否合法 /// /// @return true 合法 @@ -632,14 +740,13 @@ impl FileDescriptorVec { /// ## 参数 /// /// - `fd` 文件描述符序号 - pub fn drop_fd(&mut self, fd: i32) -> Result<(), SystemError> { + pub fn drop_fd(&mut self, fd: i32) -> Result, SystemError> { self.get_file_by_fd(fd).ok_or(SystemError::EBADF)?; // 把文件描述符数组对应位置设置为空 let file = self.fds[fd as usize].take().unwrap(); - assert!(Arc::strong_count(&file) == 1); - return Ok(()); + return Ok(file); } #[allow(dead_code)] @@ -653,7 +760,7 @@ impl FileDescriptorVec { let to_drop = file.close_on_exec(); if to_drop { if let Err(r) = self.drop_fd(i as i32) { - kerror!( + error!( "Failed to close file: pid = {:?}, fd = {}, error = {:?}", ProcessManager::current_pcb().pid(), i, diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 482d4a884..00ce6ba50 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -20,10 +20,16 @@ use crate::{ casting::DowncastArc, spinlock::{SpinLock, SpinLockGuard}, }, + mm::{fault::PageFaultMessage, VmFaultReason}, time::PosixTimeSpec, }; -use self::{core::generate_inode_id, file::FileMode, syscall::ModeType, utils::DName}; +use self::{ + core::generate_inode_id, + file::{FileMode, PageCache}, + syscall::ModeType, + utils::DName, +}; pub use self::{core::ROOT_INODE, file::FilePrivateData, mount::MountFS}; /// vfs容许的最大的路径名称长度 @@ -350,6 +356,14 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { return Err(SystemError::ENOSYS); } + fn kernel_ioctl( + &self, + _arg: Arc, + _data: &FilePrivateData, + ) -> Result { + return Err(SystemError::ENOSYS); + } + /// @brief 获取inode所在的文件系统的指针 fn fs(&self) -> Arc; @@ -548,6 +562,14 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { fn parent(&self) -> Result, SystemError> { return self.find(".."); } + + fn page_cache(&self) -> Option> { + log::error!( + "function page_cache() has not yet been implemented for inode:{}", + crate::libs::name::get_type_name(&self) + ); + None + } } impl DowncastArc for dyn IndexNode { @@ -797,6 +819,25 @@ pub trait FileSystem: Any + Sync + Send + Debug { fn name(&self) -> &str; fn super_block(&self) -> SuperBlock; + + unsafe fn fault(&self, _pfm: &mut PageFaultMessage) -> VmFaultReason { + panic!( + "fault() has not yet been implemented for filesystem: {}", + crate::libs::name::get_type_name(&self) + ) + } + + unsafe fn map_pages( + &self, + _pfm: &mut PageFaultMessage, + _start_pgoff: usize, + _end_pgoff: usize, + ) -> VmFaultReason { + panic!( + "map_pages() has not yet been implemented for filesystem: {}", + crate::libs::name::get_type_name(&self) + ) + } } impl DowncastArc for dyn FileSystem { @@ -882,7 +923,7 @@ macro_rules! producefs { match $initializer_slice.iter().find(|&m| m.name == $filesystem) { Some(maker) => maker.call(), None => { - kerror!("mismatch filesystem type : {}", $filesystem); + log::error!("mismatch filesystem type : {}", $filesystem); Err(SystemError::EINVAL) } } diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index 7d7515833..cc479883b 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -19,11 +19,14 @@ use crate::{ rwlock::RwLock, spinlock::{SpinLock, SpinLockGuard}, }, + mm::{fault::PageFaultMessage, VmFaultReason}, }; use super::{ - file::FileMode, syscall::ModeType, utils::DName, FilePrivateData, FileSystem, FileType, - IndexNode, InodeId, Magic, SuperBlock, + file::{FileMode, PageCache}, + syscall::ModeType, + utils::DName, + FilePrivateData, FileSystem, FileType, IndexNode, InodeId, Magic, SuperBlock, }; const MOUNTFS_BLOCK_SIZE: u64 = 512; @@ -160,25 +163,6 @@ impl MountFSInode { } } - /// 将新的挂载点-挂载文件系统添加到父级的挂载树 - pub(super) fn do_mount( - &self, - inode_id: InodeId, - new_mount_fs: Arc, - ) -> Result<(), SystemError> { - let mut guard = self.mount_fs.mountpoints.lock(); - if guard.contains_key(&inode_id) { - return Err(SystemError::EBUSY); - } - guard.insert(inode_id, new_mount_fs); - - return Ok(()); - } - - pub(super) fn inode_id(&self) -> InodeId { - self.metadata().map(|x| x.inode_id).unwrap() - } - fn do_find(&self, name: &str) -> Result, SystemError> { // 直接调用当前inode所在的文件系统的find方法进行查找 // 由于向下查找可能会跨越文件系统的边界,因此需要尝试替换inode @@ -456,7 +440,7 @@ impl IndexNode for MountFSInode { if self.is_mountpoint_root()? { return Err(SystemError::EBUSY); } - // kdebug!("from {:?}, to {:?}", from, self); + // debug!("from {:?}, to {:?}", from, self); let new_mount_fs = from.umount()?; self.mount_fs .mountpoints @@ -520,6 +504,10 @@ impl IndexNode for MountFSInode { fn parent(&self) -> Result, SystemError> { return self.do_parent().map(|inode| inode as Arc); } + + fn page_cache(&self) -> Option> { + self.inner_inode.page_cache() + } } impl FileSystem for MountFS { @@ -547,6 +535,19 @@ impl FileSystem for MountFS { fn super_block(&self) -> SuperBlock { SuperBlock::new(Magic::MOUNT_MAGIC, MOUNTFS_BLOCK_SIZE, MOUNTFS_MAX_NAMELEN) } + + unsafe fn fault(&self, pfm: &mut PageFaultMessage) -> VmFaultReason { + self.inner_filesystem.fault(pfm) + } + + unsafe fn map_pages( + &self, + pfm: &mut PageFaultMessage, + start_pgoff: usize, + end_pgoff: usize, + ) -> VmFaultReason { + self.inner_filesystem.map_pages(pfm, start_pgoff, end_pgoff) + } } /// MountList diff --git a/kernel/src/filesystem/vfs/open.rs b/kernel/src/filesystem/vfs/open.rs index 3e2e5660c..14f31fa10 100644 --- a/kernel/src/filesystem/vfs/open.rs +++ b/kernel/src/filesystem/vfs/open.rs @@ -1,11 +1,7 @@ use alloc::sync::Arc; +use log::warn; use system_error::SystemError; -use crate::{ - driver::base::block::SeekFrom, process::ProcessManager, - syscall::user_access::check_and_clone_cstr, -}; - use super::{ fcntl::AtFlags, file::{File, FileMode}, @@ -13,6 +9,13 @@ use super::{ utils::{rsplit_path, user_path_at}, FileType, IndexNode, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; +use crate::filesystem::vfs::syscall::UtimensFlags; +use crate::time::{syscall::PosixTimeval, PosixTimeSpec}; +use crate::{ + driver::base::block::SeekFrom, process::ProcessManager, + syscall::user_access::check_and_clone_cstr, +}; +use alloc::string::String; pub(super) fn do_faccessat( dirfd: i32, @@ -35,8 +38,9 @@ pub(super) fn do_faccessat( // let follow_symlink = flags & AtFlags::AT_SYMLINK_NOFOLLOW.bits() as u32 == 0; let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = path.to_str().map_err(|_| SystemError::EINVAL)?; - let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, &path)?; + let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; // 如果找不到文件,则返回错误码ENOENT let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; @@ -47,13 +51,14 @@ pub(super) fn do_faccessat( pub fn do_fchmodat(dirfd: i32, path: *const u8, _mode: ModeType) -> Result { let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = path.to_str().map_err(|_| SystemError::EINVAL)?; - let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, &path)?; + let (inode, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; // 如果找不到文件,则返回错误码ENOENT let _inode = inode.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; - kwarn!("do_fchmodat: not implemented yet\n"); + warn!("do_fchmodat: not implemented yet\n"); // todo: 真正去改变文件的权限 return Ok(0); @@ -76,7 +81,7 @@ fn do_sys_openat2( how: OpenHow, follow_symlink: bool, ) -> Result { - // kdebug!("open path: {}, how: {:?}", path, how); + // debug!("open path: {}, how: {:?}", path, how); let path = path.trim(); let (inode_begin, path) = user_path_at(&ProcessManager::current_pcb(), dirfd, path)?; @@ -146,3 +151,84 @@ fn do_sys_openat2( return r; } + +/// On Linux, futimens() is a library function implemented on top of +/// the utimensat() system call. To support this, the Linux +/// utimensat() system call implements a nonstandard feature: if +/// pathname is NULL, then the call modifies the timestamps of the +/// file referred to by the file descriptor dirfd (which may refer to +/// any type of file). +pub fn do_utimensat( + dirfd: i32, + pathname: Option, + times: Option<[PosixTimeSpec; 2]>, + flags: UtimensFlags, +) -> Result { + const UTIME_NOW: i64 = (1i64 << 30) - 1i64; + const UTIME_OMIT: i64 = (1i64 << 30) - 2i64; + // log::debug!("do_utimensat: dirfd:{}, pathname:{:?}, times:{:?}, flags:{:?}", dirfd, pathname, times, flags); + let inode = match pathname { + Some(path) => { + let (inode_begin, path) = + user_path_at(&ProcessManager::current_pcb(), dirfd, path.as_str())?; + let inode = if flags.contains(UtimensFlags::AT_SYMLINK_NOFOLLOW) { + inode_begin.lookup(path.as_str())? + } else { + inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)? + }; + inode + } + None => { + let binding = ProcessManager::current_pcb().fd_table(); + let fd_table_guard = binding.write(); + let file = fd_table_guard + .get_file_by_fd(dirfd) + .ok_or(SystemError::EBADF)?; + file.inode() + } + }; + let now = PosixTimeSpec::now(); + let mut meta = inode.metadata()?; + + if let Some([atime, mtime]) = times { + if atime.tv_nsec == UTIME_NOW { + meta.atime = now; + } else if atime.tv_nsec != UTIME_OMIT { + meta.atime = atime; + } + if mtime.tv_nsec == UTIME_NOW { + meta.mtime = now; + } else if mtime.tv_nsec != UTIME_OMIT { + meta.mtime = mtime; + } + inode.set_metadata(&meta).unwrap(); + } else { + meta.atime = now; + meta.mtime = now; + inode.set_metadata(&meta).unwrap(); + } + return Ok(0); +} + +pub fn do_utimes(path: &str, times: Option<[PosixTimeval; 2]>) -> Result { + // log::debug!("do_utimes: path:{:?}, times:{:?}", path, times); + let (inode_begin, path) = user_path_at( + &ProcessManager::current_pcb(), + AtFlags::AT_FDCWD.bits(), + path, + )?; + let inode = inode_begin.lookup_follow_symlink(path.as_str(), VFS_MAX_FOLLOW_SYMLINK_TIMES)?; + let mut meta = inode.metadata()?; + + if let Some([atime, mtime]) = times { + meta.atime = PosixTimeSpec::from(atime); + meta.mtime = PosixTimeSpec::from(mtime); + inode.set_metadata(&meta)?; + } else { + let now = PosixTimeSpec::now(); + meta.atime = now; + meta.mtime = now; + inode.set_metadata(&meta)?; + } + return Ok(0); +} diff --git a/kernel/src/filesystem/vfs/syscall.rs b/kernel/src/filesystem/vfs/syscall.rs index 7449f5acb..53e674f47 100644 --- a/kernel/src/filesystem/vfs/syscall.rs +++ b/kernel/src/filesystem/vfs/syscall.rs @@ -1,15 +1,15 @@ use core::ffi::c_void; use core::mem::size_of; -use alloc::string::ToString; use alloc::{string::String, sync::Arc, vec::Vec}; +use log::warn; use system_error::SystemError; use crate::producefs; +use crate::syscall::user_access::UserBufferReader; use crate::{ driver::base::{block::SeekFrom, device::device_number::DeviceNumber}, filesystem::vfs::{core as Vcore, file::FileDescriptorVec}, - kerror, libs::rwlock::RwLockWriteGuard, mm::{verify_area, VirtAddr}, process::ProcessManager, @@ -17,19 +17,18 @@ use crate::{ user_access::{self, check_and_clone_cstr, UserBufferWriter}, Syscall, }, - time::PosixTimeSpec, + time::{syscall::PosixTimeval, PosixTimeSpec}, }; use super::{ core::{do_mkdir_at, do_remove_dir, do_unlink_at}, fcntl::{AtFlags, FcntlCommand, FD_CLOEXEC}, file::{File, FileMode}, - open::{do_faccessat, do_fchmodat, do_sys_open}, + open::{do_faccessat, do_fchmodat, do_sys_open, do_utimensat, do_utimes}, utils::{rsplit_path, user_path_at}, Dirent, FileType, IndexNode, SuperBlock, FSMAKER, MAX_PATHLEN, ROOT_INODE, VFS_MAX_FOLLOW_SYMLINK_TIMES, }; -// use crate::kdebug; pub const SEEK_SET: u32 = 0; pub const SEEK_CUR: u32 = 1; @@ -324,6 +323,13 @@ bitflags! { } } +bitflags! { + pub struct UtimensFlags: u32 { + /// 不需要解释符号链接 + const AT_SYMLINK_NOFOLLOW = 0x100; + } +} + #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct PosixStatfs { @@ -391,6 +397,7 @@ impl PosixOpenHow { } } +#[allow(dead_code)] #[derive(Debug, Clone, Copy)] pub struct OpenHow { pub o_flags: FileMode, @@ -476,7 +483,10 @@ impl Syscall { mode: u32, follow_symlink: bool, ) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + let open_flags: FileMode = FileMode::from_bits(o_flags).ok_or(SystemError::EINVAL)?; let mode = ModeType::from_bits(mode).ok_or(SystemError::EINVAL)?; return do_sys_open( @@ -495,7 +505,10 @@ impl Syscall { mode: u32, follow_symlink: bool, ) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + let open_flags: FileMode = FileMode::from_bits(o_flags).ok_or(SystemError::EINVAL)?; let mode = ModeType::from_bits(mode).ok_or(SystemError::EINVAL)?; return do_sys_open(dirfd, &path, open_flags, mode, follow_symlink); @@ -509,8 +522,9 @@ impl Syscall { pub fn close(fd: usize) -> Result { let binding = ProcessManager::current_pcb().fd_table(); let mut fd_table_guard = binding.write(); - - fd_table_guard.drop_fd(fd as i32).map(|_| 0) + let _file = fd_table_guard.drop_fd(fd as i32)?; + drop(fd_table_guard); + Ok(0) } /// @brief 发送命令到文件描述符对应的设备, @@ -674,7 +688,10 @@ impl Syscall { return Err(SystemError::EFAULT); } - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + let proc = ProcessManager::current_pcb(); // Copy path to kernel space to avoid some security issues let mut new_path = String::from(""); @@ -778,7 +795,10 @@ impl Syscall { /// /// @return uint64_t 负数错误码 / 0表示成功 pub fn mkdir(path: *const u8, mode: usize) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + do_mkdir_at( AtFlags::AT_FDCWD.bits(), &path, @@ -853,7 +873,10 @@ impl Syscall { pub fn link(old: *const u8, new: *const u8) -> Result { let get_path = |cstr: *const u8| -> Result { - let res = check_and_clone_cstr(cstr, Some(MAX_PATHLEN))?; + let res = check_and_clone_cstr(cstr, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + if res.len() >= MAX_PATHLEN { return Err(SystemError::ENAMETOOLONG); } @@ -880,8 +903,12 @@ impl Syscall { new: *const u8, flags: i32, ) -> Result { - let old = check_and_clone_cstr(old, Some(MAX_PATHLEN))?; - let new = check_and_clone_cstr(new, Some(MAX_PATHLEN))?; + let old = check_and_clone_cstr(old, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + let new = check_and_clone_cstr(new, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; if old.len() >= MAX_PATHLEN || new.len() >= MAX_PATHLEN { return Err(SystemError::ENAMETOOLONG); } @@ -905,10 +932,12 @@ impl Syscall { pub fn unlinkat(dirfd: i32, path: *const u8, flags: u32) -> Result { let flags = AtFlags::from_bits(flags as i32).ok_or(SystemError::EINVAL)?; - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; if flags.contains(AtFlags::AT_REMOVEDIR) { - // kdebug!("rmdir"); + // debug!("rmdir"); match do_remove_dir(dirfd, &path) { Err(err) => { return Err(err); @@ -930,12 +959,16 @@ impl Syscall { } pub fn rmdir(path: *const u8) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; return do_remove_dir(AtFlags::AT_FDCWD.bits(), &path).map(|v| v as usize); } pub fn unlink(path: *const u8) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; return do_unlink_at(AtFlags::AT_FDCWD.bits(), &path).map(|v| v as usize); } @@ -962,8 +995,14 @@ impl Syscall { filename_to: *const u8, _flags: u32, ) -> Result { - let filename_from = check_and_clone_cstr(filename_from, Some(MAX_PATHLEN)).unwrap(); - let filename_to = check_and_clone_cstr(filename_to, Some(MAX_PATHLEN)).unwrap(); + let filename_from = check_and_clone_cstr(filename_from, Some(MAX_PATHLEN)) + .unwrap() + .into_string() + .map_err(|_| SystemError::EINVAL)?; + let filename_to = check_and_clone_cstr(filename_to, Some(MAX_PATHLEN)) + .unwrap() + .into_string() + .map_err(|_| SystemError::EINVAL)?; // 文件名过长 if filename_from.len() > MAX_PATHLEN || filename_to.len() > MAX_PATHLEN { return Err(SystemError::ENAMETOOLONG); @@ -1092,7 +1131,7 @@ impl Syscall { /// - `cmd`:命令 /// - `arg`:参数 pub fn fcntl(fd: i32, cmd: FcntlCommand, arg: i32) -> Result { - // kdebug!("fcntl ({cmd:?}) fd: {fd}, arg={arg}"); + // debug!("fcntl ({cmd:?}) fd: {fd}, arg={arg}"); match cmd { FcntlCommand::DupFd | FcntlCommand::DupFdCloexec => { if arg < 0 || arg as usize >= FileDescriptorVec::PROCESS_MAX_FD { @@ -1186,7 +1225,7 @@ impl Syscall { // TODO: unimplemented // 未实现的命令,返回0,不报错。 - kwarn!("fcntl: unimplemented command: {:?}, defaults to 0.", cmd); + warn!("fcntl: unimplemented command: {:?}, defaults to 0.", cmd); return Err(SystemError::ENOSYS); } } @@ -1307,7 +1346,10 @@ impl Syscall { ModeType::empty().bits(), true, )?; - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN)).unwrap(); + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN)) + .unwrap() + .into_string() + .map_err(|_| SystemError::EINVAL)?; let pcb = ProcessManager::current_pcb(); let (_inode_begin, remain_path) = user_path_at(&pcb, fd as i32, &path)?; let inode = ROOT_INODE().lookup_follow_symlink(&remain_path, MAX_PATHLEN)?; @@ -1442,7 +1484,9 @@ impl Syscall { mode: ModeType, dev_t: DeviceNumber, ) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; let path = path.as_str().trim(); let inode: Result, SystemError> = @@ -1491,7 +1535,9 @@ impl Syscall { user_buf: *mut u8, buf_size: usize, ) -> Result { - let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let path = check_and_clone_cstr(path, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; let path = path.as_str().trim(); let mut user_buf = UserBufferWriter::new(user_buf, buf_size, true)?; @@ -1568,7 +1614,7 @@ impl Syscall { // fchmod没完全实现,因此不修改文件的权限 // todo: 实现fchmod - kwarn!("fchmod not fully implemented"); + warn!("fchmod not fully implemented"); return Ok(0); } /// #挂载文件系统 @@ -1593,13 +1639,16 @@ impl Syscall { _mountflags: usize, _data: *const c_void, ) -> Result { - let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))?; + let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; - let filesystemtype = user_access::check_and_clone_cstr(filesystemtype, Some(MAX_PATHLEN))?; + let fstype_str = user_access::check_and_clone_cstr(filesystemtype, Some(MAX_PATHLEN))?; + let fstype_str = fstype_str.to_str().map_err(|_| SystemError::EINVAL)?; - let filesystemtype = producefs!(FSMAKER, filesystemtype)?; + let fstype = producefs!(FSMAKER, fstype_str)?; - Vcore::do_mount(filesystemtype, target.to_string().as_str())?; + Vcore::do_mount(fstype, &target)?; return Ok(0); } @@ -1613,7 +1662,9 @@ impl Syscall { /// /// [umount(2) — Linux manual page](https://www.man7.org/linux/man-pages/man2/umount.2.html) pub fn umount2(target: *const u8, flags: i32) -> Result<(), SystemError> { - let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))?; + let target = user_access::check_and_clone_cstr(target, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; Vcore::do_umount2( AtFlags::AT_FDCWD.bits(), &target, @@ -1621,6 +1672,48 @@ impl Syscall { )?; return Ok(()); } + + pub fn sys_utimensat( + dirfd: i32, + pathname: *const u8, + times: *const PosixTimeSpec, + flags: u32, + ) -> Result { + let pathname = if pathname.is_null() { + None + } else { + let pathname = check_and_clone_cstr(pathname, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + Some(pathname) + }; + let flags = UtimensFlags::from_bits(flags).ok_or(SystemError::EINVAL)?; + let times = if times.is_null() { + None + } else { + let times_reader = UserBufferReader::new(times, size_of::() * 2, true)?; + let times = times_reader.read_from_user::(0)?; + Some([times[0], times[1]]) + }; + do_utimensat(dirfd, pathname, times, flags) + } + + pub fn sys_utimes( + pathname: *const u8, + times: *const PosixTimeval, + ) -> Result { + let pathname = check_and_clone_cstr(pathname, Some(MAX_PATHLEN))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; + let times = if times.is_null() { + None + } else { + let times_reader = UserBufferReader::new(times, size_of::() * 2, true)?; + let times = times_reader.read_from_user::(0)?; + Some([times[0], times[1]]) + }; + do_utimes(&pathname, times) + } } #[repr(C)] @@ -1662,8 +1755,7 @@ impl IoVecs { // 将用户空间的IoVec转换为引用(注意:这里的引用是静态的,因为用户空间的IoVec不会被释放) let iovs: &[IoVec] = core::slice::from_raw_parts(iov, iovcnt); - let mut slices: Vec<&mut [u8]> = vec![]; - slices.reserve(iovs.len()); + let mut slices: Vec<&mut [u8]> = Vec::with_capacity(iovs.len()); for iov in iovs.iter() { if iov.iov_len == 0 { diff --git a/kernel/src/filesystem/vfs/utils.rs b/kernel/src/filesystem/vfs/utils.rs index 7e9ce6c79..770de48d1 100644 --- a/kernel/src/filesystem/vfs/utils.rs +++ b/kernel/src/filesystem/vfs/utils.rs @@ -1,8 +1,7 @@ use core::cmp::Ordering; -use core::fmt::Debug; +use core::fmt::{self, Debug}; use core::hash::Hash; -use alloc::string::ToString; use alloc::{string::String, sync::Arc}; use system_error::SystemError; @@ -135,9 +134,9 @@ impl Clone for DName { } } -impl ToString for DName { - fn to_string(&self) -> String { - (*self.0).clone() +impl fmt::Display for DName { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) } } diff --git a/kernel/src/init/init.rs b/kernel/src/init/init.rs index 309dd753b..c42fa4cc7 100644 --- a/kernel/src/init/init.rs +++ b/kernel/src/init/init.rs @@ -75,12 +75,11 @@ fn do_start_kernel() { time_init(); timer_init(); kthread_init(); + setup_arch_post().expect("setup_arch_post failed"); clocksource_boot_finish(); Futex::init(); - setup_arch_post().expect("setup_arch_post failed"); - #[cfg(all(target_arch = "x86_64", feature = "kvm"))] crate::virt::kvm::kvm_init(); } diff --git a/kernel/src/init/initial_kthread.rs b/kernel/src/init/initial_kthread.rs index 3ff270694..0f7056a4c 100644 --- a/kernel/src/init/initial_kthread.rs +++ b/kernel/src/init/initial_kthread.rs @@ -2,14 +2,14 @@ use core::sync::atomic::{compiler_fence, Ordering}; -use alloc::string::{String, ToString}; +use alloc::{ffi::CString, string::ToString}; +use log::{debug, error}; use system_error::SystemError; use crate::{ arch::{interrupt::TrapFrame, process::arch_switch_to_user}, driver::{net::e1000e::e1000e::e1000e_init, virtio::virtio::virtio_probe}, filesystem::vfs::core::mount_root_fs, - kdebug, kerror, net::net_core::net_init, process::{kthread::KernelThreadMechanism, stdio::stdio_init, ProcessFlags, ProcessManager}, smp::smp_init, @@ -35,16 +35,14 @@ fn kernel_init() -> Result<(), SystemError> { #[cfg(target_arch = "x86_64")] crate::driver::disk::ahci::ahci_init().expect("Failed to initialize AHCI"); - virtio_probe(); mount_root_fs().expect("Failed to mount root fs"); - e1000e_init(); net_init().unwrap_or_else(|err| { - kerror!("Failed to initialize network: {:?}", err); + error!("Failed to initialize network: {:?}", err); }); - kdebug!("initial kernel thread done."); + debug!("initial kernel thread done."); return Ok(()); } @@ -88,25 +86,23 @@ fn switch_to_user() -> ! { } fn try_to_run_init_process(path: &str, trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - if let Err(e) = run_init_process(path.to_string(), trap_frame) { + if let Err(e) = run_init_process(path, trap_frame) { if e != SystemError::ENOENT { - kerror!( + error!( "Failed to run init process: {path} exists but couldn't execute it (error {:?})", e ); } return Err(e); } - Ok(()) } -fn run_init_process(path: String, trap_frame: &mut TrapFrame) -> Result<(), SystemError> { - let argv = vec![path.clone()]; - let envp = vec![String::from("PATH=/")]; +fn run_init_process(path: &str, trap_frame: &mut TrapFrame) -> Result<(), SystemError> { + let argv = vec![CString::new(path).unwrap()]; + let envp = vec![CString::new("PATH=/").unwrap()]; compiler_fence(Ordering::SeqCst); - Syscall::do_execve(path, argv, envp, trap_frame)?; - + Syscall::do_execve(path.to_string(), argv, envp, trap_frame)?; Ok(()) } diff --git a/kernel/src/init/mod.rs b/kernel/src/init/mod.rs index c67a9001e..b52416100 100644 --- a/kernel/src/init/mod.rs +++ b/kernel/src/init/mod.rs @@ -27,6 +27,7 @@ fn init_intertrait() { #[derive(Debug)] pub struct BootParams { pub screen_info: BootTimeScreenInfo, + #[allow(dead_code)] pub arch: ArchBootParams, boot_command_line: [u8; Self::BOOT_COMMAND_LINE_SIZE], } diff --git a/kernel/src/ipc/pipe.rs b/kernel/src/ipc/pipe.rs index a0349a4e2..864bc750c 100644 --- a/kernel/src/ipc/pipe.rs +++ b/kernel/src/ipc/pipe.rs @@ -1,6 +1,4 @@ use crate::{ - arch::CurrentIrqArch, - exception::InterruptArch, filesystem::vfs::{ core::generate_inode_id, file::FileMode, syscall::ModeType, FilePrivateData, FileSystem, FileType, IndexNode, Metadata, @@ -11,13 +9,14 @@ use crate::{ }, net::event_poll::{EPollEventType, EPollItem, EventPoll}, process::ProcessState, - sched::{schedule, SchedMode}, + sched::SchedMode, time::PosixTimeSpec, }; use alloc::{ collections::LinkedList, sync::{Arc, Weak}, + vec::Vec, }; use system_error::SystemError; @@ -104,6 +103,25 @@ impl InnerPipeInode { self.epitems.lock().push_back(epitem); Ok(()) } + + fn buf_full(&self) -> bool { + return self.valid_cnt as usize == PIPE_BUFF_SIZE; + } + + pub fn remove_epoll(&self, epoll: &Weak>) -> Result<(), SystemError> { + let is_remove = !self + .epitems + .lock_irqsave() + .extract_if(|x| x.epoll().ptr_eq(epoll)) + .collect::>() + .is_empty(); + + if is_remove { + return Ok(()); + } + + Err(SystemError::ENOENT) + } } impl LockedPipeInode { @@ -150,6 +168,16 @@ impl LockedPipeInode { pub fn inner(&self) -> &SpinLock { &self.inner } + + fn readable(&self) -> bool { + let inode = self.inner.lock(); + return inode.valid_cnt > 0 || inode.writer == 0; + } + + fn writeable(&self) -> bool { + let inode = self.inner.lock(); + return !inode.buf_full() || inode.reader == 0; + } } impl IndexNode for LockedPipeInode { @@ -173,6 +201,7 @@ impl IndexNode for LockedPipeInode { if buf.len() < len { return Err(SystemError::EINVAL); } + // log::debug!("pipe mode: {:?}", mode); // 加锁 let mut inode = self.inner.lock(); @@ -193,14 +222,12 @@ impl IndexNode for LockedPipeInode { } // 否则在读等待队列中睡眠,并释放锁 - unsafe { - let irq_guard = CurrentIrqArch::save_and_disable_irq(); - - drop(inode); - self.read_wait_queue.sleep_without_schedule(); - drop(irq_guard); + drop(inode); + let r = wq_wait_event_interruptible!(self.read_wait_queue, self.readable(), {}); + if r.is_err() { + return Err(SystemError::ERESTARTSYS); } - schedule(SchedMode::SM_NONE); + inode = self.inner.lock(); } @@ -351,13 +378,11 @@ impl IndexNode for LockedPipeInode { } // 解锁并睡眠 - unsafe { - let irq_guard = CurrentIrqArch::save_and_disable_irq(); - drop(inode); - self.write_wait_queue.sleep_without_schedule(); - drop(irq_guard); + drop(inode); + let r = wq_wait_event_interruptible!(self.write_wait_queue, self.writeable(), {}); + if r.is_err() { + return Err(SystemError::ERESTARTSYS); } - schedule(SchedMode::SM_NONE); inode = self.inner.lock(); } diff --git a/kernel/src/ipc/shm.rs b/kernel/src/ipc/shm.rs index 6e4b7224d..99cce2e45 100644 --- a/kernel/src/ipc/shm.rs +++ b/kernel/src/ipc/shm.rs @@ -14,10 +14,11 @@ use crate::{ syscall::user_access::{UserBufferReader, UserBufferWriter}, time::PosixTimeSpec, }; -use alloc::vec::Vec; +use alloc::{sync::Arc, vec::Vec}; use core::sync::atomic::{compiler_fence, Ordering}; use hashbrown::{HashMap, HashSet}; use ida::IdAllocator; +use log::info; use num::ToPrimitive; use system_error::SystemError; @@ -28,14 +29,14 @@ pub const IPC_PRIVATE: ShmKey = ShmKey::new(0); /// 初始化SHM_MANAGER pub fn shm_manager_init() { - kinfo!("shm_manager_init"); + info!("shm_manager_init"); let shm_manager = SpinLock::new(ShmManager::new()); compiler_fence(Ordering::SeqCst); unsafe { SHM_MANAGER = Some(shm_manager) }; compiler_fence(Ordering::SeqCst); - kinfo!("shm_manager_init done"); + info!("shm_manager_init done"); } pub fn shm_manager_lock() -> SpinLockGuard<'static, ShmManager> { @@ -164,10 +165,10 @@ impl ShmManager { let mut page_manager_guard = page_manager_lock_irqsave(); let mut cur_phys = PhysPageFrame::new(phys_page.0); for _ in 0..page_count.data() { - let mut page = Page::new(true); - page.set_shm_id(shm_id); + let page = Arc::new(Page::new(true, cur_phys.phys_address())); + page.write_irqsave().set_shm_id(shm_id); let paddr = cur_phys.phys_address(); - page_manager_guard.insert(paddr, page); + page_manager_guard.insert(paddr, &page); cur_phys = cur_phys.next(); } @@ -323,8 +324,8 @@ impl ShmManager { if map_count > 0 { // 设置共享内存物理页当映射计数等于0时可被回收 for _ in 0..count.data() { - let page = page_manager_guard.get_mut(&cur_phys.phys_address()); - page.set_dealloc_when_zero(true); + let page = page_manager_guard.get_unwrap(&cur_phys.phys_address()); + page.write_irqsave().set_dealloc_when_zero(true); cur_phys = cur_phys.next(); } @@ -435,7 +436,7 @@ impl KernelShm { /// 共享内存段的映射计数(有多少个不同的VMA映射) pub fn map_count(&self) -> usize { - let page_manager_guard = page_manager_lock_irqsave(); + let mut page_manager_guard = page_manager_lock_irqsave(); let mut id_set: HashSet = HashSet::new(); let mut cur_phys = PhysPageFrame::new(self.shm_start_paddr); let page_count = PageFrameCount::from_bytes(page_align_up(self.shm_size)).unwrap(); @@ -443,7 +444,8 @@ impl KernelShm { for _ in 0..page_count.data() { let page = page_manager_guard.get(&cur_phys.phys_address()).unwrap(); id_set.extend( - page.anon_vma() + page.read_irqsave() + .anon_vma() .iter() .map(|vma| vma.id()) .collect::>(), diff --git a/kernel/src/ipc/signal.rs b/kernel/src/ipc/signal.rs index df4f0f466..fe91bd68d 100644 --- a/kernel/src/ipc/signal.rs +++ b/kernel/src/ipc/signal.rs @@ -1,12 +1,12 @@ use core::sync::atomic::compiler_fence; use alloc::sync::Arc; +use log::warn; use system_error::SystemError; use crate::{ arch::ipc::signal::{SigCode, SigFlags, SigSet, Signal}, ipc::signal_types::SigactionType, - kwarn, libs::spinlock::SpinLockGuard, process::{pid::PidType, Pid, ProcessControlBlock, ProcessFlags, ProcessManager}, }; @@ -16,6 +16,35 @@ use super::signal_types::{ }; impl Signal { + pub fn signal_pending_state( + interruptible: bool, + task_wake_kill: bool, + pcb: &Arc, + ) -> bool { + if !interruptible && !task_wake_kill { + return false; + } + + if !pcb.has_pending_signal() { + return false; + } + + return interruptible || Self::fatal_signal_pending(pcb); + } + + /// 判断当前进程是否收到了SIGKILL信号 + pub fn fatal_signal_pending(pcb: &Arc) -> bool { + let guard = pcb.sig_info_irqsave(); + if guard + .sig_pending() + .signal() + .contains(Signal::SIGKILL.into()) + { + return true; + } + + return false; + } /// 向目标进程发送信号 /// /// ## 参数 @@ -35,7 +64,7 @@ impl Signal { // 如果 pid 等于 -1,那么信号的发送范围是:调用进程有权将信号发往的每个目标进程,除去 init(进程 ID 为 1)和调用进程自身。如果特权级进程发起这一调用,那么会发送信号给系统中的所有进程,上述两个进程除外。显而易见,有时也将这种信号发送方式称之为广播信号 // 如果并无进程与指定的 pid 相匹配,那么 kill() 调用失败,同时将 errno 置为 ESRCH(“查无此进程”) if pid.lt(&Pid::from(0)) { - kwarn!("Kill operation not support: pid={:?}", pid); + warn!("Kill operation not support: pid={:?}", pid); return Err(SystemError::ENOSYS); } compiler_fence(core::sync::atomic::Ordering::SeqCst); @@ -47,7 +76,7 @@ impl Signal { let pcb = ProcessManager::find(pid); if pcb.is_none() { - kwarn!("No such process."); + warn!("No such process."); return retval; } @@ -85,7 +114,7 @@ impl Signal { if !self.prepare_sianal(pcb.clone(), force_send) { return Err(SystemError::EINVAL); } - // kdebug!("force send={}", force_send); + // debug!("force send={}", force_send); let pcb_info = pcb.sig_info_irqsave(); let pending = if matches!(pt, PidType::PID) { pcb_info.sig_shared_pending() @@ -141,7 +170,7 @@ impl Signal { /// @param pt siginfo结构体中,pid字段代表的含义 #[allow(clippy::if_same_then_else)] fn complete_signal(&self, pcb: Arc, pt: PidType) { - // kdebug!("complete_signal"); + // debug!("complete_signal"); compiler_fence(core::sync::atomic::Ordering::SeqCst); // ===== 寻找需要wakeup的目标进程 ===== @@ -284,14 +313,14 @@ impl Signal { fn signal_wake_up(pcb: Arc, _guard: SpinLockGuard, fatal: bool) { // 如果是 fatal 的话就唤醒 stop 和 block 的进程来响应,因为唤醒后就会终止 // 如果不是 fatal 的就只唤醒 stop 的进程来响应 - // kdebug!("signal_wake_up"); + // debug!("signal_wake_up"); // 如果目标进程已经在运行,则发起一个ipi,使得它陷入内核 let state = pcb.sched_info().inner_lock_read_irqsave().state(); let mut wakeup_ok = true; if state.is_blocked_interruptable() { ProcessManager::wakeup(&pcb).unwrap_or_else(|e| { wakeup_ok = false; - kwarn!( + warn!( "Current pid: {:?}, signal_wake_up target {:?} error: {:?}", ProcessManager::current_pcb().pid(), pcb.pid(), @@ -301,7 +330,7 @@ fn signal_wake_up(pcb: Arc, _guard: SpinLockGuard, force_default: bool) { compiler_fence(core::sync::atomic::Ordering::SeqCst); - // kdebug!("hand=0x{:018x}", hand as *const sighand_struct as usize); + // debug!("hand=0x{:018x}", hand as *const sighand_struct as usize); let actions = &mut pcb.sig_struct_irqsave().handlers; for sigaction in actions.iter_mut() { diff --git a/kernel/src/ipc/signal_types.rs b/kernel/src/ipc/signal_types.rs index 1ed085470..6ebfbf611 100644 --- a/kernel/src/ipc/signal_types.rs +++ b/kernel/src/ipc/signal_types.rs @@ -64,6 +64,7 @@ pub struct SignalStruct { } #[derive(Debug)] +#[allow(dead_code)] pub struct InnerSignalStruct { pub cnt: AtomicI64, /// 如果对应linux,这部分会有一个引用计数,但是没发现在哪里有用到需要计算引用的地方,因此 @@ -80,6 +81,12 @@ impl SignalStruct { } } +impl Default for SignalStruct { + fn default() -> Self { + Self::new() + } +} + impl Deref for SignalStruct { type Target = InnerSignalStruct; @@ -430,7 +437,7 @@ impl SigPending { /// @brief 从当前进程的sigpending中取出下一个待处理的signal,并返回给调用者。(调用者应当处理这个信号) /// 请注意,进入本函数前,当前进程应当持有current_pcb().sighand.siglock pub fn dequeue_signal(&mut self, sig_mask: &SigSet) -> (Signal, Option) { - // kdebug!("dequeue signal"); + // debug!("dequeue signal"); // 获取下一个要处理的信号的编号 let sig = self.next_signal(sig_mask); diff --git a/kernel/src/ipc/syscall.rs b/kernel/src/ipc/syscall.rs index 995dcfbd5..2d0b9b6b2 100644 --- a/kernel/src/ipc/syscall.rs +++ b/kernel/src/ipc/syscall.rs @@ -3,6 +3,7 @@ use core::{ sync::atomic::compiler_fence, }; +use log::{error, warn}; use system_error::SystemError; use crate::{ @@ -15,12 +16,11 @@ use crate::{ FilePrivateData, }, ipc::shm::{shm_manager_lock, IPC_PRIVATE}, - kerror, kwarn, libs::align::page_align_up, libs::spinlock::SpinLock, mm::{ allocator::page_frame::{PageFrameCount, PhysPageFrame, VirtPageFrame}, - page::{page_manager_lock_irqsave, PageFlags, PageFlushAll}, + page::{page_manager_lock_irqsave, EntryFlags, PageFlushAll}, syscall::ProtFlags, ucontext::{AddressSpace, VMA}, VirtAddr, VmFlags, @@ -96,7 +96,7 @@ impl Syscall { let sig = Signal::from(sig); if sig == Signal::INVALID { // 传入的signal数值不合法 - kwarn!("Not a valid signal number"); + warn!("Not a valid signal number"); return Err(SystemError::EINVAL); } @@ -173,12 +173,12 @@ impl Syscall { } // TODO 如果为空,赋默认值? - // kdebug!("new_ka={:?}", new_ka); + // debug!("new_ka={:?}", new_ka); // 如果用户手动给了sa_restorer,那么就置位SA_FLAG_RESTORER,否则报错。(用户必须手动指定restorer) if new_ka.restorer().is_some() { new_ka.flags_mut().insert(SigFlags::SA_RESTORER); } else if new_ka.action().is_customized() { - kerror!( + error!( "pid:{:?}: in sys_sigaction: User must manually sprcify a sa_restorer for signal {}.", ProcessManager::current_pcb().pid(), sig @@ -229,7 +229,7 @@ impl Syscall { } } SigactionType::SaSigaction(_) => { - kerror!("unsupported type: SaSigaction"); + error!("unsupported type: SaSigaction"); VirtAddr::new(USER_SIG_DFL as usize) } }; @@ -261,7 +261,7 @@ impl Syscall { pub fn shmget(key: ShmKey, size: usize, shmflg: ShmFlags) -> Result { // 暂不支持巨页 if shmflg.contains(ShmFlags::SHM_HUGETLB) { - kerror!("shmget: not support huge page"); + error!("shmget: not support huge page"); return Err(SystemError::ENOSYS); } @@ -324,8 +324,8 @@ impl Syscall { .ok_or(SystemError::EINVAL)?; let vm_flags = VmFlags::from(shmflg); let destination = VirtPageFrame::new(region.start()); - let page_flags: PageFlags = - PageFlags::from_prot_flags(ProtFlags::from(vm_flags), true); + let page_flags: EntryFlags = + EntryFlags::from_prot_flags(ProtFlags::from(vm_flags), true); let flusher: PageFlushAll = PageFlushAll::new(); // 将共享内存映射到对应虚拟区域 @@ -351,14 +351,14 @@ impl Syscall { .mappings .contains(vaddr) .ok_or(SystemError::EINVAL)?; - if vma.lock().region().start() != vaddr { + if vma.lock_irqsave().region().start() != vaddr { return Err(SystemError::EINVAL); } // 验证用户虚拟内存区域是否有效 let _ = UserBufferReader::new(vaddr.data() as *const u8, size, true)?; - // 必须在取消映射前获取到PageFlags + // 必须在取消映射前获取到EntryFlags let page_flags = address_write_guard .user_mapper .utable @@ -386,7 +386,8 @@ impl Syscall { // 将vma加入到对应Page的anon_vma page_manager_guard - .get_mut(&phys.phys_address()) + .get_unwrap(&phys.phys_address()) + .write_irqsave() .insert_vma(vma.clone()); phys = phys.next(); @@ -394,7 +395,7 @@ impl Syscall { } // 更新vma的映射状态 - vma.lock().set_mapped(true); + vma.lock_irqsave().set_mapped(true); vaddr.data() } @@ -427,7 +428,7 @@ impl Syscall { .ok_or(SystemError::EINVAL)?; // 判断vaddr是否为起始地址 - if vma.lock().region().start() != vaddr { + if vma.lock_irqsave().region().start() != vaddr { return Err(SystemError::EINVAL); } @@ -440,9 +441,9 @@ impl Syscall { .0; // 如果物理页的shm_id为None,代表不是共享页 - let page_manager_guard = page_manager_lock_irqsave(); + let mut page_manager_guard = page_manager_lock_irqsave(); let page = page_manager_guard.get(&paddr).ok_or(SystemError::EINVAL)?; - let shm_id = page.shm_id().ok_or(SystemError::EINVAL)?; + let shm_id = page.read_irqsave().shm_id().ok_or(SystemError::EINVAL)?; drop(page_manager_guard); // 获取对应共享页管理信息 diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index b26a7ca71..99af0cc42 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -7,22 +7,21 @@ #![feature(const_for)] #![feature(const_mut_refs)] #![feature(const_trait_impl)] -#![feature(const_transmute_copy)] #![feature(const_refs_to_cell)] #![feature(core_intrinsics)] #![feature(c_void_variant)] #![feature(extract_if)] #![feature(fn_align)] -#![feature(inline_const)] +#![feature(linked_list_retain)] #![feature(naked_functions)] #![feature(new_uninit)] -#![feature(panic_info_message)] #![feature(ptr_internals)] -#![feature(ptr_to_from_bits)] #![feature(trait_upcasting)] #![feature(slice_ptr_get)] +#![feature(sync_unsafe_cell)] #![feature(vec_into_raw_parts)] #![cfg_attr(target_os = "none", no_std)] +#![allow(internal_features)] // clippy的配置 #![deny(clippy::all)] #![allow(clippy::bad_bit_mask)] @@ -85,6 +84,8 @@ extern crate x86; extern crate klog_types; extern crate uefi; extern crate uefi_raw; +#[macro_use] +extern crate wait_queue_macros; use crate::mm::allocator::kernel_allocator::KernelAllocator; @@ -106,7 +107,9 @@ pub static KERNEL_ALLOCATOR: KernelAllocator = KernelAllocator; #[panic_handler] #[no_mangle] pub fn panic(info: &PanicInfo) -> ! { - kerror!("Kernel Panic Occurred."); + use log::error; + + error!("Kernel Panic Occurred."); match info.location() { Some(loc) => { @@ -121,15 +124,7 @@ pub fn panic(info: &PanicInfo) -> ! { println!("No location info"); } } - - match info.message() { - Some(msg) => { - println!("Message:\n\t{}", msg); - } - None => { - println!("No panic message."); - } - } + println!("Message:\n\t{}", info.message()); #[cfg(all(feature = "backtrace", target_arch = "x86_64"))] { diff --git a/kernel/src/libs/elf.rs b/kernel/src/libs/elf.rs index 71bb91e37..6c74dec1a 100644 --- a/kernel/src/libs/elf.rs +++ b/kernel/src/libs/elf.rs @@ -12,12 +12,12 @@ use elf::{ file::FileHeader, segment::{ProgramHeader, SegmentTable}, }; +use log::error; use system_error::SystemError; use crate::{ arch::{CurrentElfArch, MMArch}, driver::base::block::SeekFrom, - kdebug, kerror, libs::align::page_align_up, mm::{ allocator::page_frame::{PageFrameCount, VirtPageFrame}, @@ -124,7 +124,7 @@ impl ElfLoader { ) -> Result<(), ExecError> { let start = Self::elf_page_start(start); let end = Self::elf_page_align_up(end); - + // debug!("set_elf_brk: start={:?}, end={:?}", start, end); if end > start { let r = user_vm_guard.map_anonymous( start, @@ -134,9 +134,9 @@ impl ElfLoader { false, true, ); - // kdebug!("set_elf_brk: map_anonymous: r={:?}", r); + // debug!("set_elf_brk: map_anonymous: r={:?}", r); if r.is_err() { - kerror!("set_elf_brk: map_anonymous failed, err={:?}", r); + error!("set_elf_brk: map_anonymous failed, err={:?}", r); return Err(ExecError::OutOfMemory); } } @@ -207,7 +207,7 @@ impl ElfLoader { map_flags: &MapFlags, total_size: usize, ) -> Result<(VirtAddr, bool), SystemError> { - // kdebug!("load_elf_segment: addr_to_map={:?}", addr_to_map); + // debug!("load_elf_segment: addr_to_map={:?}", addr_to_map); // 映射位置的偏移量(页内偏移) let beginning_page_offset = Self::elf_page_offset(addr_to_map); @@ -228,7 +228,7 @@ impl ElfLoader { let map_err_handler = |err: SystemError| { if err == SystemError::EEXIST { - kerror!( + error!( "Pid: {:?}, elf segment at {:p} overlaps with existing mapping", ProcessManager::current_pcb().pid(), addr_to_map.as_ptr::() @@ -255,7 +255,7 @@ impl ElfLoader { if total_size != 0 { let total_size = Self::elf_page_align_up(VirtAddr::new(total_size)).data(); - // kdebug!("total_size={}", total_size); + // log::debug!("total_size={}", total_size); map_addr = user_vm_guard .map_anonymous(addr_to_map, total_size, tmp_prot, *map_flags, false, true) @@ -285,12 +285,12 @@ impl ElfLoader { )?; } } else { - // kdebug!("total size = 0"); + // debug!("total size = 0"); map_addr = user_vm_guard .map_anonymous(addr_to_map, map_size, tmp_prot, *map_flags, false, true)? .virt_address(); - // kdebug!( + // debug!( // "map ok: addr_to_map={:?}, map_addr={map_addr:?},beginning_page_offset={beginning_page_offset:?}", // addr_to_map // ); @@ -311,7 +311,7 @@ impl ElfLoader { )?; } } - // kdebug!("load_elf_segment OK: map_addr={:?}", map_addr); + // debug!("load_elf_segment OK: map_addr={:?}", map_addr); return Ok((map_addr, true)); } @@ -329,7 +329,7 @@ impl ElfLoader { interp_elf_ex: &mut ExecParam, load_bias: usize, ) -> Result { - kdebug!("loading elf interp"); + log::debug!("loading elf interp"); let mut head_buf = [0u8; 512]; interp_elf_ex .file_mut() @@ -361,29 +361,28 @@ impl ElfLoader { let mut last_bss: VirtAddr = VirtAddr::new(0); let mut bss_prot: Option = None; for section in phdr_table { - kdebug!("loading {:?}", section); if section.p_type == PT_LOAD { + log::debug!("loading {:?}", section); let mut elf_type = MapFlags::MAP_PRIVATE; let elf_prot = Self::make_prot(section.p_flags, true, true); let vaddr = TryInto::::try_into(section.p_vaddr).unwrap(); + let mut addr_to_map = load_addr + vaddr; if interp_hdr.e_type == ET_EXEC || load_addr_set { elf_type.insert(MapFlags::MAP_FIXED) //TODO 应当为MapFlags::MAP_FIXED,暂时未支持 - } - load_addr += vaddr; - if load_bias != 0 && interp_hdr.e_type == ET_DYN { - load_addr -= vaddr; + } else if load_bias != 0 && interp_hdr.e_type == ET_DYN { + addr_to_map = VirtAddr::new(0); } let map_addr = Self::load_elf_segment( &mut interp_elf_ex.vm().clone().write(), interp_elf_ex, §ion, - load_addr, + addr_to_map, &elf_prot, &elf_type, total_size, ) .map_err(|e| { - kerror!("Failed to load elf interpreter :{:?}", e); + log::error!("Failed to load elf interpreter :{:?}", e); return ExecError::InvalidParemeter; })?; if !map_addr.1 { @@ -452,14 +451,13 @@ impl ElfLoader { _ => return ExecError::InvalidParemeter, })?; } - if load_addr + TryInto::::try_into(interp_hdr.e_entry).unwrap() - > MMArch::USER_END_VADDR - { + load_addr += TryInto::::try_into(interp_hdr.e_entry).unwrap(); + if load_addr > MMArch::USER_END_VADDR { return Err(ExecError::BadAddress(Some( load_addr + TryInto::::try_into(interp_hdr.e_entry).unwrap(), ))); } - kdebug!("sucessfully load elf interp"); + log::debug!("sucessfully load elf interp"); return Ok(BinaryLoaderResult::new(load_addr)); } @@ -491,7 +489,7 @@ impl ElfLoader { while remain > 0 { let read_size = min(remain, buf_size); file.read(read_size, &mut buf[..read_size])?; - // kdebug!("copy_to_user: vaddr={:?}, read_size = {read_size}", vaddr); + // debug!("copy_to_user: vaddr={:?}, read_size = {read_size}", vaddr); unsafe { copy_to_user(vaddr, &buf[..read_size]).map_err(|_| SystemError::EFAULT)?; } @@ -708,14 +706,14 @@ impl BinaryLoader for ElfLoader { // https://code.dragonos.org.cn/xref/linux-5.19.10/fs/binfmt_elf.c?r=&mo=22652&fi=824#1034 let elf_type = ElfType::from(ehdr.e_type); - // kdebug!("ehdr = {:?}", ehdr); + // debug!("ehdr = {:?}", ehdr); let binding = param.vm().clone(); let mut user_vm = binding.write(); // todo: 增加对user stack上的内存是否具有可执行权限的处理(方法:寻找phdr里面的PT_GNU_STACK段) - // kdebug!("to parse segments"); + // debug!("to parse segments"); // 加载ELF文件并映射到用户空间 let mut phdr_buf = Vec::new(); let phdr_table = Self::parse_segments(param, &ehdr, &mut phdr_buf) @@ -738,9 +736,8 @@ impl BinaryLoader for ElfLoader { if seg.p_filesz > 4096 || seg.p_filesz < 2 { return Err(ExecError::NotExecutable); } - kdebug!("seg:{:?}", seg); - let mut buffer = Vec::new(); - buffer.resize(seg.p_filesz.try_into().unwrap(), 0); + log::debug!("seg:{:?}", seg); + let mut buffer = vec![0; seg.p_filesz.try_into().unwrap()]; let r = param .file_mut() .pread( @@ -749,11 +746,11 @@ impl BinaryLoader for ElfLoader { buffer.as_mut_slice(), ) .map_err(|e| { - kerror!("Failed to load interpreter :{:?}", e); + log::error!("Failed to load interpreter :{:?}", e); return ExecError::NotSupported; })?; if r != seg.p_filesz.try_into().unwrap() { - kerror!("Failed to load interpreter "); + log::error!("Failed to load interpreter "); return Err(ExecError::NotSupported); } let interpreter_path = core::str::from_utf8( @@ -765,11 +762,11 @@ impl BinaryLoader for ElfLoader { e )) })?; - kdebug!("opening interpreter at :{}", interpreter_path); + log::debug!("opening interpreter at :{}", interpreter_path); interpreter = Some( - ExecParam::new(&interpreter_path, param.vm().clone(), ExecParamFlags::EXEC) + ExecParam::new(interpreter_path, param.vm().clone(), ExecParamFlags::EXEC) .map_err(|e| { - kerror!("Failed to load interpreter :{:?}", e); + log::error!("Failed to load interpreter :{:?}", e); return ExecError::NotSupported; })?, ); @@ -804,9 +801,9 @@ impl BinaryLoader for ElfLoader { .filter(|seg| seg.p_type == elf::abi::PT_LOAD); for seg_to_load in loadable_sections { - kdebug!("seg_to_load = {:?}", seg_to_load); + log::debug!("seg_to_load = {:?}", seg_to_load); if unlikely(elf_brk > elf_bss) { - // kdebug!( + // debug!( // "to set brk, elf_brk = {:?}, elf_bss = {:?}", // elf_brk, // elf_bss @@ -869,6 +866,7 @@ impl BinaryLoader for ElfLoader { // 加载这个段到用户空间 + log::debug!("bias: {load_bias}"); let e = Self::load_elf_segment( &mut user_vm, param, @@ -883,7 +881,7 @@ impl BinaryLoader for ElfLoader { SystemError::ENOMEM => ExecError::OutOfMemory, _ => ExecError::Other(format!("load_elf_segment failed: {:?}", e)), })?; - + log::debug!("e.0={:?}", e.0); // 如果地址不对,那么就报错 if !e.1 { return Err(ExecError::BadAddress(Some(e.0))); @@ -902,9 +900,9 @@ impl BinaryLoader for ElfLoader { } } - // kdebug!("seg_to_load.p_offset={}", seg_to_load.p_offset); - // kdebug!("e_phoff={}", ehdr.e_phoff); - // kdebug!("seg_to_load.p_filesz={}", seg_to_load.p_filesz); + // debug!("seg_to_load.p_offset={}", seg_to_load.p_offset); + // debug!("e_phoff={}", ehdr.e_phoff); + // debug!("seg_to_load.p_filesz={}", seg_to_load.p_filesz); // Figure out which segment in the file contains the Program Header Table, // and map to the associated virtual address. if (seg_to_load.p_offset <= ehdr.e_phoff) @@ -934,7 +932,7 @@ impl BinaryLoader for ElfLoader { || Self::elf_page_align_up(p_vaddr + seg_to_load.p_memsz as usize) >= MMArch::USER_END_VADDR { - // kdebug!("ERR: p_vaddr={p_vaddr:?}"); + // debug!("ERR: p_vaddr={p_vaddr:?}"); return Err(ExecError::InvalidParemeter); } @@ -967,7 +965,7 @@ impl BinaryLoader for ElfLoader { elf_brk = seg_end_vaddr; } } - // kdebug!("elf load: phdr_vaddr={phdr_vaddr:?}"); + // debug!("elf load: phdr_vaddr={phdr_vaddr:?}"); let program_entrypoint = VirtAddr::new(ehdr.e_entry as usize + load_bias); let phdr_vaddr = phdr_vaddr.map(|phdr_vaddr| phdr_vaddr + load_bias); @@ -978,7 +976,7 @@ impl BinaryLoader for ElfLoader { start_data = start_data.map(|v| v + load_bias); end_data = end_data.map(|v| v + load_bias); let mut interp_load_addr: Option = None; - // kdebug!( + // debug!( // "to set brk: elf_bss: {:?}, elf_brk: {:?}, bss_prot_flags: {:?}", // elf_bss, // elf_brk, @@ -987,7 +985,7 @@ impl BinaryLoader for ElfLoader { self.set_elf_brk(&mut user_vm, elf_bss, elf_brk, bss_prot_flags)?; if likely(elf_bss != elf_brk) && unlikely(Self::pad_zero(elf_bss).is_err()) { - // kdebug!("elf_bss = {elf_bss:?}, elf_brk = {elf_brk:?}"); + // debug!("elf_bss = {elf_bss:?}, elf_brk = {elf_brk:?}"); return Err(ExecError::BadAddress(Some(elf_bss))); } drop(user_vm); @@ -1005,22 +1003,17 @@ impl BinaryLoader for ElfLoader { .map(|fd| fd as usize) .map_err(|_| ExecError::InvalidParemeter)?; } - // kdebug!("to create auxv"); + // debug!("to create auxv"); let mut user_vm = binding.write(); - self.create_auxv( - param, - interp_load_addr.unwrap_or(program_entrypoint), - phdr_vaddr, - &ehdr, - )?; + self.create_auxv(param, program_entrypoint, phdr_vaddr, &ehdr)?; - // kdebug!("auxv create ok"); + // debug!("auxv create ok"); user_vm.start_code = start_code.unwrap_or(VirtAddr::new(0)); user_vm.end_code = end_code.unwrap_or(VirtAddr::new(0)); user_vm.start_data = start_data.unwrap_or(VirtAddr::new(0)); user_vm.end_data = end_data.unwrap_or(VirtAddr::new(0)); - let result = BinaryLoaderResult::new(program_entrypoint); + let result = BinaryLoaderResult::new(interp_load_addr.unwrap_or(program_entrypoint)); // kdebug!("elf load OK!!!"); return Ok(result); } diff --git a/kernel/src/libs/ffi_convert.rs b/kernel/src/libs/ffi_convert.rs deleted file mode 100644 index 4e39d51a7..000000000 --- a/kernel/src/libs/ffi_convert.rs +++ /dev/null @@ -1,15 +0,0 @@ -/// @brief 由bindgen生成的结构体转换成rust原生定义的结构体的特性 -pub trait FFIBind2Rust { - /// 转换为不可变引用 - fn convert_ref(src: *const T) -> Option<&'static Self>; - /// 转换为可变引用 - fn convert_mut(src: *mut T) -> Option<&'static mut Self>; -} - -pub fn __convert_mut<'a, S, D>(src: *mut S) -> Option<&'a mut D> { - return unsafe { core::mem::transmute::<*mut S, *mut D>(src).as_mut() }; -} - -pub fn __convert_ref<'a, S, D>(src: *const S) -> Option<&'a D> { - return unsafe { core::mem::transmute::<*const S, *const D>(src).as_ref() }; -} diff --git a/kernel/src/libs/font/mod.rs b/kernel/src/libs/font/mod.rs index 02309e632..90eeeb354 100644 --- a/kernel/src/libs/font/mod.rs +++ b/kernel/src/libs/font/mod.rs @@ -2,6 +2,7 @@ use self::font_type::vga8x16::FONT_VGA_8X16; pub mod font_type; +#[allow(dead_code)] pub struct FontDesc { pub index: usize, pub name: &'static str, diff --git a/kernel/src/libs/futex/futex.rs b/kernel/src/libs/futex/futex.rs index c1897f968..a3a26d7db 100644 --- a/kernel/src/libs/futex/futex.rs +++ b/kernel/src/libs/futex/futex.rs @@ -8,6 +8,7 @@ use core::{ mem, sync::atomic::AtomicU64, }; +use log::warn; use hashbrown::HashMap; use system_error::SystemError; @@ -288,7 +289,7 @@ impl Futex { let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; // 满足条件则将当前进程在该bucket上挂起 bucket_mut.sleep_no_sched(futex_q.clone()).map_err(|e| { - kwarn!("error:{e:?}"); + warn!("error:{e:?}"); e })?; drop(futex_map_guard); @@ -558,7 +559,7 @@ impl Futex { let cmparg = sign_extend32(encoded_op & 0x00000fff, 11); if (encoded_op & (FutexOP::FUTEX_OP_OPARG_SHIFT.bits() << 28) != 0) && oparg > 31 { - kwarn!( + warn!( "futex_wake_op: pid:{} tries to shift op by {}; fix this program", ProcessManager::current_pcb().pid().data(), oparg diff --git a/kernel/src/libs/ida/src/lib.rs b/kernel/src/libs/ida/src/lib.rs index eb7d8622b..8a7082ead 100644 --- a/kernel/src/libs/ida/src/lib.rs +++ b/kernel/src/libs/ida/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![feature(core_intrinsics)] +#![allow(internal_features)] #![allow(clippy::needless_return)] use core::intrinsics::unlikely; diff --git a/kernel/src/libs/keyboard_parser.rs b/kernel/src/libs/keyboard_parser.rs index 90271d810..2cfe57b3e 100644 --- a/kernel/src/libs/keyboard_parser.rs +++ b/kernel/src/libs/keyboard_parser.rs @@ -64,7 +64,7 @@ pub enum TypeOneFSMState { impl TypeOneFSMState { /// @brief 状态机总控程序 fn parse(&self, scancode: u8, scancode_status: &mut ScanCodeStatus) -> TypeOneFSMState { - // kdebug!("the code is {:#x}\n", scancode); + // debug!("the code is {:#x}\n", scancode); match self { TypeOneFSMState::Start => { return self.handle_start(scancode, scancode_status); @@ -87,7 +87,7 @@ impl TypeOneFSMState { /// @brief 处理起始状态 fn handle_start(&self, scancode: u8, scancode_status: &mut ScanCodeStatus) -> TypeOneFSMState { - //kdebug!("in handle_start the code is {:#x}\n",scancode); + //debug!("in handle_start the code is {:#x}\n",scancode); match scancode { 0xe1 => { return TypeOneFSMState::PauseBreak(1); @@ -96,7 +96,7 @@ impl TypeOneFSMState { return TypeOneFSMState::Func0; } _ => { - //kdebug!("in _d the code is {:#x}\n",scancode); + //debug!("in _d the code is {:#x}\n",scancode); return TypeOneFSMState::Type3.handle_type3(scancode, scancode_status); } } @@ -274,7 +274,7 @@ impl TypeOneFSMState { let mut col: bool = false; let index = scancode & 0x7f; - //kdebug!("in type3 ch is {:#x}\n",ch); + //debug!("in type3 ch is {:#x}\n",ch); let mut key = KeyFlag::OtherKey; // 可视字符 match index { @@ -306,14 +306,15 @@ impl TypeOneFSMState { } _ => { if !flag_make { - // kdebug!("in type3 ch is {:#x}\n",ch); + // debug!("in type3 ch is {:#x}\n",ch); key = KeyFlag::NoneFlag; } } } // shift被按下 - if scancode_status.shift_l || scancode_status.shift_r { + let shift = scancode_status.shift_l || scancode_status.shift_r; + if shift { col = true; } @@ -327,9 +328,8 @@ impl TypeOneFSMState { let mut ch = TYPE1_KEY_CODE_MAPTABLE[col as usize + 2 * index as usize]; if key != KeyFlag::NoneFlag { - // kdebug!("EMIT: ch is '{}', keyflag is {:?}\n", ch as char, key); if scancode_status.ctrl_l || scancode_status.ctrl_r { - ch = Self::to_ctrl(ch); + ch = Self::to_ctrl(ch, shift); } Self::emit(ch); } @@ -337,10 +337,16 @@ impl TypeOneFSMState { } #[inline] - fn to_ctrl(ch: u8) -> u8 { + fn to_ctrl(ch: u8, shift: bool) -> u8 { return match ch as char { - 'a'..='z' => ch - 0x40, - 'A'..='Z' => ch - 0x40, + 'a'..='z' => ch - 0x60, + 'A'..='Z' => { + if shift { + ch + } else { + ch - 0x40 + } + } '@'..='_' => ch - 0x40, _ => ch, }; diff --git a/kernel/src/libs/lib_ui/screen_manager.rs b/kernel/src/libs/lib_ui/screen_manager.rs index 5ed9d49a3..00f0bf96f 100644 --- a/kernel/src/libs/lib_ui/screen_manager.rs +++ b/kernel/src/libs/lib_ui/screen_manager.rs @@ -240,6 +240,7 @@ pub trait ScmUiFramework: Sync + Send + Debug { return Err(SystemError::ENOSYS); } // 卸载ui框架的回调函数 + #[allow(dead_code)] fn uninstall(&self) -> Result { return Err(SystemError::ENOSYS); } diff --git a/kernel/src/libs/lib_ui/textui.rs b/kernel/src/libs/lib_ui/textui.rs index 59747b15e..d04b64bb5 100644 --- a/kernel/src/libs/lib_ui/textui.rs +++ b/kernel/src/libs/lib_ui/textui.rs @@ -4,7 +4,6 @@ use crate::{ tty::{tty_port::tty_port, virtual_terminal::virtual_console::CURRENT_VCNUM}, video::video_refresh_manager, }, - kdebug, kinfo, libs::{ lib_ui::font::FONT_8x16, rwlock::RwLock, @@ -20,6 +19,7 @@ use core::{ ptr::copy_nonoverlapping, sync::atomic::{AtomicBool, AtomicI32, AtomicU32, Ordering}, }; +use log::{debug, info}; use system_error::SystemError; use super::{ @@ -73,9 +73,9 @@ pub fn textui_framework() -> Arc { /// 初始化TEXTUI_FRAMEWORK fn textui_framwork_init() { if unsafe { __TEXTUI_FRAMEWORK.is_none() } { - kinfo!("textuiframework init"); + info!("textuiframework init"); let metadata = ScmUiFrameworkMetadata::new("TextUI".to_string(), ScmFramworkType::Text); - kdebug!("textui metadata: {:?}", metadata); + debug!("textui metadata: {:?}", metadata); // 为textui框架生成第一个窗口 let vlines_num = (metadata.buf_info().height() / TEXTUI_CHAR_HEIGHT) as usize; @@ -106,7 +106,7 @@ fn textui_framwork_init() { }; scm_register(textui_framework()).expect("register textui framework failed"); - kdebug!("textui framework init success"); + debug!("textui framework init success"); send_to_default_serial8250_port("\ntext ui initialized\n\0".as_bytes()); unsafe { TEXTUI_IS_INIT = true }; @@ -822,7 +822,7 @@ impl TextuiWindow { //进行换行操作 if character == '\n' { // 换行时还需要输出\r - send_to_default_serial8250_port(&[b'\r']); + send_to_default_serial8250_port(b"\r"); if is_enable_window { self.textui_new_line()?; } @@ -989,8 +989,8 @@ impl ScmUiFramework for TextUiFramework { let mut new_buf = textui_framework().metadata.read().buf_info(); new_buf.copy_from_nonoverlapping(&old_buf); - kdebug!("textui change buf_info: old: {:?}", old_buf); - kdebug!("textui change buf_info: new: {:?}", new_buf); + debug!("textui change buf_info: old: {:?}", old_buf); + debug!("textui change buf_info: new: {:?}", new_buf); return Ok(0); } diff --git a/kernel/src/libs/lib_ui/textui_no_alloc.rs b/kernel/src/libs/lib_ui/textui_no_alloc.rs index 9387cdc55..36718f932 100644 --- a/kernel/src/libs/lib_ui/textui_no_alloc.rs +++ b/kernel/src/libs/lib_ui/textui_no_alloc.rs @@ -54,7 +54,7 @@ pub fn no_init_textui_putchar_window( match character { // 进行换行操作 '\n' => { - send_to_default_serial8250_port(&[b'\r']); + send_to_default_serial8250_port(b"\r"); if is_put_to_window { next_line(); } diff --git a/kernel/src/libs/mod.rs b/kernel/src/libs/mod.rs index f15630bd3..42788bddc 100644 --- a/kernel/src/libs/mod.rs +++ b/kernel/src/libs/mod.rs @@ -2,7 +2,6 @@ pub mod align; pub mod casting; pub mod cpumask; pub mod elf; -pub mod ffi_convert; #[macro_use] pub mod int_like; pub mod keyboard_parser; @@ -27,3 +26,4 @@ pub mod rand; pub mod wait_queue; pub mod font; +pub mod name; diff --git a/kernel/src/libs/name.rs b/kernel/src/libs/name.rs new file mode 100644 index 000000000..bca4467c1 --- /dev/null +++ b/kernel/src/libs/name.rs @@ -0,0 +1,13 @@ +use core::any::type_name; + +use alloc::string::{String, ToString}; + +#[allow(dead_code)] +pub fn get_full_type_name(_: &T) -> String { + type_name::().to_string() +} + +pub fn get_type_name(_: &T) -> String { + let full_name = type_name::(); + full_name[(full_name.rfind("::").unwrap_or(0) + 2)..].to_string() +} diff --git a/kernel/src/libs/notifier.rs b/kernel/src/libs/notifier.rs index ea4dcdc76..ee470d174 100644 --- a/kernel/src/libs/notifier.rs +++ b/kernel/src/libs/notifier.rs @@ -1,11 +1,9 @@ #![allow(dead_code)] use core::fmt::Debug; -use crate::{ - kwarn, - libs::{rwlock::RwLock, spinlock::SpinLock}, -}; +use crate::libs::{rwlock::RwLock, spinlock::SpinLock}; use alloc::{sync::Arc, vec::Vec}; +use log::warn; use system_error::SystemError; /// @brief 通知链节点 @@ -39,7 +37,7 @@ impl NotifierChain { for b in self.0.iter() { // 判断之前是否已经注册过该节点 if Arc::ptr_eq(&block, b) { - kwarn!( + warn!( "notifier callback {:?} already registered", Arc::as_ptr(&block) ); diff --git a/kernel/src/libs/printk.rs b/kernel/src/libs/printk.rs index 29ed9c165..65f630041 100644 --- a/kernel/src/libs/printk.rs +++ b/kernel/src/libs/printk.rs @@ -4,7 +4,7 @@ use core::{ }; use alloc::string::ToString; -use log::{info, Log}; +use log::{info, Level, Log}; use super::lib_ui::textui::{textui_putstr, FontColor}; @@ -33,49 +33,6 @@ macro_rules! println { ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*))); } -#[macro_export] -macro_rules! kdebug { - ($($arg:tt)*) => { - $crate::libs::printk::Logger.log(7,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); - $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("[ DEBUG ] ({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))) - } -} - -#[macro_export] -macro_rules! kinfo { - ($($arg:tt)*) => { - $crate::libs::printk::Logger.log(6,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); - $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("[ INFO ] ({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))) - } -} - -#[macro_export] -macro_rules! kwarn { - ($($arg:tt)*) => { - $crate::libs::printk::Logger.log(4,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); - $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("\x1B[1;33m[ WARN ] \x1B[0m")); - $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); - } -} - -#[macro_export] -macro_rules! kerror { - ($($arg:tt)*) => { - $crate::libs::printk::Logger.log(3,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); - $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("\x1B[41m[ ERROR ] \x1B[0m")); - $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); - } -} - -#[macro_export] -macro_rules! kBUG { - ($($arg:tt)*) => { - $crate::libs::printk::Logger.log(1,format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); - $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("\x1B[41m[ BUG ] \x1B[0m")); - $crate::libs::printk::PrintkWriter.__write_fmt(format_args!("({}:{})\t {}\n", file!(), line!(),format_args!($($arg)*))); - } -} - pub struct PrintkWriter; impl PrintkWriter { @@ -133,10 +90,10 @@ impl Logger { /// 内核自定义日志器 /// -/// todo: 完善他的功能,并且逐步把kinfo等宏,迁移到这个logger上面来。 -struct CustomLogger; +/// todo: https://github.com/DragonOS-Community/DragonOS/issues/762 +struct KernelLogger; -impl Log for CustomLogger { +impl Log for KernelLogger { fn enabled(&self, _metadata: &log::Metadata) -> bool { // 这里可以自定义日志过滤规则 true @@ -145,17 +102,8 @@ impl Log for CustomLogger { fn log(&self, record: &log::Record) { if self.enabled(record.metadata()) { // todo: 接入kmsg - - writeln!( - PrintkWriter, - "[ {} ] {} ({}:{}) {}", - record.level(), - record.target(), - record.file().unwrap_or(""), - record.line().unwrap_or(0), - record.args() - ) - .unwrap(); + Self::kernel_log(record); + Self::iodisplay(record) } } @@ -164,8 +112,77 @@ impl Log for CustomLogger { } } +impl KernelLogger { + fn iodisplay(record: &log::Record) { + match record.level() { + Level::Debug | Level::Info | Level::Trace => { + write!(PrintkWriter, "[ {} ] ", record.level(),) + } + Level::Error => { + write!(PrintkWriter, "\x1B[41m[ ERROR ] \x1B[0m",) + } + Level::Warn => { + write!(PrintkWriter, "\x1B[1;33m[ WARN ] \x1B[0m",) + } + } + .unwrap(); + writeln!( + PrintkWriter, + "({}:{})\t {}", + record.file().unwrap_or(""), + record.line().unwrap_or(0), + record.args() + ) + .unwrap(); + } + + fn kernel_log(record: &log::Record) { + match record.level() { + Level::Debug => Logger.log( + 7, + format_args!( + "({}:{})\t {}\n", + record.file().unwrap_or(""), + record.line().unwrap_or(0), + record.args() + ), + ), + Level::Error => Logger.log( + 3, + format_args!( + "({}:{})\t {}\n", + record.file().unwrap_or(""), + record.line().unwrap_or(0), + record.args() + ), + ), + Level::Info => Logger.log( + 6, + format_args!( + "({}:{})\t {}\n", + record.file().unwrap_or(""), + record.line().unwrap_or(0), + record.args() + ), + ), + Level::Warn => Logger.log( + 4, + format_args!( + "({}:{})\t {}\n", + record.file().unwrap_or(""), + record.line().unwrap_or(0), + record.args() + ), + ), + Level::Trace => { + todo!() + } + } + } +} + pub fn early_init_logging() { - log::set_logger(&CustomLogger).unwrap(); + log::set_logger(&KernelLogger).unwrap(); log::set_max_level(log::LevelFilter::Debug); info!("Logging initialized"); } diff --git a/kernel/src/libs/rbtree.rs b/kernel/src/libs/rbtree.rs index aed58744f..f1fed00f0 100644 --- a/kernel/src/libs/rbtree.rs +++ b/kernel/src/libs/rbtree.rs @@ -22,8 +22,7 @@ use core::ops::Index; use core::ptr; use alloc::boxed::Box; - -use crate::kdebug; +use log::debug; #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum Color { @@ -303,7 +302,7 @@ impl NodePtr { /// /// // check for a specific one. /// if !book_reviews.contains_key(&"Les Misérables") { -/// kdebug!( +/// debug!( /// "We've got {} reviews, but Les Misérables ain't one.", /// book_reviews.len() /// ); @@ -316,14 +315,14 @@ impl NodePtr { /// let to_find = ["Pride and Prejudice", "Alice's Adventure in Wonderland"]; /// for book in &to_find { /// match book_reviews.get(book) { -/// Some(review) => kdebug!("{}: {}", book, review), -/// None => kdebug!("{} is unreviewed.", book), +/// Some(review) => debug!("{}: {}", book, review), +/// None => debug!("{} is unreviewed.", book), /// } /// } /// /// // iterate over everything. /// for (book, review) in book_reviews.iter() { -/// kdebug!("{}: \"{}\"", book, review); +/// debug!("{}: \"{}\"", book, review); /// } /// /// book_reviews.print_tree(); @@ -386,12 +385,12 @@ impl RBTree { } if direction == 0 { unsafe { - kdebug!("'{:?}' is root node", (*node.0)); + debug!("'{:?}' is root node", (*node.0)); } } else { let direct = if direction == -1 { "left" } else { "right" }; unsafe { - kdebug!( + debug!( "{:?} is {:?}'s {:?} child ", (*node.0), *node.parent().0, @@ -405,12 +404,12 @@ impl RBTree { pub fn print_tree(&self) { if self.root.is_null() { - kdebug!("This is a empty tree"); + debug!("This is a empty tree"); return; } - kdebug!("This tree size = {:?}, begin:-------------", self.len()); + debug!("This tree size = {:?}, begin:-------------", self.len()); self.tree_print(self.root, 0); - kdebug!("end--------------------------"); + debug!("end--------------------------"); } } @@ -720,7 +719,7 @@ impl<'a, K: Ord + Debug + 'a, V: Debug + 'a> Iterator for Iter<'a, K, V> { impl<'a, K: Ord + Debug + 'a, V: Debug + 'a> DoubleEndedIterator for Iter<'a, K, V> { #[inline] fn next_back(&mut self) -> Option<(&'a K, &'a V)> { - // kdebug!("len = {:?}", self.len); + // debug!("len = {:?}", self.len); if self.len == 0 { return None; } diff --git a/kernel/src/libs/rwlock.rs b/kernel/src/libs/rwlock.rs index 081616044..63af797fc 100644 --- a/kernel/src/libs/rwlock.rs +++ b/kernel/src/libs/rwlock.rs @@ -101,7 +101,7 @@ impl RwLock { #[inline] /// @brief 获取实时的读者数并尝试加1,如果增加值成功则返回增加1后的读者数,否则panic fn current_reader(&self) -> Result { - const MAX_READERS: u32 = core::u32::MAX >> READER_BIT >> 1; //右移3位 + const MAX_READERS: u32 = u32::MAX >> READER_BIT >> 1; //右移3位 let value = self.lock.fetch_add(READER, Ordering::Acquire); //value二进制形式的MSB不能为1, 否则导致溢出 diff --git a/kernel/src/libs/semaphore.rs b/kernel/src/libs/semaphore.rs index 163c6dc45..5cf600d3d 100644 --- a/kernel/src/libs/semaphore.rs +++ b/kernel/src/libs/semaphore.rs @@ -1,8 +1,9 @@ use core::sync::atomic::{AtomicI32, Ordering}; +use log::debug; use system_error::SystemError; -use crate::{kdebug, process::ProcessManager}; +use crate::process::ProcessManager; use super::wait_queue::WaitQueue; @@ -51,7 +52,7 @@ impl Semaphore { //尝试唤醒 if !self.wait_queue.wakeup(None) { //如果唤醒失败,打印错误信息 - kdebug!( + debug!( "Semaphore wakeup failed: current pid= {}, semaphore={:?}", ProcessManager::current_pcb().pid().into(), self diff --git a/kernel/src/libs/wait_queue.rs b/kernel/src/libs/wait_queue.rs index 25e985b9e..efb3606d3 100644 --- a/kernel/src/libs/wait_queue.rs +++ b/kernel/src/libs/wait_queue.rs @@ -2,11 +2,12 @@ use core::intrinsics::unlikely; use alloc::{collections::LinkedList, sync::Arc, vec::Vec}; +use log::{error, warn}; +use system_error::SystemError; use crate::{ - arch::CurrentIrqArch, + arch::{ipc::signal::Signal, CurrentIrqArch}, exception::InterruptArch, - kerror, process::{ProcessControlBlock, ProcessManager, ProcessState}, sched::{schedule, SchedMode}, }; @@ -32,6 +33,34 @@ impl WaitQueue { WaitQueue(SpinLock::new(InnerWaitQueue::INIT)) } + pub fn prepare_to_wait_event(&self, interruptible: bool) -> Result<(), SystemError> { + let mut guard: SpinLockGuard = self.0.lock_irqsave(); + let pcb = ProcessManager::current_pcb(); + if Signal::signal_pending_state(interruptible, false, &pcb) { + return Err(SystemError::ERESTARTSYS); + } else { + ProcessManager::mark_sleep(interruptible).unwrap_or_else(|e| { + panic!("sleep error: {:?}", e); + }); + guard.wait_list.push_back(ProcessManager::current_pcb()); + drop(guard); + } + Ok(()) + } + + pub fn finish_wait(&self) { + let pcb = ProcessManager::current_pcb(); + let mut writer = pcb.sched_info().inner_lock_write_irqsave(); + let mut guard: SpinLockGuard = self.0.lock_irqsave(); + + writer.set_state(ProcessState::Runnable); + writer.set_wakeup(); + + guard.wait_list.retain(|x| !Arc::ptr_eq(x, &pcb)); + drop(guard); + drop(writer); + } + /// @brief 让当前进程在等待队列上进行等待,并且,允许被信号打断 pub fn sleep(&self) { before_sleep_check(0); @@ -50,7 +79,7 @@ impl WaitQueue { F: FnOnce(), { before_sleep_check(0); - let mut guard: SpinLockGuard = self.0.lock(); + let mut guard: SpinLockGuard = self.0.lock_irqsave(); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_sleep(true).unwrap_or_else(|e| { panic!("sleep error: {:?}", e); @@ -237,7 +266,7 @@ impl WaitQueue { if wake { ProcessManager::wakeup(&to_wakeup).unwrap_or_else(|e| { - kerror!("wakeup pid: {:?} error: {:?}", to_wakeup.pid(), e); + error!("wakeup pid: {:?} error: {:?}", to_wakeup.pid(), e); }); continue; } else { @@ -265,7 +294,7 @@ impl InnerWaitQueue { fn before_sleep_check(max_preempt: usize) { let pcb = ProcessManager::current_pcb(); if unlikely(pcb.preempt_count() > max_preempt) { - kwarn!( + warn!( "Process {:?}: Try to sleep when preempt count is {}", pcb.pid().data(), pcb.preempt_count() diff --git a/kernel/src/misc/ksysfs.rs b/kernel/src/misc/ksysfs.rs index f4f45248c..5041c650e 100644 --- a/kernel/src/misc/ksysfs.rs +++ b/kernel/src/misc/ksysfs.rs @@ -7,6 +7,7 @@ use crate::{ init::initcall::INITCALL_CORE, }; use alloc::{string::ToString, sync::Arc}; +use log::error; use system_error::SystemError; use unified_init::macros::unified_init; @@ -29,7 +30,7 @@ fn ksysfs_init() -> Result<(), SystemError> { sysfs_instance() .create_groups(&kernel_kset.as_kobject(), &[&KernelAttrGroup]) .map_err(|e| { - kerror!("Failed to create sysfs groups for kernel kset: {:?}", e); + error!("Failed to create sysfs groups for kernel kset: {:?}", e); kernel_kset.unregister(); SystemError::ENOMEM })?; diff --git a/kernel/src/mm/allocator/buddy.rs b/kernel/src/mm/allocator/buddy.rs index deff40669..50ee1a3b1 100644 --- a/kernel/src/mm/allocator/buddy.rs +++ b/kernel/src/mm/allocator/buddy.rs @@ -1,3 +1,5 @@ +use log::{debug, warn}; + /// @Author: longjin@dragonos.org /// @Author: kongweichao@dragonos.org /// @Date: 2023-03-28 16:03:47 @@ -7,7 +9,7 @@ use crate::arch::MMArch; use crate::mm::allocator::bump::BumpAllocator; use crate::mm::allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage}; use crate::mm::{MemoryManagementArch, PhysAddr, PhysMemoryArea, VirtAddr}; -use crate::{kdebug, kwarn}; + use core::cmp::min; use core::fmt::Debug; use core::intrinsics::{likely, unlikely}; @@ -78,8 +80,8 @@ impl BuddyAllocator { pub unsafe fn new(mut bump_allocator: BumpAllocator) -> Option { let initial_free_pages = bump_allocator.usage().free(); let total_memory = bump_allocator.usage().total(); - kdebug!("Free pages before init buddy: {:?}", initial_free_pages); - kdebug!("Buddy entries: {}", Self::BUDDY_ENTRIES); + debug!("Free pages before init buddy: {:?}", initial_free_pages); + debug!("Buddy entries: {}", Self::BUDDY_ENTRIES); let mut free_area: [PhysAddr; MAX_ORDER - MIN_ORDER] = [PhysAddr::new(0); MAX_ORDER - MIN_ORDER]; @@ -118,7 +120,7 @@ impl BuddyAllocator { if remain_pages.data() == 0 { continue; } - kdebug!("area: {area:?}, paddr: {paddr:#x}, remain_pages: {remain_pages:?}"); + debug!("area: {area:?}, paddr: {paddr:#x}, remain_pages: {remain_pages:?}"); total_pages_to_buddy += remain_pages; @@ -128,7 +130,7 @@ impl BuddyAllocator { // 先从低阶开始,尽可能地填满空闲链表 for i in MIN_ORDER..MAX_ORDER { - // kdebug!("i {i}, remain pages={}", remain_pages.data()); + // debug!("i {i}, remain pages={}", remain_pages.data()); if remain_pages.data() < (1 << (i - MIN_ORDER)) { break; } @@ -175,7 +177,7 @@ impl BuddyAllocator { assert!(remain_bytes == 0); } - kdebug!("Total pages to buddy: {:?}", total_pages_to_buddy); + debug!("Total pages to buddy: {:?}", total_pages_to_buddy); allocator.total = total_memory; Some(allocator) @@ -238,7 +240,7 @@ impl BuddyAllocator { if !next_page_list_addr.is_null() { // 此时page_list已经没有空闲伙伴块了,又因为非唯一页,需要删除该page_list self.free_area[Self::order2index(spec_order)] = next_page_list_addr; - // kdebug!("FREE: page_list_addr={:b}", page_list_addr.data()); + // debug!("FREE: page_list_addr={:b}", page_list_addr.data()); unsafe { self.buddy_free(page_list_addr, MMArch::PAGE_SHIFT as u8); } @@ -273,7 +275,7 @@ impl BuddyAllocator { page_list.entry_num - 1 ); } - // kdebug!("entry={entry:?}"); + // debug!("entry={entry:?}"); // 更新page_list的entry_num page_list.entry_num -= 1; @@ -302,7 +304,7 @@ impl BuddyAllocator { return None; }; let result: Option = alloc_in_specific_order(order); - // kdebug!("result={:?}", result); + // debug!("result={:?}", result); if result.is_some() { return result; } @@ -312,14 +314,14 @@ impl BuddyAllocator { let mut x: Option = None; while current_order < MAX_ORDER { x = alloc_in_specific_order(current_order as u8); - // kdebug!("current_order={:?}", current_order); + // debug!("current_order={:?}", current_order); if x.is_some() { break; } current_order += 1; } - // kdebug!("x={:?}", x); + // debug!("x={:?}", x); // 如果找到一个大的块,就进行分裂 if x.is_some() { // 分裂到order阶 @@ -328,8 +330,8 @@ impl BuddyAllocator { // 把后面那半块放回空闲链表 let buddy = *x.as_ref().unwrap() + (1 << current_order); - // kdebug!("x={:?}, buddy={:?}", x, buddy); - // kdebug!("current_order={:?}, buddy={:?}", current_order, buddy); + // debug!("x={:?}, buddy={:?}", x, buddy); + // debug!("current_order={:?}, buddy={:?}", current_order, buddy); unsafe { self.buddy_free(buddy, current_order as u8) }; } return x; @@ -359,10 +361,10 @@ impl BuddyAllocator { return None; } - // kdebug!("buddy_alloc: order = {}", order); + // debug!("buddy_alloc: order = {}", order); // 获取该阶数的一个空闲页面 let free_addr = self.pop_front(order); - // kdebug!( + // debug!( // "buddy_alloc: order = {}, free_addr = {:?}", // order, // free_addr @@ -378,7 +380,7 @@ impl BuddyAllocator { /// - `base` - 块的起始地址 /// - `order` - 块的阶数 unsafe fn buddy_free(&mut self, mut base: PhysAddr, order: u8) { - // kdebug!("buddy_free: base = {:?}, order = {}", base, order); + // debug!("buddy_free: base = {:?}, order = {}", base, order); let mut order = order as usize; while order < MAX_ORDER { @@ -557,7 +559,7 @@ impl BuddyAllocator { (first_page_list_paddr, first_page_list) }; - // kdebug!("to write entry, page_list_base={paddr:?}, page_list.entry_num={}, value={base:?}", page_list.entry_num); + // debug!("to write entry, page_list_base={paddr:?}, page_list.entry_num={}, value={base:?}", page_list.entry_num); assert!(page_list.entry_num < Self::BUDDY_ENTRIES); // 把要归还的块,写入到链表项中 unsafe { A::write(Self::entry_virt_addr(paddr, page_list.entry_num), base) } @@ -591,14 +593,14 @@ impl FrameAllocator for BuddyAllocator { unsafe fn free(&mut self, base: PhysAddr, count: PageFrameCount) { // 要求count是2的幂 if unlikely(!count.data().is_power_of_two()) { - kwarn!("buddy free: count is not power of two"); + warn!("buddy free: count is not power of two"); } let mut order = log2(count.data()); if count.data() & ((1 << order) - 1) != 0 { order += 1; } let order = (order + MIN_ORDER) as u8; - // kdebug!("free: base={:?}, count={:?}", base, count); + // debug!("free: base={:?}, count={:?}", base, count); self.buddy_free(base, order); } diff --git a/kernel/src/mm/allocator/kernel_allocator.rs b/kernel/src/mm/allocator/kernel_allocator.rs index 9e55a4ab5..6201fef33 100644 --- a/kernel/src/mm/allocator/kernel_allocator.rs +++ b/kernel/src/mm/allocator/kernel_allocator.rs @@ -20,6 +20,7 @@ use super::{ /// 类kmalloc的分配器应当实现的trait pub trait LocalAlloc { + #[allow(dead_code)] unsafe fn local_alloc(&self, layout: Layout) -> *mut u8; unsafe fn local_alloc_zeroed(&self, layout: Layout) -> *mut u8; unsafe fn local_dealloc(&self, ptr: *mut u8, layout: Layout); diff --git a/kernel/src/mm/allocator/page_frame.rs b/kernel/src/mm/allocator/page_frame.rs index 75a79529f..180a2ac25 100644 --- a/kernel/src/mm/allocator/page_frame.rs +++ b/kernel/src/mm/allocator/page_frame.rs @@ -371,8 +371,9 @@ pub unsafe fn deallocate_page_frames( if let Some(page) = page { // 如果page是共享页,将其共享页信息从SHM_MANAGER中删去 - if page.shared() { - shm_manager_lock().free_id(&page.shm_id().unwrap()); + let page_guard = page.read_irqsave(); + if page_guard.shared() { + shm_manager_lock().free_id(&page_guard.shm_id().unwrap()); } } diff --git a/kernel/src/mm/allocator/slab.rs b/kernel/src/mm/allocator/slab.rs index a21d4ca96..710f9b7b3 100644 --- a/kernel/src/mm/allocator/slab.rs +++ b/kernel/src/mm/allocator/slab.rs @@ -1,6 +1,7 @@ use core::{alloc::Layout, ptr::NonNull, sync::atomic::AtomicBool}; use alloc::boxed::Box; +use log::debug; use slabmalloc::*; // 全局slab分配器 @@ -18,7 +19,7 @@ pub(crate) struct SlabAllocator { impl SlabAllocator { /// 创建slab分配器 pub fn new() -> SlabAllocator { - kdebug!("trying to new a slab_allocator"); + debug!("trying to new a slab_allocator"); SlabAllocator { zone: ZoneAllocator::new(), } @@ -62,7 +63,7 @@ impl SlabAllocator { /// 初始化slab分配器 pub unsafe fn slab_init() { - kdebug!("trying to init a slab_allocator"); + debug!("trying to init a slab_allocator"); SLABALLOCATOR = Some(SlabAllocator::new()); SLABINITSTATE = true.into(); } diff --git a/kernel/src/mm/c_adapter.rs b/kernel/src/mm/c_adapter.rs index 35ac24de9..1c824595d 100644 --- a/kernel/src/mm/c_adapter.rs +++ b/kernel/src/mm/c_adapter.rs @@ -4,18 +4,18 @@ use core::intrinsics::unlikely; use alloc::vec::Vec; use hashbrown::HashMap; +use log::error; use system_error::SystemError; use crate::{ include::bindings::bindings::{gfp_t, PAGE_U_S}, - kerror, libs::{align::page_align_up, spinlock::SpinLock}, mm::MMArch, }; use super::{ allocator::page_frame::PageFrameCount, kernel_mapper::KernelMapper, mmio_buddy::mmio_pool, - no_init::pseudo_map_phys, page::PageFlags, MemoryManagementArch, PhysAddr, VirtAddr, + no_init::pseudo_map_phys, page::EntryFlags, MemoryManagementArch, PhysAddr, VirtAddr, }; lazy_static! { @@ -38,9 +38,9 @@ pub unsafe extern "C" fn rs_map_phys(vaddr: usize, paddr: usize, size: usize, fl let mut vaddr = VirtAddr::new(vaddr); let mut paddr = PhysAddr::new(paddr); let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); - // kdebug!("rs_map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); + // debug!("rs_map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); - let mut page_flags: PageFlags = PageFlags::new().set_execute(true).set_write(true); + let mut page_flags: EntryFlags = EntryFlags::new().set_execute(true).set_write(true); if flags & PAGE_U_S as usize != 0 { page_flags = page_flags.set_user(true); } @@ -64,13 +64,13 @@ pub unsafe extern "C" fn rs_map_phys(vaddr: usize, paddr: usize, size: usize, fl #[no_mangle] pub unsafe extern "C" fn kzalloc(size: usize, _gfp: gfp_t) -> usize { - // kdebug!("kzalloc: size: {size}"); + // debug!("kzalloc: size: {size}"); return do_kmalloc(size, true); } #[no_mangle] pub unsafe extern "C" fn kmalloc(size: usize, _gfp: gfp_t) -> usize { - // kdebug!("kmalloc: size: {size}"); + // debug!("kmalloc: size: {size}"); // 由于C代码不规范,因此都全部清空 return do_kmalloc(size, true); } @@ -109,7 +109,7 @@ pub unsafe extern "C" fn kfree(vaddr: usize) -> usize { drop(guard); if p.is_none() { - kerror!("kfree: vaddr {:?} not found in C Allocation Map", vaddr); + error!("kfree: vaddr {:?} not found in C Allocation Map", vaddr); return SystemError::EINVAL.to_posix_errno() as i64 as usize; } let (vaddr, len, cap) = p.unwrap(); @@ -135,7 +135,7 @@ unsafe extern "C" fn rs_mmio_create( res_vaddr: *mut u64, res_length: *mut u64, ) -> i32 { - // kdebug!("mmio_create"); + // debug!("mmio_create"); let r = mmio_pool().create_mmio(size as usize); if let Err(e) = r { return e.to_posix_errno(); diff --git a/kernel/src/mm/early_ioremap.rs b/kernel/src/mm/early_ioremap.rs index 225002b93..0daec6168 100644 --- a/kernel/src/mm/early_ioremap.rs +++ b/kernel/src/mm/early_ioremap.rs @@ -45,7 +45,7 @@ impl EarlyIoRemap { mut size: usize, read_only: bool, ) -> Result { - // kdebug!("map not aligned phys:{phys:?}, size:{size:?}, read_only:{read_only:?}"); + // debug!("map not aligned phys:{phys:?}, size:{size:?}, read_only:{read_only:?}"); let offset = phys.data() - page_align_down(phys.data()); size += offset; @@ -82,7 +82,7 @@ impl EarlyIoRemap { return Err(SystemError::EINVAL); } - // kdebug!("Early io remap:{phys:?}, size:{size}"); + // debug!("Early io remap:{phys:?}, size:{size}"); let mut slot_guard = SLOTS.lock(); @@ -111,7 +111,7 @@ impl EarlyIoRemap { let start_slot = start_slot.ok_or(SystemError::ENOMEM)?; let vaddr = Self::idx_to_virt(start_slot); - // kdebug!("start_slot:{start_slot}, vaddr: {vaddr:?}, slot_count: {slot_count:?}"); + // debug!("start_slot:{start_slot}, vaddr: {vaddr:?}, slot_count: {slot_count:?}"); let page_count = PageFrameCount::new(slot_count); // 执行映射 if read_only { @@ -120,7 +120,7 @@ impl EarlyIoRemap { unsafe { pseudo_map_phys(vaddr, phys, page_count) } } - // kdebug!("map ok"); + // debug!("map ok"); // 更新slot信息 let map_size = slot_count * MMArch::PAGE_SIZE; diff --git a/kernel/src/mm/fault.rs b/kernel/src/mm/fault.rs index 71e0623ae..9055cdf5f 100644 --- a/kernel/src/mm/fault.rs +++ b/kernel/src/mm/fault.rs @@ -1,11 +1,17 @@ -use core::{alloc::Layout, intrinsics::unlikely, panic}; +use core::{ + alloc::Layout, + cmp::{max, min}, + intrinsics::unlikely, + panic, +}; use alloc::sync::Arc; use crate::{ arch::{mm::PageMapper, MMArch}, + libs::align::align_down, mm::{ - page::{page_manager_lock_irqsave, PageFlags}, + page::{page_manager_lock_irqsave, EntryFlags}, ucontext::LockedVMA, VirtAddr, VmFaultReason, VmFlags, }, @@ -14,39 +20,68 @@ use crate::{ use crate::mm::MemoryManagementArch; +use super::{ + allocator::page_frame::FrameAllocator, + page::{page_reclaimer_lock_irqsave, Page, PageFlags}, +}; + bitflags! { pub struct FaultFlags: u64{ - const FAULT_FLAG_WRITE = 1 << 0; - const FAULT_FLAG_MKWRITE = 1 << 1; - const FAULT_FLAG_ALLOW_RETRY = 1 << 2; - const FAULT_FLAG_RETRY_NOWAIT = 1 << 3; - const FAULT_FLAG_KILLABLE = 1 << 4; - const FAULT_FLAG_TRIED = 1 << 5; - const FAULT_FLAG_USER = 1 << 6; - const FAULT_FLAG_REMOTE = 1 << 7; - const FAULT_FLAG_INSTRUCTION = 1 << 8; - const FAULT_FLAG_INTERRUPTIBLE =1 << 9; - const FAULT_FLAG_UNSHARE = 1 << 10; - const FAULT_FLAG_ORIG_PTE_VALID = 1 << 11; - const FAULT_FLAG_VMA_LOCK = 1 << 12; + const FAULT_FLAG_WRITE = 1 << 0; + const FAULT_FLAG_MKWRITE = 1 << 1; + const FAULT_FLAG_ALLOW_RETRY = 1 << 2; + const FAULT_FLAG_RETRY_NOWAIT = 1 << 3; + const FAULT_FLAG_KILLABLE = 1 << 4; + const FAULT_FLAG_TRIED = 1 << 5; + const FAULT_FLAG_USER = 1 << 6; + const FAULT_FLAG_REMOTE = 1 << 7; + const FAULT_FLAG_INSTRUCTION = 1 << 8; + const FAULT_FLAG_INTERRUPTIBLE =1 << 9; + const FAULT_FLAG_UNSHARE = 1 << 10; + const FAULT_FLAG_ORIG_PTE_VALID = 1 << 11; + const FAULT_FLAG_VMA_LOCK = 1 << 12; } } /// # 缺页异常信息结构体 /// 包含了页面错误处理的相关信息,例如出错的地址、VMA等 #[derive(Debug)] -pub struct PageFaultMessage { +pub struct PageFaultMessage<'a> { + /// 产生缺页的VMA结构体 vma: Arc, + /// 缺页地址 address: VirtAddr, + /// 异常处理标志 flags: FaultFlags, + /// 页表映射器 + mapper: &'a mut PageMapper, + /// 缺页的文件页在文件中的偏移量 + file_pgoff: Option, + /// 缺页对应PageCache中的文件页 + page: Option>, + /// 写时拷贝需要的页面 + cow_page: Option>, } -impl PageFaultMessage { - pub fn new(vma: Arc, address: VirtAddr, flags: FaultFlags) -> Self { +impl<'a> PageFaultMessage<'a> { + pub fn new( + vma: Arc, + address: VirtAddr, + flags: FaultFlags, + mapper: &'a mut PageMapper, + ) -> Self { + let guard = vma.lock_irqsave(); + let file_pgoff = guard.file_page_offset().map(|file_page_offset| { + ((address - guard.region().start()) >> MMArch::PAGE_SHIFT) + file_page_offset + }); Self { vma: vma.clone(), - address, + address: VirtAddr::new(crate::libs::align::page_align_down(address.data())), flags, + file_pgoff, + page: None, + mapper, + cow_page: None, } } @@ -75,16 +110,6 @@ impl PageFaultMessage { } } -impl Clone for PageFaultMessage { - fn clone(&self) -> Self { - Self { - vma: self.vma.clone(), - address: self.address, - flags: self.flags, - } - } -} - /// 缺页中断处理结构体 pub struct PageFaultHandler; @@ -97,7 +122,7 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn handle_mm_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn handle_mm_fault(mut pfm: PageFaultMessage) -> VmFaultReason { let flags = pfm.flags(); let vma = pfm.vma(); let current_pcb = ProcessManager::current_pcb(); @@ -113,13 +138,13 @@ impl PageFaultHandler { return VmFaultReason::VM_FAULT_SIGSEGV; } - let guard = vma.lock(); + let guard = vma.lock_irqsave(); let vm_flags = *guard.vm_flags(); drop(guard); if unlikely(vm_flags.contains(VmFlags::VM_HUGETLB)) { //TODO: 添加handle_hugetlb_fault处理大页缺页异常 } else { - Self::handle_normal_fault(pfm, mapper); + Self::handle_normal_fault(&mut pfm); } VmFaultReason::VM_FAULT_COMPLETED @@ -133,18 +158,16 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn handle_normal_fault( - pfm: PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { + pub unsafe fn handle_normal_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); + let mapper = &mut pfm.mapper; if mapper.get_entry(address, 3).is_none() { mapper .allocate_table(address, 2) .expect("failed to allocate PUD table"); } - let page_flags = vma.lock().flags(); + let page_flags = vma.lock_irqsave().flags(); for level in 2..=3 { let level = MMArch::PAGE_LEVELS - level; @@ -159,7 +182,7 @@ impl PageFaultHandler { } } - Self::handle_pte_fault(pfm, mapper) + Self::handle_pte_fault(pfm) } /// 处理页表项异常 @@ -170,35 +193,37 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn handle_pte_fault( - pfm: PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { + pub unsafe fn handle_pte_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { let address = pfm.address_aligned_down(); let flags = pfm.flags; let vma = pfm.vma.clone(); let mut ret = VmFaultReason::VM_FAULT_COMPLETED; + let mapper = &pfm.mapper; + + // pte存在 if let Some(mut entry) = mapper.get_entry(address, 0) { if !entry.present() { - ret = Self::do_swap_page(pfm.clone(), mapper); + ret = Self::do_swap_page(pfm); } + if entry.protnone() && vma.is_accessible() { - ret = Self::do_numa_page(pfm.clone(), mapper); + ret = Self::do_numa_page(pfm); } + if flags.intersects(FaultFlags::FAULT_FLAG_WRITE | FaultFlags::FAULT_FLAG_UNSHARE) { if !entry.write() { - ret = Self::do_wp_page(pfm.clone(), mapper); + ret = Self::do_wp_page(pfm); } else { - entry.set_flags(PageFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); + entry.set_flags(EntryFlags::from_data(MMArch::ENTRY_FLAG_DIRTY)); } } } else if vma.is_anonymous() { - ret = Self::do_anonymous_page(pfm.clone(), mapper); + ret = Self::do_anonymous_page(pfm); } else { - ret = Self::do_fault(pfm.clone(), mapper); + ret = Self::do_fault(pfm); } - vma.lock().set_mapped(true); + vma.lock_irqsave().set_mapped(true); return ret; } @@ -211,13 +236,12 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_anonymous_page( - pfm: PageFaultMessage, - mapper: &mut PageMapper, - ) -> VmFaultReason { + pub unsafe fn do_anonymous_page(pfm: &mut PageFaultMessage) -> VmFaultReason { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); - let guard = vma.lock(); + let guard = vma.lock_irqsave(); + let mapper = &mut pfm.mapper; + if let Some(flush) = mapper.map(address, guard.flags()) { flush.flush(); crate::debug::klog::mm::mm_debug_log( @@ -229,9 +253,9 @@ impl PageFaultHandler { klog_types::LogSource::Buddy, ); let paddr = mapper.translate(address).unwrap().0; - let mut anon_vma_guard = page_manager_lock_irqsave(); - let page = anon_vma_guard.get_mut(&paddr); - page.insert_vma(vma.clone()); + let mut page_manager_guard = page_manager_lock_irqsave(); + let page = page_manager_guard.get_unwrap(&paddr); + page.write_irqsave().insert_vma(vma.clone()); VmFaultReason::VM_FAULT_COMPLETED } else { VmFaultReason::VM_FAULT_OOM @@ -246,16 +270,19 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - #[allow(unused_variables)] - pub unsafe fn do_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - panic!( - "do_fault has not yet been implemented, - fault message: {:?}, - pid: {}\n", - pfm, - crate::process::ProcessManager::current_pid().data() - ); - // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_fault + pub unsafe fn do_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + if !pfm.flags().contains(FaultFlags::FAULT_FLAG_WRITE) { + return Self::do_read_fault(pfm); + } else if !pfm + .vma() + .lock_irqsave() + .vm_flags() + .contains(VmFlags::VM_SHARED) + { + return Self::do_cow_fault(pfm); + } else { + return Self::do_shared_fault(pfm); + } } /// 处理私有文件映射的写时复制 @@ -266,16 +293,54 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - #[allow(dead_code, unused_variables)] - pub unsafe fn do_cow_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - panic!( - "do_cow_fault has not yet been implemented, - fault message: {:?}, - pid: {}\n", - pfm, - crate::process::ProcessManager::current_pid().data() + pub unsafe fn do_cow_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + let mut ret = Self::filemap_fault(pfm); + + if unlikely(ret.intersects( + VmFaultReason::VM_FAULT_ERROR + | VmFaultReason::VM_FAULT_NOPAGE + | VmFaultReason::VM_FAULT_RETRY + | VmFaultReason::VM_FAULT_DONE_COW, + )) { + return ret; + } + + let cache_page = pfm.page.clone().unwrap(); + let mapper = &mut pfm.mapper; + + let cow_page_phys = mapper.allocator_mut().allocate_one(); + if cow_page_phys.is_none() { + return VmFaultReason::VM_FAULT_OOM; + } + let cow_page_phys = cow_page_phys.unwrap(); + + let cow_page = Arc::new(Page::new(false, cow_page_phys)); + pfm.cow_page = Some(cow_page.clone()); + + //复制PageCache内容到新的页内 + let new_frame = MMArch::phys_2_virt(cow_page_phys).unwrap(); + (new_frame.data() as *mut u8).copy_from_nonoverlapping( + MMArch::phys_2_virt(cache_page.read_irqsave().phys_address()) + .unwrap() + .data() as *mut u8, + MMArch::PAGE_SIZE, + ); + + let mut page_manager_guard = page_manager_lock_irqsave(); + + // 新页加入页管理器中 + page_manager_guard.insert(cow_page_phys, &cow_page); + cow_page.write_irqsave().set_page_cache_index( + cache_page.read_irqsave().page_cache(), + cache_page.read_irqsave().index(), ); - // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_cow_fault + + // 将vma插入页的vma表中 + cow_page.write_irqsave().insert_vma(pfm.vma()); + + ret = ret.union(Self::finish_fault(pfm)); + + ret } /// 处理文件映射页的缺页异常 @@ -286,16 +351,19 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - #[allow(dead_code, unused_variables)] - pub unsafe fn do_read_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - panic!( - "do_read_fault has not yet been implemented, - fault message: {:?}, - pid: {}\n", - pfm, - crate::process::ProcessManager::current_pid().data() - ); - // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_read_fault + pub unsafe fn do_read_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + let fs = pfm.vma().lock_irqsave().vm_file().unwrap().inode().fs(); + + let mut ret = Self::do_fault_around(pfm); + if !ret.is_empty() { + return ret; + } + + ret = fs.fault(pfm); + + ret = ret.union(Self::finish_fault(pfm)); + + ret } /// 处理对共享文件映射区写入引起的缺页 @@ -306,16 +374,16 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - #[allow(dead_code, unused_variables)] - pub unsafe fn do_shared_fault(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { - panic!( - "do_shared_fault has not yet been implemented, - fault message: {:?}, - pid: {}\n", - pfm, - crate::process::ProcessManager::current_pid().data() - ); - // TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/memory.c#do_shared_fault + pub unsafe fn do_shared_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + let mut ret = Self::filemap_fault(pfm); + + let cache_page = pfm.page.clone().expect("no cache_page in PageFaultMessage"); + + // 将pagecache页设为脏页,以便回收时能够回写 + cache_page.write_irqsave().add_flags(PageFlags::PG_DIRTY); + ret = ret.union(Self::finish_fault(pfm)); + + ret } /// 处理被置换页面的缺页异常 @@ -327,7 +395,7 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] - pub unsafe fn do_swap_page(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn do_swap_page(pfm: &mut PageFaultMessage) -> VmFaultReason { panic!( "do_swap_page has not yet been implemented, fault message: {:?}, @@ -347,7 +415,7 @@ impl PageFaultHandler { /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 #[allow(unused_variables)] - pub unsafe fn do_numa_page(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn do_numa_page(pfm: &mut PageFaultMessage) -> VmFaultReason { panic!( "do_numa_page has not yet been implemented, fault message: {:?}, @@ -366,43 +434,289 @@ impl PageFaultHandler { /// /// ## 返回值 /// - VmFaultReason: 页面错误处理信息标志 - pub unsafe fn do_wp_page(pfm: PageFaultMessage, mapper: &mut PageMapper) -> VmFaultReason { + pub unsafe fn do_wp_page(pfm: &mut PageFaultMessage) -> VmFaultReason { let address = pfm.address_aligned_down(); let vma = pfm.vma.clone(); + let mapper = &mut pfm.mapper; + let old_paddr = mapper.translate(address).unwrap().0; let mut page_manager = page_manager_lock_irqsave(); - let map_count = page_manager.get_mut(&old_paddr).map_count(); + let old_page = page_manager.get_unwrap(&old_paddr); + let map_count = old_page.read_irqsave().map_count(); drop(page_manager); let mut entry = mapper.get_entry(address, 0).unwrap(); - let new_flags = entry.flags().set_write(true); + let new_flags = entry.flags().set_write(true).set_dirty(true); - if map_count == 1 { + if vma.lock().vm_flags().contains(VmFlags::VM_SHARED) { + // 共享映射,直接修改页表项保护位,标记为脏页 let table = mapper.get_table(address, 0).unwrap(); let i = table.index_of(address).unwrap(); entry.set_flags(new_flags); table.set_entry(i, entry); + + old_page.write_irqsave().add_flags(PageFlags::PG_DIRTY); + VmFaultReason::VM_FAULT_COMPLETED - } else if let Some(flush) = mapper.map(address, new_flags) { - let mut page_manager = page_manager_lock_irqsave(); - let old_page = page_manager.get_mut(&old_paddr); - old_page.remove_vma(&vma); - drop(page_manager); + } else if vma.is_anonymous() { + // 私有匿名映射,根据引用计数判断是否拷贝页面 + if map_count == 1 { + let table = mapper.get_table(address, 0).unwrap(); + let i = table.index_of(address).unwrap(); + entry.set_flags(new_flags); + table.set_entry(i, entry); + VmFaultReason::VM_FAULT_COMPLETED + } else if let Some(flush) = mapper.map(address, new_flags) { + let mut page_manager_guard = page_manager_lock_irqsave(); + let old_page = page_manager_guard.get_unwrap(&old_paddr); + old_page.write_irqsave().remove_vma(&vma); + // drop(page_manager_guard); + + flush.flush(); + let paddr = mapper.translate(address).unwrap().0; + // let mut page_manager_guard = page_manager_lock_irqsave(); + let page = page_manager_guard.get_unwrap(&paddr); + page.write_irqsave().insert_vma(vma.clone()); + + (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( + MMArch::phys_2_virt(old_paddr).unwrap().data() as *mut u8, + MMArch::PAGE_SIZE, + ); + + VmFaultReason::VM_FAULT_COMPLETED + } else { + VmFaultReason::VM_FAULT_OOM + } + } else { + // 私有文件映射,必须拷贝页面 + if let Some(flush) = mapper.map(address, new_flags) { + let mut page_manager_guard = page_manager_lock_irqsave(); + let old_page = page_manager_guard.get_unwrap(&old_paddr); + old_page.write_irqsave().remove_vma(&vma); + // drop(page_manager_guard); + + flush.flush(); + let paddr = mapper.translate(address).unwrap().0; + // let mut page_manager_guard = page_manager_lock_irqsave(); + let page = page_manager_guard.get_unwrap(&paddr); + page.write_irqsave().insert_vma(vma.clone()); + + (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( + MMArch::phys_2_virt(old_paddr).unwrap().data() as *mut u8, + MMArch::PAGE_SIZE, + ); + + VmFaultReason::VM_FAULT_COMPLETED + } else { + VmFaultReason::VM_FAULT_OOM + } + } + } - flush.flush(); - let paddr = mapper.translate(address).unwrap().0; - let mut anon_vma_guard = page_manager_lock_irqsave(); - let page = anon_vma_guard.get_mut(&paddr); - page.insert_vma(vma.clone()); + /// 缺页附近页预读 + /// ## 参数 + /// + /// - `pfm`: 缺页异常信息 + /// - `mapper`: 页表映射器 + /// + /// ## 返回值 + /// - VmFaultReason: 页面错误处理信息标志 + pub unsafe fn do_fault_around(pfm: &mut PageFaultMessage) -> VmFaultReason { + let vma = pfm.vma(); + let address = pfm.address(); + let mapper = &mut pfm.mapper; - (MMArch::phys_2_virt(paddr).unwrap().data() as *mut u8).copy_from_nonoverlapping( - MMArch::phys_2_virt(old_paddr).unwrap().data() as *mut u8, - MMArch::PAGE_SIZE, - ); + if mapper.get_table(address, 0).is_none() { + mapper + .allocate_table(address, 0) + .expect("failed to allocate pte table"); + } + let vma_guard = vma.lock_irqsave(); + let vma_region = *vma_guard.region(); + drop(vma_guard); - VmFaultReason::VM_FAULT_COMPLETED + // 缺页在VMA中的偏移量 + let vm_pgoff = (address - vma_region.start()) >> MMArch::PAGE_SHIFT; + + // 缺页在PTE中的偏移量 + let pte_pgoff = (address.data() >> MMArch::PAGE_SHIFT) & (1 << MMArch::PAGE_ENTRY_SHIFT); + + // 缺页在文件中的偏移量 + let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); + + let vma_pages_count = (vma_region.end() - vma_region.start()) >> MMArch::PAGE_SHIFT; + + let fault_around_page_number = 16; + + // 开始位置不能超出当前pte和vma头部 + let from_pte = max( + align_down(pte_pgoff, fault_around_page_number), + pte_pgoff - min(vm_pgoff, pte_pgoff), + ); + + // pte结束位置不能超过: + // 1.最大预读上限(默认16) + // 2.最大pte(512) + // 3.vma结束位置(pte_pgoff + (vma_pages_count - vm_pgoff)计算出vma结束页号对当前pte开头的偏移) + let to_pte = min( + from_pte + fault_around_page_number, + min( + 1 << MMArch::PAGE_SHIFT, + pte_pgoff + (vma_pages_count - vm_pgoff), + ), + ); + + // 预先分配pte页表(如果不存在) + if mapper.get_table(address, 0).is_none() && mapper.allocate_table(address, 0).is_none() { + return VmFaultReason::VM_FAULT_OOM; + } + + let fs = pfm.vma().lock_irqsave().vm_file().unwrap().inode().fs(); + // from_pte - pte_pgoff得出预读起始pte相对缺失页的偏移,加上pfm.file_pgoff(缺失页在文件中的偏移)得出起始页在文件中的偏移,结束pte同理 + fs.map_pages( + pfm, + file_pgoff + (from_pte - pte_pgoff), + file_pgoff + (to_pte - pte_pgoff), + ); + + VmFaultReason::empty() + } + + /// 通用的VMA文件映射页面映射函数,将PageCache中的页面映射到进程空间 + /// ## 参数 + /// + /// - `pfm`: 缺页异常信息 + /// - `mapper`: 页表映射器 + /// + /// ## 返回值 + /// - VmFaultReason: 页面错误处理信息标志 + pub unsafe fn filemap_map_pages( + pfm: &mut PageFaultMessage, + + start_pgoff: usize, + end_pgoff: usize, + ) -> VmFaultReason { + let vma = pfm.vma(); + let vma_guard = vma.lock_irqsave(); + let file = vma_guard.vm_file().expect("no vm_file in vma"); + let page_cache = file.inode().page_cache().unwrap(); + let mapper = &mut pfm.mapper; + + // 起始页地址 + let addr = vma_guard.region().start + + ((start_pgoff + - vma_guard + .file_page_offset() + .expect("file_page_offset is none")) + << MMArch::PAGE_SHIFT); + + for pgoff in start_pgoff..=end_pgoff { + if let Some(page) = page_cache.get_page(pgoff) { + let page_guard = page.read_irqsave(); + if page_guard.flags().contains(PageFlags::PG_UPTODATE) { + let phys = page_guard.phys_address(); + + let address = + VirtAddr::new(addr.data() + ((pgoff - start_pgoff) << MMArch::PAGE_SHIFT)); + mapper + .map_phys(address, phys, vma_guard.flags()) + .unwrap() + .flush(); + } + } + } + VmFaultReason::empty() + } + + /// 通用的VMA文件映射错误处理函数 + /// ## 参数 + /// + /// - `pfm`: 缺页异常信息 + /// - `mapper`: 页表映射器 + /// + /// ## 返回值 + /// - VmFaultReason: 页面错误处理信息标志 + pub unsafe fn filemap_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + let vma = pfm.vma(); + let vma_guard = vma.lock_irqsave(); + let file = vma_guard.vm_file().expect("no vm_file in vma"); + let page_cache = file.inode().page_cache().unwrap(); + let file_pgoff = pfm.file_pgoff.expect("no file_pgoff"); + let mapper = &mut pfm.mapper; + let mut ret = VmFaultReason::empty(); + + if let Some(page) = page_cache.get_page(file_pgoff) { + // TODO 异步从磁盘中预读页面进PageCache + + // 直接将PageCache中的页面作为要映射的页面 + pfm.page = Some(page.clone()); } else { - VmFaultReason::VM_FAULT_OOM + // TODO 同步预读 + //涉及磁盘IO,返回标志为VM_FAULT_MAJOR + ret = VmFaultReason::VM_FAULT_MAJOR; + // let mut buf: Vec = vec![0; MMArch::PAGE_SIZE]; + + let allocator = mapper.allocator_mut(); + + // 分配一个物理页面作为加入PageCache的新页 + let new_cache_page = allocator.allocate_one().unwrap(); + // (MMArch::phys_2_virt(new_cache_page).unwrap().data() as *mut u8) + // .copy_from_nonoverlapping(buf.as_mut_ptr(), MMArch::PAGE_SIZE); + file.pread( + file_pgoff * MMArch::PAGE_SIZE, + MMArch::PAGE_SIZE, + core::slice::from_raw_parts_mut( + MMArch::phys_2_virt(new_cache_page).unwrap().data() as *mut u8, + MMArch::PAGE_SIZE, + ), + ) + .expect("failed to read file to create pagecache page"); + + let page = Arc::new(Page::new(true, new_cache_page)); + pfm.page = Some(page.clone()); + + page.write_irqsave().add_flags(PageFlags::PG_LRU); + page_manager_lock_irqsave().insert(new_cache_page, &page); + page_reclaimer_lock_irqsave().insert_page(new_cache_page, &page); + page_cache.add_page(file_pgoff, &page); + + page.write_irqsave() + .set_page_cache_index(Some(page_cache), Some(file_pgoff)); } + ret + } + + /// 将文件页映射到缺页地址 + /// ## 参数 + /// + /// - `pfm`: 缺页异常信息 + /// - `mapper`: 页表映射器 + /// + /// ## 返回值 + /// - VmFaultReason: 页面错误处理信息标志 + pub unsafe fn finish_fault(pfm: &mut PageFaultMessage) -> VmFaultReason { + let vma = pfm.vma(); + let vma_guard = vma.lock_irqsave(); + let flags = pfm.flags(); + let cache_page = pfm.page.clone(); + let cow_page = pfm.cow_page.clone(); + let address = pfm.address(); + let mapper = &mut pfm.mapper; + + let page_to_map = if flags.contains(FaultFlags::FAULT_FLAG_WRITE) + && !vma_guard.vm_flags().contains(VmFlags::VM_SHARED) + { + // 私有文件映射的写时复制 + cow_page.expect("no cow_page in PageFaultMessage") + } else { + // 直接映射到PageCache + cache_page.expect("no cache_page in PageFaultMessage") + }; + + let page_phys = page_to_map.read_irqsave().phys_address(); + + mapper.map_phys(address, page_phys, vma_guard.flags()); + page_to_map.write_irqsave().insert_vma(pfm.vma()); + VmFaultReason::VM_FAULT_COMPLETED } } diff --git a/kernel/src/mm/init.rs b/kernel/src/mm/init.rs index 0c8549b09..3fe0e72fb 100644 --- a/kernel/src/mm/init.rs +++ b/kernel/src/mm/init.rs @@ -1,12 +1,18 @@ use core::{fmt::Write, sync::atomic::Ordering}; +use log::info; + use crate::{ arch::MMArch, driver::serial::serial8250::send_to_default_serial8250_port, filesystem::procfs::kmsg::kmsg_init, ipc::shm::shm_manager_init, libs::printk::PrintkWriter, - mm::{allocator::slab::slab_init, mmio_buddy::mmio_init, page::page_manager_init}, + mm::{ + allocator::slab::slab_init, + mmio_buddy::mmio_init, + page::{page_manager_init, page_reclaimer_init}, + }, }; use super::MemoryManagementArch; @@ -55,6 +61,8 @@ pub unsafe fn mm_init() { page_manager_init(); // enable SHM_MANAGER shm_manager_init(); + // enable PAGE_RECLAIMER + page_reclaimer_init(); MM_INIT .compare_exchange( @@ -65,7 +73,7 @@ pub unsafe fn mm_init() { ) .unwrap(); MMArch::arch_post_init(); - kinfo!("mm init done."); + info!("mm init done."); } /// 获取内存管理的初始化状态 diff --git a/kernel/src/mm/kernel_mapper.rs b/kernel/src/mm/kernel_mapper.rs index 02436189d..487d15378 100644 --- a/kernel/src/mm/kernel_mapper.rs +++ b/kernel/src/mm/kernel_mapper.rs @@ -1,6 +1,6 @@ use system_error::SystemError; -use super::{page::PageFlags, PageTableKind, PhysAddr, VirtAddr}; +use super::{page::EntryFlags, PageTableKind, PhysAddr, VirtAddr}; use crate::{ arch::{ mm::{LockedFrameAllocator, PageMapper}, @@ -104,7 +104,7 @@ impl KernelMapper { mut vaddr: VirtAddr, mut paddr: PhysAddr, size: usize, - flags: PageFlags, + flags: EntryFlags, flush: bool, ) -> Result<(), SystemError> { if self.readonly { @@ -112,7 +112,7 @@ impl KernelMapper { } let count = PageFrameCount::new(page_align_up(size) / MMArch::PAGE_SIZE); - // kdebug!("kernel mapper: map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); + // debug!("kernel mapper: map_phys: vaddr: {vaddr:?}, paddr: {paddr:?}, count: {count:?}, flags: {flags:?}"); for _ in 0..count.data() { let flusher = self.mapper.map_phys(vaddr, paddr, flags).unwrap(); diff --git a/kernel/src/mm/madvise.rs b/kernel/src/mm/madvise.rs index 5e9587a40..9089f88f6 100644 --- a/kernel/src/mm/madvise.rs +++ b/kernel/src/mm/madvise.rs @@ -12,7 +12,7 @@ impl LockedVMA { _flusher: impl Flusher, ) -> Result<(), SystemError> { //TODO https://code.dragonos.org.cn/xref/linux-6.6.21/mm/madvise.c?fi=madvise#do_madvise - let mut vma = self.lock(); + let mut vma = self.lock_irqsave(); let mut new_flags = *vma.vm_flags(); match behavior { MadvFlags::MADV_REMOVE => { diff --git a/kernel/src/mm/memblock.rs b/kernel/src/mm/memblock.rs index 4ecbee529..d17bd3434 100644 --- a/kernel/src/mm/memblock.rs +++ b/kernel/src/mm/memblock.rs @@ -1,5 +1,6 @@ use core::intrinsics::unlikely; +use log::error; use system_error::SystemError; use crate::libs::{ @@ -88,7 +89,7 @@ impl MemBlockManager { .expect("Failed to count blocks to add!"); if inner.initial_memory_regions_num + blocks_to_add > INITIAL_MEMORY_REGIONS_NUM { - kerror!("Too many memory regions!"); + error!("Too many memory regions!"); return Err(SystemError::ENOMEM); } @@ -203,7 +204,7 @@ impl MemBlockManager { if this.base + this.size != next_base || this.flags != next_flags { if unlikely(this.base + this.size > next_base) { - kBUG!("this->base + this->size > next->base"); + panic!("this->base + this->size > next->base"); } i += 1; continue; diff --git a/kernel/src/mm/mmio_buddy.rs b/kernel/src/mm/mmio_buddy.rs index 0c5005721..8d88f8ace 100644 --- a/kernel/src/mm/mmio_buddy.rs +++ b/kernel/src/mm/mmio_buddy.rs @@ -2,19 +2,17 @@ use crate::libs::align::{page_align_down, page_align_up}; use crate::libs::spinlock::{SpinLock, SpinLockGuard}; use crate::mm::kernel_mapper::KernelMapper; use crate::mm::page::{PAGE_1G_SHIFT, PAGE_4K_SHIFT}; +use crate::mm::{MMArch, MemoryManagementArch}; use crate::process::ProcessManager; -use crate::{ - kdebug, - mm::{MMArch, MemoryManagementArch}, -}; -use crate::{kerror, kinfo, kwarn}; + use alloc::{collections::LinkedList, vec::Vec}; use core::mem; use core::mem::MaybeUninit; use core::sync::atomic::{AtomicBool, Ordering}; +use log::{debug, error, info, warn}; use system_error::SystemError; -use super::page::{PageFlags, PAGE_4K_SIZE}; +use super::page::{EntryFlags, PAGE_4K_SIZE}; use super::{PhysAddr, VirtAddr}; // 最大的伙伴块的幂 @@ -57,9 +55,12 @@ impl MmioBuddyMemPool { free_regions[i as usize] = MaybeUninit::new(SpinLock::new(MmioFreeRegionList::new())); } let free_regions = unsafe { - mem::transmute::<_, [SpinLock; MMIO_BUDDY_REGION_COUNT as usize]>( - free_regions, - ) + mem::transmute::< + [core::mem::MaybeUninit< + crate::libs::spinlock::SpinLock, + >; MMIO_BUDDY_REGION_COUNT as usize], + [SpinLock; MMIO_BUDDY_REGION_COUNT as usize], + >(free_regions) }; let pool = MmioBuddyMemPool { @@ -69,11 +70,11 @@ impl MmioBuddyMemPool { }; assert!(pool.pool_start_addr.data() % PAGE_1G_SIZE == 0); - kdebug!("MMIO buddy pool init: created"); + debug!("MMIO buddy pool init: created"); let mut vaddr_base = MMArch::MMIO_BASE; let mut remain_size = MMArch::MMIO_SIZE; - kdebug!( + debug!( "BASE: {:?}, TOP: {:?}, size: {:?}", MMArch::MMIO_BASE, MMArch::MMIO_TOP, @@ -92,7 +93,7 @@ impl MmioBuddyMemPool { } } - kdebug!("MMIO buddy pool init success"); + debug!("MMIO buddy pool init success"); return pool; } @@ -102,11 +103,11 @@ impl MmioBuddyMemPool { /// /// @return 创建好的地址区域结构体 fn create_region(&self, vaddr: VirtAddr) -> MmioBuddyAddrRegion { - // kdebug!("create_region for vaddr: {vaddr:?}"); + // debug!("create_region for vaddr: {vaddr:?}"); let region: MmioBuddyAddrRegion = MmioBuddyAddrRegion::new(vaddr); - // kdebug!("create_region for vaddr: {vaddr:?} OK!!!"); + // debug!("create_region for vaddr: {vaddr:?} OK!!!"); return region; } @@ -172,7 +173,7 @@ impl MmioBuddyMemPool { ) -> Result { // 申请范围错误 if !(MMIO_BUDDY_MIN_EXP..=MMIO_BUDDY_MAX_EXP).contains(&exp) { - kdebug!("query_addr_region: exp wrong"); + debug!("query_addr_region: exp wrong"); return Err(MmioResult::WRONGEXP); } // 没有恰好符合要求的内存块 @@ -203,7 +204,7 @@ impl MmioBuddyMemPool { } } Err(err) => { - kdebug!("buddy_pop_region get wrong"); + debug!("buddy_pop_region get wrong"); return Err(err); } } @@ -222,7 +223,7 @@ impl MmioBuddyMemPool { } } Err(err) => { - kdebug!("buddy_pop_region get wrong"); + debug!("buddy_pop_region get wrong"); return Err(err); } } @@ -262,7 +263,7 @@ impl MmioBuddyMemPool { ) { Ok(_) => continue, Err(err) => { - kdebug!("merge_all_exp get wrong"); + debug!("merge_all_exp get wrong"); return Err(err); } } @@ -274,7 +275,7 @@ impl MmioBuddyMemPool { ) { Ok(_) => continue, Err(err) => { - kdebug!("merge_all_exp get wrong"); + debug!("merge_all_exp get wrong"); return Err(err); } } @@ -309,7 +310,7 @@ impl MmioBuddyMemPool { match self.query_addr_region(exp, &mut list_guard) { Ok(ret) => return Ok(ret), Err(err) => { - kdebug!("mmio_buddy_query_addr_region failed"); + debug!("mmio_buddy_query_addr_region failed"); return Err(err); } } @@ -433,7 +434,7 @@ impl MmioBuddyMemPool { Err(err) => { // 如果合并失败了要将取出来的元素放回去 self.push_block(copy_region, list_guard); - kdebug!("merge_all_exp: merge_blocks failed"); + debug!("merge_all_exp: merge_blocks failed"); return Err(err); } Ok(_) => continue, @@ -489,7 +490,7 @@ impl MmioBuddyMemPool { // 计算前导0 #[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))] let mut size_exp: u32 = 63 - size.leading_zeros(); - // kdebug!("create_mmio: size_exp: {}", size_exp); + // debug!("create_mmio: size_exp: {}", size_exp); // 记录最终申请的空间大小 let mut new_size = size; // 对齐要申请的空间大小 @@ -509,7 +510,7 @@ impl MmioBuddyMemPool { return Ok(space_guard); } Err(_) => { - kerror!( + error!( "failed to create mmio. pid = {:?}", ProcessManager::current_pcb().pid() ); @@ -543,7 +544,7 @@ impl MmioBuddyMemPool { let mut bindings = KernelMapper::lock(); let mut kernel_mapper = bindings.as_mut(); if kernel_mapper.is_none() { - kwarn!("release_mmio: kernel_mapper is read only"); + warn!("release_mmio: kernel_mapper is read only"); return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); } @@ -551,7 +552,7 @@ impl MmioBuddyMemPool { unsafe { let x: Option<( PhysAddr, - PageFlags, + EntryFlags, crate::mm::page::PageFlush, )> = kernel_mapper .as_mut() @@ -676,7 +677,7 @@ impl MMIOSpaceGuard { return Err(SystemError::EINVAL); } - let flags = PageFlags::mmio_flags(); + let flags = EntryFlags::mmio_flags(); let mut kernel_mapper = KernelMapper::lock(); let r = kernel_mapper.map_phys_with_size(self.vaddr, paddr, length, flags, true); @@ -735,11 +736,11 @@ impl Drop for MMIOSpaceGuard { } pub fn mmio_init() { - kdebug!("Initializing MMIO buddy memory pool..."); + debug!("Initializing MMIO buddy memory pool..."); // 初始化mmio内存池 unsafe { __MMIO_POOL = Some(MmioBuddyMemPool::new()); } - kinfo!("MMIO buddy memory pool init done"); + info!("MMIO buddy memory pool init done"); } diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index 49e61fcd1..e95e90198 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -1,7 +1,8 @@ use alloc::sync::Arc; +use page::EntryFlags; use system_error::SystemError; -use crate::{arch::MMArch, include::bindings::bindings::PAGE_OFFSET}; +use crate::arch::MMArch; use core::{ cmp, @@ -40,7 +41,7 @@ static mut __IDLE_PROCESS_ADDRESS_SPACE: Option> = None; bitflags! { /// Virtual memory flags #[allow(clippy::bad_bit_mask)] - pub struct VmFlags:u64{ + pub struct VmFlags:usize{ const VM_NONE = 0x00000000; const VM_READ = 0x00000001; @@ -93,6 +94,27 @@ bitflags! { const VM_FAULT_NEEDDSYNC = 0x002000; const VM_FAULT_COMPLETED = 0x004000; const VM_FAULT_HINDEX_MASK = 0x0f0000; + const VM_FAULT_ERROR = 0x000001 | 0x000002 | 0x000040 | 0x000010 | 0x000020 | 0x000800; + } + + pub struct MsFlags:usize { + const MS_ASYNC = 1; + const MS_INVALIDATE = 2; + const MS_SYNC = 4; + } +} + +impl core::ops::Index for [usize] { + type Output = usize; + + fn index(&self, index: VmFlags) -> &Self::Output { + &self[index.bits] + } +} + +impl core::ops::IndexMut for [usize] { + fn index_mut(&mut self, index: VmFlags) -> &mut Self::Output { + &mut self[index.bits] } } @@ -121,18 +143,6 @@ pub unsafe fn set_IDLE_PROCESS_ADDRESS_SPACE(address_space: Arc) { __IDLE_PROCESS_ADDRESS_SPACE = Some(address_space); } -/// @brief 将内核空间的虚拟地址转换为物理地址 -#[inline(always)] -pub fn virt_2_phys(addr: usize) -> usize { - addr - PAGE_OFFSET as usize -} - -/// @brief 将物理地址转换为内核空间的虚拟地址 -#[inline(always)] -pub fn phys_2_virt(addr: usize) -> usize { - addr + PAGE_OFFSET as usize -} - #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] pub enum PageTableKind { /// 用户可访问的页表 @@ -427,6 +437,7 @@ impl Default for PhysMemoryArea { } } +#[allow(dead_code)] pub trait MemoryManagementArch: Clone + Copy + Debug { /// 是否支持缺页中断 const PAGE_FAULT_ENABLED: bool; @@ -610,7 +621,7 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { /// 创建页表项 /// - /// 这是一个低阶api,用于根据物理地址以及指定好的pageflags,创建页表项 + /// 这是一个低阶api,用于根据物理地址以及指定好的EntryFlags,创建页表项 /// /// ## 参数 /// @@ -642,6 +653,48 @@ pub trait MemoryManagementArch: Clone + Copy + Debug { ) -> bool { true } + + const PAGE_NONE: usize; + const PAGE_SHARED: usize; + const PAGE_SHARED_EXEC: usize; + const PAGE_COPY_NOEXEC: usize; + const PAGE_COPY_EXEC: usize; + const PAGE_COPY: usize; + const PAGE_READONLY: usize; + const PAGE_READONLY_EXEC: usize; + + const PAGE_READ: usize; + const PAGE_READ_EXEC: usize; + const PAGE_WRITE: usize; + const PAGE_WRITE_EXEC: usize; + const PAGE_EXEC: usize; + + const PROTECTION_MAP: [EntryFlags; 16]; + + /// 页面保护标志转换函数 + /// ## 参数 + /// + /// - `vm_flags`: VmFlags标志 + /// + /// ## 返回值 + /// - EntryFlags: 页面的保护位 + fn vm_get_page_prot(vm_flags: VmFlags) -> EntryFlags { + let map = Self::PROTECTION_MAP; + let mut ret = map[vm_flags + .intersection( + VmFlags::VM_READ | VmFlags::VM_WRITE | VmFlags::VM_EXEC | VmFlags::VM_SHARED, + ) + .bits()]; + + #[cfg(target_arch = "x86_64")] + { + // 如果xd位被保留,那么将可执行性设置为true + if crate::arch::mm::X86_64MMArch::is_xd_reserved() { + ret = ret.set_execute(true); + } + } + ret + } } /// @brief 虚拟地址范围 diff --git a/kernel/src/mm/no_init.rs b/kernel/src/mm/no_init.rs index 855a86767..fdb8d4d66 100644 --- a/kernel/src/mm/no_init.rs +++ b/kernel/src/mm/no_init.rs @@ -19,7 +19,7 @@ use core::marker::PhantomData; use super::{ allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage}, - page::PageFlags, + page::EntryFlags, PageTableKind, VirtAddr, }; @@ -141,7 +141,7 @@ impl FrameAllocator for PseudoAllocator { /// 并且,内核引导文件必须以4K页为粒度,填写了前100M的内存映射关系。(具体以本文件开头的注释为准) #[inline(never)] pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { - let flags: PageFlags = PageFlags::new().set_write(true); + let flags: EntryFlags = EntryFlags::new().set_write(true); pseudo_map_phys_with_flags(vaddr, paddr, count, flags); } @@ -150,7 +150,7 @@ pub unsafe fn pseudo_map_phys(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrame /// with READ_ONLY and EXECUTE flags. #[inline(never)] pub unsafe fn pseudo_map_phys_ro(vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount) { - let flags: PageFlags = PageFlags::new().set_write(false).set_execute(true); + let flags: EntryFlags = EntryFlags::new().set_write(false).set_execute(true); pseudo_map_phys_with_flags(vaddr, paddr, count, flags); } @@ -160,7 +160,7 @@ pub unsafe fn pseudo_map_phys_with_flags( vaddr: VirtAddr, paddr: PhysAddr, count: PageFrameCount, - flags: PageFlags, + flags: EntryFlags, ) { assert!(vaddr.check_aligned(MMArch::PAGE_SIZE)); assert!(paddr.check_aligned(MMArch::PAGE_SIZE)); diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 0bc5d5b51..27b399dd4 100644 --- a/kernel/src/mm/page.rs +++ b/kernel/src/mm/page.rs @@ -1,3 +1,4 @@ +use alloc::string::ToString; use core::{ fmt::{self, Debug, Error, Formatter}, marker::PhantomData, @@ -5,16 +6,26 @@ use core::{ ops::Add, sync::atomic::{compiler_fence, Ordering}, }; +use system_error::SystemError; +use unified_init::macros::unified_init; use alloc::sync::Arc; use hashbrown::{HashMap, HashSet}; +use log::{error, info}; +use lru::LruCache; use crate::{ - arch::{interrupt::ipi::send_ipi, MMArch}, + arch::{interrupt::ipi::send_ipi, mm::LockedFrameAllocator, MMArch}, exception::ipi::{IpiKind, IpiTarget}, + filesystem::vfs::{file::PageCache, FilePrivateData}, + init::initcall::INITCALL_CORE, ipc::shm::ShmId, - kerror, - libs::spinlock::{SpinLock, SpinLockGuard}, + libs::{ + rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, + spinlock::{SpinLock, SpinLockGuard}, + }, + process::{ProcessControlBlock, ProcessManager}, + time::{sleep::usleep, PosixTimeSpec}, }; use super::{ @@ -37,14 +48,14 @@ pub static mut PAGE_MANAGER: Option> = None; /// 初始化PAGE_MANAGER pub fn page_manager_init() { - kinfo!("page_manager_init"); + info!("page_manager_init"); let page_manager = SpinLock::new(PageManager::new()); compiler_fence(Ordering::SeqCst); unsafe { PAGE_MANAGER = Some(page_manager) }; compiler_fence(Ordering::SeqCst); - kinfo!("page_manager_init done"); + info!("page_manager_init done"); } pub fn page_manager_lock_irqsave() -> SpinLockGuard<'static, PageManager> { @@ -53,7 +64,7 @@ pub fn page_manager_lock_irqsave() -> SpinLockGuard<'static, PageManager> { // 物理页管理器 pub struct PageManager { - phys2page: HashMap, + phys2page: HashMap>, } impl PageManager { @@ -67,18 +78,21 @@ impl PageManager { self.phys2page.contains_key(paddr) } - pub fn get(&self, paddr: &PhysAddr) -> Option<&Page> { - self.phys2page.get(paddr) + pub fn get(&mut self, paddr: &PhysAddr) -> Option> { + page_reclaimer_lock_irqsave().get(paddr); + self.phys2page.get(paddr).cloned() } - pub fn get_mut(&mut self, paddr: &PhysAddr) -> &mut Page { + pub fn get_unwrap(&mut self, paddr: &PhysAddr) -> Arc { + page_reclaimer_lock_irqsave().get(paddr); self.phys2page - .get_mut(paddr) - .unwrap_or_else(|| panic!("{:?}", paddr)) + .get(paddr) + .unwrap_or_else(|| panic!("Phys Page not found, {:?}", paddr)) + .clone() } - pub fn insert(&mut self, paddr: PhysAddr, page: Page) { - self.phys2page.insert(paddr, page); + pub fn insert(&mut self, paddr: PhysAddr, page: &Arc) { + self.phys2page.insert(paddr, page.clone()); } pub fn remove_page(&mut self, paddr: &PhysAddr) { @@ -86,8 +100,236 @@ impl PageManager { } } -/// 物理页面信息 +pub static mut PAGE_RECLAIMER: Option> = None; + +pub fn page_reclaimer_init() { + info!("page_reclaimer_init"); + let page_reclaimer = SpinLock::new(PageReclaimer::new()); + + compiler_fence(Ordering::SeqCst); + unsafe { PAGE_RECLAIMER = Some(page_reclaimer) }; + compiler_fence(Ordering::SeqCst); + + info!("page_reclaimer_init done"); +} + +/// 页面回收线程 +static mut PAGE_RECLAIMER_THREAD: Option> = None; + +/// 页面回收线程初始化函数 +#[unified_init(INITCALL_CORE)] +fn page_reclaimer_thread_init() -> Result<(), SystemError> { + let closure = crate::process::kthread::KernelThreadClosure::StaticEmptyClosure(( + &(page_reclaim_thread as fn() -> i32), + (), + )); + let pcb = crate::process::kthread::KernelThreadMechanism::create_and_run( + closure, + "page_reclaim".to_string(), + ) + .ok_or("") + .expect("create tty_refresh thread failed"); + unsafe { + PAGE_RECLAIMER_THREAD = Some(pcb); + } + Ok(()) +} + +/// 页面回收线程执行的函数 +fn page_reclaim_thread() -> i32 { + loop { + let usage = unsafe { LockedFrameAllocator.usage() }; + // log::info!("usage{:?}", usage); + + // 保留4096个页面,总计16MB的空闲空间 + if usage.free().data() < 4096 { + let page_to_free = 4096; + page_reclaimer_lock_irqsave().shrink_list(PageFrameCount::new(page_to_free)); + } else { + //TODO 暂时让页面回收线程负责脏页回写任务,后续需要分离 + page_reclaimer_lock_irqsave().flush_dirty_pages(); + // 休眠5秒 + // log::info!("sleep"); + let _ = usleep(PosixTimeSpec::new(5, 0)); + } + } +} + +/// 获取页面回收器 +pub fn page_reclaimer_lock_irqsave() -> SpinLockGuard<'static, PageReclaimer> { + unsafe { PAGE_RECLAIMER.as_ref().unwrap().lock_irqsave() } +} + +/// 页面回收器 +pub struct PageReclaimer { + lru: LruCache>, +} + +impl PageReclaimer { + pub fn new() -> Self { + Self { + lru: LruCache::unbounded(), + } + } + + pub fn get(&mut self, paddr: &PhysAddr) -> Option> { + self.lru.get(paddr).cloned() + } + + pub fn insert_page(&mut self, paddr: PhysAddr, page: &Arc) { + self.lru.put(paddr, page.clone()); + } + + /// lru链表缩减 + /// ## 参数 + /// + /// - `count`: 需要缩减的页面数量 + pub fn shrink_list(&mut self, count: PageFrameCount) { + for _ in 0..count.data() { + let (paddr, page) = self.lru.pop_lru().expect("pagecache is empty"); + let page_cache = page.read_irqsave().page_cache().unwrap(); + for vma in page.read_irqsave().anon_vma() { + let address_space = vma.lock_irqsave().address_space().unwrap(); + let address_space = address_space.upgrade().unwrap(); + let mut guard = address_space.write(); + let mapper = &mut guard.user_mapper.utable; + let virt = vma.lock_irqsave().page_address(&page).unwrap(); + unsafe { + mapper.unmap(virt, false).unwrap().flush(); + } + } + page_cache.remove_page(page.read_irqsave().index().unwrap()); + page_manager_lock_irqsave().remove_page(&paddr); + if page.read_irqsave().flags.contains(PageFlags::PG_DIRTY) { + Self::page_writeback(&page, true); + } + } + } + + /// 唤醒页面回收线程 + pub fn wakeup_claim_thread() { + // log::info!("wakeup_claim_thread"); + let _ = ProcessManager::wakeup(unsafe { PAGE_RECLAIMER_THREAD.as_ref().unwrap() }); + } + + /// 脏页回写函数 + /// ## 参数 + /// + /// - `page`: 需要回写的脏页 + /// - `unmap`: 是否取消映射 + /// + /// ## 返回值 + /// - VmFaultReason: 页面错误处理信息标志 + pub fn page_writeback(page: &Arc, unmap: bool) { + if !unmap { + page.write_irqsave().remove_flags(PageFlags::PG_DIRTY); + } + + for vma in page.read_irqsave().anon_vma() { + let address_space = vma.lock_irqsave().address_space().unwrap(); + let address_space = address_space.upgrade().unwrap(); + let mut guard = address_space.write(); + let mapper = &mut guard.user_mapper.utable; + let virt = vma.lock_irqsave().page_address(page).unwrap(); + if unmap { + unsafe { + mapper.unmap(virt, false).unwrap().flush(); + } + } else { + unsafe { + // 保护位设为只读 + mapper.remap( + virt, + mapper.get_entry(virt, 0).unwrap().flags().set_write(false), + ) + }; + } + } + let inode = page + .read_irqsave() + .page_cache + .clone() + .unwrap() + .inode() + .clone() + .unwrap() + .upgrade() + .unwrap(); + inode + .write_at( + page.read_irqsave().index().unwrap(), + MMArch::PAGE_SIZE, + unsafe { + core::slice::from_raw_parts( + MMArch::phys_2_virt(page.read_irqsave().phys_addr) + .unwrap() + .data() as *mut u8, + MMArch::PAGE_SIZE, + ) + }, + SpinLock::new(FilePrivateData::Unused).lock(), + ) + .unwrap(); + } + + /// lru脏页刷新 + pub fn flush_dirty_pages(&self) { + // log::info!("flush_dirty_pages"); + let iter = self.lru.iter(); + for (_, page) in iter { + if page.read_irqsave().flags().contains(PageFlags::PG_DIRTY) { + Self::page_writeback(page, false); + } + } + } +} + +bitflags! { + pub struct PageFlags: u64 { + const PG_LOCKED = 1 << 0; + const PG_WRITEBACK = 1 << 1; + const PG_REFERENCED = 1 << 2; + const PG_UPTODATE = 1 << 3; + const PG_DIRTY = 1 << 4; + const PG_LRU = 1 << 5; + const PG_HEAD = 1 << 6; + const PG_WAITERS = 1 << 7; + const PG_ACTIVE = 1 << 8; + const PG_WORKINGSET = 1 << 9; + const PG_ERROR = 1 << 10; + const PG_SLAB = 1 << 11; + const PG_RESERVED = 1 << 14; + const PG_PRIVATE = 1 << 15; + const PG_RECLAIM = 1 << 18; + const PG_SWAPBACKED = 1 << 19; + } +} + +#[derive(Debug)] pub struct Page { + inner: RwLock, +} + +impl Page { + pub fn new(shared: bool, phys_addr: PhysAddr) -> Self { + let inner = InnerPage::new(shared, phys_addr); + Self { + inner: RwLock::new(inner), + } + } + + pub fn read_irqsave(&self) -> RwLockReadGuard { + self.inner.read_irqsave() + } + + pub fn write_irqsave(&self) -> RwLockWriteGuard { + self.inner.write_irqsave() + } +} + +#[derive(Debug)] +/// 物理页面信息 +pub struct InnerPage { /// 映射计数 map_count: usize, /// 是否为共享页 @@ -98,10 +340,17 @@ pub struct Page { shm_id: Option, /// 映射到当前page的VMA anon_vma: HashSet>, + /// 标志 + flags: PageFlags, + /// 页所在的物理页帧号 + phys_addr: PhysAddr, + /// 在pagecache中的偏移 + index: Option, + page_cache: Option>, } -impl Page { - pub fn new(shared: bool) -> Self { +impl InnerPage { + pub fn new(shared: bool, phys_addr: PhysAddr) -> Self { let dealloc_when_zero = !shared; Self { map_count: 0, @@ -109,6 +358,10 @@ impl Page { free_when_zero: dealloc_when_zero, shm_id: None, anon_vma: HashSet::new(), + flags: PageFlags::empty(), + phys_addr, + index: None, + page_cache: None, } } @@ -137,6 +390,31 @@ impl Page { self.shm_id } + pub fn index(&self) -> Option { + self.index + } + + pub fn page_cache(&self) -> Option> { + self.page_cache.clone() + } + + pub fn set_page_cache(&mut self, page_cache: Option>) { + self.page_cache = page_cache; + } + + pub fn set_index(&mut self, index: Option) { + self.index = index; + } + + pub fn set_page_cache_index( + &mut self, + page_cache: Option>, + index: Option, + ) { + self.page_cache = page_cache; + self.index = index; + } + pub fn set_shm_id(&mut self, shm_id: ShmId) { self.shm_id = Some(shm_id); } @@ -154,6 +432,31 @@ impl Page { pub fn map_count(&self) -> usize { self.map_count } + + #[inline(always)] + pub fn flags(&self) -> &PageFlags { + &self.flags + } + + #[inline(always)] + pub fn set_flags(&mut self, flags: PageFlags) { + self.flags = flags + } + + #[inline(always)] + pub fn add_flags(&mut self, flags: PageFlags) { + self.flags = self.flags.union(flags); + } + + #[inline(always)] + pub fn remove_flags(&mut self, flags: PageFlags) { + self.flags = self.flags.difference(flags); + } + + #[inline(always)] + pub fn phys_address(&self) -> PhysAddr { + self.phys_addr + } } #[derive(Debug)] @@ -329,8 +632,19 @@ impl PageTable { new_table.set_entry(i, entry); } else { let phys = allocator.allocate_one()?; - let mut anon_vma_guard = page_manager_lock_irqsave(); - anon_vma_guard.insert(phys, Page::new(false)); + let mut page_manager_guard = page_manager_lock_irqsave(); + let old_phys = entry.address().unwrap(); + let old_page = page_manager_guard.get_unwrap(&old_phys); + let new_page = + Arc::new(Page::new(old_page.read_irqsave().shared(), phys)); + if let Some(ref page_cache) = old_page.read_irqsave().page_cache() { + new_page.write_irqsave().set_page_cache_index( + Some(page_cache.clone()), + old_page.read_irqsave().index(), + ); + } + + page_manager_guard.insert(phys, &new_page); let old_phys = entry.address().unwrap(); let frame = MMArch::phys_2_virt(phys).unwrap().data() as *mut u8; frame.copy_from_nonoverlapping( @@ -372,7 +686,7 @@ impl Debug for PageEntry { impl PageEntry { #[inline(always)] - pub fn new(paddr: PhysAddr, flags: PageFlags) -> Self { + pub fn new(paddr: PhysAddr, flags: EntryFlags) -> Self { Self { data: MMArch::make_entry(paddr, flags.data()), phantom: PhantomData, @@ -420,12 +734,12 @@ impl PageEntry { } #[inline(always)] - pub fn flags(&self) -> PageFlags { - unsafe { PageFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } + pub fn flags(&self) -> EntryFlags { + unsafe { EntryFlags::from_data(self.data & Arch::ENTRY_FLAGS_MASK) } } #[inline(always)] - pub fn set_flags(&mut self, flags: PageFlags) { + pub fn set_flags(&mut self, flags: EntryFlags) { self.data = (self.data & !Arch::ENTRY_FLAGS_MASK) | flags.data(); } @@ -453,13 +767,19 @@ impl PageEntry { /// 页表项的标志位 #[derive(Copy, Clone, Hash)] -pub struct PageFlags { +pub struct EntryFlags { data: usize, phantom: PhantomData, } +impl Default for EntryFlags { + fn default() -> Self { + Self::new() + } +} + #[allow(dead_code)] -impl PageFlags { +impl EntryFlags { #[inline(always)] pub fn new() -> Self { let mut r = unsafe { @@ -480,18 +800,19 @@ impl PageFlags { return r; } - /// 根据ProtFlags生成PageFlags + /// 根据ProtFlags生成EntryFlags /// /// ## 参数 /// /// - prot_flags: 页的保护标志 /// - user: 用户空间是否可访问 - pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> PageFlags { - let flags: PageFlags = PageFlags::new() - .set_user(user) - .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) - .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); - + pub fn from_prot_flags(prot_flags: ProtFlags, user: bool) -> Self { + let vm_flags = super::VmFlags::from(prot_flags); + // let flags: EntryFlags = EntryFlags::new() + // .set_user(user) + // .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) + // .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); + let flags = Arch::vm_get_page_prot(vm_flags).set_user(user); return flags; } @@ -757,9 +1078,9 @@ impl PageFlags { } } -impl fmt::Debug for PageFlags { +impl fmt::Debug for EntryFlags { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("PageFlags") + f.debug_struct("EntryFlags") .field("bits", &format_args!("{:#0x}", self.data)) .field("present", &self.present()) .field("has_write", &self.has_write()) @@ -855,18 +1176,23 @@ impl PageMapper { pub unsafe fn map( &mut self, virt: VirtAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { compiler_fence(Ordering::SeqCst); let phys: PhysAddr = self.frame_allocator.allocate_one()?; compiler_fence(Ordering::SeqCst); + unsafe { + let vaddr = MMArch::phys_2_virt(phys).unwrap(); + MMArch::write_bytes(vaddr, 0, MMArch::PAGE_SIZE); + } + let mut page_manager_guard: SpinLockGuard<'static, PageManager> = page_manager_lock_irqsave(); if !page_manager_guard.contains(&phys) { - page_manager_guard.insert(phys, Page::new(false)) + page_manager_guard.insert(phys, &Arc::new(Page::new(false, phys))) } - + drop(page_manager_guard); return self.map_phys(virt, phys, flags); } @@ -875,14 +1201,13 @@ impl PageMapper { &mut self, virt: VirtAddr, phys: PhysAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { // 验证虚拟地址和物理地址是否对齐 if !(virt.check_aligned(Arch::PAGE_SIZE) && phys.check_aligned(Arch::PAGE_SIZE)) { - kerror!( + error!( "Try to map unaligned page: virt={:?}, phys={:?}", - virt, - phys + virt, phys ); return None; } @@ -908,7 +1233,7 @@ impl PageMapper { let next_table = table.next_level_table(i); if let Some(next_table) = next_table { table = next_table; - // kdebug!("Mapping {:?} to next level table...", virt); + // debug!("Mapping {:?} to next level table...", virt); } else { // 分配下一级页表 let frame = self.frame_allocator.allocate_one()?; @@ -916,8 +1241,8 @@ impl PageMapper { // 清空这个页帧 MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); // 设置页表项的flags - let flags: PageFlags = - PageFlags::new_page_table(virt.kind() == PageTableKind::User); + let flags: EntryFlags = + EntryFlags::new_page_table(virt.kind() == PageTableKind::User); // 把新分配的页表映射到当前页表 table.set_entry(i, PageEntry::new(frame, flags)); @@ -933,11 +1258,11 @@ impl PageMapper { pub unsafe fn map_huge_page( &mut self, virt: VirtAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { // 验证虚拟地址是否对齐 if !(virt.check_aligned(Arch::PAGE_SIZE)) { - kerror!("Try to map unaligned page: virt={:?}", virt); + error!("Try to map unaligned page: virt={:?}", virt); return None; } @@ -999,7 +1324,8 @@ impl PageMapper { MMArch::write_bytes(MMArch::phys_2_virt(frame).unwrap(), 0, MMArch::PAGE_SIZE); // 设置页表项的flags - let flags: PageFlags = PageFlags::new_page_table(virt.kind() == PageTableKind::User); + let flags: EntryFlags = + EntryFlags::new_page_table(virt.kind() == PageTableKind::User); table.set_entry(i, PageEntry::new(frame, flags)); table.next_level_table(i) @@ -1101,7 +1427,7 @@ impl PageMapper { pub unsafe fn map_linearly( &mut self, phys: PhysAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option<(VirtAddr, PageFlush)> { let virt: VirtAddr = Arch::phys_2_virt(phys)?; return self.map_phys(virt, phys, flags).map(|flush| (virt, flush)); @@ -1121,7 +1447,7 @@ impl PageMapper { pub unsafe fn remap( &mut self, virt: VirtAddr, - flags: PageFlags, + flags: EntryFlags, ) -> Option> { return self .visit(virt, |p1, i| { @@ -1143,7 +1469,7 @@ impl PageMapper { /// ## 返回值 /// /// 如果查找成功,返回物理地址和页表项的flags,否则返回None - pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, PageFlags)> { + pub fn translate(&self, virt: VirtAddr) -> Option<(PhysAddr, EntryFlags)> { let entry: PageEntry = self.visit(virt, |p1, i| unsafe { p1.entry(i) })??; let paddr = entry.address().ok()?; let flags = entry.flags(); @@ -1182,9 +1508,9 @@ impl PageMapper { &mut self, virt: VirtAddr, unmap_parents: bool, - ) -> Option<(PhysAddr, PageFlags, PageFlush)> { + ) -> Option<(PhysAddr, EntryFlags, PageFlush)> { if !virt.check_aligned(Arch::PAGE_SIZE) { - kerror!("Try to unmap unaligned page: virt={:?}", virt); + error!("Try to unmap unaligned page: virt={:?}", virt); return None; } @@ -1230,7 +1556,7 @@ unsafe fn unmap_phys_inner( table: &PageTable, unmap_parents: bool, allocator: &mut impl FrameAllocator, -) -> Option<(PhysAddr, PageFlags)> { +) -> Option<(PhysAddr, EntryFlags)> { // 获取页表项的索引 let i = table.index_of(vaddr)?; diff --git a/kernel/src/mm/syscall.rs b/kernel/src/mm/syscall.rs index 99997ee54..6c9646a3f 100644 --- a/kernel/src/mm/syscall.rs +++ b/kernel/src/mm/syscall.rs @@ -1,12 +1,13 @@ -use core::intrinsics::unlikely; +use core::{intrinsics::unlikely, slice::from_raw_parts}; use alloc::sync::Arc; +use log::error; use system_error::SystemError; use crate::{ arch::MMArch, + driver::base::block::SeekFrom, ipc::shm::ShmFlags, - kerror, libs::align::{check_aligned, page_align_up}, mm::MemoryManagementArch, syscall::Syscall, @@ -15,7 +16,7 @@ use crate::{ use super::{ allocator::page_frame::{PageFrameCount, VirtPageFrame}, ucontext::{AddressSpace, DEFAULT_MMAP_MIN_ADDR}, - verify_area, VirtAddr, VmFlags, + verify_area, MsFlags, VirtAddr, VmFlags, }; bitflags! { @@ -154,6 +155,10 @@ impl From for VmFlags { vm_flags |= VmFlags::VM_SYNC; } + if map_flags.contains(MapFlags::MAP_SHARED) { + vm_flags |= VmFlags::VM_SHARED; + } + vm_flags } } @@ -246,7 +251,7 @@ impl From for ProtFlags { impl Syscall { pub fn brk(new_addr: VirtAddr) -> Result { - // kdebug!("brk: new_addr={:?}", new_addr); + // debug!("brk: new_addr={:?}", new_addr); let address_space = AddressSpace::current()?; let mut address_space = address_space.write(); @@ -296,8 +301,8 @@ impl Syscall { len: usize, prot_flags: usize, map_flags: usize, - _fd: i32, - _offset: usize, + fd: i32, + offset: usize, ) -> Result { let map_flags = MapFlags::from_bits_truncate(map_flags as u64); let prot_flags = ProtFlags::from_bits_truncate(prot_flags as u64); @@ -305,32 +310,43 @@ impl Syscall { if start_vaddr < VirtAddr::new(DEFAULT_MMAP_MIN_ADDR) && map_flags.contains(MapFlags::MAP_FIXED) { - kerror!( + error!( "mmap: MAP_FIXED is not supported for address below {}", DEFAULT_MMAP_MIN_ADDR ); return Err(SystemError::EINVAL); } - // 暂时不支持除匿名页以外的映射 - if !map_flags.contains(MapFlags::MAP_ANONYMOUS) { - kerror!("mmap: not support file mapping"); - return Err(SystemError::ENOSYS); - } // 暂时不支持巨页映射 if map_flags.contains(MapFlags::MAP_HUGETLB) { - kerror!("mmap: not support huge page mapping"); + error!("mmap: not support huge page mapping"); return Err(SystemError::ENOSYS); } let current_address_space = AddressSpace::current()?; - let start_page = current_address_space.write().map_anonymous( - start_vaddr, - len, - prot_flags, - map_flags, - true, - true, - )?; + let start_page = if map_flags.contains(MapFlags::MAP_ANONYMOUS) { + // 匿名映射 + current_address_space.write().map_anonymous( + start_vaddr, + len, + prot_flags, + map_flags, + true, + false, + )? + } else { + // 文件映射 + current_address_space.write().file_mapping( + start_vaddr, + len, + prot_flags, + map_flags, + fd, + offset, + true, + false, + )? + }; + return Ok(start_page.virt_address().data()); } @@ -390,11 +406,11 @@ impl Syscall { return Err(SystemError::EINVAL); } let vma = vma.unwrap(); - let vm_flags = *vma.lock().vm_flags(); + let vm_flags = *vma.lock_irqsave().vm_flags(); // 暂时不支持巨页映射 if vm_flags.contains(VmFlags::VM_HUGETLB) { - kerror!("mmap: not support huge page mapping"); + error!("mmap: not support huge page mapping"); return Err(SystemError::ENOSYS); } @@ -524,4 +540,115 @@ impl Syscall { .map_err(|_| SystemError::EINVAL)?; return Ok(0); } + + /// ## msync系统调用 + /// + /// ## 参数 + /// + /// - `start`:起始地址(已经对齐到页) + /// - `len`:长度(已经对齐到页) + /// - `flags`:标志 + pub fn msync(start: VirtAddr, len: usize, flags: usize) -> Result { + if !start.check_aligned(MMArch::PAGE_SIZE) || !check_aligned(len, MMArch::PAGE_SIZE) { + return Err(SystemError::EINVAL); + } + + if unlikely(verify_area(start, len).is_err()) { + return Err(SystemError::EINVAL); + } + if unlikely(len == 0) { + return Err(SystemError::EINVAL); + } + + let mut start = start.data(); + let end = start + len; + let flags = MsFlags::from_bits_truncate(flags); + let mut unmapped_error = Ok(0); + + if !flags.intersects(MsFlags::MS_ASYNC | MsFlags::MS_INVALIDATE | MsFlags::MS_SYNC) { + return Err(SystemError::EINVAL); + } + + if flags.contains(MsFlags::MS_ASYNC | MsFlags::MS_SYNC) { + return Err(SystemError::EINVAL); + } + + if end < start { + return Err(SystemError::ENOMEM); + } + + if start == end { + return Ok(0); + } + + let current_address_space = AddressSpace::current()?; + let mut err = Err(SystemError::ENOMEM); + let mut next_vma = current_address_space + .read() + .mappings + .find_nearest(VirtAddr::new(start)); + loop { + if let Some(vma) = next_vma.clone() { + let guard = vma.lock_irqsave(); + let vm_start = guard.region().start().data(); + let vm_end = guard.region().end().data(); + if start < vm_start { + if flags == MsFlags::MS_ASYNC { + break; + } + start = vm_start; + if start >= vm_end { + break; + } + unmapped_error = Err(SystemError::ENOMEM); + } + let vm_flags = *guard.vm_flags(); + if flags.contains(MsFlags::MS_INVALIDATE) && vm_flags.contains(VmFlags::VM_LOCKED) { + err = Err(SystemError::EBUSY); + break; + } + let file = guard.vm_file(); + let fstart = (start - vm_start) + + (guard.file_page_offset().unwrap_or(0) << MMArch::PAGE_SHIFT); + let fend = fstart + (core::cmp::min(end, vm_end) - start) - 1; + let old_start = start; + start = vm_end; + // log::info!("flags: {:?}", flags); + // log::info!("vm_flags: {:?}", vm_flags); + // log::info!("file: {:?}", file); + if flags.contains(MsFlags::MS_SYNC) && vm_flags.contains(VmFlags::VM_SHARED) { + if let Some(file) = file { + let old_pos = file.lseek(SeekFrom::SeekCurrent(0)).unwrap(); + file.lseek(SeekFrom::SeekSet(fstart as i64)).unwrap(); + err = file.write(len, unsafe { + from_raw_parts(old_start as *mut u8, fend - fstart + 1) + }); + file.lseek(SeekFrom::SeekSet(old_pos as i64)).unwrap(); + if err.is_err() { + break; + } else if start >= end { + err = unmapped_error; + break; + } + next_vma = current_address_space + .read() + .mappings + .find_nearest(VirtAddr::new(start)); + } + } else { + if start >= end { + err = unmapped_error; + break; + } + next_vma = current_address_space + .read() + .mappings + .find_nearest(VirtAddr::new(vm_end)); + } + } else { + return Err(SystemError::ENOMEM); + } + } + return err; + } } diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index 18d8bda4b..6b83bfe61 100644 --- a/kernel/src/mm/ucontext.rs +++ b/kernel/src/mm/ucontext.rs @@ -20,6 +20,7 @@ use system_error::SystemError; use crate::{ arch::{mm::PageMapper, CurrentIrqArch, MMArch}, exception::InterruptArch, + filesystem::vfs::file::File, libs::{ align::page_align_up, rwlock::RwLock, @@ -34,7 +35,7 @@ use super::{ allocator::page_frame::{ deallocate_page_frames, PageFrameCount, PhysPageFrame, VirtPageFrame, VirtPageFrameIter, }, - page::{Flusher, InactiveFlusher, PageFlags, PageFlushAll}, + page::{EntryFlags, Flusher, InactiveFlusher, Page, PageFlushAll}, syscall::{MadvFlags, MapFlags, MremapFlags, ProtFlags}, MemoryManagementArch, PageTableKind, VirtAddr, VirtRegion, VmFlags, }; @@ -143,7 +144,7 @@ impl InnerAddressSpace { end_data: VirtAddr(0), }; if create_stack { - // kdebug!("to create user stack."); + // debug!("to create user stack."); result.new_user_stack(UserStack::DEFAULT_USER_STACK_SIZE)?; } @@ -178,23 +179,23 @@ impl InnerAddressSpace { for vma in self.mappings.vmas.iter() { // TODO: 增加对VMA是否为文件映射的判断,如果是的话,就跳过 - let vma_guard: SpinLockGuard<'_, VMA> = vma.lock(); + let vma_guard: SpinLockGuard<'_, VMA> = vma.lock_irqsave(); // 仅拷贝VMA信息并添加反向映射,因为UserMapper克隆时已经分配了新的物理页 let new_vma = LockedVMA::new(vma_guard.clone_info_only()); new_guard.mappings.vmas.insert(new_vma.clone()); - // kdebug!("new vma: {:x?}", new_vma); - let new_vma_guard = new_vma.lock(); + // debug!("new vma: {:x?}", new_vma); + let new_vma_guard = new_vma.lock_irqsave(); let new_mapper = &new_guard.user_mapper.utable; - let mut anon_vma_guard = page_manager_lock_irqsave(); + let mut page_manager_guard = page_manager_lock_irqsave(); for page in new_vma_guard.pages().map(|p| p.virt_address()) { if let Some((paddr, _)) = new_mapper.translate(page) { - let page = anon_vma_guard.get_mut(&paddr); - page.insert_vma(new_vma.clone()); + let page = page_manager_guard.get_unwrap(&paddr); + page.write_irqsave().insert_vma(new_vma.clone()); } } - drop(anon_vma_guard); + drop(page_manager_guard); drop(vma_guard); drop(new_vma_guard); } @@ -209,7 +210,7 @@ impl InnerAddressSpace { /// - `bytes`: 拓展大小 #[allow(dead_code)] pub fn extend_stack(&mut self, mut bytes: usize) -> Result<(), SystemError> { - // kdebug!("extend user stack"); + // debug!("extend user stack"); let prot_flags = ProtFlags::PROT_READ | ProtFlags::PROT_WRITE | ProtFlags::PROT_EXEC; let map_flags = MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS | MapFlags::MAP_GROWSDOWN; let stack = self.user_stack.as_mut().unwrap(); @@ -259,7 +260,7 @@ impl InnerAddressSpace { let round_hint_to_min = |hint: VirtAddr| { // 先把hint向下对齐到页边界 let addr = hint.data() & (!MMArch::PAGE_OFFSET_MASK); - // kdebug!("map_anonymous: hint = {:?}, addr = {addr:#x}", hint); + // debug!("map_anonymous: hint = {:?}, addr = {addr:#x}", hint); // 如果hint不是0,且hint小于DEFAULT_MMAP_MIN_ADDR,则对齐到DEFAULT_MMAP_MIN_ADDR if (addr != 0) && round_to_min && (addr < DEFAULT_MMAP_MIN_ADDR) { Some(VirtAddr::new(page_align_up(DEFAULT_MMAP_MIN_ADDR))) @@ -269,8 +270,8 @@ impl InnerAddressSpace { Some(VirtAddr::new(addr)) } }; - // kdebug!("map_anonymous: start_vaddr = {:?}", start_vaddr); - // kdebug!("map_anonymous: len(no align) = {}", len); + // debug!("map_anonymous: start_vaddr = {:?}", start_vaddr); + // debug!("map_anonymous: len(no align) = {}", len); let len = page_align_up(len); @@ -280,35 +281,137 @@ impl InnerAddressSpace { | VmFlags::VM_MAYWRITE | VmFlags::VM_MAYEXEC; - // kdebug!("map_anonymous: len = {}", len); - - let start_page: VirtPageFrame = if allocate_at_once { - self.mmap( - round_hint_to_min(start_vaddr), - PageFrameCount::from_bytes(len).unwrap(), - prot_flags, - map_flags, - move |page, count, flags, mapper, flusher| { - VMA::zeroed(page, count, vm_flags, flags, mapper, flusher) - }, - )? - } else { - self.mmap( - round_hint_to_min(start_vaddr), - PageFrameCount::from_bytes(len).unwrap(), - prot_flags, - map_flags, - move |page, count, flags, _mapper, _flusher| { + // debug!("map_anonymous: len = {}", len); + + let start_page: VirtPageFrame = self.mmap( + round_hint_to_min(start_vaddr), + PageFrameCount::from_bytes(len).unwrap(), + prot_flags, + map_flags, + move |page, count, flags, mapper, flusher| { + if allocate_at_once { + VMA::zeroed(page, count, vm_flags, flags, mapper, flusher, None, None) + } else { Ok(LockedVMA::new(VMA::new( VirtRegion::new(page.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, flags, + None, + None, false, ))) - }, - )? + } + }, + )?; + + return Ok(start_page); + } + + /// 进行文件页映射 + /// + /// ## 参数 + /// + /// - `start_vaddr`:映射的起始地址 + /// - `len`:映射的长度 + /// - `prot_flags`:保护标志 + /// - `map_flags`:映射标志 + /// - `fd`:文件描述符 + /// - `offset`:映射偏移量 + /// - `round_to_min`:是否将`start_vaddr`对齐到`mmap_min`,如果为`true`,则当`start_vaddr`不为0时,会对齐到`mmap_min`,否则仅向下对齐到页边界 + /// - `allocate_at_once`:是否立即分配物理空间 + /// + /// ## 返回 + /// + /// 返回映射的起始虚拟页帧 + #[allow(clippy::too_many_arguments)] + pub fn file_mapping( + &mut self, + start_vaddr: VirtAddr, + len: usize, + prot_flags: ProtFlags, + map_flags: MapFlags, + fd: i32, + offset: usize, + round_to_min: bool, + allocate_at_once: bool, + ) -> Result { + let allocate_at_once = if MMArch::PAGE_FAULT_ENABLED { + allocate_at_once + } else { + true }; + // 用于对齐hint的函数 + let round_hint_to_min = |hint: VirtAddr| { + // 先把hint向下对齐到页边界 + let addr = hint.data() & (!MMArch::PAGE_OFFSET_MASK); + // debug!("map_anonymous: hint = {:?}, addr = {addr:#x}", hint); + // 如果hint不是0,且hint小于DEFAULT_MMAP_MIN_ADDR,则对齐到DEFAULT_MMAP_MIN_ADDR + if (addr != 0) && round_to_min && (addr < DEFAULT_MMAP_MIN_ADDR) { + Some(VirtAddr::new(page_align_up(DEFAULT_MMAP_MIN_ADDR))) + } else if addr == 0 { + None + } else { + Some(VirtAddr::new(addr)) + } + }; + // debug!("map_anonymous: start_vaddr = {:?}", start_vaddr); + // debug!("map_anonymous: len(no align) = {}", len); + + let len = page_align_up(len); + + let vm_flags = VmFlags::from(prot_flags) + | VmFlags::from(map_flags) + | VmFlags::VM_MAYREAD + | VmFlags::VM_MAYWRITE + | VmFlags::VM_MAYEXEC; + + // debug!("map_anonymous: len = {}", len); + let binding = ProcessManager::current_pcb().fd_table(); + let fd_table_guard = binding.read(); + + let file = fd_table_guard.get_file_by_fd(fd); + if file.is_none() { + return Err(SystemError::EBADF); + } + // drop guard 以避免无法调度的问题 + drop(fd_table_guard); + + // offset需要4K对齐 + if !offset & (MMArch::PAGE_SIZE - 1) == 0 { + return Err(SystemError::EINVAL); + } + let pgoff = offset >> MMArch::PAGE_SHIFT; + + let start_page: VirtPageFrame = self.mmap( + round_hint_to_min(start_vaddr), + PageFrameCount::from_bytes(len).unwrap(), + prot_flags, + map_flags, + move |page, count, flags, mapper, flusher| { + if allocate_at_once { + VMA::zeroed( + page, + count, + vm_flags, + flags, + mapper, + flusher, + file, + Some(pgoff), + ) + } else { + Ok(LockedVMA::new(VMA::new( + VirtRegion::new(page.virt_address(), count.data() * MMArch::PAGE_SIZE), + vm_flags, + flags, + file, + Some(pgoff), + false, + ))) + } + }, + )?; return Ok(start_page); } @@ -333,7 +436,7 @@ impl InnerAddressSpace { F: FnOnce( VirtPageFrame, PageFrameCount, - PageFlags, + EntryFlags, &mut PageMapper, &mut dyn Flusher, ) -> Result, SystemError>, @@ -348,7 +451,7 @@ impl InnerAddressSpace { if page_count == PageFrameCount::new(0) { return Err(SystemError::EINVAL); } - // kdebug!("mmap: addr: {addr:?}, page_count: {page_count:?}, prot_flags: {prot_flags:?}, map_flags: {map_flags:?}"); + // debug!("mmap: addr: {addr:?}, page_count: {page_count:?}, prot_flags: {prot_flags:?}, map_flags: {map_flags:?}"); // 找到未使用的区域 let region = match addr { @@ -363,7 +466,7 @@ impl InnerAddressSpace { let page = VirtPageFrame::new(region.start()); - // kdebug!("mmap: page: {:?}, region={region:?}", page.virt_address()); + // debug!("mmap: page: {:?}, region={region:?}", page.virt_address()); compiler_fence(Ordering::SeqCst); let (mut active, mut inactive); @@ -379,7 +482,7 @@ impl InnerAddressSpace { self.mappings.insert_vma(map_func( page, page_count, - PageFlags::from_prot_flags(prot_flags, true), + EntryFlags::from_prot_flags(prot_flags, true), &mut self.user_mapper.utable, flusher, )?); @@ -478,9 +581,9 @@ impl InnerAddressSpace { let regions: Vec> = self.mappings.conflicts(to_unmap).collect::>(); for r in regions { - let r = r.lock().region; + let r = r.lock_irqsave().region; let r = self.mappings.remove_vma(&r).unwrap(); - let intersection = r.lock().region().intersect(&to_unmap).unwrap(); + let intersection = r.lock_irqsave().region().intersect(&to_unmap).unwrap(); let split_result = r.extract(intersection, &self.user_mapper.utable).unwrap(); // TODO: 当引入后备页映射后,这里需要增加通知文件的逻辑 @@ -509,13 +612,13 @@ impl InnerAddressSpace { page_count: PageFrameCount, prot_flags: ProtFlags, ) -> Result<(), SystemError> { - // kdebug!( + // debug!( // "mprotect: start_page: {:?}, page_count: {:?}, prot_flags:{prot_flags:?}", // start_page, // page_count // ); let (mut active, mut inactive); - let mut flusher = if self.is_current() { + let flusher = if self.is_current() { active = PageFlushAll::new(); &mut active as &mut dyn Flusher } else { @@ -525,17 +628,17 @@ impl InnerAddressSpace { let mapper = &mut self.user_mapper.utable; let region = VirtRegion::new(start_page.virt_address(), page_count.bytes()); - // kdebug!("mprotect: region: {:?}", region); + // debug!("mprotect: region: {:?}", region); let regions = self.mappings.conflicts(region).collect::>(); - // kdebug!("mprotect: regions: {:?}", regions); + // debug!("mprotect: regions: {:?}", regions); for r in regions { - // kdebug!("mprotect: r: {:?}", r); - let r = *r.lock().region(); + // debug!("mprotect: r: {:?}", r); + let r = *r.lock_irqsave().region(); let r = self.mappings.remove_vma(&r).unwrap(); - let intersection = r.lock().region().intersect(®ion).unwrap(); + let intersection = r.lock_irqsave().region().intersect(®ion).unwrap(); let split_result = r .extract(intersection, mapper) .expect("Failed to extract VMA"); @@ -547,20 +650,21 @@ impl InnerAddressSpace { self.mappings.insert_vma(after); } - let mut r_guard = r.lock(); + let mut r_guard = r.lock_irqsave(); // 如果VMA的保护标志不允许指定的修改,则返回错误 if !r_guard.can_have_flags(prot_flags) { drop(r_guard); self.mappings.insert_vma(r.clone()); return Err(SystemError::EACCES); } + r_guard.set_vm_flags(VmFlags::from(prot_flags)); - let new_flags: PageFlags = r_guard + let new_flags: EntryFlags = r_guard .flags() .set_execute(prot_flags.contains(ProtFlags::PROT_EXEC)) .set_write(prot_flags.contains(ProtFlags::PROT_WRITE)); - r_guard.remap(new_flags, mapper, &mut flusher)?; + r_guard.remap(new_flags, mapper, &mut *flusher)?; drop(r_guard); self.mappings.insert_vma(r); } @@ -575,7 +679,7 @@ impl InnerAddressSpace { behavior: MadvFlags, ) -> Result<(), SystemError> { let (mut active, mut inactive); - let mut flusher = if self.is_current() { + let flusher = if self.is_current() { active = PageFlushAll::new(); &mut active as &mut dyn Flusher } else { @@ -589,10 +693,10 @@ impl InnerAddressSpace { let regions = self.mappings.conflicts(region).collect::>(); for r in regions { - let r = *r.lock().region(); + let r = *r.lock_irqsave().region(); let r = self.mappings.remove_vma(&r).unwrap(); - let intersection = r.lock().region().intersect(®ion).unwrap(); + let intersection = r.lock_irqsave().region().intersect(®ion).unwrap(); let split_result = r .extract(intersection, mapper) .expect("Failed to extract VMA"); @@ -603,7 +707,7 @@ impl InnerAddressSpace { if let Some(after) = split_result.after { self.mappings.insert_vma(after); } - r.do_madvise(behavior, mapper, &mut flusher)?; + r.do_madvise(behavior, mapper, &mut *flusher)?; self.mappings.insert_vma(r); } Ok(()) @@ -815,7 +919,7 @@ impl UserMappings { #[allow(dead_code)] pub fn contains(&self, vaddr: VirtAddr) -> Option> { for v in self.vmas.iter() { - let guard = v.lock(); + let guard = v.lock_irqsave(); if guard.region.contains(vaddr) { return Some(v.clone()); } @@ -835,13 +939,13 @@ impl UserMappings { pub fn find_nearest(&self, vaddr: VirtAddr) -> Option> { let mut nearest: Option> = None; for v in self.vmas.iter() { - let guard = v.lock(); + let guard = v.lock_irqsave(); if guard.region.contains(vaddr) { return Some(v.clone()); } - if guard.region.start > vaddr + if guard.region.start >= vaddr && if let Some(ref nearest) = nearest { - guard.region.start < nearest.lock().region.start + guard.region.start < nearest.lock_irqsave().region.start } else { true } @@ -857,7 +961,7 @@ impl UserMappings { let r = self .vmas .iter() - .filter(move |v| v.lock().region.intersect(&request).is_some()) + .filter(move |v| v.lock_irqsave().region.intersect(&request).is_some()) .cloned(); return r; } @@ -940,7 +1044,7 @@ impl UserMappings { /// 在当前进程的映射关系中,插入一个新的VMA。 pub fn insert_vma(&mut self, vma: Arc) { - let region = vma.lock().region; + let region = vma.lock_irqsave().region; // 要求插入的地址范围必须是空闲的,也就是说,当前进程的地址空间中,不能有任何与之重叠的VMA。 assert!(self.conflicts(region).next().is_none()); self.reserve_hole(®ion); @@ -960,7 +1064,7 @@ impl UserMappings { // 请注意,由于这里会对每个VMA加锁,因此性能很低 let vma: Arc = self .vmas - .drain_filter(|vma| vma.lock().region == *region) + .drain_filter(|vma| vma.lock_irqsave().region == *region) .next()?; self.unreserve_hole(region); @@ -1010,7 +1114,7 @@ impl LockedVMA { id: LOCKEDVMA_ID_ALLOCATOR.alloc().unwrap(), vma: SpinLock::new(vma), }); - r.vma.lock().self_ref = Arc::downgrade(&r); + r.vma.lock_irqsave().self_ref = Arc::downgrade(&r); return r; } @@ -1022,6 +1126,10 @@ impl LockedVMA { return self.vma.lock(); } + pub fn lock_irqsave(&self) -> SpinLockGuard { + return self.vma.lock_irqsave(); + } + /// 调整当前VMA的页面的标志位 /// /// TODO:增加调整虚拟页映射的物理地址的功能 @@ -1032,11 +1140,11 @@ impl LockedVMA { /// pub fn remap( &self, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { - let mut guard = self.lock(); + let mut guard = self.lock_irqsave(); for page in guard.region.pages() { // 暂时要求所有的页帧都已经映射到页表 // TODO: 引入Lazy Mapping, 通过缺页中断来映射页帧,这里就不必要求所有的页帧都已经映射到页表了 @@ -1054,7 +1162,7 @@ impl LockedVMA { pub fn unmap(&self, mapper: &mut PageMapper, mut flusher: impl Flusher) { // todo: 如果当前vma与文件相关,完善文件相关的逻辑 - let mut guard = self.lock(); + let mut guard = self.lock_irqsave(); // 获取物理页的anon_vma的守卫 let mut page_manager_guard: SpinLockGuard<'_, crate::mm::page::PageManager> = @@ -1067,12 +1175,13 @@ impl LockedVMA { .expect("Failed to unmap, beacuse of some page is not mapped"); // 从anon_vma中删除当前VMA - let page = page_manager_guard.get_mut(&paddr); - page.remove_vma(self); + let page = page_manager_guard.get_unwrap(&paddr); + page.write_irqsave().remove_vma(self); // 如果物理页的anon_vma链表长度为0并且不是共享页,则释放物理页. - if page.can_deallocate() { + if page.read_irqsave().can_deallocate() { unsafe { + drop(page); deallocate_page_frames( PhysPageFrame::new(paddr), PageFrameCount::new(1), @@ -1084,10 +1193,19 @@ impl LockedVMA { flusher.consume(flush); } guard.mapped = false; + + // 当vma对应共享文件的写映射时,唤醒脏页回写线程 + if guard.vm_file().is_some() + && guard + .vm_flags() + .contains(VmFlags::VM_SHARED | VmFlags::VM_WRITE) + { + crate::mm::page::PageReclaimer::wakeup_claim_thread(); + } } pub fn mapped(&self) -> bool { - return self.vma.lock().mapped; + return self.vma.lock_irqsave().mapped; } /// 将当前VMA进行切分,切分成3个VMA,分别是: @@ -1099,7 +1217,7 @@ impl LockedVMA { assert!(region.start().check_aligned(MMArch::PAGE_SIZE)); assert!(region.end().check_aligned(MMArch::PAGE_SIZE)); - let mut guard = self.lock(); + let mut guard = self.lock_irqsave(); { // 如果传入的region不在当前VMA的范围内,则直接返回None if unlikely(region.start() < guard.region.start() || region.end() > guard.region.end()) @@ -1126,7 +1244,7 @@ impl LockedVMA { let before: Option> = guard.region.before(®ion).map(|virt_region| { let mut vma: VMA = unsafe { guard.clone() }; vma.region = virt_region; - + vma.mapped = false; let vma: Arc = LockedVMA::new(vma); vma }); @@ -1134,7 +1252,7 @@ impl LockedVMA { let after: Option> = guard.region.after(®ion).map(|virt_region| { let mut vma: VMA = unsafe { guard.clone() }; vma.region = virt_region; - + vma.mapped = false; let vma: Arc = LockedVMA::new(vma); vma }); @@ -1142,27 +1260,27 @@ impl LockedVMA { // 重新设置before、after这两个VMA里面的物理页的anon_vma let mut page_manager_guard = page_manager_lock_irqsave(); if let Some(before) = before.clone() { - let before_guard = before.lock(); - if before_guard.mapped { - let virt_iter = before_guard.region.iter_pages(); - for frame in virt_iter { - let paddr = utable.translate(frame.virt_address()).unwrap().0; - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(before.clone()); - page.remove_vma(self); + let virt_iter = before.lock_irqsave().region.iter_pages(); + for frame in virt_iter { + if let Some((paddr, _)) = utable.translate(frame.virt_address()) { + let page = page_manager_guard.get_unwrap(&paddr); + let mut page_guard = page.write_irqsave(); + page_guard.insert_vma(before.clone()); + page_guard.remove_vma(self); + before.lock_irqsave().mapped = true; } } } if let Some(after) = after.clone() { - let after_guard = after.lock(); - if after_guard.mapped { - let virt_iter = after_guard.region.iter_pages(); - for frame in virt_iter { - let paddr = utable.translate(frame.virt_address()).unwrap().0; - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(after.clone()); - page.remove_vma(self); + let virt_iter = after.lock_irqsave().region.iter_pages(); + for frame in virt_iter { + if let Some((paddr, _)) = utable.translate(frame.virt_address()) { + let page = page_manager_guard.get_unwrap(&paddr); + let mut page_guard = page.write_irqsave(); + page_guard.insert_vma(after.clone()); + page_guard.remove_vma(self); + after.lock_irqsave().mapped = true; } } } @@ -1178,7 +1296,7 @@ impl LockedVMA { /// 判断VMA是否为外部(非当前进程空间)的VMA pub fn is_foreign(&self) -> bool { - let guard = self.lock(); + let guard = self.lock_irqsave(); if let Some(space) = guard.user_address_space.clone() { if let Some(space) = space.upgrade() { return AddressSpace::is_current(&space); @@ -1192,15 +1310,15 @@ impl LockedVMA { /// 判断VMA是否可访问 pub fn is_accessible(&self) -> bool { - let guard = self.lock(); + let guard = self.lock_irqsave(); let vm_access_flags: VmFlags = VmFlags::VM_READ | VmFlags::VM_WRITE | VmFlags::VM_EXEC; guard.vm_flags().intersects(vm_access_flags) } /// 判断VMA是否为匿名映射 pub fn is_anonymous(&self) -> bool { - //TODO: 实现匿名映射判断逻辑,目前仅支持匿名映射 - true + let guard = self.lock_irqsave(); + guard.vm_file.is_none() } /// 判断VMA是否为大页映射 @@ -1217,6 +1335,7 @@ impl Drop for LockedVMA { } /// VMA切分结果 +#[allow(dead_code)] pub struct VMASplitResult { pub prev: Option>, pub middle: Arc, @@ -1245,13 +1364,17 @@ pub struct VMA { /// 虚拟内存区域标志 vm_flags: VmFlags, /// VMA内的页帧的标志 - flags: PageFlags, + flags: EntryFlags, /// VMA内的页帧是否已经映射到页表 mapped: bool, /// VMA所属的用户地址空间 user_address_space: Option>, self_ref: Weak, + vm_file: Option>, + /// VMA映射的文件部分相对于整个文件的偏移页数 + file_pgoff: Option, + provider: Provider, } @@ -1274,7 +1397,9 @@ impl VMA { pub fn new( region: VirtRegion, vm_flags: VmFlags, - flags: PageFlags, + flags: EntryFlags, + file: Option>, + pgoff: Option, mapped: bool, ) -> Self { VMA { @@ -1285,6 +1410,8 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, + vm_file: file, + file_pgoff: pgoff, } } @@ -1296,6 +1423,14 @@ impl VMA { return &self.vm_flags; } + pub fn vm_file(&self) -> Option> { + return self.vm_file.clone(); + } + + pub fn address_space(&self) -> Option> { + return self.user_address_space.clone(); + } + pub fn set_vm_flags(&mut self, vm_flags: VmFlags) { self.vm_flags = vm_flags; } @@ -1308,6 +1443,10 @@ impl VMA { self.mapped = mapped; } + pub fn set_flags(&mut self) { + self.flags = MMArch::vm_get_page_prot(self.vm_flags); + } + /// # 拷贝当前VMA的内容 /// /// ### 安全性 @@ -1322,6 +1461,8 @@ impl VMA { user_address_space: self.user_address_space.clone(), self_ref: self.self_ref.clone(), provider: Provider::Allocated, + file_pgoff: self.file_pgoff, + vm_file: self.vm_file.clone(), }; } @@ -1334,14 +1475,21 @@ impl VMA { user_address_space: None, self_ref: Weak::default(), provider: Provider::Allocated, + file_pgoff: self.file_pgoff, + vm_file: self.vm_file.clone(), }; } #[inline(always)] - pub fn flags(&self) -> PageFlags { + pub fn flags(&self) -> EntryFlags { return self.flags; } + #[inline(always)] + pub fn file_page_offset(&self) -> Option { + return self.file_pgoff; + } + pub fn pages(&self) -> VirtPageFrameIter { return VirtPageFrameIter::new( VirtPageFrame::new(self.region.start()), @@ -1351,12 +1499,12 @@ impl VMA { pub fn remap( &mut self, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result<(), SystemError> { for page in self.region.pages() { - // kdebug!("remap page {:?}", page.virt_address()); + // debug!("remap page {:?}", page.virt_address()); if mapper.translate(page.virt_address()).is_some() { let r = unsafe { mapper @@ -1365,8 +1513,8 @@ impl VMA { }; flusher.consume(r); } - // kdebug!("consume page {:?}", page.virt_address()); - // kdebug!("remap page {:?} done", page.virt_address()); + // debug!("consume page {:?}", page.virt_address()); + // debug!("remap page {:?} done", page.virt_address()); } self.flags = flags; return Ok(()); @@ -1404,7 +1552,7 @@ impl VMA { destination: VirtPageFrame, count: PageFrameCount, vm_flags: VmFlags, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result, SystemError> { @@ -1426,23 +1574,22 @@ impl VMA { cur_dest = cur_dest.next(); } - let r: Arc = LockedVMA::new(VMA { - region: VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), + let r: Arc = LockedVMA::new(VMA::new( + VirtRegion::new(destination.virt_address(), count.data() * MMArch::PAGE_SIZE), vm_flags, flags, - mapped: true, - user_address_space: None, - self_ref: Weak::default(), - provider: Provider::Allocated, - }); + None, + None, + true, + )); // 将VMA加入到anon_vma中 let mut page_manager_guard = page_manager_lock_irqsave(); cur_phy = phys; for _ in 0..count.data() { let paddr = cur_phy.phys_address(); - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(r.clone()); + let page = page_manager_guard.get_unwrap(&paddr); + page.write_irqsave().insert_vma(r.clone()); cur_phy = cur_phy.next(); } @@ -1450,29 +1597,37 @@ impl VMA { } /// 从页分配器中分配一些物理页,并把它们映射到指定的虚拟地址,然后创建VMA + /// ## 参数 /// - /// @param destination 要映射到的虚拟地址 - /// @param count 要映射的页帧数量 - /// @param flags 页面标志位 - /// @param mapper 页表映射器 - /// @param flusher 页表项刷新器 + /// - `destination`: 要映射到的虚拟地址 + /// - `page_count`: 要映射的页帧数量 + /// - `vm_flags`: VMA标志位 + /// - `flags`: 页面标志位 + /// - `mapper`: 页表映射器 + /// - `flusher`: 页表项刷新器 + /// - `file`: 映射文件 + /// - `pgoff`: 返回映射后的虚拟内存区域 /// - /// @return 返回映射后的虚拟内存区域 + /// ## 返回值 + /// - 页面错误处理信息标志 + #[allow(clippy::too_many_arguments)] pub fn zeroed( destination: VirtPageFrame, page_count: PageFrameCount, vm_flags: VmFlags, - flags: PageFlags, + flags: EntryFlags, mapper: &mut PageMapper, mut flusher: impl Flusher, + file: Option>, + pgoff: Option, ) -> Result, SystemError> { let mut cur_dest: VirtPageFrame = destination; - // kdebug!( + // debug!( // "VMA::zeroed: page_count = {:?}, destination={destination:?}", // page_count // ); for _ in 0..page_count.data() { - // kdebug!( + // debug!( // "VMA::zeroed: cur_dest={cur_dest:?}, vaddr = {:?}", // cur_dest.virt_address() // ); @@ -1491,10 +1646,12 @@ impl VMA { ), vm_flags, flags, + file, + pgoff, true, )); drop(flusher); - // kdebug!("VMA::zeroed: flusher dropped"); + // debug!("VMA::zeroed: flusher dropped"); // 清空这些内存并将VMA加入到anon_vma中 let mut page_manager_guard = page_manager_lock_irqsave(); @@ -1504,17 +1661,24 @@ impl VMA { let paddr = mapper.translate(frame.virt_address()).unwrap().0; // 将VMA加入到anon_vma - let page = page_manager_guard.get_mut(&paddr); - page.insert_vma(r.clone()); + let page = page_manager_guard.get_unwrap(&paddr); + page.write_irqsave().insert_vma(r.clone()); + } + // debug!("VMA::zeroed: done"); + return Ok(r); + } - // 清空内存 - unsafe { - let vaddr = MMArch::phys_2_virt(paddr).unwrap(); - MMArch::write_bytes(vaddr, 0, MMArch::PAGE_SIZE); + pub fn page_address(&self, page: &Arc) -> Result { + let page_guard = page.read_irqsave(); + let index = page_guard.index().unwrap(); + if index >= self.file_pgoff.unwrap() { + let address = + self.region.start + ((index - self.file_pgoff.unwrap()) << MMArch::PAGE_SHIFT); + if address <= self.region.end() { + return Ok(address); } } - // kdebug!("VMA::zeroed: done"); - return Ok(r); + return Err(SystemError::EFAULT); } } @@ -1581,7 +1745,7 @@ impl UserStack { | MapFlags::MAP_ANONYMOUS | MapFlags::MAP_FIXED_NOREPLACE | MapFlags::MAP_GROWSDOWN; - // kdebug!( + // debug!( // "map anonymous stack: {:?} {}", // actual_stack_bottom, // guard_size @@ -1597,7 +1761,7 @@ impl UserStack { // test_buddy(); // 设置保护页只读 prot_flags.remove(ProtFlags::PROT_WRITE); - // kdebug!( + // debug!( // "to mprotect stack guard pages: {:?} {}", // actual_stack_bottom, // guard_size @@ -1608,7 +1772,7 @@ impl UserStack { prot_flags, )?; - // kdebug!( + // debug!( // "mprotect stack guard pages done: {:?} {}", // actual_stack_bottom, // guard_size @@ -1620,10 +1784,10 @@ impl UserStack { current_sp: actual_stack_bottom - guard_size, }; - // kdebug!("extend user stack: {:?} {}", stack_bottom, stack_size); + // debug!("extend user stack: {:?} {}", stack_bottom, stack_size); // 分配用户栈 user_stack.initial_extend(vm, stack_size)?; - // kdebug!("user stack created: {:?} {}", stack_bottom, stack_size); + // debug!("user stack created: {:?} {}", stack_bottom, stack_size); return Ok(user_stack); } diff --git a/kernel/src/net/event_poll/mod.rs b/kernel/src/net/event_poll/mod.rs index 0691278ba..a23209297 100644 --- a/kernel/src/net/event_poll/mod.rs +++ b/kernel/src/net/event_poll/mod.rs @@ -1,4 +1,5 @@ use core::{ + any::Any, fmt::Debug, sync::atomic::{AtomicBool, Ordering}, }; @@ -8,6 +9,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use intertrait::CastFromSync; use system_error::SystemError; use crate::{ @@ -130,6 +132,10 @@ impl EPollItem { } } +pub trait KernelIoctlData: Send + Sync + Any + Debug + CastFromSync {} + +impl KernelIoctlData for EPollItem {} + /// ### Epoll文件的私有信息 #[derive(Debug, Clone)] pub struct EPollPrivateData { @@ -430,6 +436,7 @@ impl EventPoll { } // 判断epoll上有没有就绪事件 let mut available = epoll_guard.ep_events_available(); + drop(epoll_guard); loop { if available { @@ -566,7 +573,7 @@ impl EventPoll { // 记数加一 res += 1; - // crate::kdebug!("ep send {event:?}"); + // crate::debug!("ep send {event:?}"); if ep_events.contains(EPollEventType::EPOLLONESHOT) { let mut event_writer = epitem.event.write(); @@ -753,6 +760,7 @@ impl EventPoll { /// 与C兼容的Epoll事件结构体 #[derive(Copy, Clone, Default)] #[repr(packed)] +#[repr(C)] pub struct EPollEvent { /// 表示触发的事件 events: u32, @@ -864,5 +872,8 @@ bitflags! { /// 表示epoll已经被释放,但是在目前的设计中未用到 const POLLFREE = 0x4000; + + /// listen状态的socket可以接受连接 + const EPOLL_LISTEN_CAN_ACCEPT = Self::EPOLLIN.bits | Self::EPOLLRDNORM.bits; } } diff --git a/kernel/src/net/net_core.rs b/kernel/src/net/net_core.rs index eb7efe7b6..da1486da5 100644 --- a/kernel/src/net/net_core.rs +++ b/kernel/src/net/net_core.rs @@ -1,10 +1,10 @@ use alloc::{boxed::Box, collections::BTreeMap, sync::Arc}; +use log::{debug, info, warn}; use smoltcp::{socket::dhcpv4, wire}; use system_error::SystemError; use crate::{ driver::net::NetDevice, - kdebug, kinfo, kwarn, libs::rwlock::RwLockReadGuard, net::{socket::SocketPollMethod, NET_DEVICES}, time::timer::{next_n_ms_timer_jiffies, Timer, TimerFunction}, @@ -19,6 +19,7 @@ use super::{ /// /// The main purpose of this function is to poll all network interfaces. #[derive(Debug)] +#[allow(dead_code)] struct NetWorkPollFunc; impl TimerFunction for NetWorkPollFunc { @@ -43,7 +44,9 @@ pub fn net_init() -> Result<(), SystemError> { fn dhcp_query() -> Result<(), SystemError> { let binding = NET_DEVICES.write_irqsave(); - let net_face = binding.get(&0).ok_or(SystemError::ENODEV)?.clone(); + //由于现在os未实现在用户态为网卡动态分配内存,而lo网卡的id最先分配且ip固定不能被分配 + //所以特判取用id为1的网卡(也就是virto_net) + let net_face = binding.get(&1).ok_or(SystemError::ENODEV)?.clone(); drop(binding); @@ -60,7 +63,7 @@ fn dhcp_query() -> Result<(), SystemError> { const DHCP_TRY_ROUND: u8 = 10; for i in 0..DHCP_TRY_ROUND { - kdebug!("DHCP try round: {}", i); + debug!("DHCP try round: {}", i); net_face.poll(&mut SOCKET_SET.lock_irqsave()).ok(); let mut binding = SOCKET_SET.lock_irqsave(); let event = binding.get_mut::(dhcp_handle).poll(); @@ -69,9 +72,9 @@ fn dhcp_query() -> Result<(), SystemError> { None => {} Some(dhcpv4::Event::Configured(config)) => { - // kdebug!("Find Config!! {config:?}"); - // kdebug!("Find ip address: {}", config.address); - // kdebug!("iface.ip_addrs={:?}", net_face.inner_iface.ip_addrs()); + // debug!("Find Config!! {config:?}"); + // debug!("Find ip address: {}", config.address); + // debug!("iface.ip_addrs={:?}", net_face.inner_iface.ip_addrs()); net_face .update_ip_addrs(&[wire::IpCidr::Ipv4(config.address)]) @@ -86,7 +89,7 @@ fn dhcp_query() -> Result<(), SystemError> { .unwrap(); let cidr = net_face.inner_iface().lock().ip_addrs().first().cloned(); if let Some(cidr) = cidr { - kinfo!("Successfully allocated ip by Dhcpv4! Ip:{}", cidr); + info!("Successfully allocated ip by Dhcpv4! Ip:{}", cidr); return Ok(()); } } else { @@ -99,7 +102,7 @@ fn dhcp_query() -> Result<(), SystemError> { } Some(dhcpv4::Event::Deconfigured) => { - kdebug!("Dhcp v4 deconfigured"); + debug!("Dhcp v4 deconfigured"); net_face .update_ip_addrs(&[smoltcp::wire::IpCidr::Ipv4(wire::Ipv4Cidr::new( wire::Ipv4Address::UNSPECIFIED, @@ -121,7 +124,7 @@ fn dhcp_query() -> Result<(), SystemError> { pub fn poll_ifaces() { let guard: RwLockReadGuard>> = NET_DEVICES.read_irqsave(); if guard.len() == 0 { - kwarn!("poll_ifaces: No net driver found!"); + warn!("poll_ifaces: No net driver found!"); return; } let mut sockets = SOCKET_SET.lock_irqsave(); @@ -142,7 +145,7 @@ pub fn poll_ifaces_try_lock(times: u16) -> Result<(), SystemError> { let guard: RwLockReadGuard>> = NET_DEVICES.read_irqsave(); if guard.len() == 0 { - kwarn!("poll_ifaces: No net driver found!"); + warn!("poll_ifaces: No net driver found!"); // 没有网卡,返回错误 return Err(SystemError::ENODEV); } @@ -172,7 +175,7 @@ pub fn poll_ifaces_try_lock(times: u16) -> Result<(), SystemError> { pub fn poll_ifaces_try_lock_onetime() -> Result<(), SystemError> { let guard: RwLockReadGuard>> = NET_DEVICES.read_irqsave(); if guard.len() == 0 { - kwarn!("poll_ifaces: No net driver found!"); + warn!("poll_ifaces: No net driver found!"); // 没有网卡,返回错误 return Err(SystemError::ENODEV); } @@ -189,25 +192,25 @@ fn send_event(sockets: &smoltcp::iface::SocketSet) -> Result<(), SystemError> { for (handle, socket_type) in sockets.iter() { let handle_guard = HANDLE_MAP.read_irqsave(); let global_handle = GlobalSocketHandle::new_smoltcp_handle(handle); - let item = handle_guard.get(&global_handle); + let item: Option<&super::socket::SocketHandleItem> = handle_guard.get(&global_handle); if item.is_none() { continue; } let handle_item = item.unwrap(); + let posix_item = handle_item.posix_item(); + if posix_item.is_none() { + continue; + } + let posix_item = posix_item.unwrap(); // 获取socket上的事件 - let mut events = - SocketPollMethod::poll(socket_type, handle_item.shutdown_type()).bits() as u64; + let mut events = SocketPollMethod::poll(socket_type, handle_item).bits() as u64; // 分发到相应类型socket处理 match socket_type { smoltcp::socket::Socket::Raw(_) | smoltcp::socket::Socket::Udp(_) => { - handle_guard - .get(&global_handle) - .unwrap() - .wait_queue - .wakeup_any(events); + posix_item.wakeup_any(events); } smoltcp::socket::Socket::Icmp(_) => unimplemented!("Icmp socket hasn't unimplemented"), smoltcp::socket::Socket::Tcp(inner_socket) => { @@ -217,23 +220,21 @@ fn send_event(sockets: &smoltcp::iface::SocketSet) -> Result<(), SystemError> { if inner_socket.state() == smoltcp::socket::tcp::State::Established { events |= TcpSocket::CAN_CONNECT; } - handle_guard - .get(&global_handle) - .unwrap() - .wait_queue - .wakeup_any(events); + if inner_socket.state() == smoltcp::socket::tcp::State::CloseWait { + events |= EPollEventType::EPOLLHUP.bits() as u64; + } + + posix_item.wakeup_any(events); } smoltcp::socket::Socket::Dhcpv4(_) => {} smoltcp::socket::Socket::Dns(_) => unimplemented!("Dns socket hasn't unimplemented"), } - drop(handle_guard); - let mut handle_guard = HANDLE_MAP.write_irqsave(); - let handle_item = handle_guard.get_mut(&global_handle).unwrap(); EventPoll::wakeup_epoll( - &handle_item.epitems, + &posix_item.epitems, EPollEventType::from_bits_truncate(events as u32), )?; - // crate::kdebug!( + drop(handle_guard); + // crate::debug!( // "{} send_event {:?}", // handle, // EPollEventType::from_bits_truncate(events as u32) diff --git a/kernel/src/net/socket/inet.rs b/kernel/src/net/socket/inet.rs index 6c4580a6a..a7cb975cf 100644 --- a/kernel/src/net/socket/inet.rs +++ b/kernel/src/net/socket/inet.rs @@ -1,14 +1,13 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use log::{error, warn}; use smoltcp::{ - socket::{raw, tcp, udp, AnySocket}, + socket::{raw, tcp, udp}, wire, }; use system_error::SystemError; use crate::{ - arch::rand::rand, driver::net::NetDevice, - kerror, kwarn, libs::rwlock::RwLock, net::{ event_poll::EPollEventType, net_core::poll_ifaces, Endpoint, Protocol, ShutdownType, @@ -17,8 +16,8 @@ use crate::{ }; use super::{ - handle::GlobalSocketHandle, Socket, SocketHandleItem, SocketMetadata, SocketOptions, - SocketPollMethod, SocketType, HANDLE_MAP, PORT_MANAGER, SOCKET_SET, + handle::GlobalSocketHandle, PosixSocketHandleItem, Socket, SocketHandleItem, SocketMetadata, + SocketOptions, SocketPollMethod, SocketType, HANDLE_MAP, PORT_MANAGER, SOCKET_SET, }; /// @brief 表示原始的socket。原始套接字绕过传输层协议(如 TCP 或 UDP)并提供对网络层协议(如 IP)的直接访问。 @@ -33,6 +32,7 @@ pub struct RawSocket { header_included: bool, /// socket的metadata metadata: SocketMetadata, + posix_item: Arc, } impl RawSocket { @@ -77,18 +77,29 @@ impl RawSocket { options, ); + let posix_item = Arc::new(PosixSocketHandleItem::new(None)); + return Self { handle, header_included: false, metadata, + posix_item, }; } } impl Socket for RawSocket { + fn posix_item(&self) -> Arc { + self.posix_item.clone() + } + fn close(&mut self) { let mut socket_set_guard = SOCKET_SET.lock_irqsave(); - socket_set_guard.remove(self.handle.smoltcp_handle().unwrap()); // 删除的时候,会发送一条FINISH的信息? + if let smoltcp::socket::Socket::Udp(mut sock) = + socket_set_guard.remove(self.handle.smoltcp_handle().unwrap()) + { + sock.close(); + } drop(socket_set_guard); poll_ifaces(); } @@ -120,11 +131,7 @@ impl Socket for RawSocket { } } drop(socket_set_guard); - SocketHandleItem::sleep( - self.socket_handle(), - EPollEventType::EPOLLIN.bits() as u64, - HANDLE_MAP.read_irqsave(), - ); + self.posix_item.sleep(EPollEventType::EPOLLIN.bits() as u64); } } @@ -194,7 +201,7 @@ impl Socket for RawSocket { drop(socket_set_guard); return Ok(len); } else { - kwarn!("Unsupport Ip protocol type!"); + warn!("Unsupport Ip protocol type!"); return Err(SystemError::EINVAL); } } else { @@ -237,6 +244,7 @@ pub struct UdpSocket { pub handle: GlobalSocketHandle, remote_endpoint: Option, // 记录远程endpoint提供给connect(), 应该使用IP地址。 metadata: SocketMetadata, + posix_item: Arc, } impl UdpSocket { @@ -275,10 +283,13 @@ impl UdpSocket { options, ); + let posix_item = Arc::new(PosixSocketHandleItem::new(None)); + return Self { handle, remote_endpoint: None, metadata, + posix_item, }; } @@ -289,7 +300,7 @@ impl UdpSocket { ip.port = PORT_MANAGER.get_ephemeral_port(self.metadata.socket_type)?; } // 检测端口是否已被占用 - PORT_MANAGER.bind_port(self.metadata.socket_type, ip.port, self.clone())?; + PORT_MANAGER.bind_port(self.metadata.socket_type, ip.port)?; let bind_res = if ip.addr.is_unspecified() { socket.bind(ip.port) @@ -308,9 +319,17 @@ impl UdpSocket { } impl Socket for UdpSocket { + fn posix_item(&self) -> Arc { + self.posix_item.clone() + } + fn close(&mut self) { let mut socket_set_guard = SOCKET_SET.lock_irqsave(); - socket_set_guard.remove(self.handle.smoltcp_handle().unwrap()); // 删除的时候,会发送一条FINISH的信息? + if let smoltcp::socket::Socket::Udp(mut sock) = + socket_set_guard.remove(self.handle.smoltcp_handle().unwrap()) + { + sock.close(); + } drop(socket_set_guard); poll_ifaces(); } @@ -318,13 +337,13 @@ impl Socket for UdpSocket { /// @brief 在read函数执行之前,请先bind到本地的指定端口 fn read(&self, buf: &mut [u8]) -> (Result, Endpoint) { loop { - // kdebug!("Wait22 to Read"); + // debug!("Wait22 to Read"); poll_ifaces(); let mut socket_set_guard = SOCKET_SET.lock_irqsave(); let socket = socket_set_guard.get_mut::(self.handle.smoltcp_handle().unwrap()); - // kdebug!("Wait to Read"); + // debug!("Wait to Read"); if socket.can_recv() { if let Ok((size, metadata)) = socket.recv_slice(buf) { @@ -337,16 +356,12 @@ impl Socket for UdpSocket { // return (Err(SystemError::ENOTCONN), Endpoint::Ip(None)); } drop(socket_set_guard); - SocketHandleItem::sleep( - self.socket_handle(), - EPollEventType::EPOLLIN.bits() as u64, - HANDLE_MAP.read_irqsave(), - ); + self.posix_item.sleep(EPollEventType::EPOLLIN.bits() as u64); } } fn write(&self, buf: &[u8], to: Option) -> Result { - // kdebug!("udp to send: {:?}, len={}", to, buf.len()); + // debug!("udp to send: {:?}, len={}", to, buf.len()); let remote_endpoint: &wire::IpEndpoint = { if let Some(Endpoint::Ip(Some(ref endpoint))) = to { endpoint @@ -356,28 +371,28 @@ impl Socket for UdpSocket { return Err(SystemError::ENOTCONN); } }; - // kdebug!("udp write: remote = {:?}", remote_endpoint); + // debug!("udp write: remote = {:?}", remote_endpoint); let mut socket_set_guard = SOCKET_SET.lock_irqsave(); let socket = socket_set_guard.get_mut::(self.handle.smoltcp_handle().unwrap()); - // kdebug!("is open()={}", socket.is_open()); - // kdebug!("socket endpoint={:?}", socket.endpoint()); + // debug!("is open()={}", socket.is_open()); + // debug!("socket endpoint={:?}", socket.endpoint()); if socket.can_send() { - // kdebug!("udp write: can send"); + // debug!("udp write: can send"); match socket.send_slice(buf, *remote_endpoint) { Ok(()) => { - // kdebug!("udp write: send ok"); + // debug!("udp write: send ok"); drop(socket_set_guard); poll_ifaces(); return Ok(buf.len()); } Err(_) => { - // kdebug!("udp write: send err"); + // debug!("udp write: send err"); return Err(SystemError::ENOBUFS); } } } else { - // kdebug!("udp write: can not send"); + // debug!("udp write: can not send"); return Err(SystemError::ENOBUFS); }; } @@ -385,7 +400,7 @@ impl Socket for UdpSocket { fn bind(&mut self, endpoint: Endpoint) -> Result<(), SystemError> { let mut sockets = SOCKET_SET.lock_irqsave(); let socket = sockets.get_mut::(self.handle.smoltcp_handle().unwrap()); - // kdebug!("UDP Bind to {:?}", endpoint); + // debug!("UDP Bind to {:?}", endpoint); return self.do_bind(socket, endpoint); } @@ -477,6 +492,7 @@ pub struct TcpSocket { local_endpoint: Option, // save local endpoint for bind() is_listening: bool, metadata: SocketMetadata, + posix_item: Arc, } impl TcpSocket { @@ -509,13 +525,15 @@ impl TcpSocket { Self::DEFAULT_METADATA_BUF_SIZE, options, ); - // kdebug!("when there's a new tcp socket,its'len: {}",handles.len()); + let posix_item = Arc::new(PosixSocketHandleItem::new(None)); + // debug!("when there's a new tcp socket,its'len: {}",handles.len()); return Self { handles, local_endpoint: None, is_listening: false, metadata, + posix_item, }; } @@ -525,15 +543,13 @@ impl TcpSocket { local_endpoint: wire::IpEndpoint, ) -> Result<(), SystemError> { let listen_result = if local_endpoint.addr.is_unspecified() { - // kdebug!("Tcp Socket Listen on port {}", local_endpoint.port); socket.listen(local_endpoint.port) } else { - // kdebug!("Tcp Socket Listen on {local_endpoint}"); socket.listen(local_endpoint) }; return match listen_result { Ok(()) => { - // kdebug!( + // debug!( // "Tcp Socket Listen on {local_endpoint}, open?:{}", // socket.is_open() // ); @@ -554,16 +570,49 @@ impl TcpSocket { let tx_buffer = tcp::SocketBuffer::new(vec![0; Self::DEFAULT_TX_BUF_SIZE]); tcp::Socket::new(rx_buffer, tx_buffer) } + + /// listening状态的posix socket是需要特殊处理的 + fn tcp_poll_listening(&self) -> EPollEventType { + let socketset_guard = SOCKET_SET.lock_irqsave(); + + let can_accept = self.handles.iter().any(|h| { + if let Some(sh) = h.smoltcp_handle() { + let socket = socketset_guard.get::(sh); + socket.is_active() + } else { + false + } + }); + + if can_accept { + return EPollEventType::EPOLL_LISTEN_CAN_ACCEPT; + } else { + return EPollEventType::empty(); + } + } } impl Socket for TcpSocket { + fn posix_item(&self) -> Arc { + self.posix_item.clone() + } + fn close(&mut self) { for handle in self.handles.iter() { - let mut socket_set_guard = SOCKET_SET.lock_irqsave(); - socket_set_guard.remove(handle.smoltcp_handle().unwrap()); // 删除的时候,会发送一条FINISH的信息? - drop(socket_set_guard); + { + let mut socket_set_guard = SOCKET_SET.lock_irqsave(); + let smoltcp_handle = handle.smoltcp_handle().unwrap(); + socket_set_guard + .get_mut::(smoltcp_handle) + .close(); + drop(socket_set_guard); + } + poll_ifaces(); + SOCKET_SET + .lock_irqsave() + .remove(handle.smoltcp_handle().unwrap()); + // debug!("[Socket] [TCP] Close: {:?}", handle); } - poll_ifaces(); } fn read(&self, buf: &mut [u8]) -> (Result, Endpoint) { @@ -576,18 +625,18 @@ impl Socket for TcpSocket { { return (Err(SystemError::ENOTCONN), Endpoint::Ip(None)); } - // kdebug!("tcp socket: read, buf len={}", buf.len()); - // kdebug!("tcp socket:read, socket'len={}",self.handle.len()); + // debug!("tcp socket: read, buf len={}", buf.len()); + // debug!("tcp socket:read, socket'len={}",self.handle.len()); loop { poll_ifaces(); let mut socket_set_guard = SOCKET_SET.lock_irqsave(); let socket = socket_set_guard - .get_mut::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); + .get_mut::(self.handles.first().unwrap().smoltcp_handle().unwrap()); // 如果socket已经关闭,返回错误 if !socket.is_active() { - // kdebug!("Tcp Socket Read Error, socket is closed"); + // debug!("Tcp Socket Read Error, socket is closed"); return (Err(SystemError::ENOTCONN), Endpoint::Ip(None)); } @@ -607,7 +656,7 @@ impl Socket for TcpSocket { } } Err(tcp::RecvError::InvalidState) => { - kwarn!("Tcp Socket Read Error, InvalidState"); + warn!("Tcp Socket Read Error, InvalidState"); return (Err(SystemError::ENOTCONN), Endpoint::Ip(None)); } Err(tcp::RecvError::Finished) => { @@ -625,11 +674,8 @@ impl Socket for TcpSocket { return (Err(SystemError::ENOTCONN), Endpoint::Ip(None)); } drop(socket_set_guard); - SocketHandleItem::sleep( - self.socket_handle(), - EPollEventType::EPOLLIN.bits() as u64, - HANDLE_MAP.read_irqsave(), - ); + self.posix_item + .sleep((EPollEventType::EPOLLIN | EPollEventType::EPOLLHUP).bits() as u64); } } @@ -643,12 +689,12 @@ impl Socket for TcpSocket { { return Err(SystemError::ENOTCONN); } - // kdebug!("tcp socket:write, socket'len={}",self.handle.len()); + // debug!("tcp socket:write, socket'len={}",self.handle.len()); let mut socket_set_guard = SOCKET_SET.lock_irqsave(); let socket = socket_set_guard - .get_mut::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); + .get_mut::(self.handles.first().unwrap().smoltcp_handle().unwrap()); if socket.is_open() { if socket.can_send() { @@ -659,7 +705,7 @@ impl Socket for TcpSocket { return Ok(size); } Err(e) => { - kerror!("Tcp Socket Write Error {e:?}"); + error!("Tcp Socket Write Error {e:?}"); return Err(SystemError::ENOBUFS); } } @@ -672,37 +718,44 @@ impl Socket for TcpSocket { } fn poll(&self) -> EPollEventType { + // 处理listen的快速路径 + if self.is_listening { + return self.tcp_poll_listening(); + } + // 由于上面处理了listening状态,所以这里只处理非listening状态,这种情况下只有一个handle + + assert!(self.handles.len() == 1); + let mut socket_set_guard = SOCKET_SET.lock_irqsave(); - // kdebug!("tcp socket:poll, socket'len={}",self.handle.len()); + // debug!("tcp socket:poll, socket'len={}",self.handle.len()); let socket = socket_set_guard - .get_mut::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); - return SocketPollMethod::tcp_poll( - socket, - HANDLE_MAP - .read_irqsave() - .get(&self.socket_handle()) - .unwrap() - .shutdown_type(), - ); + .get_mut::(self.handles.first().unwrap().smoltcp_handle().unwrap()); + let handle_map_guard = HANDLE_MAP.read_irqsave(); + let handle_item = handle_map_guard.get(&self.socket_handle()).unwrap(); + let shutdown_type = handle_item.shutdown_type(); + let is_posix_listen = handle_item.is_posix_listen; + drop(handle_map_guard); + + return SocketPollMethod::tcp_poll(socket, shutdown_type, is_posix_listen); } fn connect(&mut self, endpoint: Endpoint) -> Result<(), SystemError> { let mut sockets = SOCKET_SET.lock_irqsave(); - // kdebug!("tcp socket:connect, socket'len={}",self.handle.len()); + // debug!("tcp socket:connect, socket'len={}", self.handles.len()); let socket = - sockets.get_mut::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); + sockets.get_mut::(self.handles.first().unwrap().smoltcp_handle().unwrap()); if let Endpoint::Ip(Some(ip)) = endpoint { let temp_port = PORT_MANAGER.get_ephemeral_port(self.metadata.socket_type)?; // 检测端口是否被占用 - PORT_MANAGER.bind_port(self.metadata.socket_type, temp_port, self.clone())?; + PORT_MANAGER.bind_port(self.metadata.socket_type, temp_port)?; - // kdebug!("temp_port: {}", temp_port); + // debug!("temp_port: {}", temp_port); let iface: Arc = NET_DEVICES.write_irqsave().get(&0).unwrap().clone(); let mut inner_iface = iface.inner_iface().lock(); - // kdebug!("to connect: {ip:?}"); + // debug!("to connect: {ip:?}"); match socket.connect(inner_iface.context(), ip, temp_port) { Ok(()) => { @@ -714,7 +767,7 @@ impl Socket for TcpSocket { poll_ifaces(); let mut sockets = SOCKET_SET.lock_irqsave(); let socket = sockets.get_mut::( - self.handles.get(0).unwrap().smoltcp_handle().unwrap(), + self.handles.first().unwrap().smoltcp_handle().unwrap(), ); match socket.state() { @@ -723,11 +776,7 @@ impl Socket for TcpSocket { } tcp::State::SynSent => { drop(sockets); - SocketHandleItem::sleep( - self.socket_handle(), - Self::CAN_CONNECT, - HANDLE_MAP.read_irqsave(), - ); + self.posix_item.sleep(Self::CAN_CONNECT); } _ => { return Err(SystemError::ECONNREFUSED); @@ -736,7 +785,7 @@ impl Socket for TcpSocket { } } Err(e) => { - // kerror!("Tcp Socket Connect Error {e:?}"); + // error!("Tcp Socket Connect Error {e:?}"); match e { tcp::ConnectError::InvalidState => return Err(SystemError::EISCONN), tcp::ConnectError::Unaddressable => return Err(SystemError::EADDRNOTAVAIL), @@ -750,12 +799,17 @@ impl Socket for TcpSocket { /// @brief tcp socket 监听 local_endpoint 端口 /// - /// @param backlog 未处理的连接队列的最大长度. 由于smoltcp不支持backlog,所以这个参数目前无效 + /// @param backlog 未处理的连接队列的最大长度 fn listen(&mut self, backlog: usize) -> Result<(), SystemError> { if self.is_listening { return Ok(()); } + // debug!( + // "tcp socket:listen, socket'len={}, backlog = {backlog}", + // self.handles.len() + // ); + let local_endpoint = self.local_endpoint.ok_or(SystemError::EINVAL)?; let mut sockets = SOCKET_SET.lock_irqsave(); // 获取handle的数量 @@ -763,17 +817,22 @@ impl Socket for TcpSocket { let backlog = handlen.max(backlog); // 添加剩余需要构建的socket - // kdebug!("tcp socket:before listen, socket'len={}",self.handle.len()); + // debug!("tcp socket:before listen, socket'len={}", self.handle_list.len()); let mut handle_guard = HANDLE_MAP.write_irqsave(); + let socket_handle_item_0 = handle_guard.get_mut(&self.socket_handle()).unwrap(); + socket_handle_item_0.is_posix_listen = true; + self.handles.extend((handlen..backlog).map(|_| { let socket = Self::create_new_socket(); let handle = GlobalSocketHandle::new_smoltcp_handle(sockets.add(socket)); - let handle_item = SocketHandleItem::new(); + let mut handle_item = SocketHandleItem::new(Arc::downgrade(&self.posix_item)); + handle_item.is_posix_listen = true; handle_guard.insert(handle, handle_item); handle })); - // kdebug!("tcp socket:listen, socket'len={}",self.handle.len()); - // kdebug!("tcp socket:listen, backlog={backlog}"); + + // debug!("tcp socket:listen, socket'len={}", self.handles.len()); + // debug!("tcp socket:listen, backlog={backlog}"); // 监听所有的socket for i in 0..backlog { @@ -782,11 +841,12 @@ impl Socket for TcpSocket { let socket = sockets.get_mut::(handle.smoltcp_handle().unwrap()); if !socket.is_listening() { - // kdebug!("Tcp Socket is already listening on {local_endpoint}"); + // debug!("Tcp Socket is already listening on {local_endpoint}"); self.do_listen(socket, local_endpoint)?; } - // kdebug!("Tcp Socket before listen, open={}", socket.is_open()); + // debug!("Tcp Socket before listen, open={}", socket.is_open()); } + return Ok(()); } @@ -797,11 +857,12 @@ impl Socket for TcpSocket { } // 检测端口是否已被占用 - PORT_MANAGER.bind_port(self.metadata.socket_type, ip.port, self.clone())?; - // kdebug!("tcp socket:bind, socket'len={}",self.handle.len()); + PORT_MANAGER.bind_port(self.metadata.socket_type, ip.port)?; + // debug!("tcp socket:bind, socket'len={}",self.handle.len()); self.local_endpoint = Some(ip); self.is_listening = false; + return Ok(()); } return Err(SystemError::EINVAL); @@ -818,101 +879,87 @@ impl Socket for TcpSocket { } fn accept(&mut self) -> Result<(Box, Endpoint), SystemError> { + if !self.is_listening { + return Err(SystemError::EINVAL); + } let endpoint = self.local_endpoint.ok_or(SystemError::EINVAL)?; loop { - // kdebug!("tcp accept: poll_ifaces()"); + // debug!("tcp accept: poll_ifaces()"); poll_ifaces(); - // kdebug!("tcp socket:accept, socket'len={}",self.handle.len()); - - let mut sockets = SOCKET_SET.lock_irqsave(); - - // 随机获取访问的socket的handle - let index: usize = rand() % self.handles.len(); - let handle = self.handles.get(index).unwrap(); - - let socket = sockets - .iter_mut() - .find(|y| { - tcp::Socket::downcast(y.1) - .map(|y| y.is_active()) - .unwrap_or(false) - }) - .map(|y| tcp::Socket::downcast_mut(y.1).unwrap()); - if let Some(socket) = socket { - if socket.is_active() { - // kdebug!("tcp accept: socket.is_active()"); - let remote_ep = socket.remote_endpoint().ok_or(SystemError::ENOTCONN)?; - - let new_socket = { - // The new TCP socket used for sending and receiving data. - let mut tcp_socket = Self::create_new_socket(); - self.do_listen(&mut tcp_socket, endpoint) - .expect("do_listen failed"); - - // tcp_socket.listen(endpoint).unwrap(); - - // 之所以把old_handle存入new_socket, 是因为当前时刻,smoltcp已经把old_handle对应的socket与远程的endpoint关联起来了 - // 因此需要再为当前的socket分配一个新的handle - let new_handle = - GlobalSocketHandle::new_smoltcp_handle(sockets.add(tcp_socket)); - let old_handle = ::core::mem::replace( - &mut *self.handles.get_mut(index).unwrap(), - new_handle, - ); - - let metadata = SocketMetadata::new( - SocketType::Tcp, - Self::DEFAULT_TX_BUF_SIZE, - Self::DEFAULT_RX_BUF_SIZE, - Self::DEFAULT_METADATA_BUF_SIZE, - self.metadata.options, - ); - - let new_socket = Box::new(TcpSocket { - handles: vec![old_handle], - local_endpoint: self.local_endpoint, - is_listening: false, - metadata, - }); - // kdebug!("tcp socket:after accept, socket'len={}",new_socket.handle.len()); - - // 更新端口与 socket 的绑定 - if let Some(Endpoint::Ip(Some(ip))) = self.endpoint() { - PORT_MANAGER.unbind_port(self.metadata.socket_type, ip.port)?; - PORT_MANAGER.bind_port( - self.metadata.socket_type, - ip.port, - *new_socket.clone(), - )?; - } - - // 更新handle表 - let mut handle_guard = HANDLE_MAP.write_irqsave(); - // 先删除原来的 - - let item = handle_guard.remove(&old_handle).unwrap(); - - // 按照smoltcp行为,将新的handle绑定到原来的item - handle_guard.insert(new_handle, item); - let new_item = SocketHandleItem::new(); - - // 插入新的item - handle_guard.insert(old_handle, new_item); + // debug!("tcp socket:accept, socket'len={}", self.handle_list.len()); + + let mut sockset = SOCKET_SET.lock_irqsave(); + // Get the corresponding activated handler + let global_handle_index = self.handles.iter().position(|handle| { + let con_smol_sock = sockset.get::(handle.smoltcp_handle().unwrap()); + con_smol_sock.is_active() + }); + + if let Some(handle_index) = global_handle_index { + let con_smol_sock = sockset + .get::(self.handles[handle_index].smoltcp_handle().unwrap()); + + // debug!("[Socket] [TCP] Accept: {:?}", handle); + // handle is connected socket's handle + let remote_ep = con_smol_sock + .remote_endpoint() + .ok_or(SystemError::ENOTCONN)?; + + let tcp_socket = Self::create_new_socket(); + + let new_handle = GlobalSocketHandle::new_smoltcp_handle(sockset.add(tcp_socket)); + + // let handle in TcpSock be the new empty handle, and return the old connected handle + let old_handle = core::mem::replace(&mut self.handles[handle_index], new_handle); + + let metadata = SocketMetadata::new( + SocketType::Tcp, + Self::DEFAULT_TX_BUF_SIZE, + Self::DEFAULT_RX_BUF_SIZE, + Self::DEFAULT_METADATA_BUF_SIZE, + self.metadata.options, + ); + + let sock_ret = Box::new(TcpSocket { + handles: vec![old_handle], + local_endpoint: self.local_endpoint, + is_listening: false, + metadata, + posix_item: Arc::new(PosixSocketHandleItem::new(None)), + }); + + { + let mut handle_guard = HANDLE_MAP.write_irqsave(); + // 先删除原来的 + let item = handle_guard.remove(&old_handle).unwrap(); + item.reset_shutdown_type(); + assert!(item.is_posix_listen); + + // 按照smoltcp行为,将新的handle绑定到原来的item + let new_item = SocketHandleItem::new(Arc::downgrade(&sock_ret.posix_item)); + handle_guard.insert(old_handle, new_item); + // 插入新的item + handle_guard.insert(new_handle, item); + + let socket = sockset.get_mut::( + self.handles[handle_index].smoltcp_handle().unwrap(), + ); - new_socket - }; - // kdebug!("tcp accept: new socket: {:?}", new_socket); - drop(sockets); - poll_ifaces(); + if !socket.is_listening() { + self.do_listen(socket, endpoint)?; + } - return Ok((new_socket, Endpoint::Ip(Some(remote_ep)))); + drop(handle_guard); } + + return Ok((sock_ret, Endpoint::Ip(Some(remote_ep)))); } - // kdebug!("tcp socket:before sleep, handle_guard'len={}",HANDLE_MAP.write_irqsave().len()); - drop(sockets); - SocketHandleItem::sleep(*handle, Self::CAN_ACCPET, HANDLE_MAP.read_irqsave()); - // kdebug!("tcp socket:after sleep, handle_guard'len={}",HANDLE_MAP.write_irqsave().len()); + drop(sockset); + + // debug!("[TCP] [Accept] sleeping socket with handle: {:?}", self.handles.first().unwrap().smoltcp_handle().unwrap()); + self.posix_item.sleep(Self::CAN_ACCPET); + // debug!("tcp socket:after sleep, handle_guard'len={}",HANDLE_MAP.write_irqsave().len()); } } @@ -921,10 +968,10 @@ impl Socket for TcpSocket { if result.is_none() { let sockets = SOCKET_SET.lock_irqsave(); - // kdebug!("tcp socket:endpoint, socket'len={}",self.handle.len()); + // debug!("tcp socket:endpoint, socket'len={}",self.handle.len()); let socket = - sockets.get::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); + sockets.get::(self.handles.first().unwrap().smoltcp_handle().unwrap()); if let Some(ep) = socket.local_endpoint() { result = Some(Endpoint::Ip(Some(ep))); } @@ -934,10 +981,10 @@ impl Socket for TcpSocket { fn peer_endpoint(&self) -> Option { let sockets = SOCKET_SET.lock_irqsave(); - // kdebug!("tcp socket:peer_endpoint, socket'len={}",self.handle.len()); + // debug!("tcp socket:peer_endpoint, socket'len={}",self.handle.len()); let socket = - sockets.get::(self.handles.get(0).unwrap().smoltcp_handle().unwrap()); + sockets.get::(self.handles.first().unwrap().smoltcp_handle().unwrap()); return socket.remote_endpoint().map(|x| Endpoint::Ip(Some(x))); } @@ -950,9 +997,9 @@ impl Socket for TcpSocket { } fn socket_handle(&self) -> GlobalSocketHandle { - // kdebug!("tcp socket:socket_handle, socket'len={}",self.handle.len()); + // debug!("tcp socket:socket_handle, socket'len={}",self.handle.len()); - *self.handles.get(0).unwrap() + *self.handles.first().unwrap() } fn as_any_ref(&self) -> &dyn core::any::Any { diff --git a/kernel/src/net/socket/mod.rs b/kernel/src/net/socket/mod.rs index 70fb54142..c055f30e7 100644 --- a/kernel/src/net/socket/mod.rs +++ b/kernel/src/net/socket/mod.rs @@ -8,6 +8,7 @@ use alloc::{ vec::Vec, }; use hashbrown::HashMap; +use log::warn; use smoltcp::{ iface::SocketSet, socket::{self, raw, tcp, udp}, @@ -21,10 +22,11 @@ use crate::{ Metadata, }, libs::{ - rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}, + rwlock::{RwLock, RwLockWriteGuard}, spinlock::{SpinLock, SpinLockGuard}, wait_queue::EventWaitQueue, }, + process::{Pid, ProcessManager}, sched::{schedule, SchedMode}, }; @@ -85,7 +87,7 @@ pub(super) fn new_socket( } }; - let handle_item = SocketHandleItem::new(); + let handle_item = SocketHandleItem::new(Arc::downgrade(&socket.posix_item())); HANDLE_MAP .write_irqsave() .insert(socket.socket_handle(), handle_item); @@ -226,7 +228,7 @@ pub trait Socket: Sync + Send + Debug + Any { _optname: usize, _optval: &[u8], ) -> Result<(), SystemError> { - kwarn!("setsockopt is not implemented"); + warn!("setsockopt is not implemented"); Ok(()) } @@ -241,36 +243,26 @@ pub trait Socket: Sync + Send + Debug + Any { fn as_any_mut(&mut self) -> &mut dyn Any; fn add_epoll(&mut self, epitem: Arc) -> Result<(), SystemError> { - HANDLE_MAP - .write_irqsave() - .get_mut(&self.socket_handle()) - .unwrap() - .add_epoll(epitem); + let posix_item = self.posix_item(); + posix_item.add_epoll(epitem); Ok(()) } fn remove_epoll(&mut self, epoll: &Weak>) -> Result<(), SystemError> { - HANDLE_MAP - .write_irqsave() - .get_mut(&self.socket_handle()) - .unwrap() - .remove_epoll(epoll)?; + let posix_item = self.posix_item(); + posix_item.remove_epoll(epoll)?; Ok(()) } fn clear_epoll(&mut self) -> Result<(), SystemError> { - let mut handle_map_guard = HANDLE_MAP.write_irqsave(); - let handle_item = handle_map_guard.get_mut(&self.socket_handle()).unwrap(); + let posix_item = self.posix_item(); - for epitem in handle_item.epitems.lock_irqsave().iter() { + for epitem in posix_item.epitems.lock_irqsave().iter() { let epoll = epitem.epoll(); - if epoll.upgrade().is_some() { - EventPoll::ep_remove( - &mut epoll.upgrade().unwrap().lock_irqsave(), - epitem.fd(), - None, - )?; + + if let Some(epoll) = epoll.upgrade() { + EventPoll::ep_remove(&mut epoll.lock_irqsave(), epitem.fd(), None)?; } } @@ -278,6 +270,8 @@ pub trait Socket: Sync + Send + Debug + Any { } fn close(&mut self); + + fn posix_item(&self) -> Arc; } impl Clone for Box { @@ -303,19 +297,8 @@ impl SocketInode { pub unsafe fn inner_no_preempt(&self) -> SpinLockGuard> { self.0.lock_no_preempt() } -} -impl IndexNode for SocketInode { - fn open( - &self, - _data: SpinLockGuard, - _mode: &FileMode, - ) -> Result<(), SystemError> { - self.1.fetch_add(1, core::sync::atomic::Ordering::SeqCst); - Ok(()) - } - - fn close(&self, _data: SpinLockGuard) -> Result<(), SystemError> { + fn do_close(&self) -> Result<(), SystemError> { let prev_ref_count = self.1.fetch_sub(1, core::sync::atomic::Ordering::SeqCst); if prev_ref_count == 1 { // 最后一次关闭,需要释放 @@ -326,7 +309,7 @@ impl IndexNode for SocketInode { } if let Some(Endpoint::Ip(Some(ip))) = socket.endpoint() { - PORT_MANAGER.unbind_port(socket.metadata().socket_type, ip.port)?; + PORT_MANAGER.unbind_port(socket.metadata().socket_type, ip.port); } socket.clear_epoll()?; @@ -340,6 +323,29 @@ impl IndexNode for SocketInode { Ok(()) } +} + +impl Drop for SocketInode { + fn drop(&mut self) { + for _ in 0..self.1.load(core::sync::atomic::Ordering::SeqCst) { + let _ = self.do_close(); + } + } +} + +impl IndexNode for SocketInode { + fn open( + &self, + _data: SpinLockGuard, + _mode: &FileMode, + ) -> Result<(), SystemError> { + self.1.fetch_add(1, core::sync::atomic::Ordering::SeqCst); + Ok(()) + } + + fn close(&self, _data: SpinLockGuard) -> Result<(), SystemError> { + self.do_close() + } fn read_at( &self, @@ -396,54 +402,35 @@ impl IndexNode for SocketInode { } #[derive(Debug)] -pub struct SocketHandleItem { - /// shutdown状态 - pub shutdown_type: RwLock, +pub struct PosixSocketHandleItem { /// socket的waitqueue - pub wait_queue: EventWaitQueue, - /// epitems,考虑写在这是否是最优解? + wait_queue: Arc, + pub epitems: SpinLock>>, } -impl SocketHandleItem { - pub fn new() -> Self { +impl PosixSocketHandleItem { + pub fn new(wait_queue: Option>) -> Self { Self { - shutdown_type: RwLock::new(ShutdownType::empty()), - wait_queue: EventWaitQueue::new(), + wait_queue: wait_queue.unwrap_or(Arc::new(EventWaitQueue::new())), epitems: SpinLock::new(LinkedList::new()), } } - /// ## 在socket的等待队列上睡眠 - pub fn sleep( - socket_handle: GlobalSocketHandle, - events: u64, - handle_map_guard: RwLockReadGuard<'_, HashMap>, - ) { + pub fn sleep(&self, events: u64) { unsafe { - handle_map_guard - .get(&socket_handle) - .unwrap() - .wait_queue - .sleep_without_schedule(events) - }; - drop(handle_map_guard); + ProcessManager::preempt_disable(); + self.wait_queue.sleep_without_schedule(events); + ProcessManager::preempt_enable(); + } schedule(SchedMode::SM_NONE); } - pub fn shutdown_type(&self) -> ShutdownType { - *self.shutdown_type.read() - } - - pub fn shutdown_type_writer(&mut self) -> RwLockWriteGuard { - self.shutdown_type.write_irqsave() - } - - pub fn add_epoll(&mut self, epitem: Arc) { + pub fn add_epoll(&self, epitem: Arc) { self.epitems.lock_irqsave().push_back(epitem) } - pub fn remove_epoll(&mut self, epoll: &Weak>) -> Result<(), SystemError> { + pub fn remove_epoll(&self, epoll: &Weak>) -> Result<(), SystemError> { let is_remove = !self .epitems .lock_irqsave() @@ -457,15 +444,59 @@ impl SocketHandleItem { Err(SystemError::ENOENT) } + + /// ### 唤醒该队列上等待events的进程 + /// + /// ### 参数 + /// - events: 发生的事件 + /// + /// 需要注意的是,只要触发了events中的任意一件事件,进程都会被唤醒 + pub fn wakeup_any(&self, events: u64) { + self.wait_queue.wakeup_any(events); + } +} +#[derive(Debug)] +pub struct SocketHandleItem { + /// 对应的posix socket是否为listen的 + pub is_posix_listen: bool, + /// shutdown状态 + pub shutdown_type: RwLock, + pub posix_item: Weak, +} + +impl SocketHandleItem { + pub fn new(posix_item: Weak) -> Self { + Self { + is_posix_listen: false, + shutdown_type: RwLock::new(ShutdownType::empty()), + posix_item, + } + } + + pub fn shutdown_type(&self) -> ShutdownType { + *self.shutdown_type.read() + } + + pub fn shutdown_type_writer(&mut self) -> RwLockWriteGuard { + self.shutdown_type.write_irqsave() + } + + pub fn reset_shutdown_type(&self) { + *self.shutdown_type.write() = ShutdownType::empty(); + } + + pub fn posix_item(&self) -> Option> { + self.posix_item.upgrade() + } } /// # TCP 和 UDP 的端口管理器。 /// 如果 TCP/UDP 的 socket 绑定了某个端口,它会在对应的表中记录,以检测端口冲突。 pub struct PortManager { // TCP 端口记录表 - tcp_port_table: SpinLock>>, + tcp_port_table: SpinLock>, // UDP 端口记录表 - udp_port_table: SpinLock>>, + udp_port_table: SpinLock>, } impl PortManager { @@ -517,12 +548,7 @@ impl PortManager { /// @brief 检测给定端口是否已被占用,如果未被占用则在 TCP/UDP 对应的表中记录 /// /// TODO: 增加支持端口复用的逻辑 - pub fn bind_port( - &self, - socket_type: SocketType, - port: u16, - socket: impl Socket, - ) -> Result<(), SystemError> { + pub fn bind_port(&self, socket_type: SocketType, port: u16) -> Result<(), SystemError> { if port > 0 { let mut listen_table_guard = match socket_type { SocketType::Udp => self.udp_port_table.lock(), @@ -531,7 +557,7 @@ impl PortManager { }; match listen_table_guard.get(&port) { Some(_) => return Err(SystemError::EADDRINUSE), - None => listen_table_guard.insert(port, Arc::new(socket)), + None => listen_table_guard.insert(port, ProcessManager::current_pid()), }; drop(listen_table_guard); } @@ -539,15 +565,17 @@ impl PortManager { } /// @brief 在对应的端口记录表中将端口和 socket 解绑 - pub fn unbind_port(&self, socket_type: SocketType, port: u16) -> Result<(), SystemError> { + /// should call this function when socket is closed or aborted + pub fn unbind_port(&self, socket_type: SocketType, port: u16) { let mut listen_table_guard = match socket_type { SocketType::Udp => self.udp_port_table.lock(), SocketType::Tcp => self.tcp_port_table.lock(), - _ => return Ok(()), + _ => { + return; + } }; listen_table_guard.remove(&port); drop(listen_table_guard); - return Ok(()); } } @@ -752,33 +780,47 @@ impl TryFrom for PosixSocketType { pub struct SocketPollMethod; impl SocketPollMethod { - pub fn poll(socket: &socket::Socket, shutdown: ShutdownType) -> EPollEventType { + pub fn poll(socket: &socket::Socket, handle_item: &SocketHandleItem) -> EPollEventType { + let shutdown = handle_item.shutdown_type(); match socket { socket::Socket::Udp(udp) => Self::udp_poll(udp, shutdown), - socket::Socket::Tcp(tcp) => Self::tcp_poll(tcp, shutdown), + socket::Socket::Tcp(tcp) => Self::tcp_poll(tcp, shutdown, handle_item.is_posix_listen), socket::Socket::Raw(raw) => Self::raw_poll(raw, shutdown), _ => todo!(), } } - pub fn tcp_poll(socket: &tcp::Socket, shutdown: ShutdownType) -> EPollEventType { + pub fn tcp_poll( + socket: &tcp::Socket, + shutdown: ShutdownType, + is_posix_listen: bool, + ) -> EPollEventType { let mut events = EPollEventType::empty(); - if socket.is_listening() && socket.is_active() { - events.insert(EPollEventType::EPOLLIN | EPollEventType::EPOLLRDNORM); + // debug!("enter tcp_poll! is_posix_listen:{}", is_posix_listen); + // 处理listen的socket + if is_posix_listen { + // 如果是listen的socket,那么只有EPOLLIN和EPOLLRDNORM + if socket.is_active() { + events.insert(EPollEventType::EPOLL_LISTEN_CAN_ACCEPT); + } + + // debug!("tcp_poll listen socket! events:{:?}", events); return events; } - // socket已经关闭 - if !socket.is_open() { - events.insert(EPollEventType::EPOLLHUP) + let state = socket.state(); + + if shutdown == ShutdownType::SHUTDOWN_MASK || state == tcp::State::Closed { + events.insert(EPollEventType::EPOLLHUP); } + if shutdown.contains(ShutdownType::RCV_SHUTDOWN) { events.insert( EPollEventType::EPOLLIN | EPollEventType::EPOLLRDNORM | EPollEventType::EPOLLRDHUP, ); } - let state = socket.state(); + // Connected or passive Fast Open socket? if state != tcp::State::SynSent && state != tcp::State::SynReceived { // socket有可读数据 if socket.can_recv() { @@ -786,12 +828,12 @@ impl SocketPollMethod { } if !(shutdown.contains(ShutdownType::SEND_SHUTDOWN)) { - // 缓冲区可写 + // 缓冲区可写(这里判断可写的逻辑好像跟linux不太一样) if socket.send_queue() < socket.send_capacity() { events.insert(EPollEventType::EPOLLOUT | EPollEventType::EPOLLWRNORM); } else { - // TODO:触发缓冲区已满的信号 - todo!("A signal that the buffer is full needs to be sent"); + // TODO:触发缓冲区已满的信号SIGIO + todo!("A signal SIGIO that the buffer is full needs to be sent"); } } else { // 如果我们的socket关闭了SEND_SHUTDOWN,epoll事件就是EPOLLOUT @@ -802,6 +844,7 @@ impl SocketPollMethod { } // socket发生错误 + // TODO: 这里的逻辑可能有问题,需要进一步验证是否is_active()==false就代表socket发生错误 if !socket.is_active() { events.insert(EPollEventType::EPOLLERR); } @@ -840,7 +883,7 @@ impl SocketPollMethod { } pub fn raw_poll(socket: &raw::Socket, shutdown: ShutdownType) -> EPollEventType { - //kdebug!("enter raw_poll!"); + //debug!("enter raw_poll!"); let mut event = EPollEventType::empty(); if shutdown.contains(ShutdownType::RCV_SHUTDOWN) { @@ -853,21 +896,21 @@ impl SocketPollMethod { } if socket.can_recv() { - //kdebug!("poll can recv!"); + //debug!("poll can recv!"); event.insert(EPollEventType::EPOLLIN | EPollEventType::EPOLLRDNORM); } else { - //kdebug!("poll can not recv!"); + //debug!("poll can not recv!"); } if socket.can_send() { - //kdebug!("poll can send!"); + //debug!("poll can send!"); event.insert( EPollEventType::EPOLLOUT | EPollEventType::EPOLLWRNORM | EPollEventType::EPOLLWRBAND, ); } else { - //kdebug!("poll can not send!"); + //debug!("poll can not send!"); // TODO: 缓冲区空间不够,需要使用信号处理 todo!() } diff --git a/kernel/src/net/socket/unix.rs b/kernel/src/net/socket/unix.rs index 83cd0c5d2..f15037775 100644 --- a/kernel/src/net/socket/unix.rs +++ b/kernel/src/net/socket/unix.rs @@ -4,7 +4,8 @@ use system_error::SystemError; use crate::{libs::spinlock::SpinLock, net::Endpoint}; use super::{ - handle::GlobalSocketHandle, Socket, SocketInode, SocketMetadata, SocketOptions, SocketType, + handle::GlobalSocketHandle, PosixSocketHandleItem, Socket, SocketInode, SocketMetadata, + SocketOptions, SocketType, }; #[derive(Debug, Clone)] @@ -13,6 +14,7 @@ pub struct StreamSocket { buffer: Arc>>, peer_inode: Option>, handle: GlobalSocketHandle, + posix_item: Arc, } impl StreamSocket { @@ -36,16 +38,22 @@ impl StreamSocket { options, ); + let posix_item = Arc::new(PosixSocketHandleItem::new(None)); + Self { metadata, buffer, peer_inode: None, handle: GlobalSocketHandle::new_kernel_handle(), + posix_item, } } } impl Socket for StreamSocket { + fn posix_item(&self) -> Arc { + self.posix_item.clone() + } fn socket_handle(&self) -> GlobalSocketHandle { self.handle } @@ -121,6 +129,7 @@ pub struct SeqpacketSocket { buffer: Arc>>, peer_inode: Option>, handle: GlobalSocketHandle, + posix_item: Arc, } impl SeqpacketSocket { @@ -144,16 +153,22 @@ impl SeqpacketSocket { options, ); + let posix_item = Arc::new(PosixSocketHandleItem::new(None)); + Self { metadata, buffer, peer_inode: None, handle: GlobalSocketHandle::new_kernel_handle(), + posix_item, } } } impl Socket for SeqpacketSocket { + fn posix_item(&self) -> Arc { + self.posix_item.clone() + } fn close(&mut self) {} fn read(&self, buf: &mut [u8]) -> (Result, Endpoint) { diff --git a/kernel/src/net/syscall.rs b/kernel/src/net/syscall.rs index cc9bf5696..91aff6ebb 100644 --- a/kernel/src/net/syscall.rs +++ b/kernel/src/net/syscall.rs @@ -345,7 +345,7 @@ impl Syscall { .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; let mut socket = unsafe { socket.inner_no_preempt() }; - socket.shutdown(ShutdownType::from_bits_truncate(how as u8))?; + socket.shutdown(ShutdownType::from_bits_truncate((how + 1) as u8))?; return Ok(0); } @@ -404,13 +404,13 @@ impl Syscall { let socket: Arc = ProcessManager::current_pcb() .get_socket(fd as i32) .ok_or(SystemError::EBADF)?; - // kdebug!("accept: socket={:?}", socket); + // debug!("accept: socket={:?}", socket); let mut socket = unsafe { socket.inner_no_preempt() }; // 从socket中接收连接 let (new_socket, remote_endpoint) = socket.accept()?; drop(socket); - // kdebug!("accept: new_socket={:?}", new_socket); + // debug!("accept: new_socket={:?}", new_socket); // Insert the new socket into the file descriptor vector let new_socket: Arc = SocketInode::new(new_socket); @@ -426,9 +426,9 @@ impl Syscall { .fd_table() .write() .alloc_fd(File::new(new_socket, file_mode)?, None)?; - // kdebug!("accept: new_fd={}", new_fd); + // debug!("accept: new_fd={}", new_fd); if !addr.is_null() { - // kdebug!("accept: write remote_endpoint to user"); + // debug!("accept: write remote_endpoint to user"); // 将对端地址写入用户空间 let sockaddr_in = SockAddr::from(remote_endpoint); unsafe { diff --git a/kernel/src/process/abi.rs b/kernel/src/process/abi.rs index cbaab9d0a..a39f4ff3a 100644 --- a/kernel/src/process/abi.rs +++ b/kernel/src/process/abi.rs @@ -38,7 +38,7 @@ pub enum AtType { /// Frequency at which times() increments. ClkTck, /// Secure mode boolean. - Secure, + Secure = 23, /// String identifying real platform, may differ from AT_PLATFORM. BasePlatform, /// Address of 16 random bytes. @@ -46,7 +46,7 @@ pub enum AtType { /// Extension of AT_HWCAP. HwCap2, /// Filename of program. - ExecFn, + ExecFn = 31, /// Minimal stack size for signal delivery. MinSigStackSize, } diff --git a/kernel/src/process/cred.rs b/kernel/src/process/cred.rs new file mode 100644 index 000000000..952cfbfd5 --- /dev/null +++ b/kernel/src/process/cred.rs @@ -0,0 +1,170 @@ +use core::sync::atomic::AtomicUsize; + +use alloc::vec::Vec; + +const GLOBAL_ROOT_UID: Kuid = Kuid(0); +const GLOBAL_ROOT_GID: Kgid = Kgid(0); +pub static INIT_CRED: Cred = Cred::init(); + +int_like!(Kuid, AtomicKuid, usize, AtomicUsize); +int_like!(Kgid, AtomicKgid, usize, AtomicUsize); + +bitflags! { + pub struct CAPFlags:u64{ + const CAP_EMPTY_SET = 0; + const CAP_FULL_SET = (1 << 41) - 1; + } +} + +pub enum CredFsCmp { + Equal, + Less, + Greater, +} + +/// 凭证集 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Cred { + /// 进程实际uid + pub uid: Kuid, + /// 进程实际gid + pub gid: Kgid, + /// 进程保存的uid + pub suid: Kuid, + /// 进程保存的gid + pub sgid: Kgid, + /// 进程有效的uid + pub euid: Kuid, + /// 进程有效的gid + pub egid: Kgid, + /// UID for VFS ops + pub fsuid: Kuid, + /// GID for VFS ops + pub fsgid: Kgid, + /// 子进程可以继承的权限 + pub cap_inheritable: CAPFlags, + /// 当前进程被赋予的权限 + pub cap_permitted: CAPFlags, + /// 当前进程实际使用的权限 + pub cap_effective: CAPFlags, + /// capability bounding set + pub cap_bset: CAPFlags, + /// Ambient capability set + pub cap_ambient: CAPFlags, + /// supplementary groups for euid/fsgid + pub group_info: Option, +} + +impl Cred { + pub const fn init() -> Self { + Self { + uid: GLOBAL_ROOT_UID, + gid: GLOBAL_ROOT_GID, + suid: GLOBAL_ROOT_UID, + sgid: GLOBAL_ROOT_GID, + euid: GLOBAL_ROOT_UID, + egid: GLOBAL_ROOT_GID, + fsuid: GLOBAL_ROOT_UID, + fsgid: GLOBAL_ROOT_GID, + cap_inheritable: CAPFlags::CAP_EMPTY_SET, + cap_permitted: CAPFlags::CAP_FULL_SET, + cap_effective: CAPFlags::CAP_FULL_SET, + cap_bset: CAPFlags::CAP_FULL_SET, + cap_ambient: CAPFlags::CAP_FULL_SET, + group_info: None, + } + } + + #[allow(dead_code)] + /// Compare two credentials with respect to filesystem access. + pub fn fscmp(&self, other: Cred) -> CredFsCmp { + if *self == other { + return CredFsCmp::Equal; + } + + if self.fsuid < other.fsuid { + return CredFsCmp::Less; + } + if self.fsuid > other.fsuid { + return CredFsCmp::Greater; + } + + if self.fsgid < other.fsgid { + return CredFsCmp::Less; + } + if self.fsgid > other.fsgid { + return CredFsCmp::Greater; + } + + if self.group_info == other.group_info { + return CredFsCmp::Equal; + } + + if let (Some(ga), Some(gb)) = (&self.group_info, &other.group_info) { + let ga_count = ga.gids.len(); + let gb_count = gb.gids.len(); + + if ga_count < gb_count { + return CredFsCmp::Less; + } + if ga_count > gb_count { + return CredFsCmp::Greater; + } + + for i in 0..ga_count { + if ga.gids[i] < gb.gids[i] { + return CredFsCmp::Less; + } + if ga.gids[i] > gb.gids[i] { + return CredFsCmp::Greater; + } + } + } else { + if self.group_info.is_none() { + return CredFsCmp::Less; + } + if other.group_info.is_none() { + return CredFsCmp::Greater; + } + } + + return CredFsCmp::Equal; + } + + pub fn setuid(&mut self, uid: usize) { + self.uid.0 = uid; + } + + pub fn seteuid(&mut self, euid: usize) { + self.euid.0 = euid; + } + + pub fn setsuid(&mut self, suid: usize) { + self.suid.0 = suid; + } + + pub fn setfsuid(&mut self, fsuid: usize) { + self.fsuid.0 = fsuid; + } + + pub fn setgid(&mut self, gid: usize) { + self.gid.0 = gid; + } + + pub fn setegid(&mut self, egid: usize) { + self.egid.0 = egid; + } + + pub fn setsgid(&mut self, sgid: usize) { + self.sgid.0 = sgid; + } + + pub fn setfsgid(&mut self, fsgid: usize) { + self.fsgid.0 = fsgid; + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GroupInfo { + pub gids: Vec, +} diff --git a/kernel/src/process/exec.rs b/kernel/src/process/exec.rs index 3233b06fe..9d0cba351 100644 --- a/kernel/src/process/exec.rs +++ b/kernel/src/process/exec.rs @@ -1,6 +1,6 @@ use core::{fmt::Debug, ptr::null}; -use alloc::{collections::BTreeMap, string::String, sync::Arc, vec::Vec}; +use alloc::{collections::BTreeMap, ffi::CString, string::String, sync::Arc, vec::Vec}; use system_error::SystemError; use crate::{ @@ -16,6 +16,8 @@ use crate::{ }, }; +use super::ProcessManager; + /// 系统支持的所有二进制文件加载器的列表 const BINARY_LOADERS: [&'static dyn BinaryLoader; 1] = [&ELF_LOADER]; @@ -125,7 +127,7 @@ impl ExecParam { file, vm, flags, - init_info: ProcInitInfo::new(), + init_info: ProcInitInfo::new(ProcessManager::current_pcb().basic().name()), }) } @@ -170,7 +172,7 @@ pub fn load_binary_file(param: &mut ExecParam) -> Result Result, - pub envs: Vec, + pub proc_name: CString, + pub args: Vec, + pub envs: Vec, pub auxv: BTreeMap, + pub rand_num: [u8; 16], } impl ProcInitInfo { - pub fn new() -> Self { + pub fn new(proc_name: &str) -> Self { Self { - proc_name: String::new(), + proc_name: CString::new(proc_name).unwrap_or(CString::new("").unwrap()), args: Vec::new(), envs: Vec::new(), auxv: BTreeMap::new(), + rand_num: [0u8; 16], } } @@ -223,7 +227,7 @@ impl ProcInitInfo { /// /// 返回值是一个元组,第一个元素是最终的用户栈顶地址,第二个元素是环境变量pointer数组的起始地址 pub unsafe fn push_at( - &self, + &mut self, ustack: &mut UserStack, ) -> Result<(VirtAddr, VirtAddr), SystemError> { // 先把程序的名称压入栈中 @@ -234,7 +238,7 @@ impl ProcInitInfo { .envs .iter() .map(|s| { - self.push_str(ustack, s.as_str()).expect("push_str failed"); + self.push_str(ustack, s).expect("push_str failed"); ustack.sp() }) .collect::>(); @@ -244,11 +248,25 @@ impl ProcInitInfo { .args .iter() .map(|s| { - self.push_str(ustack, s.as_str()).expect("push_str failed"); + self.push_str(ustack, s).expect("push_str failed"); ustack.sp() }) .collect::>(); + // 压入随机数,把指针放入auxv + self.push_slice(ustack, &[self.rand_num])?; + self.auxv + .insert(super::abi::AtType::Random as u8, ustack.sp().data()); + + // 实现栈的16字节对齐 + // 用当前栈顶地址减去后续要压栈的长度,得到的压栈后的栈顶地址与0xF按位与操作得到对齐要填充的字节数 + let length_to_push = (self.auxv.len() + envps.len() + 1 + argps.len() + 1 + 1) + * core::mem::align_of::(); + self.push_slice( + ustack, + &vec![0u8; (ustack.sp().data() - length_to_push) & 0xF], + )?; + // 压入auxv self.push_slice(ustack, &[null::(), null::()])?; for (&k, &v) in self.auxv.iter() { @@ -262,7 +280,6 @@ impl ProcInitInfo { // 把参数指针压入栈中 self.push_slice(ustack, &[null::()])?; self.push_slice(ustack, argps.as_slice())?; - let argv_ptr = ustack.sp(); // 把argc压入栈中 @@ -285,9 +302,9 @@ impl ProcInitInfo { return Ok(()); } - fn push_str(&self, ustack: &mut UserStack, s: &str) -> Result<(), SystemError> { - self.push_slice(ustack, &[b'\0'])?; - self.push_slice(ustack, s.as_bytes())?; + fn push_str(&self, ustack: &mut UserStack, s: &CString) -> Result<(), SystemError> { + let bytes = s.as_bytes_with_nul(); + self.push_slice(ustack, bytes)?; return Ok(()); } } diff --git a/kernel/src/process/exit.rs b/kernel/src/process/exit.rs index fd8817006..c88a9493b 100644 --- a/kernel/src/process/exit.rs +++ b/kernel/src/process/exit.rs @@ -1,6 +1,7 @@ use core::intrinsics::likely; use alloc::sync::Arc; +use log::warn; use system_error::SystemError; use crate::{ @@ -31,6 +32,7 @@ pub struct KernelWaitOption<'a> { } #[derive(Debug, Clone)] +#[allow(dead_code)] pub struct WaitIdInfo { pub pid: Pid, pub status: i32, @@ -69,11 +71,11 @@ pub fn kernel_wait4( pidtype = PidType::MAX; } else if pid < 0 { pidtype = PidType::PGID; - kwarn!("kernel_wait4: currently not support pgid, default to wait for pid\n"); + warn!("kernel_wait4: currently not support pgid, default to wait for pid\n"); pid = -pid; } else if pid == 0 { pidtype = PidType::PGID; - kwarn!("kernel_wait4: currently not support pgid, default to wait for pid\n"); + warn!("kernel_wait4: currently not support pgid, default to wait for pid\n"); pid = ProcessManager::current_pcb().pid().data() as i64; } else { pidtype = PidType::PID; @@ -167,7 +169,7 @@ fn do_wait(kwo: &mut KernelWaitOption) -> Result { schedule(SchedMode::SM_NONE); } else { // todo: 对于pgid的处理 - kwarn!("kernel_wait4: currently not support {:?}", kwo.pid_type); + warn!("kernel_wait4: currently not support {:?}", kwo.pid_type); return Err(SystemError::EINVAL); } } @@ -225,7 +227,7 @@ fn do_waitpid( } ProcessState::Exited(status) => { let pid = child_pcb.pid(); - // kdebug!("wait4: child exited, pid: {:?}, status: {status}\n", pid); + // debug!("wait4: child exited, pid: {:?}, status: {status}\n", pid); if likely(!kwo.options.contains(WaitOption::WEXITED)) { return None; @@ -244,7 +246,7 @@ fn do_waitpid( kwo.ret_status = status as i32; drop(child_pcb); - // kdebug!("wait4: to release {pid:?}"); + // debug!("wait4: to release {pid:?}"); unsafe { ProcessManager::release(pid) }; return Some(Ok(pid.into())); } diff --git a/kernel/src/process/fork.rs b/kernel/src/process/fork.rs index ee0c3e61b..2ad49fe91 100644 --- a/kernel/src/process/fork.rs +++ b/kernel/src/process/fork.rs @@ -1,6 +1,7 @@ use core::{intrinsics::unlikely, sync::atomic::Ordering}; use alloc::{string::ToString, sync::Arc}; +use log::error; use system_error::SystemError; use crate::{ @@ -84,6 +85,7 @@ bitflags! { /// /// 仅仅作为参数传递 #[derive(Debug, Clone, Copy)] +#[allow(dead_code)] pub struct KernelCloneArgs { pub flags: CloneFlags, @@ -168,7 +170,7 @@ impl ProcessManager { args.flags = clone_flags; args.exit_signal = Signal::SIGCHLD; Self::copy_process(¤t_pcb, &pcb, args, current_trapframe).map_err(|e| { - kerror!( + error!( "fork: Failed to copy process, current pid: [{:?}], new pid: [{:?}]. Error: {:?}", current_pcb.pid(), pcb.pid(), diff --git a/kernel/src/process/kthread.rs b/kernel/src/process/kthread.rs index aa6c91719..6f4d113da 100644 --- a/kernel/src/process/kthread.rs +++ b/kernel/src/process/kthread.rs @@ -10,13 +10,13 @@ use alloc::{ sync::{Arc, Weak}, }; use atomic_enum::atomic_enum; +use log::info; use system_error::SystemError; use crate::{ arch::CurrentIrqArch, exception::{irqdesc::IrqAction, InterruptArch}, init::initial_kthread::initial_kernel_thread, - kinfo, libs::{once::Once, spinlock::SpinLock}, process::{ProcessManager, ProcessState}, sched::{schedule, SchedMode}, @@ -80,6 +80,12 @@ impl KernelThreadPcbPrivate { } } +impl Default for KernelThreadPcbPrivate { + fn default() -> Self { + Self::new() + } +} + /// 内核线程的闭包,参数必须与闭包的参数一致,返回值必须是i32 /// /// 元组的第一个元素是闭包,第二个元素是闭包的参数对象 @@ -258,7 +264,7 @@ pub struct KernelThreadMechanism; impl KernelThreadMechanism { pub fn init_stage1() { assert!(ProcessManager::current_pcb().pid() == Pid::new(0)); - kinfo!("Initializing kernel thread mechanism stage1..."); + info!("Initializing kernel thread mechanism stage1..."); // 初始化第一个内核线程 @@ -290,7 +296,7 @@ impl KernelThreadMechanism { .remove(ProcessFlags::KTHREAD); drop(irq_guard); - kinfo!("Initializing kernel thread mechanism stage1 complete"); + info!("Initializing kernel thread mechanism stage1 complete"); } pub fn init_stage2() { @@ -299,7 +305,7 @@ impl KernelThreadMechanism { .contains(ProcessFlags::KTHREAD)); static INIT: Once = Once::new(); INIT.call_once(|| { - kinfo!("Initializing kernel thread mechanism stage2..."); + info!("Initializing kernel thread mechanism stage2..."); // 初始化kthreadd let closure = KernelThreadClosure::EmptyClosure((Box::new(Self::kthread_daemon), ())); let info = KernelThreadCreateInfo::new(closure, "kthreadd".to_string()); @@ -315,7 +321,7 @@ impl KernelThreadMechanism { unsafe { KTHREAD_DAEMON_PCB.replace(pcb); } - kinfo!("Initialize kernel thread mechanism stage2 complete"); + info!("Initialize kernel thread mechanism stage2 complete"); }); } diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index c22c17dde..f48d4491b 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -1,4 +1,5 @@ use core::{ + fmt, hash::Hash, hint::spin_loop, intrinsics::{likely, unlikely}, @@ -7,11 +8,14 @@ use core::{ }; use alloc::{ + ffi::CString, string::{String, ToString}, sync::{Arc, Weak}, vec::Vec, }; +use cred::INIT_CRED; use hashbrown::HashMap; +use log::{debug, error, info, warn}; use system_error::SystemError; use crate::{ @@ -28,7 +32,6 @@ use crate::{ vfs::{file::FileDescriptorVec, FileType}, }, ipc::signal_types::{SigInfo, SigPending, SignalStruct}, - kdebug, kinfo, libs::{ align::AlignedBox, casting::DowncastArc, @@ -62,10 +65,11 @@ use crate::{ }; use timer::AlarmTimer; -use self::kthread::WorkerPrivate; +use self::{cred::Cred, kthread::WorkerPrivate}; pub mod abi; pub mod c_adapter; +pub mod cred; pub mod exec; pub mod exit; pub mod fork; @@ -116,24 +120,24 @@ impl ProcessManager { unsafe { compiler_fence(Ordering::SeqCst); - kdebug!("To create address space for INIT process."); + debug!("To create address space for INIT process."); // test_buddy(); set_IDLE_PROCESS_ADDRESS_SPACE( AddressSpace::new(true).expect("Failed to create address space for INIT process."), ); - kdebug!("INIT process address space created."); + debug!("INIT process address space created."); compiler_fence(Ordering::SeqCst); }; ALL_PROCESS.lock_irqsave().replace(HashMap::new()); Self::init_switch_result(); Self::arch_init(); - kdebug!("process arch init done."); + debug!("process arch init done."); Self::init_idle(); - kdebug!("process idle init done."); + debug!("process idle init done."); unsafe { __PROCESS_MANAGEMENT_INIT_DONE = true }; - kinfo!("Process Manager initialized."); + info!("Process Manager initialized."); } fn init_switch_result() { @@ -147,6 +151,7 @@ impl ProcessManager { } /// 判断进程管理器是否已经初始化完成 + #[allow(dead_code)] pub fn initialized() -> bool { unsafe { __PROCESS_MANAGEMENT_INIT_DONE } } @@ -154,7 +159,7 @@ impl ProcessManager { /// 获取当前进程的pcb pub fn current_pcb() -> Arc { if unlikely(unsafe { !__PROCESS_MANAGEMENT_INIT_DONE }) { - kerror!("unsafe__PROCESS_MANAGEMENT_INIT_DONE == false"); + error!("unsafe__PROCESS_MANAGEMENT_INIT_DONE == false"); loop { spin_loop(); } @@ -361,7 +366,7 @@ impl ProcessManager { let parent_pcb = r.unwrap(); let r = Syscall::kill(parent_pcb.pid(), Signal::SIGCHLD as i32); if r.is_err() { - kwarn!( + warn!( "failed to send kill signal to {:?}'s parent pcb {:?}", current.pid(), parent_pcb.pid() @@ -421,7 +426,7 @@ impl ProcessManager { ProcessManager::exit_notify(); // unsafe { CurrentIrqArch::interrupt_enable() }; __schedule(SchedMode::SM_NONE); - kerror!("pid {pid:?} exited but sched again!"); + error!("pid {pid:?} exited but sched again!"); #[allow(clippy::empty_loop)] loop { spin_loop(); @@ -441,7 +446,7 @@ impl ProcessManager { // } else { // // 如果不为1就panic // let msg = format!("pcb '{:?}' is still referenced, strong count={}",pcb.pid(), Arc::strong_count(&pcb)); - // kerror!("{}", msg); + // error!("{}", msg); // panic!() // } @@ -451,7 +456,7 @@ impl ProcessManager { /// 上下文切换完成后的钩子函数 unsafe fn switch_finish_hook() { - // kdebug!("switch_finish_hook"); + // debug!("switch_finish_hook"); let prev_pcb = PROCESS_SWITCH_RESULT .as_mut() .unwrap() @@ -511,9 +516,9 @@ pub unsafe fn switch_finish_hook() { int_like!(Pid, AtomicPid, usize, AtomicUsize); -impl ToString for Pid { - fn to_string(&self) -> String { - self.0.to_string() +impl fmt::Display for Pid { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) } } @@ -646,6 +651,9 @@ pub struct ProcessControlBlock { /// 进程的robust lock列表 robust_list: RwLock>, + + /// 进程作为主体的凭证集 + cred: SpinLock, } impl ProcessControlBlock { @@ -671,14 +679,29 @@ impl ProcessControlBlock { return Self::do_create_pcb(name, kstack, true); } + /// # 函数的功能 + /// + /// 返回此函数是否是内核进程 + /// + /// # 返回值 + /// + /// 若进程是内核进程则返回true 否则返回false + pub fn is_kthread(&self) -> bool { + return matches!(self.flags(), &mut ProcessFlags::KTHREAD); + } + #[inline(never)] fn do_create_pcb(name: String, kstack: KernelStack, is_idle: bool) -> Arc { - let (pid, ppid, cwd) = if is_idle { - (Pid(0), Pid(0), "/".to_string()) + let (pid, ppid, cwd, cred) = if is_idle { + let cred = INIT_CRED.clone(); + (Pid(0), Pid(0), "/".to_string(), cred) } else { let ppid = ProcessManager::current_pcb().pid(); + let mut cred = ProcessManager::current_pcb().cred(); + cred.cap_permitted = cred.cap_ambient; + cred.cap_effective = cred.cap_ambient; let cwd = ProcessManager::current_pcb().basic().cwd(); - (Self::generate_pid(), ppid, cwd) + (Self::generate_pid(), ppid, cwd, cred) }; let basic_info = ProcessBasicInfo::new(Pid(0), ppid, name, cwd, None); @@ -713,6 +736,7 @@ impl ProcessControlBlock { thread: RwLock::new(ThreadInfo::new()), alarm_timer: SpinLock::new(None), robust_list: RwLock::new(None), + cred: SpinLock::new(cred), }; // 初始化系统调用栈 @@ -865,6 +889,11 @@ impl ProcessControlBlock { return self.basic.read().fd_table().unwrap(); } + #[inline(always)] + pub fn cred(&self) -> Cred { + self.cred.lock().clone() + } + /// 根据文件描述符序号,获取socket对象的Arc指针 /// /// ## 参数 @@ -909,11 +938,11 @@ impl ProcessControlBlock { } /// 生成进程的名字 - pub fn generate_name(program_path: &str, args: &Vec) -> String { + pub fn generate_name(program_path: &str, args: &Vec) -> String { let mut name = program_path.to_string(); for arg in args { name.push(' '); - name.push_str(arg); + name.push_str(arg.to_string_lossy().as_ref()); } return name; } @@ -946,6 +975,14 @@ impl ProcessControlBlock { return None; } + /// 判断当前进程是否有未处理的信号 + pub fn has_pending_signal(&self) -> bool { + let sig_info = self.sig_info_irqsave(); + let has_pending = sig_info.sig_pending().has_pending(); + drop(sig_info); + return has_pending; + } + pub fn sig_struct(&self) -> SpinLockGuard { self.sig_struct.lock_irqsave() } @@ -1130,6 +1167,7 @@ pub struct ProcessSchedulerInfo { } #[derive(Debug, Default)] +#[allow(dead_code)] pub struct SchedInfo { /// 记录任务在特定 CPU 上运行的次数 pub pcount: usize, @@ -1142,6 +1180,7 @@ pub struct SchedInfo { } #[derive(Debug)] +#[allow(dead_code)] pub struct PrioData { pub prio: i32, pub static_prio: i32, @@ -1367,7 +1406,7 @@ impl KernelStack { // 如果内核栈的最低地址处已经有了一个pcb,那么,这里就不再设置,直接返回错误 if unlikely(unsafe { !(*stack_bottom_ptr).is_null() }) { - kerror!("kernel stack bottom is not null: {:p}", *stack_bottom_ptr); + error!("kernel stack bottom is not null: {:p}", *stack_bottom_ptr); return Err(SystemError::EPERM); } // 将pcb的地址放到内核栈的最低地址处 diff --git a/kernel/src/process/syscall.rs b/kernel/src/process/syscall.rs index 61dfea100..1a2fec460 100644 --- a/kernel/src/process/syscall.rs +++ b/kernel/src/process/syscall.rs @@ -1,14 +1,12 @@ use core::ffi::c_void; -use alloc::{ - string::{String, ToString}, - sync::Arc, - vec::Vec, -}; +use alloc::{ffi::CString, string::ToString, sync::Arc, vec::Vec}; +use log::error; use system_error::SystemError; use super::{ abi::WaitOption, + cred::{Kgid, Kuid}, exit::kernel_wait4, fork::{CloneFlags, KernelCloneArgs}, resource::{RLimit64, RLimitID, RUsage, RUsageWho}, @@ -98,13 +96,13 @@ impl Syscall { envp: *const *const u8, frame: &mut TrapFrame, ) -> Result<(), SystemError> { - // kdebug!( + // debug!( // "execve path: {:?}, argv: {:?}, envp: {:?}\n", // path, // argv, // envp // ); - // kdebug!( + // debug!( // "before execve: strong count: {}", // Arc::strong_count(&ProcessManager::current_pcb()) // ); @@ -114,16 +112,16 @@ impl Syscall { } let x = || { - let path: String = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; - let argv: Vec = check_and_clone_cstr_array(argv)?; - let envp: Vec = check_and_clone_cstr_array(envp)?; + let path: CString = check_and_clone_cstr(path, Some(MAX_PATHLEN))?; + let argv: Vec = check_and_clone_cstr_array(argv)?; + let envp: Vec = check_and_clone_cstr_array(envp)?; Ok((path, argv, envp)) }; - let r: Result<(String, Vec, Vec), SystemError> = x(); - if let Err(e) = r { - panic!("Failed to execve: {:?}", e); - } - let (path, argv, envp) = r.unwrap(); + let (path, argv, envp) = x().inspect_err(|e: &SystemError| { + error!("Failed to execve: {:?}", e); + })?; + + let path = path.into_string().map_err(|_| SystemError::EINVAL)?; ProcessManager::current_pcb() .basic_mut() .set_name(ProcessControlBlock::generate_name(&path, &argv)); @@ -133,7 +131,7 @@ impl Syscall { // 关闭设置了O_CLOEXEC的文件描述符 let fd_table = ProcessManager::current_pcb().fd_table(); fd_table.write().close_on_exec(); - // kdebug!( + // debug!( // "after execve: strong count: {}", // Arc::strong_count(&ProcessManager::current_pcb()) // ); @@ -289,25 +287,125 @@ impl Syscall { } pub fn getuid() -> Result { - // todo: 增加credit功能之后,需要修改 - return Ok(0); + let pcb = ProcessManager::current_pcb(); + return Ok(pcb.cred.lock().uid.data()); } pub fn getgid() -> Result { - // todo: 增加credit功能之后,需要修改 - return Ok(0); + let pcb = ProcessManager::current_pcb(); + return Ok(pcb.cred.lock().gid.data()); } pub fn geteuid() -> Result { - // todo: 增加credit功能之后,需要修改 - return Ok(0); + let pcb = ProcessManager::current_pcb(); + return Ok(pcb.cred.lock().euid.data()); } pub fn getegid() -> Result { - // todo: 增加credit功能之后,需要修改 + let pcb = ProcessManager::current_pcb(); + return Ok(pcb.cred.lock().egid.data()); + } + + pub fn setuid(uid: usize) -> Result { + let pcb = ProcessManager::current_pcb(); + let mut guard = pcb.cred.lock(); + + if guard.uid.data() == 0 { + guard.setuid(uid); + guard.seteuid(uid); + guard.setsuid(uid); + } else if uid == guard.uid.data() || uid == guard.suid.data() { + guard.seteuid(uid); + } else { + return Err(SystemError::EPERM); + } + + return Ok(0); + } + + pub fn setgid(gid: usize) -> Result { + let pcb = ProcessManager::current_pcb(); + let mut guard = pcb.cred.lock(); + + if guard.egid.data() == 0 { + guard.setgid(gid); + guard.setegid(gid); + guard.setsgid(gid); + guard.setfsgid(gid); + } else if guard.gid.data() == gid || guard.sgid.data() == gid { + guard.setegid(gid); + guard.setfsgid(gid); + } else { + return Err(SystemError::EPERM); + } + return Ok(0); } + pub fn seteuid(euid: usize) -> Result { + let pcb = ProcessManager::current_pcb(); + let mut guard = pcb.cred.lock(); + + if euid == usize::MAX || (euid == guard.euid.data() && euid == guard.fsuid.data()) { + return Ok(0); + } + + if euid != usize::MAX { + guard.seteuid(euid); + } + + let euid = guard.euid.data(); + guard.setfsuid(euid); + + return Ok(0); + } + + pub fn setegid(egid: usize) -> Result { + let pcb = ProcessManager::current_pcb(); + let mut guard = pcb.cred.lock(); + + if egid == usize::MAX || (egid == guard.egid.data() && egid == guard.fsgid.data()) { + return Ok(0); + } + + if egid != usize::MAX { + guard.setegid(egid); + } + + let egid = guard.egid.data(); + guard.setfsgid(egid); + + return Ok(0); + } + + pub fn setfsuid(fsuid: usize) -> Result { + let fsuid = Kuid::new(fsuid); + + let pcb = ProcessManager::current_pcb(); + let mut guard = pcb.cred.lock(); + let old_fsuid = guard.fsuid; + + if fsuid == guard.uid || fsuid == guard.euid || fsuid == guard.suid { + guard.setfsuid(fsuid.data()); + } + + Ok(old_fsuid.data()) + } + + pub fn setfsgid(fsgid: usize) -> Result { + let fsgid = Kgid::new(fsgid); + + let pcb = ProcessManager::current_pcb(); + let mut guard = pcb.cred.lock(); + let old_fsgid = guard.fsgid; + + if fsgid == guard.gid || fsgid == guard.egid || fsgid == guard.sgid { + guard.setfsgid(fsgid.data()); + } + + Ok(old_fsgid.data()) + } + pub fn get_rusage(who: i32, rusage: *mut RUsage) -> Result { let who = RUsageWho::try_from(who)?; let mut writer = UserBufferWriter::new(rusage, core::mem::size_of::(), true)?; diff --git a/kernel/src/process/timer.rs b/kernel/src/process/timer.rs index 7099d0748..d206fbd23 100644 --- a/kernel/src/process/timer.rs +++ b/kernel/src/process/timer.rs @@ -84,7 +84,7 @@ impl AlarmTimer { >::from(Duration::from_secs(self.expired_second)) .timer_jiffies(); let remain_second = Duration::from(Jiffies::new(end_jiffies - now_jiffies)); - // kdebug!( + // debug!( // "end: {} - now: {} = remain: {}", // end_jiffies, // now_jiffies, diff --git a/kernel/src/sched/completion.rs b/kernel/src/sched/completion.rs index 2d3450f2d..61488c25f 100644 --- a/kernel/src/sched/completion.rs +++ b/kernel/src/sched/completion.rs @@ -7,8 +7,8 @@ use crate::{ time::timer::schedule_timeout, }; -const COMPLETE_ALL: u32 = core::u32::MAX; -const MAX_TIMEOUT: i64 = core::i64::MAX; +const COMPLETE_ALL: u32 = u32::MAX; +const MAX_TIMEOUT: i64 = i64::MAX; #[derive(Debug)] pub struct Completion { diff --git a/kernel/src/sched/fair.rs b/kernel/src/sched/fair.rs index 0428b1b93..eb8de762d 100644 --- a/kernel/src/sched/fair.rs +++ b/kernel/src/sched/fair.rs @@ -144,7 +144,10 @@ impl FairSchedEntity { #[allow(clippy::mut_from_ref)] pub fn force_mut(&self) -> &mut Self { - unsafe { &mut *(self as *const Self as usize as *mut Self) } + unsafe { + let p = self as *const Self as usize; + (p as *mut Self).as_mut().unwrap() + } } /// 判断是否是进程持有的调度实体 @@ -416,7 +419,11 @@ impl CfsRunQueue { #[inline] #[allow(clippy::mut_from_ref)] pub fn force_mut(&self) -> &mut Self { - unsafe { &mut *(self as *const Self as usize as *mut Self) } + unsafe { + (self as *const Self as usize as *mut Self) + .as_mut() + .unwrap() + } } #[inline] @@ -567,7 +574,7 @@ impl CfsRunQueue { fence(Ordering::SeqCst); if unlikely(now <= curr.exec_start) { - // kwarn!( + // warn!( // "update_current return now <= curr.exec_start now {now} execstart {}", // curr.exec_start // ); @@ -596,11 +603,11 @@ impl CfsRunQueue { fn account_cfs_rq_runtime(&mut self, delta_exec: u64) { if likely(self.runtime_remaining > delta_exec) { self.runtime_remaining -= delta_exec; - // kerror!("runtime_remaining {}", self.runtime_remaining); + // error!("runtime_remaining {}", self.runtime_remaining); return; } - // kwarn!( + // warn!( // "runtime_remaining {} delta exec {delta_exec} nr_running {}", // self.runtime_remaining, // self.nr_running @@ -609,14 +616,14 @@ impl CfsRunQueue { self.runtime_remaining = 5000 * NSEC_PER_MSEC as u64; if likely(self.current().is_some()) && self.nr_running > 1 { - // kerror!("account_cfs_rq_runtime"); + // error!("account_cfs_rq_runtime"); self.rq().resched_current(); } } /// 计算deadline,如果vruntime到期会重调度 pub fn update_deadline(&mut self, se: &Arc) { - // kerror!("vruntime {} deadline {}", se.vruntime, se.deadline); + // error!("vruntime {} deadline {}", se.vruntime, se.deadline); if se.vruntime < se.deadline { return; } @@ -1131,7 +1138,7 @@ impl CfsRunQueue { self.avg_vruntime_add(se); se.force_mut().min_deadline = se.deadline; self.entities.insert(se.vruntime, se.clone()); - // kwarn!( + // warn!( // "enqueue pcb {:?} cfsrq {:?}", // se.pcb().pid(), // self.entities @@ -1153,7 +1160,7 @@ impl CfsRunQueue { } fn inner_dequeue_entity(&mut self, se: &Arc) { - // kwarn!( + // warn!( // "before dequeue pcb {:?} cfsrq {:?}", // se.pcb().pid(), // self.entities @@ -1196,7 +1203,7 @@ impl CfsRunQueue { // ) // .as_bytes(), // ); - // kwarn!( + // warn!( // "after dequeue pcb {:?}(real: {:?}) cfsrq {:?}", // se.pcb().pid(), // remove.pcb().pid(), @@ -1366,6 +1373,11 @@ impl CfsRunQueue { } } +impl Default for CfsRunQueue { + fn default() -> Self { + Self::new() + } +} pub struct CompletelyFairScheduler; impl CompletelyFairScheduler { diff --git a/kernel/src/sched/mod.rs b/kernel/src/sched/mod.rs index c50782e82..9a0f2ff7e 100644 --- a/kernel/src/sched/mod.rs +++ b/kernel/src/sched/mod.rs @@ -111,7 +111,9 @@ pub trait Scheduler { flags: WakeupFlags, ); + #[allow(dead_code)] /// ## 选择接下来最适合运行的任务 + #[allow(dead_code)] fn pick_task(rq: &mut CpuRunQueue) -> Option>; /// ## 选择接下来最适合运行的任务 @@ -272,6 +274,7 @@ pub trait SchedArch { /// 开启当前核心的调度 fn enable_sched_local(); /// 关闭当前核心的调度 + #[allow(dead_code)] fn disable_sched_local(); /// 在第一次开启调度之前,进行初始化工作。 @@ -363,14 +366,22 @@ impl CpuRunQueue { { // 在本cpu已上锁则可以直接拿 ( - unsafe { &mut *(self as *const Self as usize as *mut Self) }, + unsafe { + (self as *const Self as usize as *mut Self) + .as_mut() + .unwrap() + }, None, ) } else { // 否则先上锁再拿 let guard = self.lock(); ( - unsafe { &mut *(self as *const Self as usize as *mut Self) }, + unsafe { + (self as *const Self as usize as *mut Self) + .as_mut() + .unwrap() + }, Some(guard), ) } @@ -510,7 +521,7 @@ impl CpuRunQueue { let delta = clock - self.clock; self.clock += delta; - // kerror!("clock {}", self.clock); + // error!("clock {}", self.clock); self.update_rq_clock_task(delta); } @@ -518,7 +529,7 @@ impl CpuRunQueue { pub fn update_rq_clock_task(&mut self, mut delta: u64) { let mut irq_delta = irq_time_read(self.cpu) - self.prev_irq_time; // if self.cpu == 0 { - // kerror!( + // error!( // "cpu 0 delta {delta} irq_delta {} irq_time_read(self.cpu) {} self.prev_irq_time {}", // irq_delta, // irq_time_read(self.cpu), @@ -542,7 +553,7 @@ impl CpuRunQueue { self.clock_task += delta; compiler_fence(Ordering::SeqCst); // if self.cpu == 0 { - // kerror!("cpu {} clock_task {}", self.cpu, self.clock_task); + // error!("cpu {} clock_task {}", self.cpu, self.clock_task); // } // todo: pelt? } @@ -656,7 +667,7 @@ impl CpuRunQueue { if let Some(pcb) = p.as_ref() { return pcb.clone(); } else { - // kerror!( + // error!( // "pick idle cfs rq {:?}", // self.cfs_rq() // .entities @@ -849,7 +860,7 @@ pub fn __schedule(sched_mod: SchedMode) { // .map(|x| { x.1.vruntime }) // .collect::>(), // ); - // kwarn!( + // warn!( // "before cfs rq {:?} prev {:?}", // rq.cfs // .entities @@ -859,12 +870,12 @@ pub fn __schedule(sched_mod: SchedMode) { // prev.pid() // ); - // kerror!("prev pid {:?} {:?}", prev.pid(), prev.sched_info().policy()); + // error!("prev pid {:?} {:?}", prev.pid(), prev.sched_info().policy()); if !sched_mod.contains(SchedMode::SM_MASK_PREEMPT) && prev.sched_info().policy() != SchedPolicy::IDLE && prev.sched_info().inner_lock_read_irqsave().is_mark_sleep() { - // kwarn!("deactivate_task prev {:?}", prev.pid()); + // warn!("deactivate_task prev {:?}", prev.pid()); // TODO: 这里需要处理信号 // https://code.dragonos.org.cn/xref/linux-6.6.21/kernel/sched/core.c?r=&mo=172979&fi=6578#6630 rq.deactivate_task( @@ -889,13 +900,13 @@ pub fn __schedule(sched_mod: SchedMode) { // .collect::>(), // ); - // kerror!("next {:?}", next.pid()); + // error!("next {:?}", next.pid()); prev.flags().remove(ProcessFlags::NEED_SCHEDULE); fence(Ordering::SeqCst); if likely(!Arc::ptr_eq(&prev, &next)) { rq.set_current(Arc::downgrade(&next)); - // kwarn!( + // warn!( // "switch_process prev {:?} next {:?} sched_mode {sched_mod:?}", // prev.pid(), // next.pid() diff --git a/kernel/src/smp/cpu/mod.rs b/kernel/src/smp/cpu/mod.rs index 62c0b4d14..186184ef5 100644 --- a/kernel/src/smp/cpu/mod.rs +++ b/kernel/src/smp/cpu/mod.rs @@ -1,6 +1,7 @@ use core::sync::atomic::AtomicU32; use alloc::{sync::Arc, vec::Vec}; +use log::{debug, error, info}; use system_error::SystemError; use crate::{ @@ -202,14 +203,14 @@ impl SmpCpuManager { continue; } - kdebug!("Bring up CPU {}", cpu_id.data()); + debug!("Bring up CPU {}", cpu_id.data()); if let Err(e) = self.cpu_up(cpu_id, CpuHpState::Online) { - kerror!("Failed to bring up CPU {}: {:?}", cpu_id.data(), e); + error!("Failed to bring up CPU {}: {:?}", cpu_id.data(), e); } } - kinfo!("All non-boot CPUs have been brought up"); + info!("All non-boot CPUs have been brought up"); } fn cpu_up(&self, cpu_id: ProcessorId, target_state: CpuHpState) -> Result<(), SystemError> { @@ -218,7 +219,7 @@ impl SmpCpuManager { } let cpu_state = self.cpuhp_state(cpu_id).state; - kdebug!( + debug!( "cpu_up: cpu_id: {}, cpu_state: {:?}, target_state: {:?}", cpu_id.data(), cpu_state, diff --git a/kernel/src/smp/init.rs b/kernel/src/smp/init.rs index 689116124..27d622093 100644 --- a/kernel/src/smp/init.rs +++ b/kernel/src/smp/init.rs @@ -1,3 +1,5 @@ +use log::info; + use crate::{ arch::{syscall::arch_syscall_init, CurrentIrqArch, CurrentSchedArch}, exception::InterruptArch, @@ -22,6 +24,6 @@ pub fn smp_ap_start_stage2() -> ! { #[inline(never)] fn do_ap_start_stage2() { - kinfo!("Successfully started AP {}", smp_get_processor_id().data()); + info!("Successfully started AP {}", smp_get_processor_id().data()); arch_syscall_init().expect("AP core failed to initialize syscall"); } diff --git a/kernel/src/syscall/misc.rs b/kernel/src/syscall/misc.rs index 7398e85a8..f78029291 100644 --- a/kernel/src/syscall/misc.rs +++ b/kernel/src/syscall/misc.rs @@ -5,6 +5,7 @@ use crate::{ }; use alloc::vec::Vec; use core::cmp; +use log::warn; use system_error::SystemError; use super::{user_access::UserBufferWriter, Syscall}; @@ -59,7 +60,7 @@ impl Syscall { } pub fn umask(_mask: u32) -> Result { - kwarn!("SYS_UMASK has not yet been implemented\n"); + warn!("SYS_UMASK has not yet been implemented\n"); return Ok(0o777); } diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index e6d03a67e..574e34a8b 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -20,6 +20,7 @@ use crate::{ syscall::user_access::check_and_clone_cstr, }; +use log::{info, warn}; use num_traits::FromPrimitive; use system_error::SystemError; @@ -28,10 +29,9 @@ use crate::{ filesystem::vfs::{ fcntl::{AtFlags, FcntlCommand}, file::FileMode, - syscall::{ModeType, PosixKstat}, + syscall::{ModeType, PosixKstat, UtimensFlags}, MAX_PATHLEN, }, - kinfo, libs::align::page_align_up, mm::{verify_area, MemoryManagementArch, VirtAddr}, net::syscall::SockAddr, @@ -69,9 +69,9 @@ impl Syscall { if prev { panic!("Cannot initialize syscall more than once!"); } - kinfo!("Initializing syscall..."); + info!("Initializing syscall..."); let r = crate::arch::syscall::arch_syscall_init(); - kinfo!("Syscall init successfully!"); + info!("Syscall init successfully!"); return r; } @@ -369,7 +369,7 @@ impl Syscall { SYS_KILL => { let pid = Pid::new(args[0]); let sig = args[1] as c_int; - // kdebug!("KILL SYSCALL RECEIVED"); + // debug!("KILL SYSCALL RECEIVED"); Self::kill(pid, sig) } @@ -383,7 +383,7 @@ impl Syscall { SYS_GETPID => Self::getpid().map(|pid| pid.into()), SYS_SCHED => { - kwarn!("syscall sched"); + warn!("syscall sched"); schedule(SchedMode::SM_NONE); Ok(0) } @@ -650,7 +650,7 @@ impl Syscall { Err(SystemError::EINVAL) }; - // kdebug!("FCNTL: fd: {}, cmd: {:?}, arg: {}, res: {:?}", fd, cmd, arg, res); + // debug!("FCNTL: fd: {}, cmd: {:?}, arg: {}, res: {:?}", fd, cmd, arg, res); res } @@ -658,7 +658,7 @@ impl Syscall { let fd = args[0] as i32; let len = args[1]; let res = Self::ftruncate(fd, len); - // kdebug!("FTRUNCATE: fd: {}, len: {}, res: {:?}", fd, len, res); + // debug!("FTRUNCATE: fd: {}, len: {}, res: {:?}", fd, len, res); res } @@ -835,33 +835,35 @@ impl Syscall { #[cfg(target_arch = "x86_64")] SYS_POLL => { - kwarn!("SYS_POLL has not yet been implemented"); + warn!("SYS_POLL has not yet been implemented"); Ok(0) } SYS_SETPGID => { - kwarn!("SYS_SETPGID has not yet been implemented"); + warn!("SYS_SETPGID has not yet been implemented"); Ok(0) } SYS_RT_SIGPROCMASK => { - kwarn!("SYS_RT_SIGPROCMASK has not yet been implemented"); + warn!("SYS_RT_SIGPROCMASK has not yet been implemented"); Ok(0) } SYS_TKILL => { - kwarn!("SYS_TKILL has not yet been implemented"); + warn!("SYS_TKILL has not yet been implemented"); Ok(0) } SYS_SIGALTSTACK => { - kwarn!("SYS_SIGALTSTACK has not yet been implemented"); + warn!("SYS_SIGALTSTACK has not yet been implemented"); Ok(0) } SYS_EXIT_GROUP => { - kwarn!("SYS_EXIT_GROUP has not yet been implemented"); - Ok(0) + let exit_code = args[0]; + Self::exit(exit_code) + // warn!("SYS_EXIT_GROUP has not yet been implemented"); + // Ok(0) } SYS_MADVISE => { @@ -875,7 +877,6 @@ impl Syscall { } SYS_GETTID => Self::gettid().map(|tid| tid.into()), - SYS_GETUID => Self::getuid(), SYS_SYSLOG => { let syslog_action_type = args[0]; @@ -889,27 +890,29 @@ impl Syscall { Self::do_syslog(syslog_action_type, user_buf, len) } + SYS_GETUID => Self::getuid(), SYS_GETGID => Self::getgid(), - SYS_SETUID => { - kwarn!("SYS_SETUID has not yet been implemented"); - Ok(0) - } - SYS_SETGID => { - kwarn!("SYS_SETGID has not yet been implemented"); - Ok(0) - } + SYS_SETUID => Self::setuid(args[0]), + SYS_SETGID => Self::setgid(args[0]), + + SYS_GETEUID => Self::geteuid(), + SYS_GETEGID => Self::getegid(), + SYS_SETRESUID => Self::seteuid(args[1]), + SYS_SETRESGID => Self::setegid(args[1]), + + SYS_SETFSUID => Self::setfsuid(args[0]), + SYS_SETFSGID => Self::setfsgid(args[0]), + SYS_SETSID => { - kwarn!("SYS_SETSID has not yet been implemented"); + warn!("SYS_SETSID has not yet been implemented"); Ok(0) } - SYS_GETEUID => Self::geteuid(), - SYS_GETEGID => Self::getegid(), + SYS_GETRUSAGE => { let who = args[0] as c_int; let rusage = args[1] as *mut RUsage; Self::get_rusage(who, rusage) } - #[cfg(target_arch = "x86_64")] SYS_READLINK => { let path = args[0] as *const u8; @@ -975,17 +978,17 @@ impl Syscall { } SYS_FCHOWN => { - kwarn!("SYS_FCHOWN has not yet been implemented"); + warn!("SYS_FCHOWN has not yet been implemented"); Ok(0) } SYS_FSYNC => { - kwarn!("SYS_FSYNC has not yet been implemented"); + warn!("SYS_FSYNC has not yet been implemented"); Ok(0) } SYS_RSEQ => { - kwarn!("SYS_RSEQ has not yet been implemented"); + warn!("SYS_RSEQ has not yet been implemented"); Ok(0) } @@ -1103,7 +1106,40 @@ impl Syscall { Self::shmctl(id, cmd, user_buf, from_user) } - + SYS_MSYNC => { + let start = page_align_up(args[0]); + let len = page_align_up(args[1]); + let flags = args[2]; + Self::msync(VirtAddr::new(start), len, flags) + } + SYS_UTIMENSAT => Self::sys_utimensat( + args[0] as i32, + args[1] as *const u8, + args[2] as *const PosixTimeSpec, + args[3] as u32, + ), + #[cfg(target_arch = "x86_64")] + SYS_FUTIMESAT => { + let flags = UtimensFlags::empty(); + Self::sys_utimensat( + args[0] as i32, + args[1] as *const u8, + args[2] as *const PosixTimeSpec, + flags.bits(), + ) + } + #[cfg(target_arch = "x86_64")] + SYS_UTIMES => Self::sys_utimes(args[0] as *const u8, args[1] as *const PosixTimeval), + #[cfg(target_arch = "x86_64")] + SYS_EVENTFD => { + let initval = args[0] as u32; + Self::sys_eventfd(initval, 0) + } + SYS_EVENTFD2 => { + let initval = args[0] as u32; + let flags = args[1] as u32; + Self::sys_eventfd(initval, flags) + } _ => panic!("Unsupported syscall ID: {}", syscall_num), }; @@ -1123,7 +1159,9 @@ impl Syscall { back_color: u32, ) -> Result { // todo: 删除这个系统调用 - let s = check_and_clone_cstr(s, Some(4096))?; + let s = check_and_clone_cstr(s, Some(4096))? + .into_string() + .map_err(|_| SystemError::EINVAL)?; let fr = (front_color & 0x00ff0000) >> 16; let fg = (front_color & 0x0000ff00) >> 8; let fb = front_color & 0x000000ff; diff --git a/kernel/src/syscall/user_access.rs b/kernel/src/syscall/user_access.rs index 664931561..18a74764e 100644 --- a/kernel/src/syscall/user_access.rs +++ b/kernel/src/syscall/user_access.rs @@ -2,10 +2,11 @@ use core::{ mem::size_of, + num::NonZero, slice::{from_raw_parts, from_raw_parts_mut}, }; -use alloc::{string::String, vec::Vec}; +use alloc::{ffi::CString, vec::Vec}; use crate::mm::{verify_area, VirtAddr}; @@ -70,10 +71,11 @@ pub unsafe fn copy_from_user(dst: &mut [u8], src: VirtAddr) -> Result, -) -> Result { +) -> Result { if user.is_null() { return Err(SystemError::EFAULT); } @@ -93,9 +95,12 @@ pub fn check_and_clone_cstr( if c[0] == 0 { break; } - buffer.push(c[0]); + buffer.push(NonZero::new(c[0]).ok_or(SystemError::EINVAL)?); } - String::from_utf8(buffer).map_err(|_| SystemError::EFAULT) + + let cstr = CString::from(buffer); + + return Ok(cstr); } /// 检查并从用户态拷贝一个 C 字符串数组 @@ -112,11 +117,11 @@ pub fn check_and_clone_cstr( /// ## 错误 /// /// - `EFAULT`:用户态地址不合法 -pub fn check_and_clone_cstr_array(user: *const *const u8) -> Result, SystemError> { +pub fn check_and_clone_cstr_array(user: *const *const u8) -> Result, SystemError> { if user.is_null() { Ok(Vec::new()) } else { - // kdebug!("check_and_clone_cstr_array: {:p}\n", user); + // debug!("check_and_clone_cstr_array: {:p}\n", user); let mut buffer = Vec::new(); for i in 0.. { let addr = unsafe { user.add(i) }; @@ -129,7 +134,7 @@ pub fn check_and_clone_cstr_array(user: *const *const u8) -> Result, let dst = core::mem::transmute::<[u8; size_of::()], [usize; 1]>(dst); str_ptr = dst[0] as *const u8; - // kdebug!("str_ptr: {:p}, addr:{addr:?}\n", str_ptr); + // debug!("str_ptr: {:p}, addr:{addr:?}\n", str_ptr); } if str_ptr.is_null() { diff --git a/kernel/src/time/clocksource.rs b/kernel/src/time/clocksource.rs index df0ed81af..6565038ce 100644 --- a/kernel/src/time/clocksource.rs +++ b/kernel/src/time/clocksource.rs @@ -11,6 +11,7 @@ use alloc::{ vec::Vec, }; use lazy_static::__Deref; +use log::{debug, info}; use system_error::SystemError; use unified_init::macros::unified_init; @@ -18,7 +19,6 @@ use crate::{ arch::CurrentIrqArch, exception::InterruptArch, init::initcall::INITCALL_LATE, - kdebug, kinfo, libs::spinlock::SpinLock, process::{ kthread::{KernelThreadClosure, KernelThreadMechanism}, @@ -41,7 +41,7 @@ lazy_static! { pub static ref WATCHDOG_LIST: SpinLock>> = SpinLock::new(LinkedList::new()); - pub static ref CLOCKSOUCE_WATCHDOG:SpinLock = SpinLock::new(ClocksouceWatchdog::new()); + pub static ref CLOCKSOURCE_WATCHDOG:SpinLock = SpinLock::new(ClocksouceWatchdog::new()); pub static ref OVERRIDE_NAME: SpinLock = SpinLock::new(String::from("")); @@ -53,7 +53,7 @@ static mut WATCHDOG_KTHREAD: Option> = None; /// 正在被使用时钟源 pub static CUR_CLOCKSOURCE: SpinLock>> = SpinLock::new(None); /// 是否完成加载 -pub static mut FINISHED_BOOTING: AtomicBool = AtomicBool::new(false); +pub static FINISHED_BOOTING: AtomicBool = AtomicBool::new(false); /// Interval: 0.5sec Threshold: 0.0625s /// 系统节拍率 @@ -138,8 +138,6 @@ pub struct ClocksouceWatchdog { watchdog: Option>, /// 检查器是否在工作的标志 is_running: bool, - /// 上一次检查的时刻 - last_check: CycleNum, /// 定时监视器的过期时间 timer_expires: u64, } @@ -148,7 +146,6 @@ impl ClocksouceWatchdog { Self { watchdog: None, is_running: false, - last_check: CycleNum(0), timer_expires: 0, } } @@ -168,7 +165,13 @@ impl ClocksouceWatchdog { // 生成一个定时器 let wd_timer_func: Box = Box::new(WatchdogTimerFunc {}); self.timer_expires += clock() + WATCHDOG_INTERVAL; - self.last_check = self.watchdog.as_ref().unwrap().clone().read(); + let mut wd_data = self.watchdog.as_ref().unwrap().clone().clocksource_data(); + wd_data.watchdog_last = self.watchdog.as_ref().unwrap().clone().read(); + self.watchdog + .as_ref() + .unwrap() + .update_clocksource_data(wd_data) + .expect("clocksource_start_watchdog: failed to update watchdog data"); let wd_timer = Timer::new(wd_timer_func, self.timer_expires); wd_timer.activate(); self.is_running = true; @@ -204,10 +207,12 @@ pub trait Clocksource: Send + Sync + Debug { return Err(SystemError::ENOSYS); } /// optional function to disable the clocksource + #[allow(dead_code)] fn disable(&self) -> Result<(), SystemError> { return Err(SystemError::ENOSYS); } /// vsyscall based read + #[allow(dead_code)] fn vread(&self) -> Result { return Err(SystemError::ENOSYS); } @@ -271,15 +276,14 @@ impl dyn Clocksource { let cs_data_guard = self.clocksource_data(); let mut max_cycles: u64; - // 这里我有问题,不知道要不要修改,暂时不修改它 - max_cycles = (1 << (63 - (log2(cs_data_guard.mult) + 1))) as u64; + max_cycles = (1 << (63 - (log2(cs_data_guard.mult + cs_data_guard.maxadj) + 1))) as u64; max_cycles = max_cycles.min(cs_data_guard.mask.bits); let max_nsecs = clocksource_cyc2ns( CycleNum(max_cycles), - cs_data_guard.mult, + cs_data_guard.mult - cs_data_guard.maxadj, cs_data_guard.shift, ); - return max_nsecs - (max_nsecs >> 5); + return max_nsecs - (max_nsecs >> 3); } /// # 计算时钟源的mult和shift,以便将一个时钟源的频率转换为另一个时钟源的频率 @@ -318,9 +322,8 @@ impl dyn Clocksource { /// # 更新时钟源频率,初始化mult/shift 和 max_idle_ns fn clocksource_update_freq_scale(&self, scale: u32, freq: u32) -> Result<(), SystemError> { - let mut cs_data = self.clocksource_data(); - if freq != 0 { + let mut cs_data = self.clocksource_data(); let mut sec: u64 = cs_data.mask.bits(); sec /= freq as u64; @@ -335,8 +338,10 @@ impl dyn Clocksource { self.clocks_calc_mult_shift(freq, NSEC_PER_SEC / scale, sec as u32 * scale); cs_data.set_mult(mult); cs_data.set_shift(shift); + self.update_clocksource_data(cs_data)?; } + let mut cs_data = self.clocksource_data(); if scale != 0 && freq != 0 && cs_data.uncertainty_margin == 0 { cs_data.set_uncertainty_margin(NSEC_PER_SEC / (scale * freq)); if cs_data.uncertainty_margin < 2 * WATCHDOG_MAX_SKEW { @@ -348,10 +353,25 @@ impl dyn Clocksource { // 确保时钟源没有太大的mult值造成溢出 cs_data.set_maxadj(self.clocksource_max_adjustment()); + self.update_clocksource_data(cs_data)?; + while freq != 0 + && (self.clocksource_data().mult + self.clocksource_data().maxadj + < self.clocksource_data().mult + || self.clocksource_data().mult - self.clocksource_data().maxadj + > self.clocksource_data().mult) + { + let mut cs_data = self.clocksource_data(); + cs_data.set_mult(cs_data.mult >> 1); + cs_data.set_shift(cs_data.shift - 1); + self.update_clocksource_data(cs_data)?; + let mut cs_data = self.clocksource_data(); + cs_data.set_maxadj(self.clocksource_max_adjustment()); + self.update_clocksource_data(cs_data)?; + } + let mut cs_data = self.clocksource_data(); let ns = self.clocksource_max_deferment(); cs_data.set_max_idle_ns(ns as u32); - self.update_clocksource_data(cs_data)?; return Ok(()); @@ -378,7 +398,7 @@ impl dyn Clocksource { .expect("register: failed to enqueue watchdog list"); // 选择一个最好的时钟源 clocksource_select(); - kdebug!("clocksource_register successfully"); + debug!("clocksource_register successfully"); return Ok(()); } @@ -387,7 +407,7 @@ impl dyn Clocksource { // 根据rating由大到小排序 let cs_data = self.clocksource_data(); let mut list_guard = CLOCKSOURCE_LIST.lock(); - let mut spilt_pos: usize = 0; + let mut spilt_pos: usize = list_guard.len(); for (pos, ele) in list_guard.iter().enumerate() { if ele.clocksource_data().rating < cs_data.rating { spilt_pos = pos; @@ -398,7 +418,7 @@ impl dyn Clocksource { let cs = self.clocksource(); list_guard.push_back(cs); list_guard.append(&mut temp_list); - // kdebug!( + // debug!( // "CLOCKSOURCE_LIST len = {:?},clocksource_enqueue sccessfully", // list_guard.len() // ); @@ -445,7 +465,7 @@ impl dyn Clocksource { drop(list_guard); // 对比当前注册的时间源的精度和监视器的精度 - let mut cs_watchdog = CLOCKSOUCE_WATCHDOG.lock_irqsave(); + let mut cs_watchdog = CLOCKSOURCE_WATCHDOG.lock_irqsave(); if cs_watchdog.watchdog.is_none() || cs_data.rating > cs_watchdog @@ -473,10 +493,9 @@ impl dyn Clocksource { pub fn set_unstable(&self, delta: i64) -> Result { let mut cs_data = self.clocksource_data(); // 打印出unstable的时钟源信息 - kdebug!( + debug!( "clocksource :{:?} is unstable, its delta is {:?}", - cs_data.name, - delta + cs_data.name, delta ); cs_data.flags.remove( ClocksourceFlags::CLOCK_SOURCE_VALID_FOR_HRES | ClocksourceFlags::CLOCK_SOURCE_WATCHDOG, @@ -487,7 +506,7 @@ impl dyn Clocksource { self.update_clocksource_data(cs_data)?; // 启动watchdog线程 进行后续处理 - if unsafe { FINISHED_BOOTING.load(Ordering::Relaxed) } { + if FINISHED_BOOTING.load(Ordering::Relaxed) { // TODO 在实现了工作队列后,将启动线程换成schedule work run_watchdog_kthread(); } @@ -497,7 +516,7 @@ impl dyn Clocksource { /// # 将时间源从监视链表中弹出 fn clocksource_dequeue_watchdog(&self) { let data = self.clocksource_data(); - let mut locked_watchdog = CLOCKSOUCE_WATCHDOG.lock_irqsave(); + let mut locked_watchdog = CLOCKSOURCE_WATCHDOG.lock_irqsave(); let watchdog = locked_watchdog .get_watchdog() .clone() @@ -653,10 +672,14 @@ pub struct ClocksourceData { pub max_idle_ns: u32, pub flags: ClocksourceFlags, pub watchdog_last: CycleNum, + /// 用于watchdog机制中的字段,记录主时钟源上一次被读取的周期数 + pub cs_last: CycleNum, // 用于描述时钟源的不确定性边界,时钟源读取的时间可能存在的不确定性和误差范围 pub uncertainty_margin: u32, // 最大的时间调整量 pub maxadj: u32, + /// 上一次读取时钟源时的周期数 + pub cycle_last: CycleNum, } impl ClocksourceData { @@ -682,8 +705,10 @@ impl ClocksourceData { max_idle_ns, flags, watchdog_last: CycleNum(0), + cs_last: CycleNum(0), uncertainty_margin, maxadj, + cycle_last: CycleNum(0), }; return csd; } @@ -728,6 +753,9 @@ impl ClocksourceData { /// converts clocksource cycles to nanoseconds /// pub fn clocksource_cyc2ns(cycles: CycleNum, mult: u32, shift: u32) -> u64 { + // info!(""); + // info!("cycles = {:?}, mult = {:?}, shift = {:?}", cycles, mult, shift); + // info!("ret = {:?}", (cycles.data() * mult as u64) >> shift); return (cycles.data() * mult as u64) >> shift; } @@ -740,7 +768,7 @@ pub fn clocksource_resume() { match ele.resume() { Ok(_) => continue, Err(_) => { - kdebug!("clocksource {:?} resume failed", data.name); + debug!("clocksource {:?} resume failed", data.name); } } } @@ -756,7 +784,7 @@ pub fn clocksource_suspend() { match ele.suspend() { Ok(_) => continue, Err(_) => { - kdebug!("clocksource {:?} suspend failed", data.name); + debug!("clocksource {:?} suspend failed", data.name); } } } @@ -769,25 +797,15 @@ pub fn clocksource_suspend() { /// * `Ok()` - 检查完成 /// * `Err(SystemError)` - 错误码 pub fn clocksource_watchdog() -> Result<(), SystemError> { - let mut cs_watchdog = CLOCKSOUCE_WATCHDOG.lock_irqsave(); - // kdebug!("clocksource_watchdog start"); + let cs_watchdog = CLOCKSOURCE_WATCHDOG.lock_irqsave(); + // debug!("clocksource_watchdog start"); // watchdog没有在运行的话直接退出 if !cs_watchdog.is_running || cs_watchdog.watchdog.is_none() { - // kdebug!("is_running = {:?},watchdog = {:?}", cs_watchdog.is_running, cs_watchdog.watchdog); + // debug!("is_running = {:?},watchdog = {:?}", cs_watchdog.is_running, cs_watchdog.watchdog); return Ok(()); } - let cur_watchdog = cs_watchdog.watchdog.as_ref().unwrap().clone(); - let cur_wd_data = cur_watchdog.as_ref().clocksource_data(); - let cur_wd_nowclock = cur_watchdog.as_ref().read().data(); - - let wd_last = cs_watchdog.last_check.data(); - let wd_dev_nsec = clocksource_cyc2ns( - CycleNum((cur_wd_nowclock - wd_last) & cur_wd_data.mask.bits), - cur_wd_data.mult, - cur_wd_data.shift, - ); - cs_watchdog.last_check = CycleNum(cur_wd_nowclock); + drop(cs_watchdog); let watchdog_list = WATCHDOG_LIST.lock_irqsave(); for cs in watchdog_list.iter() { @@ -797,47 +815,71 @@ pub fn clocksource_watchdog() -> Result<(), SystemError> { .flags .contains(ClocksourceFlags::CLOCK_SOURCE_UNSTABLE) { - // kdebug!("clocksource_watchdog unstable"); + // debug!("clocksource_watchdog unstable"); // 启动watchdog_kthread - run_watchdog_kthread(); + if FINISHED_BOOTING.load(Ordering::Relaxed) { + // TODO 在实现了工作队列后,将启动线程换成schedule work + run_watchdog_kthread(); + } continue; } + // 读取时钟源现在的时间 let cs_now_clock = cs.read(); + // 读取watchdog现在的时间 + let wd = CLOCKSOURCE_WATCHDOG.lock_irqsave(); + let wd_now = wd.watchdog.as_ref().unwrap().clone(); + let wd_now_data = wd_now.as_ref().clocksource_data(); + let wd_now_clock = wd_now.as_ref().read().data(); + + // info!("cs_name = {:?}", cs_data.name); + // info!("cs_last = {:?}", cs_data.cs_last); + // info!("cs_now_clock = {:?}", cs_now_clock); + // info!("wd_name"); + // info!("wd_last = {:?}", cs_data.watchdog_last); + // info!("wd_now_clock = {:?}", wd_now_clock); // 如果时钟源没有被监视,则开始监视他 if !cs_data .flags .contains(ClocksourceFlags::CLOCK_SOURCE_WATCHDOG) { - // kdebug!("clocksource_watchdog start watch"); + // debug!("clocksource_watchdog start watch"); cs_data .flags .insert(ClocksourceFlags::CLOCK_SOURCE_WATCHDOG); // 记录此次检查的时刻 - cs_data.watchdog_last = cs_now_clock; + cs_data.watchdog_last = CycleNum::new(wd_now_clock); + cs_data.cs_last = cs_now_clock; cs.update_clocksource_data(cs_data.clone())?; continue; } - // kdebug!("cs_data.watchdog_last = {:?},cs_now_clock = {:?}", cs_data.watchdog_last, cs_now_clock); - // 计算时钟源的误差 + + let wd_dev_nsec = clocksource_cyc2ns( + CycleNum((wd_now_clock - cs_data.watchdog_last.data()) & wd_now_data.mask.bits), + wd_now_data.mult, + wd_now_data.shift, + ); + let cs_dev_nsec = clocksource_cyc2ns( - CycleNum(cs_now_clock.div(cs_data.watchdog_last).data() & cs_data.mask.bits), - cs_data.mult, - cs_data.shift, + CycleNum(cs_now_clock.div(cs_data.cs_last).data() & cs_data.mask.bits), + cs_data.mult, // 2343484437 + cs_data.shift, // 23 ); // 记录此次检查的时刻 - cs_data.watchdog_last = cs_now_clock; + cs_data.watchdog_last = CycleNum::new(wd_now_clock); + cs_data.cs_last = cs_now_clock; cs.update_clocksource_data(cs_data.clone())?; + + // 判断是否有误差 if cs_dev_nsec.abs_diff(wd_dev_nsec) > WATCHDOG_THRESHOLD.into() { - // kdebug!("set_unstable"); + // debug!("set_unstable"); // 误差过大,标记为unstable - kinfo!("cs_dev_nsec = {}", cs_dev_nsec); - kinfo!("wd_dev_nsec = {}", wd_dev_nsec); - cs.set_unstable((cs_dev_nsec - wd_dev_nsec).try_into().unwrap())?; + info!("cs_dev_nsec = {}", cs_dev_nsec); + info!("wd_dev_nsec = {}", wd_dev_nsec); + cs.set_unstable(cs_dev_nsec.abs_diff(wd_dev_nsec).try_into().unwrap())?; continue; } - // kdebug!("clocksource_watchdog aaa"); // 判断是否要切换为高精度模式 if !cs_data @@ -846,7 +888,7 @@ pub fn clocksource_watchdog() -> Result<(), SystemError> { && cs_data .flags .contains(ClocksourceFlags::CLOCK_SOURCE_IS_CONTINUOUS) - && cur_wd_data + && wd_now_data .flags .contains(ClocksourceFlags::CLOCK_SOURCE_IS_CONTINUOUS) { @@ -862,7 +904,7 @@ pub fn clocksource_watchdog() -> Result<(), SystemError> { } fn create_new_watchdog_timer_function() { - let mut cs_watchdog = CLOCKSOUCE_WATCHDOG.lock_irqsave(); + let mut cs_watchdog = CLOCKSOURCE_WATCHDOG.lock_irqsave(); cs_watchdog.timer_expires += WATCHDOG_INTERVAL; //创建定时器执行watchdog @@ -891,25 +933,13 @@ fn __clocksource_watchdog_kthread() { } // 检查是否需要停止watchdog - CLOCKSOUCE_WATCHDOG + CLOCKSOURCE_WATCHDOG .lock_irqsave() .clocksource_stop_watchdog(wd_list.len()); drop(wd_list); // 将不稳定的时钟源精度都设置为最低,然后删除unstable标记 for clock in del_clocks.iter() { clock.clocksource_change_rating(0); - let mut data = clock.clocksource_data(); - data.watchdog_last = clock.read(); - kdebug!("kthread: watchdog_last = {:?}", data.watchdog_last); - data.flags.remove(ClocksourceFlags::CLOCK_SOURCE_UNSTABLE); - clock - .update_clocksource_data(data) - .expect("clocksource_watchdog_kthread: failed to update clocksource data"); - - // 重新插入监视链表 - clock - .clocksource_enqueue_watchdog() - .expect("clocksource_watchdog_kthread: failed to enqueue watchdog list"); } } @@ -917,7 +947,7 @@ fn __clocksource_watchdog_kthread() { pub fn clocksource_watchdog_kthread() -> i32 { // return 0; loop { - // kdebug!("clocksource_watchdog_kthread start"); + // debug!("clocksource_watchdog_kthread start"); __clocksource_watchdog_kthread(); if KernelThreadMechanism::should_stop(&ProcessManager::current_pcb()) { break; @@ -948,7 +978,7 @@ pub fn clocksource_resume_watchdog() { /// # 根据精度选择最优的时钟源,或者接受用户指定的时间源 pub fn clocksource_select() { let list_guard = CLOCKSOURCE_LIST.lock(); - if unsafe { FINISHED_BOOTING.load(Ordering::Relaxed) } || list_guard.is_empty() { + if !FINISHED_BOOTING.load(Ordering::Relaxed) || list_guard.is_empty() { return; } let mut best = list_guard.front().unwrap().clone(); @@ -969,26 +999,26 @@ pub fn clocksource_select() { let cur_clocksource = CUR_CLOCKSOURCE.lock().as_ref().unwrap().clone(); let best_name = &best.clocksource_data().name; if cur_clocksource.clocksource_data().name.ne(best_name) { - kinfo!("Switching to the clocksource {:?}\n", best_name); + info!("Switching to the clocksource {:?}\n", best_name); drop(cur_clocksource); - CUR_CLOCKSOURCE.lock().replace(best); + CUR_CLOCKSOURCE.lock().replace(best.clone()); // TODO 通知timerkeeping 切换了时间源 } } else { // 当前时钟源为空 - CUR_CLOCKSOURCE.lock().replace(best); + CUR_CLOCKSOURCE.lock().replace(best.clone()); } - kdebug!(" clocksource_select finish"); + debug!("clocksource_select finish, CUR_CLOCKSOURCE = {best:?}"); } /// # clocksource模块加载完成 pub fn clocksource_boot_finish() { let mut cur_clocksource = CUR_CLOCKSOURCE.lock(); cur_clocksource.replace(clocksource_default_clock()); - unsafe { FINISHED_BOOTING.store(true, Ordering::Relaxed) }; + FINISHED_BOOTING.store(true, Ordering::Relaxed); // 清除不稳定的时钟源 __clocksource_watchdog_kthread(); - kdebug!("clocksource_boot_finish"); + debug!("clocksource_boot_finish"); } fn run_watchdog_kthread() { diff --git a/kernel/src/time/jiffies.rs b/kernel/src/time/jiffies.rs index e01451fa4..4025295f1 100644 --- a/kernel/src/time/jiffies.rs +++ b/kernel/src/time/jiffies.rs @@ -2,9 +2,10 @@ use alloc::{ string::ToString, sync::{Arc, Weak}, }; +use log::{error, info}; use system_error::SystemError; -use crate::{arch::time::CLOCK_TICK_RATE, kerror, kinfo, libs::spinlock::SpinLock}; +use crate::{arch::time::CLOCK_TICK_RATE, libs::spinlock::SpinLock}; use super::{ clocksource::{Clocksource, ClocksourceData, ClocksourceFlags, ClocksourceMask, CycleNum, HZ}, @@ -47,16 +48,20 @@ impl Clocksource for ClocksourceJiffies { fn clocksource(&self) -> Arc { self.0.lock_irqsave().self_ref.upgrade().unwrap() } - fn update_clocksource_data(&self, _data: ClocksourceData) -> Result<(), SystemError> { + fn update_clocksource_data(&self, data: ClocksourceData) -> Result<(), SystemError> { let d = &mut self.0.lock_irqsave().data; - d.set_flags(_data.flags); - d.set_mask(_data.mask); - d.set_max_idle_ns(_data.max_idle_ns); - d.set_mult(_data.mult); - d.set_name(_data.name); - d.set_rating(_data.rating); - d.set_shift(_data.shift); - d.watchdog_last = _data.watchdog_last; + d.set_name(data.name); + d.set_rating(data.rating); + d.set_mask(data.mask); + d.set_mult(data.mult); + d.set_shift(data.shift); + d.set_max_idle_ns(data.max_idle_ns); + d.set_flags(data.flags); + d.watchdog_last = data.watchdog_last; + d.cs_last = data.cs_last; + d.set_uncertainty_margin(data.uncertainty_margin); + d.set_maxadj(data.maxadj); + d.cycle_last = data.cycle_last; return Ok(()); } @@ -75,8 +80,10 @@ impl ClocksourceJiffies { max_idle_ns: Default::default(), flags: ClocksourceFlags::new(0), watchdog_last: CycleNum::new(0), + cs_last: CycleNum::new(0), uncertainty_margin: 0, maxadj: 0, + cycle_last: CycleNum::new(0), }; let jiffies = Arc::new(ClocksourceJiffies(SpinLock::new(InnerJiffies { data, @@ -96,10 +103,10 @@ pub fn jiffies_init() { let jiffies = clocksource_default_clock() as Arc; match jiffies.register(1, 0) { Ok(_) => { - kinfo!("jiffies_init sccessfully"); + info!("jiffies_init sccessfully"); } Err(_) => { - kerror!("jiffies_init failed, no default clock running"); + error!("jiffies_init failed, no default clock running"); } }; } diff --git a/kernel/src/time/mod.rs b/kernel/src/time/mod.rs index d42a6d4af..9a0c829f5 100644 --- a/kernel/src/time/mod.rs +++ b/kernel/src/time/mod.rs @@ -5,6 +5,7 @@ use core::{ }; use crate::arch::CurrentTimeArch; +use crate::time::syscall::PosixTimeval; use self::timekeeping::getnstimeofday; @@ -12,6 +13,7 @@ pub mod clocksource; pub mod jiffies; pub mod sleep; pub mod syscall; +pub mod tick_common; pub mod timeconv; pub mod timekeep; pub mod timekeeping; @@ -114,6 +116,15 @@ impl From for PosixTimeSpec { } } +impl From for PosixTimeSpec { + fn from(value: PosixTimeval) -> Self { + PosixTimeSpec { + tv_sec: value.tv_sec, + tv_nsec: value.tv_usec as i64 * 1000, + } + } +} + impl From for Duration { fn from(val: PosixTimeSpec) -> Self { Duration::from_micros(val.tv_sec as u64 * 1000000 + val.tv_nsec as u64 / 1000) @@ -130,7 +141,6 @@ impl From for Duration { /// * A value less than `0` indicates a time before the starting /// point. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Instant { micros: i64, } @@ -306,7 +316,6 @@ impl ops::Sub for Instant { /// A relative amount of time. #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Duration { micros: u64, } diff --git a/kernel/src/time/syscall.rs b/kernel/src/time/syscall.rs index 77fd13348..76f9349eb 100644 --- a/kernel/src/time/syscall.rs +++ b/kernel/src/time/syscall.rs @@ -3,6 +3,7 @@ use core::{ time::Duration, }; +use log::warn; use num_traits::FromPrimitive; use system_error::SystemError; @@ -138,7 +139,7 @@ impl Syscall { pub fn clock_gettime(clock_id: c_int, tp: *mut PosixTimeSpec) -> Result { let clock_id = PosixClockID::try_from(clock_id)?; if clock_id != PosixClockID::Realtime { - kwarn!("clock_gettime: currently only support Realtime clock, but got {:?}. Defaultly return realtime!!!\n", clock_id); + warn!("clock_gettime: currently only support Realtime clock, but got {:?}. Defaultly return realtime!!!\n", clock_id); } if tp.is_null() { return Err(SystemError::EFAULT); diff --git a/kernel/src/time/tick_common.rs b/kernel/src/time/tick_common.rs new file mode 100644 index 000000000..24e89c682 --- /dev/null +++ b/kernel/src/time/tick_common.rs @@ -0,0 +1,25 @@ +use crate::{ + arch::interrupt::TrapFrame, + process::ProcessManager, + smp::{core::smp_get_processor_id, cpu::ProcessorId}, + time::timer::run_local_timer, +}; + +use super::timer::update_timer_jiffies; + +/// # 函数的功能 +/// 用于周期滴答的事件处理 +pub fn tick_handle_periodic(trap_frame: &TrapFrame) { + let cpu_id = smp_get_processor_id(); + + tick_periodic(cpu_id, trap_frame); +} + +fn tick_periodic(cpu_id: ProcessorId, trap_frame: &TrapFrame) { + if cpu_id.data() == 0 { + update_timer_jiffies(1); + run_local_timer(); + } + + ProcessManager::update_process_times(trap_frame.is_from_user()); +} diff --git a/kernel/src/time/timekeep.rs b/kernel/src/time/timekeep.rs index 0d81d6fa2..b003b2075 100644 --- a/kernel/src/time/timekeep.rs +++ b/kernel/src/time/timekeep.rs @@ -1,10 +1,16 @@ #![allow(dead_code)] +use core::intrinsics::unlikely; + use system_error::SystemError; use crate::driver::rtc::interface::rtc_read_time_default; -use super::PosixTimeSpec; +use super::{PosixTimeSpec, NSEC_PER_SEC}; + +// 参考:https://code.dragonos.org.cn/xref/linux-3.4.99/include/linux/time.h#110 +const KTIME_MAX: i64 = !(1u64 << 63) as i64; +const KTIME_SEC_MAX: i64 = KTIME_MAX / NSEC_PER_SEC as i64; #[allow(non_camel_case_types)] pub type ktime_t = i64; @@ -31,3 +37,25 @@ pub fn ktime_get_real_ns() -> i64 { let kt: ktime_t = ktime_get_real().unwrap_or(0); return ktime_to_ns(kt); } + +// # 用于将两个ktime_t类型的变量相加 +// #[inline(always)] +// pub(super) fn ktime_add(add1: ktime_t, add2: ktime_t) -> ktime_t { +// let res = add1 + add2; +// } + +/// # 通过sec和nsec构造一个ktime_t +#[inline(always)] +fn ktime_set(secs: i64, nsecs: u64) -> ktime_t { + if unlikely(secs >= KTIME_SEC_MAX) { + return KTIME_MAX; + } + + return secs * NSEC_PER_SEC as i64 + nsecs as i64; +} + +/// # 将PosixTimeSpec转换成ktime_t +#[inline(always)] +pub fn timespec_to_ktime(ts: PosixTimeSpec) -> ktime_t { + return ktime_set(ts.tv_sec, ts.tv_nsec as u64); +} diff --git a/kernel/src/time/timekeeping.rs b/kernel/src/time/timekeeping.rs index 136805751..09bcf5ccb 100644 --- a/kernel/src/time/timekeeping.rs +++ b/kernel/src/time/timekeeping.rs @@ -1,12 +1,13 @@ use alloc::sync::Arc; -use core::sync::atomic::{compiler_fence, AtomicBool, AtomicI64, AtomicUsize, Ordering}; +use core::intrinsics::{likely, unlikely}; +use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; +use log::{debug, info, warn}; use system_error::SystemError; use crate::{ - arch::{CurrentIrqArch, CurrentTimeArch}, + arch::CurrentIrqArch, exception::InterruptArch, - kdebug, kinfo, - libs::rwlock::{RwLock, RwLockReadGuard}, + libs::rwlock::RwLock, time::{ jiffies::{clocksource_default_clock, jiffies_init}, timekeep::ktime_get_real_ns, @@ -14,10 +15,11 @@ use crate::{ }, }; +use super::timekeep::{ktime_t, timespec_to_ktime}; use super::{ clocksource::{clocksource_cyc2ns, Clocksource, CycleNum, HZ}, syscall::PosixTimeval, - TimeArch, NSEC_PER_SEC, + NSEC_PER_SEC, }; /// NTP周期频率 pub const NTP_INTERVAL_FREQ: u64 = HZ; @@ -28,17 +30,12 @@ pub const NTP_SCALE_SHIFT: u32 = 32; /// timekeeping休眠标志,false为未休眠 pub static TIMEKEEPING_SUSPENDED: AtomicBool = AtomicBool::new(false); -/// 已经递增的微秒数 -static __ADDED_USEC: AtomicI64 = AtomicI64::new(0); /// timekeeper全局变量,用于管理timekeeper模块 static mut __TIMEKEEPER: Option = None; #[derive(Debug)] pub struct Timekeeper { inner: RwLock, - - /// 上一次更新墙上时间时的CPU周期数 - last_update_cpu_cycle: AtomicUsize, } #[allow(dead_code)] @@ -52,7 +49,6 @@ pub struct TimekeeperData { cycle_interval: CycleNum, /// 一个NTP间隔中时钟移位的纳秒数。 xtime_interval: u64, - /// xtime_remainder: i64, /// 每个NTP间隔累积的原始纳米秒 raw_interval: i64, @@ -68,6 +64,8 @@ pub struct TimekeeperData { wall_to_monotonic: PosixTimeSpec, total_sleep_time: PosixTimeSpec, xtime: PosixTimeSpec, + /// 单调时间和实时时间的偏移量 + real_time_offset: ktime_t, } impl TimekeeperData { pub fn new() -> Self { @@ -98,6 +96,7 @@ impl TimekeeperData { tv_nsec: 0, tv_sec: 0, }, + real_time_offset: 0, } } } @@ -105,7 +104,6 @@ impl Timekeeper { fn new() -> Self { Self { inner: RwLock::new(TimekeeperData::new()), - last_update_cpu_cycle: AtomicUsize::new(0), } } @@ -118,9 +116,9 @@ impl Timekeeper { let mut timekeeper = self.inner.write_irqsave(); // 更新clock let mut clock_data = clock.clocksource_data(); - clock_data.watchdog_last = clock.read(); + clock_data.cycle_last = clock.read(); if clock.update_clocksource_data(clock_data).is_err() { - kdebug!("timekeeper_setup_internals:update_clocksource_data run failed"); + debug!("timekeeper_setup_internals:update_clocksource_data run failed"); } timekeeper.clock.replace(clock.clone()); @@ -144,35 +142,150 @@ impl Timekeeper { timekeeper.mult = clock_data.mult; } - /// # 获取当前时钟源距离上次watchdog检测走过的纳秒数 - #[allow(dead_code)] - pub fn tk_get_ns(&self) -> u64 { - let timekeeper: RwLockReadGuard<'_, TimekeeperData> = self.inner.read_irqsave(); + pub fn timekeeping_get_ns(&self) -> i64 { + let timekeeper = self.inner.read_irqsave(); let clock = timekeeper.clock.clone().unwrap(); - drop(timekeeper); - let clock_now = clock.read(); + let cycle_now = clock.read(); let clock_data = clock.clocksource_data(); - let clock_delta = clock_now.div(clock_data.watchdog_last).data() & clock_data.mask.bits(); + let cycle_delta = (cycle_now.div(clock_data.cycle_last)).data() & clock_data.mask.bits(); return clocksource_cyc2ns( - CycleNum::new(clock_delta), - clock_data.mult, - clock_data.shift, - ); + CycleNum::new(cycle_delta), + timekeeper.mult, + timekeeper.shift as u32, + ) as i64; } - #[inline] - fn do_read_cpu_cycle_ns(&self) -> usize { - CurrentTimeArch::cycles2ns( - CurrentTimeArch::get_cycles() - .wrapping_sub(self.last_update_cpu_cycle.load(Ordering::SeqCst)), - ) + /// # 处理大幅度调整 + pub fn timekeeping_bigadjust(&self, error: i64, interval: i64, offset: i64) -> (i64, i64, i32) { + let mut error = error; + let mut interval = interval; + let mut offset = offset; + + // TODO: 计算look_head并调整ntp误差 + + let tmp = interval; + let mut mult = 1; + let mut adj = 0; + if error < 0 { + error = -error; + interval = -interval; + offset = -offset; + mult = -1; + } + while error > tmp { + adj += 1; + error >>= 1; + } + + interval <<= adj; + offset <<= adj; + mult <<= adj; + + return (interval, offset, mult); } - fn mark_update_wall_time_ok(&self) { - self.last_update_cpu_cycle - .store(CurrentTimeArch::get_cycles(), Ordering::SeqCst); + /// # 调整时钟的mult减少ntp_error + pub fn timekeeping_adjust(&self, offset: i64) -> i64 { + let mut timekeeper = self.inner.write_irqsave(); + let mut interval = timekeeper.cycle_interval.data() as i64; + let mut offset = offset; + let adj: i32; + + // 计算误差 + let mut error = timekeeper.ntp_error >> (timekeeper.ntp_error_shift - 1); + + // 误差超过一个interval,就要进行调整 + if error >= 0 { + if error > interval { + error >>= 2; + if likely(error <= interval) { + adj = 1; + } else { + (interval, offset, adj) = self.timekeeping_bigadjust(error, interval, offset); + } + } else { + // 不需要校准 + return offset; + } + } else if -error > interval { + if likely(-error <= interval) { + adj = -1; + interval = -interval; + offset = -offset; + } else { + (interval, offset, adj) = self.timekeeping_bigadjust(error, interval, offset); + } + } else { + // 不需要校准 + return offset; + } + + // 检查最大调整值,确保调整值不会超过时钟源允许的最大值 + let clock_data = timekeeper.clock.clone().unwrap().clocksource_data(); + if unlikely( + clock_data.maxadj != 0 + && (timekeeper.mult as i32 + adj + > clock_data.mult as i32 + clock_data.maxadj as i32), + ) { + warn!( + "Adjusting {:?} more than ({} vs {})", + clock_data.name, + timekeeper.mult as i32 + adj, + clock_data.mult as i32 + clock_data.maxadj as i32 + ); + } + + if error > 0 { + timekeeper.mult += adj as u32; + timekeeper.xtime_interval += interval as u64; + timekeeper.xtime_nsec -= offset as u64; + } else { + timekeeper.mult -= adj as u32; + timekeeper.xtime_interval -= interval as u64; + timekeeper.xtime_nsec += offset as u64; + } + timekeeper.ntp_error -= (interval - offset) << timekeeper.ntp_error_shift; + + return offset; + } + /// # 用于累积时间间隔,并将其转换为纳秒时间 + pub fn logarithmic_accumulation(&self, offset: u64, shift: i32) -> u64 { + let mut timekeeper = self.inner.write_irqsave(); + let clock = timekeeper.clock.clone().unwrap(); + let clock_data = clock.clocksource_data(); + let nsecps = (NSEC_PER_SEC as u64) << timekeeper.shift; + let mut offset = offset; + + // 检查offset是否小于一个NTP周期间隔 + if offset < timekeeper.cycle_interval.data() << shift { + return offset; + } + + // 累积一个移位的interval + offset -= timekeeper.cycle_interval.data() << shift; + clock_data + .cycle_last + .add(CycleNum::new(timekeeper.cycle_interval.data() << shift)); + if clock.update_clocksource_data(clock_data).is_err() { + debug!("logarithmic_accumulation:update_clocksource_data run failed"); + } + timekeeper.clock.replace(clock.clone()); + + // 更新xime_nsec + timekeeper.xtime_nsec += timekeeper.xtime_interval << shift; + while timekeeper.xtime_nsec >= nsecps { + timekeeper.xtime_nsec -= nsecps; + timekeeper.xtime.tv_sec += 1; + // TODO: 处理闰秒 + } + + // TODO:更新raw_time + + // TODO:计算ntp_error + + return offset; } } @@ -193,7 +306,7 @@ pub fn timekeeper_init() { /// /// * 'TimeSpec' - 时间戳 pub fn getnstimeofday() -> PosixTimeSpec { - // kdebug!("enter getnstimeofday"); + // debug!("enter getnstimeofday"); let nsecs; let mut xtime: PosixTimeSpec; @@ -203,22 +316,18 @@ pub fn getnstimeofday() -> PosixTimeSpec { Some(tk) => { xtime = tk.xtime; drop(tk); - // 提供基于cpu周期数的ns时间,以便在两次update_wall_time之间提供更好的精度 - let cpu_delta_ns = timekeeper().do_read_cpu_cycle_ns() as u64; - // 尚未同步到xtime的时间 - let tmp_delta_ns = __ADDED_USEC.load(Ordering::SeqCst) as u64 * 1000; + nsecs = timekeeper().timekeeping_get_ns(); - nsecs = cpu_delta_ns + tmp_delta_ns; // TODO 不同架构可能需要加上不同的偏移量 break; } } } - xtime.tv_nsec += nsecs as i64; + xtime.tv_nsec += nsecs; xtime.tv_sec += xtime.tv_nsec / NSEC_PER_SEC as i64; xtime.tv_nsec %= NSEC_PER_SEC as i64; - // kdebug!("getnstimeofday: xtime = {:?}, nsecs = {:}", xtime, nsecs); + // debug!("getnstimeofday: xtime = {:?}, nsecs = {:}", xtime, nsecs); // TODO 将xtime和当前时间源的时间相加 @@ -248,7 +357,7 @@ pub fn do_settimeofday64(time: PosixTimeSpec) -> Result<(), SystemError> { /// # 初始化timekeeping模块 #[inline(never)] pub fn timekeeping_init() { - kinfo!("Initializing timekeeping module..."); + info!("Initializing timekeeping module..."); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; timekeeper_init(); @@ -269,17 +378,16 @@ pub fn timekeeping_init() { timekeeper.wall_to_monotonic.tv_sec, ) = (-timekeeper.xtime.tv_nsec, -timekeeper.xtime.tv_sec); - __ADDED_USEC.store(0, Ordering::SeqCst); - drop(irq_guard); drop(timekeeper); jiffies_init(); - kinfo!("timekeeping_init successfully"); + info!("timekeeping_init successfully"); } /// # 使用当前时钟源增加wall time -pub fn update_wall_time(delta_us: i64) { - // kdebug!("enter update_wall_time, stack_use = {:}",stack_use); +/// 参考:https://code.dragonos.org.cn/xref/linux-3.4.99/kernel/time/timekeeping.c#1041 +pub fn update_wall_time() { + // debug!("enter update_wall_time, stack_use = {:}",stack_use); compiler_fence(Ordering::SeqCst); let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; // 如果在休眠那就不更新 @@ -287,60 +395,74 @@ pub fn update_wall_time(delta_us: i64) { return; } - // ===== 请不要删除这些注释 ===== - // let clock = timekeeper.clock.clone().unwrap(); - // let clock_data = clock.clocksource_data(); - // let offset = (clock.read().div(clock_data.watchdog_last).data()) & clock_data.mask.bits(); - - // timekeeper.xtime_nsec = (timekeeper.xtime.tv_nsec as u64) << timekeeper.shift; - // // TODO 当有ntp模块之后 需要将timekeep与ntp进行同步并检查 - // timekeeper.xtime.tv_nsec = ((timekeeper.xtime_nsec as i64) >> timekeeper.shift) + 1; - // timekeeper.xtime_nsec -= (timekeeper.xtime.tv_nsec as u64) << timekeeper.shift; - - // timekeeper.xtime.tv_nsec += offset as i64; - // while unlikely(timekeeper.xtime.tv_nsec >= NSEC_PER_SEC.into()) { - // timekeeper.xtime.tv_nsec -= NSEC_PER_SEC as i64; - // timekeeper.xtime.tv_sec += 1; - // // TODO 需要处理闰秒 - // } - // ================ - compiler_fence(Ordering::SeqCst); + let mut tk = timekeeper().inner.write_irqsave(); + // 获取当前时钟源 + let clock = tk.clock.clone().unwrap(); + let clock_data = clock.clocksource_data(); + // 计算从上一次更新周期以来经过的时钟周期数 + let mut offset = (clock.read().div(clock_data.cycle_last).data()) & clock_data.mask.bits(); + // 检查offset是否达到了一个NTP周期间隔 + if offset < tk.cycle_interval.data() { + return; + } - __ADDED_USEC.fetch_add(delta_us, Ordering::SeqCst); - compiler_fence(Ordering::SeqCst); - let mut retry = 10; + // 将纳秒部分转换为更高精度的格式 + tk.xtime_nsec = (tk.xtime.tv_nsec as u64) << tk.shift; + + let mut shift = (offset.ilog2() - tk.cycle_interval.data().ilog2()) as i32; + shift = shift.max(0); + // let max_shift = (64 - (ntp_tick_length().ilog2()+1)) - 1; + // shift = min(shift, max_shift) + while offset >= tk.cycle_interval.data() { + offset = timekeeper().logarithmic_accumulation(offset, shift); + if offset < tk.cycle_interval.data() << shift { + shift -= 1; + } + } - let usec = __ADDED_USEC.load(Ordering::SeqCst); + timekeeper().timekeeping_adjust(offset as i64); - // 一分钟同步一次 - loop { - if (usec & !((1 << 26) - 1)) != 0 { - if __ADDED_USEC - .compare_exchange(usec, 0, Ordering::SeqCst, Ordering::SeqCst) - .is_ok() - || retry == 0 - { - // 同步时间 - // 我感觉这里会出问题:多个读者不退出的话,写者就无法写入 - // 然后这里会超时,导致在中断返回之后,会不断的进入这个中断,最终爆栈。 - let mut timekeeper = timekeeper().inner.write_irqsave(); - timekeeper.xtime.tv_nsec = ktime_get_real_ns(); - timekeeper.xtime.tv_sec = 0; - __ADDED_USEC.store(0, Ordering::SeqCst); - - drop(timekeeper); - break; - } - retry -= 1; - } else { - break; - } + // 处理xtime_nsec下溢问题,并对NTP误差进行调整 + if unlikely((tk.xtime_nsec as i64) < 0) { + let neg = -(tk.xtime_nsec as i64); + tk.xtime_nsec = 0; + tk.ntp_error += neg << tk.ntp_error_shift; } - timekeeper().mark_update_wall_time_ok(); - // TODO 需要检查是否更新时间源 + + // 将纳秒部分舍入后存储在xtime.tv_nsec中 + tk.xtime.tv_nsec = ((tk.xtime_nsec as i64) >> tk.shift) + 1; + tk.xtime_nsec -= (tk.xtime.tv_nsec as u64) << tk.shift; + + // 确保经过舍入后的xtime.tv_nsec不会大于NSEC_PER_SEC,并在超过1秒的情况下进行适当的调整 + if unlikely(tk.xtime.tv_nsec >= NSEC_PER_SEC.into()) { + tk.xtime.tv_nsec -= NSEC_PER_SEC as i64; + tk.xtime.tv_sec += 1; + // TODO: 处理闰秒 + } + + // 更新时间的相关信息 + timekeeping_update(); + compiler_fence(Ordering::SeqCst); drop(irq_guard); compiler_fence(Ordering::SeqCst); } -// TODO timekeeping_adjust // TODO wall_to_monotic + +/// 参考:https://code.dragonos.org.cn/xref/linux-3.4.99/kernel/time/timekeeping.c#190 +pub fn timekeeping_update() { + // TODO:如果clearntp为true,则会清除NTP错误并调用ntp_clear() + + // 更新实时时钟偏移量,用于跟踪硬件时钟与系统时间的差异,以便进行时间校正 + update_rt_offset(); +} + +/// # 更新实时偏移量(墙上之间与单调时间的差值) +pub fn update_rt_offset() { + let mut timekeeper = timekeeper().inner.write_irqsave(); + let ts = PosixTimeSpec::new( + -timekeeper.wall_to_monotonic.tv_sec, + -timekeeper.wall_to_monotonic.tv_nsec, + ); + timekeeper.real_time_offset = timespec_to_ktime(ts); +} diff --git a/kernel/src/time/timer.rs b/kernel/src/time/timer.rs index 1dce3bb27..917502ca6 100644 --- a/kernel/src/time/timer.rs +++ b/kernel/src/time/timer.rs @@ -10,6 +10,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::{error, info, warn}; use system_error::SystemError; use crate::{ @@ -18,7 +19,6 @@ use crate::{ softirq::{softirq_vectors, SoftirqNumber, SoftirqVec}, InterruptArch, }, - kerror, kinfo, libs::spinlock::{SpinLock, SpinLockGuard}, process::{ProcessControlBlock, ProcessManager}, sched::{schedule, SchedMode}, @@ -160,7 +160,7 @@ impl Timer { let mut split_pos: usize = 0; for (pos, elt) in timer_list.iter().enumerate() { if Arc::ptr_eq(&self_arc, &elt.1) { - kwarn!("Timer already in list"); + warn!("Timer already in list"); } if elt.0 > expire_jiffies { split_pos = pos; @@ -180,7 +180,7 @@ impl Timer { drop(timer); let r = func.map(|mut f| f.run()).unwrap_or(Ok(())); if unlikely(r.is_err()) { - kerror!( + error!( "Failed to run timer function: {self:?} {:?}", r.as_ref().err().unwrap() ); @@ -246,7 +246,7 @@ impl SoftirqVec for DoTimerSoftirq { } // 最多只处理TIMER_RUN_CYCLE_THRESHOLD个计时器 for _ in 0..TIMER_RUN_CYCLE_THRESHOLD { - // kdebug!("DoTimerSoftirq run"); + // debug!("DoTimerSoftirq run"); let timer_list = TIMER_LIST.try_lock_irqsave(); if timer_list.is_err() { continue; @@ -258,7 +258,7 @@ impl SoftirqVec for DoTimerSoftirq { } let (front_jiffies, timer_list_front) = timer_list.first().unwrap().clone(); - // kdebug!("to lock timer_list_front"); + // debug!("to lock timer_list_front"); if front_jiffies >= TIMER_JIFFIES.load(Ordering::SeqCst) { break; @@ -280,7 +280,7 @@ pub fn timer_init() { softirq_vectors() .register_softirq(SoftirqNumber::TIMER, do_timer_softirq) .expect("Failed to register timer softirq"); - kinfo!("timer initialized successfully"); + info!("timer initialized successfully"); } /// 计算接下来n毫秒对应的定时器时间片 @@ -300,7 +300,7 @@ pub fn next_n_us_timer_jiffies(expire_us: u64) -> u64 { /// /// @return Err(SystemError) 错误码 pub fn schedule_timeout(mut timeout: i64) -> Result { - // kdebug!("schedule_timeout"); + // debug!("schedule_timeout"); if timeout == MAX_TIMEOUT { let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; ProcessManager::mark_sleep(true).ok(); @@ -308,7 +308,7 @@ pub fn schedule_timeout(mut timeout: i64) -> Result { schedule(SchedMode::SM_NONE); return Ok(MAX_TIMEOUT); } else if timeout < 0 { - kerror!("timeout can't less than 0"); + error!("timeout can't less than 0"); return Err(SystemError::EINVAL); } else { // 禁用中断,防止在这段期间发生调度,造成死锁 @@ -337,16 +337,16 @@ pub fn schedule_timeout(mut timeout: i64) -> Result { pub fn timer_get_first_expire() -> Result { // FIXME - // kdebug!("rs_timer_get_first_expire,timer_jif = {:?}", TIMER_JIFFIES); + // debug!("rs_timer_get_first_expire,timer_jif = {:?}", TIMER_JIFFIES); for _ in 0..10 { match TIMER_LIST.try_lock_irqsave() { Ok(timer_list) => { - // kdebug!("rs_timer_get_first_expire TIMER_LIST lock successfully"); + // debug!("rs_timer_get_first_expire TIMER_LIST lock successfully"); if timer_list.is_empty() { - // kdebug!("timer_list is empty"); + // debug!("timer_list is empty"); return Ok(0); } else { - // kdebug!("timer_list not empty"); + // debug!("timer_list not empty"); return Ok(timer_list.first().unwrap().0); } } @@ -366,11 +366,17 @@ pub fn try_raise_timer_softirq() { } } +/// 处理本地定时器中断 +pub fn run_local_timer() { + assert!(!CurrentIrqArch::is_irq_enabled()); + try_raise_timer_softirq(); +} + /// 更新系统时间片 -pub fn update_timer_jiffies(add_jiffies: u64, time_us: i64) -> u64 { +pub fn update_timer_jiffies(add_jiffies: u64) -> u64 { let prev = TIMER_JIFFIES.fetch_add(add_jiffies, Ordering::SeqCst); compiler_fence(Ordering::SeqCst); - update_wall_time(time_us); + update_wall_time(); compiler_fence(Ordering::SeqCst); return prev + add_jiffies; diff --git a/kernel/src/virt/kvm/host_mem.rs b/kernel/src/virt/kvm/host_mem.rs index 75780f0a5..95291b146 100644 --- a/kernel/src/virt/kvm/host_mem.rs +++ b/kernel/src/virt/kvm/host_mem.rs @@ -1,10 +1,8 @@ +use log::debug; use system_error::SystemError; use super::{vcpu::Vcpu, vm}; -use crate::{ - kdebug, - mm::{kernel_mapper::KernelMapper, page::PageFlags, VirtAddr}, -}; +use crate::mm::{kernel_mapper::KernelMapper, page::EntryFlags, VirtAddr}; /* * Address types: @@ -87,7 +85,7 @@ pub fn kvm_vcpu_memslots(_vcpu: &mut dyn Vcpu) -> KvmMemorySlots { } fn __gfn_to_memslot(slots: KvmMemorySlots, gfn: u64) -> Option { - kdebug!("__gfn_to_memslot"); + debug!("__gfn_to_memslot"); // TODO: 使用二分查找的方式优化 for i in 0..slots.used_slots { let memslot = slots.memslots[i as usize]; @@ -107,7 +105,7 @@ fn __gfn_to_hva_many( nr_pages: Option<&mut u64>, write: bool, ) -> Result { - kdebug!("__gfn_to_hva_many"); + debug!("__gfn_to_hva_many"); if slot.is_none() { return Err(SystemError::KVM_HVA_ERR_BAD); } @@ -141,10 +139,10 @@ fn __gfn_to_hva_many( // host端虚拟地址到物理地址的转换,有两种方式,hva_to_pfn_fast、hva_to_pfn_slow // 正确性待验证 fn hva_to_pfn(addr: u64, _atomic: bool, _writable: &mut bool) -> Result { - kdebug!("hva_to_pfn"); + debug!("hva_to_pfn"); unsafe { let raw = addr as *const i32; - kdebug!("raw={:x}", *raw); + debug!("raw={:x}", *raw); } // let hpa = MMArch::virt_2_phys(VirtAddr::new(addr)).unwrap().data() as u64; let hva = VirtAddr::new(addr as usize); @@ -154,7 +152,7 @@ fn hva_to_pfn(addr: u64, _atomic: bool, _writable: &mut bool) -> Result> PAGE_SHIFT); } unsafe { - mapper.map(hva, PageFlags::mmio_flags()); + mapper.map(hva, EntryFlags::mmio_flags()); } let (hpa, _) = mapper.translate(hva).unwrap(); return Ok(hpa.data() as u64 >> PAGE_SHIFT); @@ -167,11 +165,11 @@ pub fn __gfn_to_pfn( write: bool, writable: &mut bool, ) -> Result { - kdebug!("__gfn_to_pfn"); + debug!("__gfn_to_pfn"); let mut nr_pages = 0; let addr = __gfn_to_hva_many(slot, gfn, Some(&mut nr_pages), write)?; let pfn = hva_to_pfn(addr, atomic, writable)?; - kdebug!("hva={}, pfn={}", addr, pfn); + debug!("hva={}, pfn={}", addr, pfn); return Ok(pfn); } diff --git a/kernel/src/virt/kvm/kvm_dev.rs b/kernel/src/virt/kvm/kvm_dev.rs index 3a900f6ad..065c85395 100644 --- a/kernel/src/virt/kvm/kvm_dev.rs +++ b/kernel/src/virt/kvm/kvm_dev.rs @@ -1,4 +1,5 @@ use crate::driver::base::device::device_number::DeviceNumber; +use crate::filesystem; use crate::filesystem::devfs::{DevFS, DeviceINode}; use crate::filesystem::vfs::{ core::generate_inode_id, @@ -8,7 +9,6 @@ use crate::filesystem::vfs::{ use crate::libs::spinlock::SpinLockGuard; use crate::process::ProcessManager; use crate::{arch::KVMArch, libs::spinlock::SpinLock, time::PosixTimeSpec}; -use crate::{filesystem, kdebug}; // use crate::virt::kvm::{host_stack}; use super::push_vm; use crate::virt::kvm::vm_dev::LockedVmInode; @@ -17,6 +17,7 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; +use log::debug; use system_error::SystemError; pub const KVM_API_VERSION: u32 = 12; @@ -94,7 +95,7 @@ impl IndexNode for LockedKvmInode { _data: SpinLockGuard, _mode: &FileMode, ) -> Result<(), SystemError> { - kdebug!("file private data:{:?}", _data); + debug!("file private data:{:?}", _data); return Ok(()); } @@ -141,12 +142,12 @@ impl IndexNode for LockedKvmInode { ) -> Result { match cmd { 0xdeadbeef => { - kdebug!("kvm ioctl"); + debug!("kvm ioctl"); Ok(0) } KVM_GET_API_VERSION => Ok(KVM_API_VERSION as usize), KVM_CREATE_VM => { - kdebug!("kvm KVM_CREATE_VM"); + debug!("kvm KVM_CREATE_VM"); kvm_dev_ioctl_create_vm(data) } KVM_CHECK_EXTENSION diff --git a/kernel/src/virt/kvm/mod.rs b/kernel/src/virt/kvm/mod.rs index deb4b2fee..123984cb8 100644 --- a/kernel/src/virt/kvm/mod.rs +++ b/kernel/src/virt/kvm/mod.rs @@ -1,9 +1,10 @@ use self::kvm_dev::LockedKvmInode; use crate::arch::KVMArch; use crate::filesystem::devfs::devfs_register; -use crate::kdebug; + use crate::libs::mutex::Mutex; use alloc::vec::Vec; +use log::debug; use vm::Vm; pub mod host_mem; @@ -23,7 +24,7 @@ pub static VM_LIST: Mutex> = Mutex::new(Vec::new()); pub fn push_vm(id: usize) -> Result<(), ()> { let mut vm_list = VM_LIST.lock(); if vm_list.iter().any(|x| x.id == id) { - kdebug!("push_vm: vm {} already exists", id); + debug!("push_vm: vm {} already exists", id); Err(()) } else { vm_list.push(Vm::new(id).unwrap()); @@ -54,14 +55,14 @@ pub fn vm(id: usize) -> Option { #[inline(never)] pub fn kvm_init() { - kdebug!("kvm init"); + debug!("kvm init"); match KVMArch::kvm_arch_cpu_supports_vm() { Ok(_) => { - kdebug!("[+] CPU supports Intel VMX"); + debug!("[+] CPU supports Intel VMX"); } Err(e) => { - kdebug!("[-] CPU does not support Intel VMX: {:?}", e); + debug!("[-] CPU does not support Intel VMX: {:?}", e); } }; @@ -76,9 +77,9 @@ pub fn kvm_init() { // let host_stack = vec![0xCC; HOST_STACK_SIZE]; // let guest_rsp = guest_stack.as_ptr() as u64 + GUEST_STACK_SIZE as u64; // let host_rsp = (host_stack.as_ptr() as u64) + HOST_STACK_SIZE as u64; - // kdebug!("guest rsp: {:x}", guest_rsp); - // kdebug!("guest rip: {:x}", guest_code as *const () as u64); - // kdebug!("host rsp: {:x}", host_rsp); + // debug!("guest rsp: {:x}", guest_rsp); + // debug!("guest rip: {:x}", guest_code as *const () as u64); + // debug!("host rsp: {:x}", host_rsp); // let hypervisor = Hypervisor::new(1, host_rsp, 0).expect("Cannot create hypervisor"); // let vcpu = VmxVcpu::new(1, Arc::new(Mutex::new(hypervisor)), host_rsp, guest_rsp, guest_code as *const () as u64).expect("Cannot create VcpuData"); // vcpu.virtualize_cpu().expect("Cannot virtualize cpu"); diff --git a/kernel/src/virt/kvm/vcpu.rs b/kernel/src/virt/kvm/vcpu.rs index 3d4b84e52..ac87d50c6 100644 --- a/kernel/src/virt/kvm/vcpu.rs +++ b/kernel/src/virt/kvm/vcpu.rs @@ -1,5 +1,6 @@ use system_error::SystemError; +#[allow(dead_code)] pub trait Vcpu: Send + Sync { /// Virtualize the CPU fn virtualize_cpu(&mut self) -> Result<(), SystemError>; diff --git a/kernel/src/virt/kvm/vcpu_dev.rs b/kernel/src/virt/kvm/vcpu_dev.rs index 53e56e8cd..ee8719e11 100644 --- a/kernel/src/virt/kvm/vcpu_dev.rs +++ b/kernel/src/virt/kvm/vcpu_dev.rs @@ -1,6 +1,7 @@ use crate::arch::kvm::vmx::vcpu::VcpuContextFrame; use crate::arch::KVMArch; use crate::driver::base::device::device_number::DeviceNumber; +use crate::filesystem; use crate::filesystem::devfs::DevFS; use crate::filesystem::vfs::{ core::generate_inode_id, file::FileMode, FilePrivateData, FileSystem, FileType, IndexNode, @@ -11,13 +12,13 @@ use crate::mm::VirtAddr; use crate::syscall::user_access::copy_from_user; use crate::virt::kvm::vcpu::Vcpu; use crate::virt::kvm::vm; -use crate::{filesystem, kdebug}; use crate::{libs::spinlock::SpinLock, time::PosixTimeSpec}; use alloc::{ string::String, sync::{Arc, Weak}, vec::Vec, }; +use log::debug; use system_error::SystemError; // pub const KVM_API_VERSION:u32 = 12; @@ -102,7 +103,7 @@ impl IndexNode for LockedVcpuInode { _data: SpinLockGuard, _mode: &FileMode, ) -> Result<(), SystemError> { - kdebug!("file private data:{:?}", _data); + debug!("file private data:{:?}", _data); return Ok(()); } @@ -149,11 +150,11 @@ impl IndexNode for LockedVcpuInode { ) -> Result { match cmd { 0xdeadbeef => { - kdebug!("kvm_cpu ioctl"); + debug!("kvm_cpu ioctl"); Ok(0) } KVM_RUN => { - kdebug!("kvm_cpu ioctl"); + debug!("kvm_cpu ioctl"); // let guest_stack = vec![0xCC; GUEST_STACK_SIZE]; // let host_stack = vec![0xCC; HOST_STACK_SIZE]; // let guest_rsp = guest_stack.as_ptr() as u64 + GUEST_STACK_SIZE as u64; @@ -177,12 +178,9 @@ impl IndexNode for LockedVcpuInode { VirtAddr::new(data), )?; } - kdebug!( + debug!( "rip={:x}, rflags={:x}, rsp={:x}, rax={:x}", - kvm_regs.rip, - kvm_regs.rflags, - kvm_regs.regs[6], - kvm_regs.regs[0], + kvm_regs.rip, kvm_regs.rflags, kvm_regs.regs[6], kvm_regs.regs[0], ); let vcpu = vm(0).unwrap().vcpu[0].clone(); @@ -191,7 +189,7 @@ impl IndexNode for LockedVcpuInode { Ok(0) } _ => { - kdebug!("kvm_cpu ioctl"); + debug!("kvm_cpu ioctl"); Ok(usize::MAX) } } diff --git a/kernel/src/virt/kvm/vm.rs b/kernel/src/virt/kvm/vm.rs index 5b2bbb272..68444dfe4 100644 --- a/kernel/src/virt/kvm/vm.rs +++ b/kernel/src/virt/kvm/vm.rs @@ -1,10 +1,11 @@ use crate::arch::kvm::vmx::vcpu::VmxVcpu; +use crate::arch::KVMArch; use crate::arch::MMArch; use crate::libs::mutex::Mutex; use crate::mm::MemoryManagementArch; -use crate::{arch::KVMArch, kdebug}; use alloc::sync::Arc; use alloc::vec::Vec; +use log::debug; use system_error::SystemError; // use super::HOST_STACK_SIZE; @@ -13,7 +14,6 @@ use super::host_mem::{ KVM_ADDRESS_SPACE_NUM, KVM_MEM_LOG_DIRTY_PAGES, KVM_MEM_MAX_NR_PAGES, KVM_MEM_READONLY, KVM_MEM_SLOTS_NUM, KVM_USER_MEM_SLOTS, PAGE_SHIFT, }; -// use crate::kdebug; #[derive(Debug, Clone)] pub struct Vm { @@ -24,6 +24,7 @@ pub struct Vm { // memory config pub nr_mem_slots: u32, /* Number of memory slots in each address space */ pub memslots: [KvmMemorySlots; KVM_ADDRESS_SPACE_NUM], + #[allow(dead_code)] // arch related config pub arch: KVMArch, } @@ -48,10 +49,10 @@ impl Vm { &mut self, mem: &KvmUserspaceMemoryRegion, ) -> Result<(), SystemError> { - kdebug!("set_user_memory_region"); + debug!("set_user_memory_region"); let id: u16 = mem.slot as u16; // slot id let as_id = mem.slot >> 16; // address space id - kdebug!("id={}, as_id={}", id, as_id); + debug!("id={}, as_id={}", id, as_id); // 检查slot是否合法 if mem.slot as usize >= self.nr_mem_slots as usize { diff --git a/kernel/src/virt/kvm/vm_dev.rs b/kernel/src/virt/kvm/vm_dev.rs index 4b2f1facf..9e7fd26cc 100644 --- a/kernel/src/virt/kvm/vm_dev.rs +++ b/kernel/src/virt/kvm/vm_dev.rs @@ -1,4 +1,5 @@ use crate::driver::base::device::device_number::DeviceNumber; +use crate::filesystem; use crate::filesystem::devfs::DevFS; use crate::filesystem::vfs::{ core::generate_inode_id, @@ -14,12 +15,12 @@ use crate::virt::kvm::update_vm; use crate::virt::kvm::vcpu_dev::LockedVcpuInode; use crate::virt::kvm::vm; use crate::{arch::KVMArch, libs::spinlock::SpinLock, time::PosixTimeSpec}; -use crate::{filesystem, kdebug}; use alloc::{ string::String, sync::{Arc, Weak}, vec::Vec, }; +use log::debug; use system_error::SystemError; // pub const KVM_API_VERSION:u32 = 12; @@ -100,7 +101,7 @@ impl IndexNode for LockedVmInode { _data: SpinLockGuard, _mode: &FileMode, ) -> Result<(), SystemError> { - kdebug!("file private data:{:?}", _data); + debug!("file private data:{:?}", _data); return Ok(()); } @@ -147,15 +148,15 @@ impl IndexNode for LockedVmInode { ) -> Result { match cmd { 0xdeadbeef => { - kdebug!("kvm_vm ioctl"); + debug!("kvm_vm ioctl"); Ok(0) } KVM_CREATE_VCPU => { - kdebug!("kvm_vcpu ioctl KVM_CREATE_VCPU"); + debug!("kvm_vcpu ioctl KVM_CREATE_VCPU"); kvm_vm_ioctl_create_vcpu(data as u32) } KVM_SET_USER_MEMORY_REGION => { - kdebug!("kvm_vcpu ioctl KVM_SET_USER_MEMORY_REGION data={:x}", data); + debug!("kvm_vcpu ioctl KVM_SET_USER_MEMORY_REGION data={:x}", data); let mut kvm_userspace_mem = KvmUserspaceMemoryRegion::default(); // = unsafe { (data as *const KvmUserspaceMemoryRegion).as_ref().unwrap() }; unsafe { copy_from_user( @@ -166,7 +167,7 @@ impl IndexNode for LockedVmInode { VirtAddr::new(data), )?; } - kdebug!( + debug!( "slot={}, flag={}, memory_size={:x}, guest_phys_addr={}, userspace_addr={}", kvm_userspace_mem.slot, kvm_userspace_mem.flags, @@ -184,7 +185,7 @@ impl IndexNode for LockedVmInode { Err(SystemError::ENOSYS) } _ => { - kdebug!("kvm_vm ioctl"); + debug!("kvm_vm ioctl"); Ok(usize::MAX) } } diff --git a/tools/Makefile b/tools/Makefile index 93bb1ab24..c4faeea5f 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -6,4 +6,4 @@ clean: @cargo clean check: - @cargo check --all \ No newline at end of file + @cargo +nightly-2024-07-23 check --workspace --message-format=json diff --git a/tools/bootstrap.sh b/tools/bootstrap.sh index 02555caeb..d3d583f87 100644 --- a/tools/bootstrap.sh +++ b/tools/bootstrap.sh @@ -94,6 +94,20 @@ install_ubuntu_debian_pkg() } + +#################################### +# 当检测到gentoo时,执行此函数 # +#################################### +gentoo() +{ + pkgman="emerge" + echo "检测到Gentoo发行版" + echo "正在更新包管理器的列表..." + sudo "${pkgman}" --sync + echo "正在安装所需的包..." + sudo "${pkgman}" net-misc/curl net-misc/wget net-misc/bridge-utils net-dns/dnsmasq sys-apps/diffutils dev-util/pkgconf sys-apps/which app-arch/unzip sys-apps/util-linux sys-fs/dosfstools sys-devel/gcc dev-build/make sys-devel/flex sys-apps/texinfo dev-libs/gmp dev-libs/mpfr app-emulation/qemu dev-libs/mpc dev-libs/openssl +} + install_archlinux_pkg() { pkgman="pacman" @@ -214,25 +228,26 @@ rustInstall() { fi echo "正在安装DragonOS所需的rust组件...首次安装需要一些时间来更新索引,请耐心等待..." cargo install cargo-binutils - rustup toolchain install nightly-2023-01-21-x86_64-unknown-linux-gnu rustup toolchain install nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup component add rust-src --toolchain nightly-2023-01-21-x86_64-unknown-linux-gnu + rustup toolchain install nightly-2024-07-23-x86_64-unknown-linux-gnu + rustup component add rust-src --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu rustup component add rust-src --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu - rustup target add x86_64-unknown-none --toolchain nightly-2023-01-21-x86_64-unknown-linux-gnu + rustup target add x86_64-unknown-none --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu rustup target add x86_64-unknown-none --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu rustup target add x86_64-unknown-linux-musl --toolchain nightly-2023-08-15-x86_64-unknown-linux-gnu + rustup target add x86_64-unknown-linux-musl --toolchain nightly-2024-07-23-x86_64-unknown-linux-gnu - rustup toolchain install nightly-2023-01-21-riscv64gc-unknown-linux-gnu --force-non-host + rustup toolchain install nightly-2024-07-23-riscv64gc-unknown-linux-gnu --force-non-host rustup toolchain install nightly-2023-08-15-riscv64gc-unknown-linux-gnu --force-non-host - rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2023-01-21-riscv64gc-unknown-linux-gnu - rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2023-01-21-riscv64gc-unknown-linux-gnu + rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2024-07-23-riscv64gc-unknown-linux-gnu + rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2024-07-23-riscv64gc-unknown-linux-gnu rustup target add riscv64gc-unknown-none-elf --toolchain nightly-2023-08-15-riscv64gc-unknown-linux-gnu rustup target add riscv64imac-unknown-none-elf --toolchain nightly-2023-08-15-riscv64gc-unknown-linux-gnu rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu rustup component add rust-src rustup component add llvm-tools-preview - rustup default nightly + rustup default nightly-2024-07-23 echo "Rust已经成功的在您的计算机上安装!请运行 source ~/.cargo/env 以使rust在当前窗口生效!" fi diff --git a/tools/change_rust_src.sh b/tools/change_rust_src.sh index 7e78a72ef..c62270e08 100644 --- a/tools/change_rust_src.sh +++ b/tools/change_rust_src.sh @@ -1,8 +1,33 @@ -# 更换Rust镜像源 -echo -e "[source.crates-io] \n \ +echo "正在为rust换源" +echo "bash change_rust_src.sh --sparse以使用稀疏索引" +sparse="false" +while true; do + if [ -z "$1" ]; then + break; + fi + case "$1" in + "--sparse") + echo "使用稀疏索引" + sparse="" + ;; + esac + shift 1 + done +if [ -z ${sparse} ]; then + echo -e "[source.crates-io] \n \ +registry = \"https://github.com/rust-lang/crates.io-index\" \n \ +\n \ +replace-with = 'tuna' \n \ +[source.tuna] \n \ +registry = \"sparse+https://mirrors.tuna.tsinghua.edu.cn/crates.io-index/\" \n \ +" > ~/.cargo/config.toml +else + echo -e "[source.crates-io] \n \ registry = \"https://github.com/rust-lang/crates.io-index\" \n \ \n \ replace-with = 'tuna' \n \ [source.tuna] \n \ registry = \"https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git\" \n \ -" > ~/.cargo/config \ No newline at end of file +" > ~/.cargo/config.toml + +fi diff --git a/tools/debugging/logmonitor/src/backend/monitor/mm.rs b/tools/debugging/logmonitor/src/backend/monitor/mm.rs index 7da9b8930..3c0de957a 100644 --- a/tools/debugging/logmonitor/src/backend/monitor/mm.rs +++ b/tools/debugging/logmonitor/src/backend/monitor/mm.rs @@ -154,7 +154,7 @@ impl MMMonitorThread { info!("MMMonitorThread::run(): kmem_path: {:?}", self.kmem_path); let mut kmem_file = { - let mut file: File; + let file: File; loop { let f = self.open_kmem_file(); if f.is_ok() { diff --git a/tools/grub_auto_install.sh b/tools/grub_auto_install.sh index 1837b9e8f..09b41fd3e 100644 --- a/tools/grub_auto_install.sh +++ b/tools/grub_auto_install.sh @@ -38,14 +38,25 @@ export OBJCOPY=objcopy if [ -d ${grub_dir_i386_efi}/bin ] && [ -d ${grub_dir_i386_legacy}/bin ] && [ -d ${grub_dir_x86_64_efi}/bin ] ; then exit 0 fi -#仅支持Ubuntu/Debain, Arch, Centos/RHEL8/Fedora下的自动安装 -supported_package_manager="apt-get pacman dnf" +#仅支持Ubuntu/Debain, Arch, Centos/RHEL8/Fedora gentoo下的自动安装 +supported_package_manager="apt-get pacman dnf yum emerge" packages=("make binutils bison gcc gettext flex bison automake autoconf wget gawk" \ - "make binutils bison gcc gettext flex bison automake autoconf wget gawk") + "make binutils bison gcc gettext flex bison automake autoconf wget gawk" \ + "make binutils bison gcc gettext flex bison automake autoconf wget gawk" \ + "make binutils bison gcc gettext flex bison automake autoconf wget gawk" \ + "dev-build/make sys-devel/binutils sys-devel/bison sys-devel/gcc sys-devel/gettext sys-devel/flex dev-build/automake dev-build/autoconf net-misc/wget sys-apps/gawk") update_options=("update" \ - "-Sy") + "-Sy" \ + "update" \ + "update" \ + "--sync" + ) install_options=("install -y" \ - "-S --needed --noconfirm") + "-S --needed --noconfirm" \ + "install -y" \ + "install -y" \ + "" + ) found_pm=0 pm_index=0 for pm in ${supported_package_manager}; do diff --git a/triagebot.toml b/triagebot.toml index e408f4432..887cec0c3 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -72,7 +72,7 @@ trigger_files = ["kernel/src/arch/x86_64"] [autolabel."O-riscv64"] trigger_files = ["kernel/src/arch/riscv64"] -[autolabel."T-driver"] +[autolabel."A-driver"] trigger_files = [ "kernel/src/driver", "kernel/src/arch/x86_64/driver", @@ -116,29 +116,36 @@ users_on_vacation = [] [assign.adhoc_groups] # 驱动程序 -driver = ["@fslongjin", "@YJwu2023", "@GnoCiYeH"] +driver = ["@dragonos/main"] + +# SIG-MM +sig-mm = ["@dragonos/mm"] # 虚拟化 -virtulization = ["@fslongjin", "@ZXXYy"] +virtulization = ["@dragonos/virtualization"] -filesystem = ["@fslongjin"] +main = [ "@dragonos/main" ] +network = [ "@dragonos/network" ] riscv64 = ["@fslongjin"] x86_64 = ["@fslongjin", "@GnoCiYeH", "@Chiichen"] # CI/CD -infra-ci = ["@fslongjin", "@Chiichen"] +infra = ["@dragonos/infra"] -bootstrap = ["@fslongjin"] [assign.owners] -"/.github/workflows" = ["infra-ci"] -"/triagebot.toml" = ["infra-ci"] -"/kernel/src/driver" = ["driver"] -"/kernel/src/filesystem" = ["filesystem"] +"/.github/workflows" = ["infra"] +"/.github/actions" = ["infra"] +"/triagebot.toml" = ["infra"] +"/kernel/src/driver" = ["main"] +"/kernel/src/filesystem" = ["main"] +"/kernel/src/sched" = ["main"] +"/kernel/src/process" = ["main"] +"/kernel/src/net" = ["network"] "/kernel/src/virt" = ["virtulization"] "/kernel/src/arch/x86_64/kvm" = ["virtulization"] "/kernel/src/arch/x86_64" = ["x86_64"] "/kernel/src/arch/riscv64" = ["riscv64"] -"/tools" = ["bootstrap"] +"/tools" = ["infra"] diff --git a/user/apps/about/about.c b/user/apps/about/about.c index f559e2dcd..059618eac 100644 --- a/user/apps/about/about.c +++ b/user/apps/about/about.c @@ -17,7 +17,7 @@ void print_copyright() printf(" DragonOS - An opensource operating system.\n"); printf(" Copyright: DragonOS Community. 2022-2024, All rights reserved.\n"); printf(" Version: "); - printf("\033[1;32m%s\033[0m", "V0.1.9\n"); + printf("\033[1;32m%s\033[0m", "V0.1.10\n"); printf(" Git commit SHA1: %s\n", DRAGONOS_GIT_COMMIT_SHA1); printf(" Build time: %s %s\n", __DATE__, __TIME__); printf(" \nYou can visit the project via:\n"); diff --git a/user/apps/clear/Makefile b/user/apps/clear/Makefile index 0239a0625..127c6ccb3 100644 --- a/user/apps/clear/Makefile +++ b/user/apps/clear/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN="+nightly-2023-08-15-x86_64-unknown-linux-gnu" +TOOLCHAIN="+nightly-2024-07-23-x86_64-unknown-linux-gnu" # RUSTFLAGS+="-C target-feature=+crt-static -C link-arg=-no-pie" ifdef DADK_CURRENT_BUILD_DIR diff --git a/user/apps/http_server/main.c b/user/apps/http_server/main.c index 95dbb2d1c..76fb33546 100644 --- a/user/apps/http_server/main.c +++ b/user/apps/http_server/main.c @@ -233,6 +233,8 @@ int main(int argc, char const *argv[]) // 关闭客户端连接 close(new_socket); } + // 关闭tcp socket + close(server_fd); return 0; } \ No newline at end of file diff --git a/user/apps/libgcc/Makefile b/user/apps/libgcc/Makefile new file mode 100644 index 000000000..1a15d2447 --- /dev/null +++ b/user/apps/libgcc/Makefile @@ -0,0 +1,2 @@ +install: + cp -r lib $(DADK_CURRENT_BUILD_DIR) \ No newline at end of file diff --git a/user/apps/libgcc/lib/libgcc_s.so.1 b/user/apps/libgcc/lib/libgcc_s.so.1 new file mode 100644 index 0000000000000000000000000000000000000000..f758425aee23c11880dd691b64a869bdc4d00d7c GIT binary patch literal 732952 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�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