DragonOS/kernel/mm/mmio.c
fslongjin 0223232f3f new: 初步完成了mmio地址空间自动分配(未测试)
bugfix: mmio虚拟地址分配的bug
2022-08-20 14:54:19 +08:00

119 lines
3.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "mmio.h"
#include "mmio-buddy.h"
#include <common/math.h>
void mmio_init()
{
mmio_buddy_init();
}
/**
* @brief 创建一块mmio区域并将vma绑定到initial_mm
*
* @param size mmio区域的大小字节
* @param vm_flags 要把vma设置成的标志
* @param res_vaddr 返回值-分配得到的虚拟地址
* @param res_length 返回值-分配的虚拟地址空间长度
* @return int 错误码
*/
int mmio_create(uint32_t size, vm_flags_t vm_flags, uint64_t *res_vaddr, uint64_t *res_size)
{
int retval = 0;
// 申请的内存超过允许的最大大小
if (unlikely(size > PAGE_1G_SIZE || size == 0))
return -EPERM;
// 计算要从buddy中申请地址空间大小(按照2的n次幂来对齐)
int size_exp = 31 - __clz(size);
if (size_exp < PAGE_4K_SHIFT)
{
size_exp = PAGE_4K_SHIFT;
size = PAGE_4K_SIZE;
}
else if (size & (~(1 << size_exp)))
{
++size_exp;
size = 1 << size_exp;
}
// 申请内存
struct __mmio_buddy_addr_region *buddy_region = mmio_buddy_query_addr_region(size_exp);
if (buddy_region == NULL) // 没有空闲的mmio空间了
return -ENOMEM;
*res_vaddr = buddy_region->vaddr;
*res_size = size;
// 释放region
__mmio_buddy_release_addr_region(buddy_region);
// ====创建vma===
// 设置vma flags
vm_flags |= (VM_IO | VM_DONTCOPY);
uint64_t len_4k = size % PAGE_2M_SIZE;
uint64_t len_2m = size - len_4k;
// 先创建2M的vma然后创建4k的
for (uint32_t i = 0; i < len_2m; i += PAGE_2M_SIZE)
{
retval = mm_create_vma(&initial_mm, buddy_region->vaddr + i, PAGE_2M_SIZE, vm_flags, NULL, NULL);
if (unlikely(retval != 0))
goto failed;
}
for (uint32_t i = len_2m; i < size; i += PAGE_4K_SIZE)
{
retval = mm_create_vma(&initial_mm, buddy_region->vaddr + i, PAGE_4K_SIZE, vm_flags, NULL, NULL);
if (unlikely(retval != 0))
goto failed;
}
return 0;
failed:;
kerror("failed to create mmio vma. pid=%d", current_pcb->pid);
// todo: 当失败时将已创建的vma删除
return retval;
}
/**
* @brief 取消mmio的映射并将地址空间归还到buddy中
*
* @param vaddr 起始的虚拟地址
* @param length 要归还的地址空间的长度
* @return int 错误码
*/
int mmio_release(uint64_t vaddr, uint64_t length)
{
int retval = 0;
// 先将这些区域都unmap了
mm_unmap(&initial_mm, vaddr, length, false);
// 将这些区域加入buddy
for (uint64_t i = 0; i < length;)
{
struct vm_area_struct *vma = vma_find(&initial_mm, vaddr + i);
if (unlikely(vma == NULL))
{
kerror("mmio_release failed: vma not found. At address: %#018lx, pid=%ld", vaddr + i, current_pcb->pid);
return -EINVAL;
}
if (unlikely(vma->vm_start != (vaddr + i)))
{
kerror("mmio_release failed: addr_start is not equal to current: %#018lx.", vaddr + i);
return -EINVAL;
}
// 往buddy中插入内存块
retval = __mmio_buddy_give_back(vma->vm_start, 31 - __clz(vma->vm_end - vma->vm_start));
i += vma->vm_end - vma->vm_start;
// 释放vma结构体
vm_area_del(vma);
vm_area_free(vma);
if (unlikely(retval != 0))
goto give_back_failed;
}
return 0;
give_back_failed:;
kerror("mmio_release give_back failed: ");
return retval;
}