|
@@ -0,0 +1,634 @@
|
|
|
+use crate::{
|
|
|
+ arch::asm::current::current_pcb,
|
|
|
+ include::bindings::bindings::{
|
|
|
+ initial_mm, mm_create_vma, mm_unmap, vm_area_del, vm_area_free, vm_area_struct, vm_flags_t,
|
|
|
+ vma_find, EINVAL, ENOMEM, EPERM, MMIO_BASE, MMIO_TOP, PAGE_1G_SHIFT, PAGE_1G_SIZE,
|
|
|
+ PAGE_2M_SIZE, PAGE_4K_SHIFT, PAGE_4K_SIZE, VM_DONTCOPY, VM_IO,
|
|
|
+ },
|
|
|
+ kdebug, kerror,
|
|
|
+ libs::spinlock::{SpinLock, SpinLockGuard},
|
|
|
+};
|
|
|
+use alloc::{boxed::Box, collections::LinkedList, vec::Vec};
|
|
|
+use core::{mem, ptr::null_mut};
|
|
|
+
|
|
|
+// 最大的伙伴块的幂
|
|
|
+const MMIO_BUDDY_MAX_EXP: u32 = PAGE_1G_SHIFT;
|
|
|
+// 最小的伙伴块的幂
|
|
|
+const MMIO_BUDDY_MIN_EXP: u32 = PAGE_4K_SHIFT;
|
|
|
+// 内存池数组的范围
|
|
|
+const MMIO_BUDDY_REGION_COUNT: u32 = MMIO_BUDDY_MAX_EXP - MMIO_BUDDY_MIN_EXP + 1;
|
|
|
+
|
|
|
+lazy_static! {
|
|
|
+ pub static ref MMIO_POOL: MmioBuddyMemPool = MmioBuddyMemPool::new();
|
|
|
+}
|
|
|
+
|
|
|
+pub enum MmioResult {
|
|
|
+ SUCCESS,
|
|
|
+ EINVAL,
|
|
|
+ ENOFOUND,
|
|
|
+ WRONGEXP,
|
|
|
+ ISEMPTY,
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief buddy内存池
|
|
|
+pub struct MmioBuddyMemPool {
|
|
|
+ pool_start_addr: u64,
|
|
|
+ pool_size: u64,
|
|
|
+ free_regions: [SpinLock<MmioFreeRegionList>; MMIO_BUDDY_REGION_COUNT as usize],
|
|
|
+}
|
|
|
+impl Default for MmioBuddyMemPool {
|
|
|
+ fn default() -> Self {
|
|
|
+ MmioBuddyMemPool {
|
|
|
+ pool_start_addr: MMIO_BASE as u64,
|
|
|
+ pool_size: (MMIO_TOP - MMIO_BASE) as u64,
|
|
|
+ free_regions: unsafe { mem::zeroed() },
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+impl MmioBuddyMemPool {
|
|
|
+ fn new() -> Self {
|
|
|
+ return MmioBuddyMemPool {
|
|
|
+ ..Default::default()
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief 创建新的地址区域结构体
|
|
|
+ ///
|
|
|
+ /// @param vaddr 虚拟地址
|
|
|
+ ///
|
|
|
+ /// @return 创建好的地址区域结构体
|
|
|
+ fn __create_region(&self, vaddr: u64) -> Box<MmioBuddyAddrRegion> {
|
|
|
+ let mut region: Box<MmioBuddyAddrRegion> = Box::new(MmioBuddyAddrRegion::new());
|
|
|
+ region.vaddr = vaddr;
|
|
|
+ return region;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief 将内存块归还给buddy
|
|
|
+ ///
|
|
|
+ /// @param vaddr 虚拟地址
|
|
|
+ ///
|
|
|
+ /// @param exp 内存空间的大小(2^exp)
|
|
|
+ ///
|
|
|
+ /// @param list_guard 【exp】对应的链表
|
|
|
+ ///
|
|
|
+ /// @return Ok(i32) 返回0
|
|
|
+ ///
|
|
|
+ /// @return Err(i32) 返回错误码
|
|
|
+ fn __give_back_block(&self, vaddr: u64, exp: u32) -> Result<i32, i32> {
|
|
|
+ // 确保内存对齐,低位都要为0
|
|
|
+ if (vaddr & ((1 << exp) - 1)) != 0 {
|
|
|
+ return Err(-(EINVAL as i32));
|
|
|
+ }
|
|
|
+ let region: Box<MmioBuddyAddrRegion> = self.__create_region(vaddr);
|
|
|
+ // 加入buddy
|
|
|
+ let list_guard: &mut SpinLockGuard<MmioFreeRegionList> =
|
|
|
+ &mut self.free_regions[__exp2index(exp)].lock();
|
|
|
+ self.__buddy_add_region_obj(region, list_guard);
|
|
|
+ return Ok(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief 将给定大小为2^{exp}的内存块一分为二,并插入内存块大小为2^{exp-1}的链表中
|
|
|
+ ///
|
|
|
+ /// @param region 要被分割的地址区域结构体(保证其已经从链表中取出)
|
|
|
+ ///
|
|
|
+ /// @param exp 要被分割的地址区域的大小的幂
|
|
|
+ ///
|
|
|
+ /// @param list_guard 【exp-1】对应的链表
|
|
|
+ fn __buddy_split(
|
|
|
+ &self,
|
|
|
+ region: Box<MmioBuddyAddrRegion>,
|
|
|
+ exp: u32,
|
|
|
+ low_list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
|
|
|
+ ) {
|
|
|
+ let vaddr: u64 = self.__buddy_block_vaddr(region.vaddr, exp - 1);
|
|
|
+ let new_region: Box<MmioBuddyAddrRegion> = self.__create_region(vaddr);
|
|
|
+ self.__buddy_add_region_obj(region, low_list_guard);
|
|
|
+ self.__buddy_add_region_obj(new_region, low_list_guard);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief 从buddy中申请一块指定大小的内存区域
|
|
|
+ ///
|
|
|
+ /// @param exp 要申请的内存块的大小的幂(2^exp)
|
|
|
+ ///
|
|
|
+ /// @param list_guard exp对应的链表
|
|
|
+ ///
|
|
|
+ /// @return Ok(Box<MmioBuddyAddrRegion>) 符合要求的内存区域。
|
|
|
+ ///
|
|
|
+ /// @return Err(MmioResult)
|
|
|
+ /// - 没有满足要求的内存块时,返回ENOFOUND
|
|
|
+ /// - 申请的内存块大小超过合法范围,返回WRONGEXP
|
|
|
+ /// - 调用函数出错时,返回出错函数对应错误码
|
|
|
+ fn __query_addr_region(
|
|
|
+ &self,
|
|
|
+ exp: u32,
|
|
|
+ list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
|
|
|
+ ) -> Result<Box<MmioBuddyAddrRegion>, MmioResult> {
|
|
|
+ // 申请范围错误
|
|
|
+ if exp < MMIO_BUDDY_MIN_EXP || exp > MMIO_BUDDY_MAX_EXP {
|
|
|
+ kdebug!("__query_addr_region: exp wrong");
|
|
|
+ return Err(MmioResult::WRONGEXP);
|
|
|
+ }
|
|
|
+ // 没有恰好符合要求的内存块
|
|
|
+ // 注意:exp对应的链表list_guard已上锁【注意避免死锁问题】
|
|
|
+ if list_guard.num_free == 0 {
|
|
|
+ // 找到最小符合申请范围的内存块
|
|
|
+ // 将大的内存块依次分成小块内存,直到能够满足exp大小,即将exp+1分成两块exp
|
|
|
+ for e in exp + 1..MMIO_BUDDY_MAX_EXP + 1 {
|
|
|
+ if self.free_regions[__exp2index(e) as usize].lock().num_free == 0 {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ for e2 in (exp + 1..e + 1).rev() {
|
|
|
+ match self
|
|
|
+ .__buddy_pop_region(&mut self.free_regions[__exp2index(e2) as usize].lock())
|
|
|
+ {
|
|
|
+ Ok(region) => {
|
|
|
+ if e2 != exp + 1 {
|
|
|
+ // 要将分裂后的内存块插入到更小的链表中
|
|
|
+ let low_list_guard: &mut SpinLockGuard<MmioFreeRegionList> =
|
|
|
+ &mut self.free_regions[__exp2index(e2 - 1) as usize].lock();
|
|
|
+ self.__buddy_split(region, e2, low_list_guard);
|
|
|
+ } else {
|
|
|
+ // 由于exp对应的链表list_guard已经被锁住了 不能再加锁
|
|
|
+ // 所以直接将list_guard传入
|
|
|
+ self.__buddy_split(region, e2, list_guard);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Err(err) => {
|
|
|
+ kdebug!("buddy_pop_region get wrong");
|
|
|
+ return Err(err);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // 判断是否获得了exp大小的内存块
|
|
|
+ if list_guard.num_free > 0 {
|
|
|
+ return Ok(list_guard.list.pop_back().unwrap());
|
|
|
+ }
|
|
|
+ // 拆分大内存块无法获得exp大小内存块
|
|
|
+ // 尝试用小内存块合成
|
|
|
+ // 即将两块exp合成一块exp+1
|
|
|
+ for e in MMIO_BUDDY_MIN_EXP..exp {
|
|
|
+ if e != exp - 1 {
|
|
|
+ let high_list_guard: &mut SpinLockGuard<MmioFreeRegionList> =
|
|
|
+ &mut self.free_regions[__exp2index(exp + 1)].lock();
|
|
|
+ match self.__buddy_merge(
|
|
|
+ e,
|
|
|
+ &mut self.free_regions[__exp2index(e) as usize].lock(),
|
|
|
+ high_list_guard,
|
|
|
+ ) {
|
|
|
+ Ok(_) => continue,
|
|
|
+ Err(err) => {
|
|
|
+ return Err(err);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ match self.__buddy_merge(
|
|
|
+ e,
|
|
|
+ &mut self.free_regions[__exp2index(e) as usize].lock(),
|
|
|
+ list_guard,
|
|
|
+ ) {
|
|
|
+ Ok(_) => continue,
|
|
|
+ Err(err) => {
|
|
|
+ return Err(err);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ //判断是否获得了exp大小的内存块
|
|
|
+ if list_guard.num_free > 0 {
|
|
|
+ return Ok(list_guard.list.pop_back().unwrap());
|
|
|
+ }
|
|
|
+ return Err(MmioResult::ENOFOUND);
|
|
|
+ } else {
|
|
|
+ return Ok(list_guard.list.pop_back().unwrap());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief 对query_addr_region进行封装
|
|
|
+ ///
|
|
|
+ /// @param exp 内存区域的大小(2^exp)
|
|
|
+ ///
|
|
|
+ /// @return Ok(Box<MmioBuddyAddrRegion>)符合要求的内存块信息结构体。
|
|
|
+ /// @return Err(MmioResult) 没有满足要求的内存块时,返回__query_addr_region的错误码。
|
|
|
+ fn mmio_buddy_query_addr_region(
|
|
|
+ &self,
|
|
|
+ exp: u32,
|
|
|
+ ) -> Result<Box<MmioBuddyAddrRegion>, MmioResult> {
|
|
|
+ let list_guard: &mut SpinLockGuard<MmioFreeRegionList> =
|
|
|
+ &mut self.free_regions[__exp2index(exp)].lock();
|
|
|
+ match self.__query_addr_region(exp, list_guard) {
|
|
|
+ Ok(ret) => return Ok(ret),
|
|
|
+ Err(err) => {
|
|
|
+ kdebug!("mmio_buddy_query_addr_region failed");
|
|
|
+ return Err(err);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /// @brief 往指定的地址空间链表中添加一个地址区域
|
|
|
+ ///
|
|
|
+ /// @param region 要被添加的地址结构体
|
|
|
+ ///
|
|
|
+ /// @param list_guard 目标链表
|
|
|
+ fn __buddy_add_region_obj(
|
|
|
+ &self,
|
|
|
+ region: Box<MmioBuddyAddrRegion>,
|
|
|
+ list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
|
|
|
+ ) {
|
|
|
+ list_guard.list.push_back(region);
|
|
|
+ list_guard.num_free += 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief 根据地址和内存块大小,计算伙伴块虚拟内存的地址
|
|
|
+ #[inline(always)]
|
|
|
+ fn __buddy_block_vaddr(&self, vaddr: u64, exp: u32) -> u64 {
|
|
|
+ return vaddr ^ (1 << exp);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief 寻找并弹出指定内存块的伙伴块
|
|
|
+ ///
|
|
|
+ /// @param region 对应内存块的信息
|
|
|
+ ///
|
|
|
+ /// @param exp 内存块大小
|
|
|
+ ///
|
|
|
+ /// @param list_guard 【exp】对应的链表
|
|
|
+ ///
|
|
|
+ /// @return Ok(Box<MmioBuddyAddrRegion) 返回伙伴块的引用
|
|
|
+ /// @return Err(MmioResult)
|
|
|
+ /// - 当链表为空,返回ISEMPTY
|
|
|
+ /// - 没有找到伙伴块,返回ENOFOUND
|
|
|
+ fn __pop_buddy_block(
|
|
|
+ &self,
|
|
|
+ vaddr: u64,
|
|
|
+ exp: u32,
|
|
|
+ list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
|
|
|
+ ) -> Result<Box<MmioBuddyAddrRegion>, MmioResult> {
|
|
|
+ if list_guard.list.len() == 0 {
|
|
|
+ return Err(MmioResult::ISEMPTY);
|
|
|
+ } else {
|
|
|
+ //计算伙伴块的地址
|
|
|
+ let buddy_vaddr = self.__buddy_block_vaddr(vaddr, exp);
|
|
|
+
|
|
|
+ // element 只会有一个元素
|
|
|
+ let mut element: Vec<Box<MmioBuddyAddrRegion>> = list_guard
|
|
|
+ .list
|
|
|
+ .drain_filter(|x| x.vaddr == buddy_vaddr)
|
|
|
+ .collect();
|
|
|
+ if element.len() == 1 {
|
|
|
+ list_guard.num_free -= 1;
|
|
|
+ return Ok(element.pop().unwrap());
|
|
|
+ }
|
|
|
+
|
|
|
+ //没有找到对应的伙伴块
|
|
|
+ return Err(MmioResult::ENOFOUND);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief 从指定空闲链表中取出内存区域
|
|
|
+ ///
|
|
|
+ /// @param list_guard 【exp】对应的链表
|
|
|
+ ///
|
|
|
+ /// @return Ok(Box<MmioBuddyAddrRegion>) 内存块信息结构体的引用。
|
|
|
+ ///
|
|
|
+ /// @return Err(MmioResult) 当链表为空,无法删除时,返回ISEMPTY
|
|
|
+ fn __buddy_pop_region(
|
|
|
+ &self,
|
|
|
+ list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
|
|
|
+ ) -> Result<Box<MmioBuddyAddrRegion>, MmioResult> {
|
|
|
+ if !list_guard.list.is_empty() {
|
|
|
+ list_guard.num_free -= 1;
|
|
|
+ return Ok(list_guard.list.pop_back().unwrap());
|
|
|
+ }
|
|
|
+ return Err(MmioResult::ISEMPTY);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief 合并所有2^{exp}大小的内存块
|
|
|
+ ///
|
|
|
+ /// @param exp 内存块大小的幂(2^exp)
|
|
|
+ ///
|
|
|
+ /// @param list_guard exp对应的链表
|
|
|
+ ///
|
|
|
+ /// @param high_list_guard exp+1对应的链表
|
|
|
+ ///
|
|
|
+ /// @return Ok(MmioResult) 合并成功返回SUCCESS
|
|
|
+ /// @return Err(MmioResult)
|
|
|
+ /// - 内存块过少,无法合并,返回EINVAL
|
|
|
+ /// - __pop_buddy_block调用出错,返回其错误码
|
|
|
+ /// - __buddy_merge_blocks调用出错,返回其错误码
|
|
|
+ fn __buddy_merge(
|
|
|
+ &self,
|
|
|
+ exp: u32,
|
|
|
+ list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
|
|
|
+ high_list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
|
|
|
+ ) -> Result<MmioResult, MmioResult> {
|
|
|
+ // 至少要两个内存块才能合并
|
|
|
+ if list_guard.num_free <= 1 {
|
|
|
+ return Err(MmioResult::EINVAL);
|
|
|
+ }
|
|
|
+ loop {
|
|
|
+ if list_guard.num_free <= 1 {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // 获取内存块
|
|
|
+ let vaddr: u64 = list_guard.list.back().unwrap().vaddr;
|
|
|
+ // 获取伙伴内存块
|
|
|
+ match self.__pop_buddy_block(vaddr, exp, list_guard) {
|
|
|
+ Err(err) => {
|
|
|
+ return Err(err);
|
|
|
+ }
|
|
|
+ Ok(buddy_region) => {
|
|
|
+ let region: Box<MmioBuddyAddrRegion> = list_guard.list.pop_back().unwrap();
|
|
|
+ let copy_region: Box<MmioBuddyAddrRegion> = Box::new(MmioBuddyAddrRegion {
|
|
|
+ vaddr: region.vaddr,
|
|
|
+ });
|
|
|
+ // 在两块内存都被取出之后才进行合并
|
|
|
+ match self.__buddy_merge_blocks(region, buddy_region, exp, high_list_guard) {
|
|
|
+ Err(err) => {
|
|
|
+ // 如果合并失败了要将取出来的元素放回去
|
|
|
+ self.__buddy_add_region_obj(copy_region, list_guard);
|
|
|
+ kdebug!("__buddy_merge: __buddy_merge_blocks failed");
|
|
|
+ return Err(err);
|
|
|
+ }
|
|
|
+ Ok(_) => continue,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Ok(MmioResult::SUCCESS);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// @brief 合并两个【已经从链表中取出】的内存块
|
|
|
+ ///
|
|
|
+ /// @param region_1 第一个内存块
|
|
|
+ ///
|
|
|
+ /// @param region_2 第二个内存
|
|
|
+ ///
|
|
|
+ /// @return Ok(MmioResult) 成功返回SUCCESS
|
|
|
+ ///
|
|
|
+ /// @return Err(MmioResult) 两个内存块不是伙伴块,返回EINVAL
|
|
|
+ fn __buddy_merge_blocks(
|
|
|
+ &self,
|
|
|
+ region_1: Box<MmioBuddyAddrRegion>,
|
|
|
+ region_2: Box<MmioBuddyAddrRegion>,
|
|
|
+ exp: u32,
|
|
|
+ high_list_guard: &mut SpinLockGuard<MmioFreeRegionList>,
|
|
|
+ ) -> Result<MmioResult, MmioResult> {
|
|
|
+ // 判断是否为伙伴块
|
|
|
+ if region_1.vaddr != self.__buddy_block_vaddr(region_2.vaddr, exp) {
|
|
|
+ return Err(MmioResult::EINVAL);
|
|
|
+ }
|
|
|
+ // 将大的块放进下一级链表
|
|
|
+ self.__buddy_add_region_obj(region_1, high_list_guard);
|
|
|
+ return Ok(MmioResult::SUCCESS);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief mmio伙伴系统内部的地址区域结构体
|
|
|
+pub struct MmioBuddyAddrRegion {
|
|
|
+ vaddr: u64,
|
|
|
+}
|
|
|
+impl MmioBuddyAddrRegion {
|
|
|
+ pub fn new() -> Self {
|
|
|
+ return MmioBuddyAddrRegion {
|
|
|
+ ..Default::default()
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
+impl Default for MmioBuddyAddrRegion {
|
|
|
+ fn default() -> Self {
|
|
|
+ MmioBuddyAddrRegion {
|
|
|
+ vaddr: Default::default(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief 空闲页数组结构体
|
|
|
+pub struct MmioFreeRegionList {
|
|
|
+ /// 存储mmio_buddy的地址链表
|
|
|
+ list: LinkedList<Box<MmioBuddyAddrRegion>>,
|
|
|
+ /// 空闲块的数量
|
|
|
+ num_free: i64,
|
|
|
+}
|
|
|
+impl MmioFreeRegionList {
|
|
|
+ fn new() -> Self {
|
|
|
+ return MmioFreeRegionList {
|
|
|
+ ..Default::default()
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
+impl Default for MmioFreeRegionList {
|
|
|
+ fn default() -> Self {
|
|
|
+ MmioFreeRegionList {
|
|
|
+ list: Default::default(),
|
|
|
+ num_free: 0,
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief 初始化mmio的伙伴系统
|
|
|
+#[no_mangle]
|
|
|
+pub extern "C" fn __mmio_buddy_init() {
|
|
|
+ // 创建一堆1GB的地址块
|
|
|
+ let cnt_1g_blocks: u32 = ((MMIO_TOP - MMIO_BASE) / PAGE_1G_SIZE as i64) as u32;
|
|
|
+ let mut vaddr_base: u64 = MMIO_BASE as u64;
|
|
|
+ for _ in 0..cnt_1g_blocks {
|
|
|
+ match MMIO_POOL.__give_back_block(vaddr_base, PAGE_1G_SHIFT) {
|
|
|
+ Ok(_) => {
|
|
|
+ vaddr_base += PAGE_1G_SIZE as u64;
|
|
|
+ }
|
|
|
+ Err(_) => {
|
|
|
+ kerror!("__mmio_buddy_init failed");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief 将内存对象大小的幂转换成内存池中的数组的下标
|
|
|
+///
|
|
|
+/// @param exp内存大小
|
|
|
+///
|
|
|
+/// @return 内存池数组下标
|
|
|
+#[inline(always)]
|
|
|
+fn __exp2index(exp: u32) -> usize {
|
|
|
+ return (exp - 12) as usize;
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief 创建一块mmio区域,并将vma绑定到initial_mm
|
|
|
+///
|
|
|
+/// @param size mmio区域的大小(字节)
|
|
|
+///
|
|
|
+/// @param vm_flags 要把vma设置成的标志
|
|
|
+///
|
|
|
+/// @param res_vaddr 返回值-分配得到的虚拟地址
|
|
|
+///
|
|
|
+/// @param res_length 返回值-分配的虚拟地址空间长度
|
|
|
+///
|
|
|
+/// @return int 错误码
|
|
|
+#[no_mangle]
|
|
|
+pub extern "C" fn mmio_create(
|
|
|
+ size: u32,
|
|
|
+ vm_flags: vm_flags_t,
|
|
|
+ res_vaddr: *mut u64,
|
|
|
+ res_length: *mut u64,
|
|
|
+) -> i32 {
|
|
|
+ if size > PAGE_1G_SIZE || size == 0 {
|
|
|
+ return -(EPERM as i32);
|
|
|
+ }
|
|
|
+ let mut retval: i32 = 0;
|
|
|
+ // 计算前导0
|
|
|
+ let mut size_exp: u32 = 31 - size.leading_zeros();
|
|
|
+ // 记录最终申请的空间大小
|
|
|
+ let mut new_size: u32 = size;
|
|
|
+ // 对齐要申请的空间大小
|
|
|
+ // 如果要申请的空间大小小于4k,则分配4k
|
|
|
+ if size_exp < PAGE_4K_SHIFT {
|
|
|
+ new_size = PAGE_4K_SIZE;
|
|
|
+ size_exp = PAGE_4K_SHIFT;
|
|
|
+ } else if (new_size & (!(1 << size_exp))) != 0 {
|
|
|
+ // 向左对齐空间大小
|
|
|
+ size_exp += 1;
|
|
|
+ new_size = 1 << size_exp;
|
|
|
+ }
|
|
|
+ match MMIO_POOL.mmio_buddy_query_addr_region(size_exp) {
|
|
|
+ Ok(region) => {
|
|
|
+ unsafe {
|
|
|
+ *res_vaddr = region.vaddr;
|
|
|
+ *res_length = new_size as u64;
|
|
|
+ }
|
|
|
+ // 创建vma
|
|
|
+ let flags: u64 = vm_flags | (VM_IO | VM_DONTCOPY) as u64;
|
|
|
+ let len_4k: u64 = (new_size % PAGE_2M_SIZE) as u64;
|
|
|
+ let len_2m: u64 = new_size as u64 - len_4k;
|
|
|
+ let mut loop_i: u64 = 0;
|
|
|
+ // 先分配2M的vma
|
|
|
+ loop {
|
|
|
+ if loop_i >= len_2m {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ let vma: *mut *mut vm_area_struct = null_mut();
|
|
|
+ retval = unsafe {
|
|
|
+ mm_create_vma(
|
|
|
+ &mut initial_mm,
|
|
|
+ region.vaddr + loop_i,
|
|
|
+ PAGE_2M_SIZE.into(),
|
|
|
+ flags,
|
|
|
+ null_mut(),
|
|
|
+ vma,
|
|
|
+ )
|
|
|
+ };
|
|
|
+ if retval != 0 {
|
|
|
+ kdebug!(
|
|
|
+ "failed to create mmio 2m vma. pid = {:?}",
|
|
|
+ current_pcb().pid
|
|
|
+ );
|
|
|
+ unsafe {
|
|
|
+ vm_area_del(*vma);
|
|
|
+ vm_area_free(*vma);
|
|
|
+ }
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ loop_i += PAGE_2M_SIZE as u64;
|
|
|
+ }
|
|
|
+ // 分配4K的vma
|
|
|
+ loop_i = len_2m;
|
|
|
+ loop {
|
|
|
+ if loop_i >= size as u64 {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ let vma: *mut *mut vm_area_struct = null_mut();
|
|
|
+ retval = unsafe {
|
|
|
+ mm_create_vma(
|
|
|
+ &mut initial_mm,
|
|
|
+ region.vaddr + loop_i,
|
|
|
+ PAGE_4K_SIZE.into(),
|
|
|
+ flags,
|
|
|
+ null_mut(),
|
|
|
+ vma,
|
|
|
+ )
|
|
|
+ };
|
|
|
+ if retval != 0 {
|
|
|
+ kdebug!(
|
|
|
+ "failed to create mmio 4k vma. pid = {:?}",
|
|
|
+ current_pcb().pid
|
|
|
+ );
|
|
|
+ unsafe {
|
|
|
+ vm_area_del(*vma);
|
|
|
+ vm_area_free(*vma);
|
|
|
+ }
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ loop_i += PAGE_4K_SIZE as u64;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Err(_) => {
|
|
|
+ kdebug!("failed to create mmio vma.pid = {:?}", current_pcb().pid);
|
|
|
+ return -(ENOMEM as i32);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief 取消mmio的映射并将地址空间归还到buddy中
|
|
|
+///
|
|
|
+/// @param vaddr 起始的虚拟地址
|
|
|
+///
|
|
|
+/// @param length 要归还的地址空间的长度
|
|
|
+///
|
|
|
+/// @return Ok(i32) 成功返回0
|
|
|
+///
|
|
|
+/// @return Err(i32) 失败返回错误码
|
|
|
+#[no_mangle]
|
|
|
+pub extern "C" fn mmio_release(vaddr: u64, length: u64) -> i32 {
|
|
|
+ //先将要释放的空间取消映射
|
|
|
+ unsafe {
|
|
|
+ mm_unmap(&mut initial_mm, vaddr, length, false);
|
|
|
+ }
|
|
|
+ let mut loop_i: u64 = 0;
|
|
|
+ loop {
|
|
|
+ if loop_i >= length {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // 获取要释放的vma的结构体
|
|
|
+ let vma: *mut vm_area_struct = unsafe { vma_find(&mut initial_mm, vaddr + loop_i) };
|
|
|
+ if vma == null_mut() {
|
|
|
+ kdebug!(
|
|
|
+ "mmio_release failed: vma not found. At address: {:?}, pid = {:?}",
|
|
|
+ vaddr + loop_i,
|
|
|
+ current_pcb().pid
|
|
|
+ );
|
|
|
+ return -(EINVAL as i32);
|
|
|
+ }
|
|
|
+ // 检查vma起始地址是否正确
|
|
|
+ if unsafe { (*vma).vm_start != (vaddr + loop_i) } {
|
|
|
+ kdebug!(
|
|
|
+ "mmio_release failed: addr_start is not equal to current: {:?}. pid = {:?}",
|
|
|
+ vaddr + loop_i,
|
|
|
+ current_pcb().pid
|
|
|
+ );
|
|
|
+ return -(EINVAL as i32);
|
|
|
+ }
|
|
|
+ // 将vma对应空间归还
|
|
|
+ match MMIO_POOL.__give_back_block(unsafe { (*vma).vm_start }, unsafe {
|
|
|
+ 31 - ((*vma).vm_end - (*vma).vm_start).leading_zeros()
|
|
|
+ }) {
|
|
|
+ Ok(_) => {
|
|
|
+ loop_i += unsafe { (*vma).vm_end - (*vma).vm_start };
|
|
|
+ unsafe {
|
|
|
+ vm_area_del(vma);
|
|
|
+ vm_area_free(vma);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Err(err) => {
|
|
|
+ // vma对应空间没有成功归还的话,就不删除vma
|
|
|
+ kdebug!(
|
|
|
+ "mmio_release give_back failed: pid = {:?}",
|
|
|
+ current_pcb().pid
|
|
|
+ );
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|