mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-23 03:53:26 +00:00
新增rust ffi (#77)
* 引入cargo * 取消对Cargo.lock的跟踪 * 解决vscode报错问题 * new: rust的代码能够调用c语言的printk_color * 1、将原本run.sh的工作拆解,变为几个不同的make命令 2、在docker镜像中编译rust * 更改workflow * update workflow * new: 解决workflow无法通过编译的问题
This commit is contained in:
29
kernel/src/mm/Makefile
Normal file
29
kernel/src/mm/Makefile
Normal file
@ -0,0 +1,29 @@
|
||||
|
||||
CFLAGS += -I .
|
||||
|
||||
|
||||
all:mm.o slab.o mm-stat.o vma.o mmap.o utils.o mmio.o mmio-buddy.o
|
||||
|
||||
mm.o: mm.c
|
||||
$(CC) $(CFLAGS) -c mm.c -o mm.o
|
||||
|
||||
slab.o: slab.c
|
||||
$(CC) $(CFLAGS) -c slab.c -o slab.o
|
||||
|
||||
mm-stat.o: mm-stat.c
|
||||
$(CC) $(CFLAGS) -c mm-stat.c -o mm-stat.o
|
||||
|
||||
vma.o: vma.c
|
||||
$(CC) $(CFLAGS) -c vma.c -o vma.o
|
||||
|
||||
mmap.o: mmap.c
|
||||
$(CC) $(CFLAGS) -c mmap.c -o mmap.o
|
||||
|
||||
utils.o: utils.c
|
||||
$(CC) $(CFLAGS) -c utils.c -o utils.o
|
||||
|
||||
mmio.o: mmio.c
|
||||
$(CC) $(CFLAGS) -c mmio.c -o mmio.o
|
||||
|
||||
mmio-buddy.o: mmio-buddy.c
|
||||
$(CC) $(CFLAGS) -c mmio-buddy.c -o mmio-buddy.o
|
79
kernel/src/mm/internal.h
Normal file
79
kernel/src/mm/internal.h
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "mm.h"
|
||||
|
||||
|
||||
// 当vma被成功合并后的返回值
|
||||
#define __VMA_MERGED 1
|
||||
|
||||
/**
|
||||
* @brief 将vma结构体插入mm_struct的链表之中
|
||||
*
|
||||
* @param mm 内存空间分布结构体
|
||||
* @param vma 待插入的VMA结构体
|
||||
* @param prev 链表的前一个结点
|
||||
*/
|
||||
void __vma_link_list(struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev);
|
||||
|
||||
/**
|
||||
* @brief 将vma给定结构体从vma链表的结点之中删除
|
||||
*
|
||||
* @param mm 内存空间分布结构体
|
||||
* @param vma 待插入的VMA结构体
|
||||
*/
|
||||
void __vma_unlink_list(struct mm_struct *mm, struct vm_area_struct *vma);
|
||||
|
||||
/**
|
||||
* @brief 获取指定虚拟地址处映射的物理地址
|
||||
*
|
||||
* @param mm 内存空间分布结构体
|
||||
* @param vaddr 虚拟地址
|
||||
* @return uint64_t 已映射的物理地址
|
||||
*/
|
||||
uint64_t __mm_get_paddr(struct mm_struct *mm, uint64_t vaddr);
|
||||
|
||||
/**
|
||||
* @brief 创建anon_vma,并将其与页面结构体进行绑定
|
||||
* 若提供的页面结构体指针为NULL,则只创建,不绑定
|
||||
*
|
||||
* @param page 页面结构体的指针
|
||||
* @param lock_page 是否将页面结构体加锁
|
||||
* @return struct anon_vma_t* 创建好的anon_vma
|
||||
*/
|
||||
struct anon_vma_t *__anon_vma_create_alloc(struct Page *page, bool lock_page);
|
||||
|
||||
/**
|
||||
* @brief 释放anon vma结构体
|
||||
*
|
||||
* @param anon_vma 待释放的anon_vma结构体
|
||||
* @return int 返回码
|
||||
*/
|
||||
int __anon_vma_free(struct anon_vma_t *anon_vma);
|
||||
|
||||
/**
|
||||
* @brief 将指定的vma加入到anon_vma的管理范围之中
|
||||
*
|
||||
* @param anon_vma 页面的anon_vma
|
||||
* @param vma 待加入的vma
|
||||
* @return int 返回码
|
||||
*/
|
||||
int __anon_vma_add(struct anon_vma_t *anon_vma, struct vm_area_struct *vma);
|
||||
|
||||
/**
|
||||
* @brief 从anon_vma的管理范围中删除指定的vma
|
||||
* (在进入这个函数之前,应该要对anon_vma加锁)
|
||||
* @param vma 将要取消对应的anon_vma管理的vma结构体
|
||||
* @return int 返回码
|
||||
*/
|
||||
int __anon_vma_del(struct vm_area_struct *vma);
|
||||
|
||||
/**
|
||||
* @brief 创建mmio对应的页结构体
|
||||
*
|
||||
* @param paddr 物理地址
|
||||
* @return struct Page* 创建成功的page
|
||||
*/
|
||||
struct Page* __create_mmio_page_struct(uint64_t paddr);
|
||||
|
||||
// 判断给定的两个值是否跨越了2M边界
|
||||
#define CROSS_2M_BOUND(val1, val2) ((val1 & PAGE_2M_MASK) != (val2 & PAGE_2M_MASK))
|
195
kernel/src/mm/mm-stat.c
Normal file
195
kernel/src/mm/mm-stat.c
Normal file
@ -0,0 +1,195 @@
|
||||
/**
|
||||
* @file mm-stat.c
|
||||
* @author longjin(longjin@RinGoTek.cn)
|
||||
* @brief 查询内存信息
|
||||
* @version 0.1
|
||||
* @date 2022-08-06
|
||||
*
|
||||
* @copyright Copyright (c) 2022
|
||||
*
|
||||
*/
|
||||
|
||||
#include "mm.h"
|
||||
#include "slab.h"
|
||||
#include <common/errno.h>
|
||||
#include <process/ptrace.h>
|
||||
|
||||
extern const struct slab kmalloc_cache_group[16];
|
||||
|
||||
static int __empty_2m_pages(int zone);
|
||||
static int __count_in_using_2m_pages(int zone);
|
||||
static uint64_t __count_kmalloc_free();
|
||||
static uint64_t __count_kmalloc_using();
|
||||
static uint64_t __count_kmalloc_total();
|
||||
uint64_t sys_mm_stat(struct pt_regs *regs);
|
||||
|
||||
/**
|
||||
* @brief 获取指定zone中的空闲2M页的数量
|
||||
*
|
||||
* @param zone 内存zone号
|
||||
* @return int 空闲2M页数量
|
||||
*/
|
||||
static int __count_empty_2m_pages(int zone)
|
||||
{
|
||||
int zone_start = 0, zone_end = 0;
|
||||
|
||||
uint64_t attr = 0;
|
||||
switch (zone)
|
||||
{
|
||||
case ZONE_DMA:
|
||||
// DMA区域
|
||||
zone_start = 0;
|
||||
zone_end = ZONE_DMA_INDEX;
|
||||
attr |= PAGE_PGT_MAPPED;
|
||||
break;
|
||||
case ZONE_NORMAL:
|
||||
zone_start = ZONE_DMA_INDEX;
|
||||
zone_end = ZONE_NORMAL_INDEX;
|
||||
attr |= PAGE_PGT_MAPPED;
|
||||
break;
|
||||
case ZONE_UNMAPPED_IN_PGT:
|
||||
zone_start = ZONE_NORMAL_INDEX;
|
||||
zone_end = ZONE_UNMAPPED_INDEX;
|
||||
attr = 0;
|
||||
break;
|
||||
default:
|
||||
kerror("In __count_empty_2m_pages: param: zone invalid.");
|
||||
// 返回错误码
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
uint64_t result = 0;
|
||||
for (int i = zone_start; i <= zone_end; ++i)
|
||||
{
|
||||
result += (memory_management_struct.zones_struct + i)->count_pages_free;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取指定zone中的正在使用的2M页的数量
|
||||
*
|
||||
* @param zone 内存zone号
|
||||
* @return int 空闲2M页数量
|
||||
*/
|
||||
static int __count_in_using_2m_pages(int zone)
|
||||
{
|
||||
int zone_start = 0, zone_end = 0;
|
||||
|
||||
uint64_t attr = 0;
|
||||
switch (zone)
|
||||
{
|
||||
case ZONE_DMA:
|
||||
// DMA区域
|
||||
zone_start = 0;
|
||||
zone_end = ZONE_DMA_INDEX;
|
||||
attr |= PAGE_PGT_MAPPED;
|
||||
break;
|
||||
case ZONE_NORMAL:
|
||||
zone_start = ZONE_DMA_INDEX;
|
||||
zone_end = ZONE_NORMAL_INDEX;
|
||||
attr |= PAGE_PGT_MAPPED;
|
||||
break;
|
||||
case ZONE_UNMAPPED_IN_PGT:
|
||||
zone_start = ZONE_NORMAL_INDEX;
|
||||
zone_end = ZONE_UNMAPPED_INDEX;
|
||||
attr = 0;
|
||||
break;
|
||||
default:
|
||||
kerror("In __count_in_using_2m_pages: param: zone invalid.");
|
||||
// 返回错误码
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
uint64_t result = 0;
|
||||
for (int i = zone_start; i <= zone_end; ++i)
|
||||
{
|
||||
result += (memory_management_struct.zones_struct + i)->count_pages_using;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算kmalloc缓冲区中的空闲内存
|
||||
*
|
||||
* @return uint64_t 空闲内存(字节)
|
||||
*/
|
||||
static uint64_t __count_kmalloc_free()
|
||||
{
|
||||
uint64_t result = 0;
|
||||
for (int i = 0; i < sizeof(kmalloc_cache_group) / sizeof(struct slab); ++i)
|
||||
{
|
||||
result += kmalloc_cache_group[i].size * kmalloc_cache_group[i].count_total_free;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算kmalloc缓冲区中已使用的内存
|
||||
*
|
||||
* @return uint64_t 已使用的内存(字节)
|
||||
*/
|
||||
static uint64_t __count_kmalloc_using()
|
||||
{
|
||||
uint64_t result = 0;
|
||||
for (int i = 0; i < sizeof(kmalloc_cache_group) / sizeof(struct slab); ++i)
|
||||
{
|
||||
result += kmalloc_cache_group[i].size * kmalloc_cache_group[i].count_total_using;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算kmalloc缓冲区中总共占用的内存
|
||||
*
|
||||
* @return uint64_t 缓冲区占用的内存(字节)
|
||||
*/
|
||||
static uint64_t __count_kmalloc_total()
|
||||
{
|
||||
uint64_t result = 0;
|
||||
for (int i = 0; i < sizeof(kmalloc_cache_group) / sizeof(struct slab); ++i)
|
||||
{
|
||||
result += kmalloc_cache_group[i].size * (kmalloc_cache_group[i].count_total_free + kmalloc_cache_group[i].count_total_using);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取系统当前的内存信息(未上锁,不一定精准)
|
||||
*
|
||||
* @return struct mm_stat_t 内存信息结构体
|
||||
*/
|
||||
struct mm_stat_t mm_stat()
|
||||
{
|
||||
struct mm_stat_t tmp = {0};
|
||||
// 统计物理页的信息
|
||||
tmp.used = __count_in_using_2m_pages(ZONE_NORMAL) * PAGE_2M_SIZE;
|
||||
tmp.free = __count_empty_2m_pages(ZONE_NORMAL) * PAGE_2M_SIZE;
|
||||
tmp.total = tmp.used + tmp.free;
|
||||
tmp.shared = 0;
|
||||
// 统计kmalloc slab中的信息
|
||||
tmp.cache_free = __count_kmalloc_free();
|
||||
tmp.cache_used = __count_kmalloc_using();
|
||||
tmp.available = tmp.free + tmp.cache_free;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取内存信息的系统调用
|
||||
*
|
||||
* @param r8 返回的内存信息结构体的地址
|
||||
* @return uint64_t
|
||||
*/
|
||||
uint64_t sys_mstat(struct pt_regs *regs)
|
||||
{
|
||||
if (regs->r8 == NULL)
|
||||
return -EINVAL;
|
||||
struct mm_stat_t stat = mm_stat();
|
||||
if (regs->cs == (USER_CS | 0x3))
|
||||
copy_to_user((void *)regs->r8, &stat, sizeof(struct mm_stat_t));
|
||||
else
|
||||
memcpy((void *)regs->r8, &stat, sizeof(struct mm_stat_t));
|
||||
return 0;
|
||||
}
|
182
kernel/src/mm/mm-types.h
Normal file
182
kernel/src/mm/mm-types.h
Normal file
@ -0,0 +1,182 @@
|
||||
#pragma once
|
||||
#include <common/glib.h>
|
||||
#include <common/semaphore.h>
|
||||
#include <common/spinlock.h>
|
||||
#include <common/atomic.h>
|
||||
|
||||
struct mm_struct;
|
||||
struct anon_vma_t;
|
||||
typedef uint64_t vm_flags_t;
|
||||
|
||||
/**
|
||||
* @brief 内存页表结构体
|
||||
*
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
unsigned long pml4t;
|
||||
} pml4t_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long pdpt;
|
||||
} pdpt_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long pdt;
|
||||
} pdt_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned long pt;
|
||||
} pt_t;
|
||||
|
||||
// Address Range Descriptor Structure 地址范围描述符
|
||||
struct ARDS
|
||||
{
|
||||
ul BaseAddr; // 基地址
|
||||
ul Length; // 内存长度 以字节为单位
|
||||
unsigned int type; // 本段内存的类型
|
||||
// type=1 表示可以被操作系统使用
|
||||
// type=2 ARR - 内存使用中或被保留,操作系统不能使用
|
||||
// 其他 未定义,操作系统需要将其视为ARR
|
||||
} __attribute__((packed)); // 修饰该结构体不会生成对齐空间,改用紧凑格式
|
||||
|
||||
struct memory_desc
|
||||
{
|
||||
|
||||
struct ARDS e820[32]; // 物理内存段结构数组
|
||||
ul len_e820; // 物理内存段长度
|
||||
|
||||
ul *bmp; // 物理空间页映射位图
|
||||
ul bmp_len; // bmp的长度
|
||||
ul bits_size; // 物理地址空间页数量
|
||||
|
||||
struct Page *pages_struct;
|
||||
ul count_pages; // struct page结构体的总数
|
||||
ul pages_struct_len; // pages_struct链表的长度
|
||||
|
||||
struct Zone *zones_struct;
|
||||
ul count_zones; // zone结构体的数量
|
||||
ul zones_struct_len; // zones_struct列表的长度
|
||||
|
||||
ul kernel_code_start, kernel_code_end; // 内核程序代码段起始地址、结束地址
|
||||
ul kernel_data_end, rodata_end; // 内核程序数据段结束地址、 内核程序只读段结束地址
|
||||
uint64_t start_brk; // 堆地址的起始位置
|
||||
|
||||
ul end_of_struct; // 内存页管理结构的结束地址
|
||||
};
|
||||
|
||||
struct Zone
|
||||
{
|
||||
// 指向内存页的指针
|
||||
struct Page *pages_group;
|
||||
ul count_pages; // 本区域的struct page结构体总数
|
||||
|
||||
// 本内存区域的起始、结束的页对齐地址
|
||||
ul zone_addr_start;
|
||||
ul zone_addr_end;
|
||||
ul zone_length; // 区域长度
|
||||
|
||||
// 本区域空间的属性
|
||||
ul attr;
|
||||
|
||||
struct memory_desc *gmd_struct;
|
||||
|
||||
// 本区域正在使用中和空闲中的物理页面数量
|
||||
ul count_pages_using;
|
||||
ul count_pages_free;
|
||||
|
||||
// 物理页被引用次数
|
||||
ul total_pages_link;
|
||||
};
|
||||
|
||||
struct Page
|
||||
{
|
||||
// 本页所属的内存域结构体
|
||||
struct Zone *zone;
|
||||
// 本页对应的物理地址
|
||||
ul addr_phys;
|
||||
// 页面属性
|
||||
ul attr;
|
||||
// 页面被引用的次数
|
||||
ul ref_counts;
|
||||
// 本页的创建时间
|
||||
ul age;
|
||||
|
||||
struct anon_vma_t *anon_vma; // 本页对应的anon_vma
|
||||
|
||||
spinlock_t op_lock; // 页面操作锁
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 虚拟内存区域(VMA)结构体
|
||||
*
|
||||
*/
|
||||
struct vm_area_struct
|
||||
{
|
||||
struct vm_area_struct *vm_prev, *vm_next;
|
||||
|
||||
// 虚拟内存区域的范围是一个左闭右开的区间:[vm_start, vm_end)
|
||||
uint64_t vm_start; // 区域的起始地址
|
||||
uint64_t vm_end; // 区域的结束地址
|
||||
struct mm_struct *vm_mm; // 虚拟内存区域对应的mm结构体
|
||||
vm_flags_t vm_flags; // 虚拟内存区域的标志位, 具体可选值请见mm.h
|
||||
|
||||
|
||||
struct List anon_vma_list; // anon_vma的链表结点
|
||||
struct anon_vma_t * anon_vma; // 属于的anon_vma
|
||||
|
||||
struct vm_operations_t *vm_ops; // 操作方法
|
||||
atomic_t ref_count; // 引用计数
|
||||
pgoff_t page_offset; // 起始地址在当前VMA所占的2M物理页中的偏移量
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 内存空间分布结构体
|
||||
* 包含了进程内存空间分布的信息
|
||||
*/
|
||||
struct mm_struct
|
||||
{
|
||||
pml4t_t *pgd; // 内存页表指针
|
||||
struct vm_area_struct *vmas; // VMA列表
|
||||
// 代码段空间
|
||||
uint64_t code_addr_start, code_addr_end;
|
||||
// 数据段空间
|
||||
uint64_t data_addr_start, data_addr_end;
|
||||
// 只读数据段空间
|
||||
uint64_t rodata_addr_start, rodata_addr_end;
|
||||
// BSS段的空间
|
||||
uint64_t bss_start, bss_end;
|
||||
// 动态内存分配区(堆区域)
|
||||
uint64_t brk_start, brk_end;
|
||||
// 应用层栈基地址
|
||||
uint64_t stack_start;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 匿名vma对象的结构体
|
||||
*
|
||||
* anon_vma与每个内存页结构体进行一对一绑定
|
||||
* anon_vma也连接着一切使用到该内存页的vma,当发生页面换出时,应当更新与该page相关的所有vma在页表中的映射信息。
|
||||
*/
|
||||
struct anon_vma_t
|
||||
{
|
||||
// anon vma的操作信号量
|
||||
semaphore_t sem;
|
||||
|
||||
/**
|
||||
* 记录当前有多少个vma与该anon_vma关联,当vma被释放时,
|
||||
* 应当检查这个值。当该值为0时,应当释放anon_vma结构体
|
||||
*/
|
||||
atomic_t ref_count;
|
||||
|
||||
// todo: 把下面的循环链表更换成红黑树
|
||||
// 与当前anon_vma相关的vma的列表
|
||||
struct List vma_list;
|
||||
// 当前anon vma对应的page
|
||||
struct Page* page;
|
||||
};
|
686
kernel/src/mm/mm.c
Normal file
686
kernel/src/mm/mm.c
Normal file
@ -0,0 +1,686 @@
|
||||
#include "mm.h"
|
||||
#include "mm-types.h"
|
||||
#include "mmio.h"
|
||||
#include "slab.h"
|
||||
#include <common/printk.h>
|
||||
#include <common/kprint.h>
|
||||
#include <driver/multiboot2/multiboot2.h>
|
||||
#include <process/process.h>
|
||||
#include <common/compiler.h>
|
||||
#include <common/errno.h>
|
||||
#include <debug/traceback/traceback.h>
|
||||
|
||||
uint64_t mm_Total_Memory = 0;
|
||||
uint64_t mm_total_2M_pages = 0;
|
||||
struct mm_struct initial_mm = {0};
|
||||
|
||||
struct memory_desc memory_management_struct = {{0}, 0};
|
||||
|
||||
/**
|
||||
* @brief 从页表中获取pdt页表项的内容
|
||||
*
|
||||
* @param proc_page_table_addr 页表的地址
|
||||
* @param is_phys 页表地址是否为物理地址
|
||||
* @param virt_addr_start 要清除的虚拟地址的起始地址
|
||||
* @param length 要清除的区域的长度
|
||||
* @param clear 是否清除标志位
|
||||
*/
|
||||
uint64_t mm_get_PDE(ul proc_page_table_addr, bool is_phys, ul virt_addr, bool clear);
|
||||
|
||||
/**
|
||||
* @brief 检查页表是否存在不为0的页表项
|
||||
*
|
||||
* @param ptr 页表基指针
|
||||
* @return int8_t 存在 -> 1
|
||||
* 不存在 -> 0
|
||||
*/
|
||||
int8_t mm_check_page_table(uint64_t *ptr)
|
||||
{
|
||||
for (int i = 0; i < 512; ++i, ++ptr)
|
||||
{
|
||||
if (*ptr != 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mm_init()
|
||||
{
|
||||
kinfo("Initializing memory management unit...");
|
||||
// 设置内核程序不同部分的起止地址
|
||||
memory_management_struct.kernel_code_start = (ul)&_text;
|
||||
memory_management_struct.kernel_code_end = (ul)&_etext;
|
||||
memory_management_struct.kernel_data_end = (ul)&_edata;
|
||||
memory_management_struct.rodata_end = (ul)&_erodata;
|
||||
memory_management_struct.start_brk = (ul)&_end;
|
||||
|
||||
struct multiboot_mmap_entry_t mb2_mem_info[512];
|
||||
int count;
|
||||
|
||||
multiboot2_iter(multiboot2_get_memory, mb2_mem_info, &count);
|
||||
io_mfence();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
io_mfence();
|
||||
// 可用的内存
|
||||
if (mb2_mem_info->type == 1)
|
||||
mm_Total_Memory += mb2_mem_info->len;
|
||||
|
||||
// kdebug("[i=%d] mb2_mem_info[i].type=%d, mb2_mem_info[i].addr=%#018lx", i, mb2_mem_info[i].type, mb2_mem_info[i].addr);
|
||||
// 保存信息到mms
|
||||
memory_management_struct.e820[i].BaseAddr = mb2_mem_info[i].addr;
|
||||
memory_management_struct.e820[i].Length = mb2_mem_info[i].len;
|
||||
memory_management_struct.e820[i].type = mb2_mem_info[i].type;
|
||||
memory_management_struct.len_e820 = i;
|
||||
|
||||
// 脏数据
|
||||
if (mb2_mem_info[i].type > 4 || mb2_mem_info[i].len == 0 || mb2_mem_info[i].type < 1)
|
||||
break;
|
||||
}
|
||||
printk("[ INFO ] Total amounts of RAM : %ld bytes\n", mm_Total_Memory);
|
||||
|
||||
// 计算有效内存页数
|
||||
io_mfence();
|
||||
for (int i = 0; i < memory_management_struct.len_e820; ++i)
|
||||
{
|
||||
if (memory_management_struct.e820[i].type != 1)
|
||||
continue;
|
||||
io_mfence();
|
||||
// 将内存段的起始物理地址按照2M进行对齐
|
||||
ul addr_start = PAGE_2M_ALIGN(memory_management_struct.e820[i].BaseAddr);
|
||||
// 将内存段的终止物理地址的低2M区域清空,以实现对齐
|
||||
ul addr_end = ((memory_management_struct.e820[i].BaseAddr + memory_management_struct.e820[i].Length) & PAGE_2M_MASK);
|
||||
|
||||
// 内存段不可用
|
||||
if (addr_end <= addr_start)
|
||||
continue;
|
||||
io_mfence();
|
||||
mm_total_2M_pages += ((addr_end - addr_start) >> PAGE_2M_SHIFT);
|
||||
}
|
||||
kinfo("Total amounts of 2M pages : %ld.", mm_total_2M_pages);
|
||||
|
||||
// 物理地址空间的最大地址(包含了物理内存、内存空洞、ROM等)
|
||||
ul max_addr = memory_management_struct.e820[memory_management_struct.len_e820].BaseAddr + memory_management_struct.e820[memory_management_struct.len_e820].Length;
|
||||
// 初始化mms的bitmap
|
||||
// bmp的指针指向截止位置的4k对齐的上边界(防止修改了别的数据)
|
||||
io_mfence();
|
||||
memory_management_struct.bmp = (unsigned long *)((memory_management_struct.start_brk + PAGE_4K_SIZE - 1) & PAGE_4K_MASK);
|
||||
memory_management_struct.bits_size = max_addr >> PAGE_2M_SHIFT; // 物理地址空间的最大页面数
|
||||
memory_management_struct.bmp_len = (((unsigned long)(max_addr >> PAGE_2M_SHIFT) + sizeof(unsigned long) * 8 - 1) / 8) & (~(sizeof(unsigned long) - 1)); // bmp由多少个unsigned long变量组成
|
||||
io_mfence();
|
||||
|
||||
// 初始化bitmap, 先将整个bmp空间全部置位。稍后再将可用物理内存页复位。
|
||||
memset(memory_management_struct.bmp, 0xff, memory_management_struct.bmp_len);
|
||||
io_mfence();
|
||||
// 初始化内存页结构
|
||||
// 将页结构映射于bmp之后
|
||||
memory_management_struct.pages_struct = (struct Page *)(((unsigned long)memory_management_struct.bmp + memory_management_struct.bmp_len + PAGE_4K_SIZE - 1) & PAGE_4K_MASK);
|
||||
|
||||
memory_management_struct.count_pages = max_addr >> PAGE_2M_SHIFT;
|
||||
memory_management_struct.pages_struct_len = ((max_addr >> PAGE_2M_SHIFT) * sizeof(struct Page) + sizeof(long) - 1) & (~(sizeof(long) - 1));
|
||||
// 将pages_struct全部清空,以备后续初始化
|
||||
memset(memory_management_struct.pages_struct, 0x00, memory_management_struct.pages_struct_len); // init pages memory
|
||||
|
||||
io_mfence();
|
||||
// 初始化内存区域
|
||||
memory_management_struct.zones_struct = (struct Zone *)(((ul)memory_management_struct.pages_struct + memory_management_struct.pages_struct_len + PAGE_4K_SIZE - 1) & PAGE_4K_MASK);
|
||||
io_mfence();
|
||||
// 由于暂时无法计算zone结构体的数量,因此先将其设为0
|
||||
memory_management_struct.count_zones = 0;
|
||||
io_mfence();
|
||||
// zones-struct 成员变量暂时按照5个来计算
|
||||
memory_management_struct.zones_struct_len = (10 * sizeof(struct Zone) + sizeof(ul) - 1) & (~(sizeof(ul) - 1));
|
||||
io_mfence();
|
||||
memset(memory_management_struct.zones_struct, 0x00, memory_management_struct.zones_struct_len);
|
||||
|
||||
// ==== 遍历e820数组,完成成员变量初始化工作 ===
|
||||
|
||||
for (int i = 0; i < memory_management_struct.len_e820; ++i)
|
||||
{
|
||||
io_mfence();
|
||||
if (memory_management_struct.e820[i].type != 1) // 不是操作系统可以使用的物理内存
|
||||
continue;
|
||||
ul addr_start = PAGE_2M_ALIGN(memory_management_struct.e820[i].BaseAddr);
|
||||
ul addr_end = (memory_management_struct.e820[i].BaseAddr + memory_management_struct.e820[i].Length) & PAGE_2M_MASK;
|
||||
|
||||
if (addr_end <= addr_start)
|
||||
continue;
|
||||
|
||||
// zone init
|
||||
struct Zone *z = memory_management_struct.zones_struct + memory_management_struct.count_zones;
|
||||
++memory_management_struct.count_zones;
|
||||
|
||||
z->zone_addr_start = addr_start;
|
||||
z->zone_addr_end = addr_end;
|
||||
z->zone_length = addr_end - addr_start;
|
||||
|
||||
z->count_pages_using = 0;
|
||||
z->count_pages_free = (addr_end - addr_start) >> PAGE_2M_SHIFT;
|
||||
z->total_pages_link = 0;
|
||||
|
||||
z->attr = 0;
|
||||
z->gmd_struct = &memory_management_struct;
|
||||
|
||||
z->count_pages = (addr_end - addr_start) >> PAGE_2M_SHIFT;
|
||||
z->pages_group = (struct Page *)(memory_management_struct.pages_struct + (addr_start >> PAGE_2M_SHIFT));
|
||||
|
||||
// 初始化页
|
||||
struct Page *p = z->pages_group;
|
||||
|
||||
for (int j = 0; j < z->count_pages; ++j, ++p)
|
||||
{
|
||||
p->zone = z;
|
||||
p->addr_phys = addr_start + PAGE_2M_SIZE * j;
|
||||
p->attr = 0;
|
||||
|
||||
p->ref_counts = 0;
|
||||
p->age = 0;
|
||||
|
||||
// 将bmp中对应的位 复位
|
||||
*(memory_management_struct.bmp + ((p->addr_phys >> PAGE_2M_SHIFT) >> 6)) ^= (1UL << ((p->addr_phys >> PAGE_2M_SHIFT) % 64));
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化0~2MB的物理页
|
||||
// 由于这个区间的内存由多个内存段组成,因此不会被以上代码初始化,需要我们手动配置page[0]。
|
||||
io_mfence();
|
||||
memory_management_struct.pages_struct->zone = memory_management_struct.zones_struct;
|
||||
memory_management_struct.pages_struct->addr_phys = 0UL;
|
||||
set_page_attr(memory_management_struct.pages_struct, PAGE_PGT_MAPPED | PAGE_KERNEL_INIT | PAGE_KERNEL);
|
||||
memory_management_struct.pages_struct->ref_counts = 1;
|
||||
memory_management_struct.pages_struct->age = 0;
|
||||
// 将第0页的标志位给置上
|
||||
//*(memory_management_struct.bmp) |= 1UL;
|
||||
|
||||
// 计算zone结构体的总长度(按照64位对齐)
|
||||
memory_management_struct.zones_struct_len = (memory_management_struct.count_zones * sizeof(struct Zone) + sizeof(ul) - 1) & (~(sizeof(ul) - 1));
|
||||
|
||||
ZONE_DMA_INDEX = 0;
|
||||
ZONE_NORMAL_INDEX = 0;
|
||||
ZONE_UNMAPPED_INDEX = 0;
|
||||
|
||||
// kdebug("ZONE_DMA_INDEX=%d\tZONE_NORMAL_INDEX=%d\tZONE_UNMAPPED_INDEX=%d", ZONE_DMA_INDEX, ZONE_NORMAL_INDEX, ZONE_UNMAPPED_INDEX);
|
||||
// 设置内存页管理结构的地址,预留了一段空间,防止内存越界。
|
||||
memory_management_struct.end_of_struct = (ul)((ul)memory_management_struct.zones_struct + memory_management_struct.zones_struct_len + sizeof(long) * 32) & (~(sizeof(long) - 1));
|
||||
|
||||
// 初始化内存管理单元结构所占的物理页的结构体
|
||||
ul mms_max_page = (virt_2_phys(memory_management_struct.end_of_struct) >> PAGE_2M_SHIFT); // 内存管理单元所占据的序号最大的物理页
|
||||
// kdebug("mms_max_page=%ld", mms_max_page);
|
||||
|
||||
struct Page *tmp_page = NULL;
|
||||
ul page_num;
|
||||
// 第0个page已经在上方配置
|
||||
for (ul j = 1; j <= mms_max_page; ++j)
|
||||
{
|
||||
barrier();
|
||||
tmp_page = memory_management_struct.pages_struct + j;
|
||||
page_init(tmp_page, PAGE_PGT_MAPPED | PAGE_KERNEL | PAGE_KERNEL_INIT);
|
||||
barrier();
|
||||
page_num = tmp_page->addr_phys >> PAGE_2M_SHIFT;
|
||||
*(memory_management_struct.bmp + (page_num >> 6)) |= (1UL << (page_num % 64));
|
||||
++tmp_page->zone->count_pages_using;
|
||||
--tmp_page->zone->count_pages_free;
|
||||
}
|
||||
|
||||
kinfo("Memory management unit initialize complete!");
|
||||
|
||||
flush_tlb();
|
||||
// todo: 在这里增加代码,暂时停止视频输出,否则可能会导致图像数据写入slab的区域,从而造成异常
|
||||
// 初始化slab内存池
|
||||
slab_init();
|
||||
page_table_init();
|
||||
|
||||
initial_mm.pgd = (pml4t_t *)get_CR3();
|
||||
|
||||
initial_mm.code_addr_start = memory_management_struct.kernel_code_start;
|
||||
initial_mm.code_addr_end = memory_management_struct.kernel_code_end;
|
||||
|
||||
initial_mm.data_addr_start = (ul)&_data;
|
||||
initial_mm.data_addr_end = memory_management_struct.kernel_data_end;
|
||||
|
||||
initial_mm.rodata_addr_start = (ul)&_rodata;
|
||||
initial_mm.rodata_addr_end = (ul)&_erodata;
|
||||
initial_mm.bss_start = (uint64_t)&_bss;
|
||||
initial_mm.bss_end = (uint64_t)&_ebss;
|
||||
|
||||
initial_mm.brk_start = memory_management_struct.start_brk;
|
||||
initial_mm.brk_end = current_pcb->addr_limit;
|
||||
|
||||
initial_mm.stack_start = _stack_start;
|
||||
initial_mm.vmas = NULL;
|
||||
|
||||
|
||||
|
||||
mmio_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化内存页
|
||||
*
|
||||
* @param page 内存页结构体
|
||||
* @param flags 标志位
|
||||
* 本函数只负责初始化内存页,允许对同一页面进行多次初始化
|
||||
* 而维护计数器及置位bmp标志位的功能,应当在分配页面的时候手动完成
|
||||
* @return unsigned long
|
||||
*/
|
||||
unsigned long page_init(struct Page *page, ul flags)
|
||||
{
|
||||
page->attr |= flags;
|
||||
// 若页面的引用计数为0或是共享页,增加引用计数
|
||||
if ((!page->ref_counts) || (page->attr & PAGE_SHARED))
|
||||
{
|
||||
++page->ref_counts;
|
||||
barrier();
|
||||
if (page->zone)
|
||||
++page->zone->total_pages_link;
|
||||
}
|
||||
page->anon_vma = NULL;
|
||||
spin_init(&(page->op_lock));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从已初始化的页结构中搜索符合申请条件的、连续num个struct page
|
||||
*
|
||||
* @param zone_select 选择内存区域, 可选项:dma, mapped in pgt(normal), unmapped in pgt
|
||||
* @param num 需要申请的连续内存页的数量 num<64
|
||||
* @param flags 将页面属性设置成flag
|
||||
* @return struct Page*
|
||||
*/
|
||||
struct Page *alloc_pages(unsigned int zone_select, int num, ul flags)
|
||||
{
|
||||
ul zone_start = 0, zone_end = 0;
|
||||
if (num >= 64 && num <= 0)
|
||||
{
|
||||
kerror("alloc_pages(): num is invalid.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ul attr = flags;
|
||||
switch (zone_select)
|
||||
{
|
||||
case ZONE_DMA:
|
||||
// DMA区域
|
||||
zone_start = 0;
|
||||
zone_end = ZONE_DMA_INDEX;
|
||||
attr |= PAGE_PGT_MAPPED;
|
||||
break;
|
||||
case ZONE_NORMAL:
|
||||
zone_start = ZONE_DMA_INDEX;
|
||||
zone_end = ZONE_NORMAL_INDEX;
|
||||
attr |= PAGE_PGT_MAPPED;
|
||||
break;
|
||||
case ZONE_UNMAPPED_IN_PGT:
|
||||
zone_start = ZONE_NORMAL_INDEX;
|
||||
zone_end = ZONE_UNMAPPED_INDEX;
|
||||
attr = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
kerror("In alloc_pages: param: zone_select incorrect.");
|
||||
// 返回空
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = zone_start; i <= zone_end; ++i)
|
||||
{
|
||||
if ((memory_management_struct.zones_struct + i)->count_pages_free < num)
|
||||
continue;
|
||||
|
||||
struct Zone *z = memory_management_struct.zones_struct + i;
|
||||
// 区域对应的起止页号
|
||||
ul page_start = (z->zone_addr_start >> PAGE_2M_SHIFT);
|
||||
ul page_end = (z->zone_addr_end >> PAGE_2M_SHIFT);
|
||||
|
||||
ul tmp = 64 - page_start % 64;
|
||||
for (ul j = page_start; j < page_end; j += ((j % 64) ? tmp : 64))
|
||||
{
|
||||
// 按照bmp中的每一个元素进行查找
|
||||
// 先将p定位到bmp的起始元素
|
||||
ul *p = memory_management_struct.bmp + (j >> 6);
|
||||
|
||||
ul shift = j % 64;
|
||||
ul tmp_num = ((1UL << num) - 1);
|
||||
for (ul k = shift; k < 64; ++k)
|
||||
{
|
||||
// 寻找连续num个空页
|
||||
if (!((k ? ((*p >> k) | (*(p + 1) << (64 - k))) : *p) & tmp_num))
|
||||
|
||||
{
|
||||
ul start_page_num = j + k - shift; // 计算得到要开始获取的内存页的页号
|
||||
for (ul l = 0; l < num; ++l)
|
||||
{
|
||||
struct Page *x = memory_management_struct.pages_struct + start_page_num + l;
|
||||
|
||||
// 分配页面,手动配置属性及计数器
|
||||
// 置位bmp
|
||||
*(memory_management_struct.bmp + ((x->addr_phys >> PAGE_2M_SHIFT) >> 6)) |= (1UL << (x->addr_phys >> PAGE_2M_SHIFT) % 64);
|
||||
++(z->count_pages_using);
|
||||
--(z->count_pages_free);
|
||||
page_init(x, attr);
|
||||
}
|
||||
// 成功分配了页面,返回第一个页面的指针
|
||||
// kwarn("start page num=%d\n", start_page_num);
|
||||
return (struct Page *)(memory_management_struct.pages_struct + start_page_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
kBUG("Cannot alloc page, ZONE=%d\tnums=%d, mm_total_2M_pages=%d", zone_select, num, mm_total_2M_pages);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清除页面的引用计数, 计数为0时清空除页表已映射以外的所有属性
|
||||
*
|
||||
* @param p 物理页结构体
|
||||
* @return unsigned long
|
||||
*/
|
||||
unsigned long page_clean(struct Page *p)
|
||||
{
|
||||
--p->ref_counts;
|
||||
--p->zone->total_pages_link;
|
||||
|
||||
// 若引用计数为空,则清空除PAGE_PGT_MAPPED以外的所有属性
|
||||
if (!p->ref_counts)
|
||||
{
|
||||
p->attr &= PAGE_PGT_MAPPED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the page's attr
|
||||
*
|
||||
* @param page 内存页结构体
|
||||
* @return ul 属性
|
||||
*/
|
||||
ul get_page_attr(struct Page *page)
|
||||
{
|
||||
if (page == NULL)
|
||||
{
|
||||
kBUG("get_page_attr(): page == NULL");
|
||||
return EPAGE_NULL;
|
||||
}
|
||||
else
|
||||
return page->attr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the page's attr
|
||||
*
|
||||
* @param page 内存页结构体
|
||||
* @param flags 属性
|
||||
* @return ul 错误码
|
||||
*/
|
||||
ul set_page_attr(struct Page *page, ul flags)
|
||||
{
|
||||
if (page == NULL)
|
||||
{
|
||||
kBUG("get_page_attr(): page == NULL");
|
||||
return EPAGE_NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
page->attr = flags;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief 释放连续number个内存页
|
||||
*
|
||||
* @param page 第一个要被释放的页面的结构体
|
||||
* @param number 要释放的内存页数量 number<64
|
||||
*/
|
||||
|
||||
void free_pages(struct Page *page, int number)
|
||||
{
|
||||
if (page == NULL)
|
||||
{
|
||||
kerror("free_pages() page is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (number >= 64 || number <= 0)
|
||||
{
|
||||
kerror("free_pages(): number %d is invalid.", number);
|
||||
return;
|
||||
}
|
||||
|
||||
ul page_num;
|
||||
for (int i = 0; i < number; ++i, ++page)
|
||||
{
|
||||
page_num = page->addr_phys >> PAGE_2M_SHIFT;
|
||||
// 复位bmp
|
||||
*(memory_management_struct.bmp + (page_num >> 6)) &= ~(1UL << (page_num % 64));
|
||||
// 更新计数器
|
||||
--page->zone->count_pages_using;
|
||||
++page->zone->count_pages_free;
|
||||
page->attr = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重新初始化页表的函数
|
||||
* 将所有物理页映射到线性地址空间
|
||||
*/
|
||||
void page_table_init()
|
||||
{
|
||||
kinfo("Re-Initializing page table...");
|
||||
ul *global_CR3 = get_CR3();
|
||||
|
||||
int js = 0;
|
||||
ul *tmp_addr;
|
||||
for (int i = 0; i < memory_management_struct.count_zones; ++i)
|
||||
{
|
||||
struct Zone *z = memory_management_struct.zones_struct + i;
|
||||
struct Page *p = z->pages_group;
|
||||
|
||||
if (i == ZONE_UNMAPPED_INDEX && ZONE_UNMAPPED_INDEX != 0)
|
||||
break;
|
||||
|
||||
for (int j = 0; j < z->count_pages; ++j)
|
||||
{
|
||||
mm_map_proc_page_table((uint64_t)get_CR3(), true, (ul)phys_2_virt(p->addr_phys), p->addr_phys, PAGE_2M_SIZE, PAGE_KERNEL_PAGE, false, true, false);
|
||||
|
||||
++p;
|
||||
++js;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
barrier();
|
||||
// ========= 在IDLE进程的顶层页表中添加对内核地址空间的映射 =====================
|
||||
|
||||
// 由于IDLE进程的顶层页表的高地址部分会被后续进程所复制,为了使所有进程能够共享相同的内核空间,
|
||||
// 因此需要先在IDLE进程的顶层页表内映射二级页表
|
||||
|
||||
uint64_t *idle_pml4t_vaddr = (uint64_t *)phys_2_virt((uint64_t)get_CR3() & (~0xfffUL));
|
||||
|
||||
for (int i = 256; i < 512; ++i)
|
||||
{
|
||||
uint64_t *tmp = idle_pml4t_vaddr + i;
|
||||
barrier();
|
||||
if (*tmp == 0)
|
||||
{
|
||||
void *pdpt = kmalloc(PAGE_4K_SIZE, 0);
|
||||
barrier();
|
||||
memset(pdpt, 0, PAGE_4K_SIZE);
|
||||
barrier();
|
||||
set_pml4t(tmp, mk_pml4t(virt_2_phys(pdpt), PAGE_KERNEL_PGT));
|
||||
}
|
||||
}
|
||||
barrier();
|
||||
flush_tlb();
|
||||
kinfo("Page table Initialized. Affects:%d", js);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从页表中获取pdt页表项的内容
|
||||
*
|
||||
* @param proc_page_table_addr 页表的地址
|
||||
* @param is_phys 页表地址是否为物理地址
|
||||
* @param virt_addr_start 要清除的虚拟地址的起始地址
|
||||
* @param length 要清除的区域的长度
|
||||
* @param clear 是否清除标志位
|
||||
*/
|
||||
uint64_t mm_get_PDE(ul proc_page_table_addr, bool is_phys, ul virt_addr, bool clear)
|
||||
{
|
||||
ul *tmp;
|
||||
if (is_phys)
|
||||
tmp = phys_2_virt((ul *)((ul)proc_page_table_addr & (~0xfffUL)) + ((virt_addr >> PAGE_GDT_SHIFT) & 0x1ff));
|
||||
else
|
||||
tmp = (ul *)((ul)proc_page_table_addr & (~0xfffUL)) + ((virt_addr >> PAGE_GDT_SHIFT) & 0x1ff);
|
||||
|
||||
// pml4页表项为0
|
||||
if (*tmp == 0)
|
||||
return 0;
|
||||
|
||||
tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((virt_addr >> PAGE_1G_SHIFT) & 0x1ff));
|
||||
|
||||
// pdpt页表项为0
|
||||
if (*tmp == 0)
|
||||
return 0;
|
||||
|
||||
// 读取pdt页表项
|
||||
tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr) >> PAGE_2M_SHIFT) & 0x1ff)));
|
||||
|
||||
if (clear) // 清除页表项的标志位
|
||||
return *tmp & (~0x1fff);
|
||||
else
|
||||
return *tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从mms中寻找Page结构体
|
||||
*
|
||||
* @param phys_addr
|
||||
* @return struct Page*
|
||||
*/
|
||||
static struct Page *mm_find_page(uint64_t phys_addr, uint32_t zone_select)
|
||||
{
|
||||
uint32_t zone_start, zone_end;
|
||||
switch (zone_select)
|
||||
{
|
||||
case ZONE_DMA:
|
||||
// DMA区域
|
||||
zone_start = 0;
|
||||
zone_end = ZONE_DMA_INDEX;
|
||||
break;
|
||||
case ZONE_NORMAL:
|
||||
zone_start = ZONE_DMA_INDEX;
|
||||
zone_end = ZONE_NORMAL_INDEX;
|
||||
break;
|
||||
case ZONE_UNMAPPED_IN_PGT:
|
||||
zone_start = ZONE_NORMAL_INDEX;
|
||||
zone_end = ZONE_UNMAPPED_INDEX;
|
||||
break;
|
||||
|
||||
default:
|
||||
kerror("In mm_find_page: param: zone_select incorrect.");
|
||||
// 返回空
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i = zone_start; i <= zone_end; ++i)
|
||||
{
|
||||
if ((memory_management_struct.zones_struct + i)->count_pages_using == 0)
|
||||
continue;
|
||||
|
||||
struct Zone *z = memory_management_struct.zones_struct + i;
|
||||
|
||||
// 区域对应的起止页号
|
||||
ul page_start = (z->zone_addr_start >> PAGE_2M_SHIFT);
|
||||
ul page_end = (z->zone_addr_end >> PAGE_2M_SHIFT);
|
||||
|
||||
ul tmp = 64 - page_start % 64;
|
||||
for (ul j = page_start; j < page_end; j += ((j % 64) ? tmp : 64))
|
||||
{
|
||||
// 按照bmp中的每一个元素进行查找
|
||||
// 先将p定位到bmp的起始元素
|
||||
ul *p = memory_management_struct.bmp + (j >> 6);
|
||||
|
||||
ul shift = j % 64;
|
||||
for (ul k = shift; k < 64; ++k)
|
||||
{
|
||||
if ((*p >> k) & 1) // 若当前页已分配
|
||||
{
|
||||
uint64_t page_num = j + k - shift;
|
||||
struct Page *x = memory_management_struct.pages_struct + page_num;
|
||||
|
||||
if (x->addr_phys == phys_addr) // 找到对应的页
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 调整堆区域的大小(暂时只能增加堆区域)
|
||||
*
|
||||
* @todo 缩小堆区域
|
||||
* @param old_brk_end_addr 原本的堆内存区域的结束地址
|
||||
* @param offset 新的地址相对于原地址的偏移量
|
||||
* @return uint64_t
|
||||
*/
|
||||
uint64_t mm_do_brk(uint64_t old_brk_end_addr, int64_t offset)
|
||||
{
|
||||
|
||||
uint64_t end_addr = PAGE_2M_ALIGN(old_brk_end_addr + offset);
|
||||
if (offset >= 0)
|
||||
{
|
||||
for (uint64_t i = old_brk_end_addr; i < end_addr; i += PAGE_2M_SIZE)
|
||||
{
|
||||
struct vm_area_struct *vma = NULL;
|
||||
mm_create_vma(current_pcb->mm, i, PAGE_2M_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma);
|
||||
mm_map(current_pcb->mm, i, PAGE_2M_SIZE, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys);
|
||||
// mm_map_vma(vma, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, 0, PAGE_2M_SIZE);
|
||||
}
|
||||
current_pcb->mm->brk_end = end_addr;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// 释放堆内存
|
||||
for (uint64_t i = end_addr; i < old_brk_end_addr; i += PAGE_2M_SIZE)
|
||||
{
|
||||
uint64_t phys = mm_get_PDE((uint64_t)phys_2_virt((uint64_t)current_pcb->mm->pgd), false, i, true);
|
||||
|
||||
// 找到对应的页
|
||||
struct Page *p = mm_find_page(phys, ZONE_NORMAL);
|
||||
if (p == NULL)
|
||||
{
|
||||
kerror("cannot find page addr=%#018lx", phys);
|
||||
return end_addr;
|
||||
}
|
||||
|
||||
free_pages(p, 1);
|
||||
}
|
||||
|
||||
mm_unmap_proc_table((uint64_t)phys_2_virt((uint64_t)current_pcb->mm->pgd), false, end_addr, PAGE_2M_ALIGN(ABS(offset)));
|
||||
// 在页表中取消映射
|
||||
}
|
||||
return end_addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建mmio对应的页结构体
|
||||
*
|
||||
* @param paddr 物理地址
|
||||
* @return struct Page* 创建成功的page
|
||||
*/
|
||||
struct Page *__create_mmio_page_struct(uint64_t paddr)
|
||||
{
|
||||
struct Page *p = (struct Page *)kzalloc(sizeof(struct Page), 0);
|
||||
if (p == NULL)
|
||||
return NULL;
|
||||
p->addr_phys = paddr;
|
||||
page_init(p, PAGE_DEVICE);
|
||||
return p;
|
||||
}
|
546
kernel/src/mm/mm.h
Normal file
546
kernel/src/mm/mm.h
Normal file
@ -0,0 +1,546 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/glib.h>
|
||||
#include <common/gfp.h>
|
||||
#include <mm/mm-types.h>
|
||||
#include <process/process.h>
|
||||
|
||||
// 每个页表的项数
|
||||
// 64位下,每个页表4k,每条页表项8B,故一个页表有512条
|
||||
#define PTRS_PER_PGT 512
|
||||
|
||||
// 内核层的起始地址
|
||||
#define PAGE_OFFSET 0xffff800000000000UL
|
||||
#define KERNEL_BASE_LINEAR_ADDR 0xffff800000000000UL
|
||||
#define USER_MAX_LINEAR_ADDR 0x00007fffffffffffUL
|
||||
// MMIO虚拟地址空间:1TB
|
||||
#define MMIO_BASE 0xffffa10000000000UL
|
||||
#define MMIO_TOP 0xffffa20000000000UL
|
||||
|
||||
#define PAGE_4K_SHIFT 12
|
||||
#define PAGE_2M_SHIFT 21
|
||||
#define PAGE_1G_SHIFT 30
|
||||
#define PAGE_GDT_SHIFT 39
|
||||
|
||||
// 不同大小的页的容量
|
||||
#define PAGE_4K_SIZE (1UL << PAGE_4K_SHIFT)
|
||||
#define PAGE_2M_SIZE (1UL << PAGE_2M_SHIFT)
|
||||
#define PAGE_1G_SIZE (1UL << PAGE_1G_SHIFT)
|
||||
|
||||
// 屏蔽低于x的数值
|
||||
#define PAGE_4K_MASK (~(PAGE_4K_SIZE - 1))
|
||||
#define PAGE_2M_MASK (~(PAGE_2M_SIZE - 1))
|
||||
|
||||
// 将addr按照x的上边界对齐
|
||||
#define PAGE_4K_ALIGN(addr) (((unsigned long)(addr) + PAGE_4K_SIZE - 1) & PAGE_4K_MASK)
|
||||
#define PAGE_2M_ALIGN(addr) (((unsigned long)(addr) + PAGE_2M_SIZE - 1) & PAGE_2M_MASK)
|
||||
|
||||
// 虚拟地址与物理地址转换
|
||||
#define virt_2_phys(addr) ((unsigned long)(addr)-PAGE_OFFSET)
|
||||
#define phys_2_virt(addr) ((unsigned long *)((unsigned long)(addr) + PAGE_OFFSET))
|
||||
// 获取对应的页结构体
|
||||
#define Virt_To_2M_Page(kaddr) (memory_management_struct.pages_struct + (virt_2_phys(kaddr) >> PAGE_2M_SHIFT))
|
||||
#define Phy_to_2M_Page(kaddr) (memory_management_struct.pages_struct + ((unsigned long)(kaddr) >> PAGE_2M_SHIFT))
|
||||
|
||||
// 在这个地址以上的虚拟空间,用来进行特殊的映射
|
||||
#define SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE 0xffffa00000000000UL
|
||||
#define FRAME_BUFFER_MAPPING_OFFSET 0x3000000UL
|
||||
#define IO_APIC_MAPPING_OFFSET 0xfec00000UL
|
||||
#define LOCAL_APIC_MAPPING_OFFSET 0xfee00000UL
|
||||
#define AHCI_MAPPING_OFFSET 0xff200000UL // AHCI 映射偏移量,之后使用了4M的地址
|
||||
#define XHCI_MAPPING_OFFSET 0x100000000 // XHCI控制器映射偏移量(后方请预留1GB的虚拟空间来映射不同的controller)
|
||||
|
||||
// ===== 内存区域属性 =====
|
||||
// DMA区域
|
||||
#define ZONE_DMA (1 << 0)
|
||||
// 已在页表中映射的区域
|
||||
#define ZONE_NORMAL (1 << 1)
|
||||
// 未在页表中映射的区域
|
||||
#define ZONE_UNMAPPED_IN_PGT (1 << 2)
|
||||
|
||||
// ===== 页面属性 =====
|
||||
// 页面在页表中已被映射 mapped=1 unmapped=0
|
||||
#define PAGE_PGT_MAPPED (1 << 0)
|
||||
|
||||
// 内核初始化所占用的页 init-code=1 normal-code/data=0
|
||||
#define PAGE_KERNEL_INIT (1 << 1)
|
||||
|
||||
// 1=设备MMIO映射的内存 0=物理内存
|
||||
#define PAGE_DEVICE (1 << 2)
|
||||
|
||||
// 内核层页 kernel=1 memory=0
|
||||
#define PAGE_KERNEL (1 << 3)
|
||||
|
||||
// 共享的页 shared=1 single-use=0
|
||||
#define PAGE_SHARED (1 << 4)
|
||||
|
||||
// =========== 页表项权限 ========
|
||||
|
||||
// bit 63 Execution Disable:
|
||||
#define PAGE_XD (1UL << 63)
|
||||
|
||||
// bit 12 Page Attribute Table
|
||||
#define PAGE_PAT (1UL << 12)
|
||||
// 对于PTE而言,第7位是PAT
|
||||
#define PAGE_4K_PAT (1UL << 7)
|
||||
|
||||
// bit 8 Global Page:1,global;0,part
|
||||
#define PAGE_GLOBAL (1UL << 8)
|
||||
|
||||
// bit 7 Page Size:1,big page;0,small page;
|
||||
#define PAGE_PS (1UL << 7)
|
||||
|
||||
// bit 6 Dirty:1,dirty;0,clean;
|
||||
#define PAGE_DIRTY (1UL << 6)
|
||||
|
||||
// bit 5 Accessed:1,visited;0,unvisited;
|
||||
#define PAGE_ACCESSED (1UL << 5)
|
||||
|
||||
// bit 4 Page Level Cache Disable
|
||||
#define PAGE_PCD (1UL << 4)
|
||||
|
||||
// bit 3 Page Level Write Through
|
||||
#define PAGE_PWT (1UL << 3)
|
||||
|
||||
// bit 2 User Supervisor:1,user and supervisor;0,supervisor;
|
||||
#define PAGE_U_S (1UL << 2)
|
||||
|
||||
// bit 1 Read Write:1,read and write;0,read;
|
||||
#define PAGE_R_W (1UL << 1)
|
||||
|
||||
// bit 0 Present:1,present;0,no present;
|
||||
#define PAGE_PRESENT (1UL << 0)
|
||||
|
||||
// 1,0
|
||||
#define PAGE_KERNEL_PGT (PAGE_R_W | PAGE_PRESENT)
|
||||
|
||||
// 1,0
|
||||
#define PAGE_KERNEL_DIR (PAGE_R_W | PAGE_PRESENT)
|
||||
|
||||
// 1,0 (4级页表在3级页表中的页表项的属性)
|
||||
#define PAGE_KERNEL_PDE (PAGE_R_W | PAGE_PRESENT)
|
||||
|
||||
// 7,1,0
|
||||
#define PAGE_KERNEL_PAGE (PAGE_PS | PAGE_R_W | PAGE_PRESENT)
|
||||
|
||||
#define PAGE_KERNEL_4K_PAGE (PAGE_R_W | PAGE_PRESENT)
|
||||
|
||||
#define PAGE_USER_PGT (PAGE_U_S | PAGE_R_W | PAGE_PRESENT)
|
||||
|
||||
// 2,1,0
|
||||
#define PAGE_USER_DIR (PAGE_U_S | PAGE_R_W | PAGE_PRESENT)
|
||||
|
||||
// 1,0 (4级页表在3级页表中的页表项的属性)
|
||||
#define PAGE_USER_PDE (PAGE_U_S | PAGE_R_W | PAGE_PRESENT)
|
||||
// 7,2,1,0
|
||||
#define PAGE_USER_PAGE (PAGE_PS | PAGE_U_S | PAGE_R_W | PAGE_PRESENT)
|
||||
|
||||
#define PAGE_USER_4K_PAGE (PAGE_U_S | PAGE_R_W | PAGE_PRESENT)
|
||||
|
||||
// ===== 错误码定义 ====
|
||||
// 物理页结构体为空
|
||||
#define EPAGE_NULL 1
|
||||
|
||||
/**
|
||||
* @brief 刷新TLB的宏定义
|
||||
* 由于任何写入cr3的操作都会刷新TLB,因此这个宏定义可以刷新TLB
|
||||
*/
|
||||
#define flush_tlb() \
|
||||
do \
|
||||
{ \
|
||||
ul tmp; \
|
||||
io_mfence(); \
|
||||
__asm__ __volatile__( \
|
||||
"movq %%cr3, %0\n\t" \
|
||||
"movq %0, %%cr3\n\t" \
|
||||
: "=r"(tmp)::"memory"); \
|
||||
\
|
||||
} while (0);
|
||||
|
||||
/**
|
||||
* @brief 系统内存信息结构体(单位:字节)
|
||||
*
|
||||
*/
|
||||
struct mm_stat_t
|
||||
{
|
||||
uint64_t total; // 计算机的总内存数量大小
|
||||
uint64_t used; // 已使用的内存大小
|
||||
uint64_t free; // 空闲物理页所占的内存大小
|
||||
uint64_t shared; // 共享的内存大小
|
||||
uint64_t cache_used; // 位于slab缓冲区中的已使用的内存大小
|
||||
uint64_t cache_free; // 位于slab缓冲区中的空闲的内存大小
|
||||
uint64_t available; // 系统总空闲内存大小(包括kmalloc缓冲区)
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 虚拟内存区域的操作方法的结构体
|
||||
*
|
||||
*/
|
||||
struct vm_operations_t
|
||||
{
|
||||
/**
|
||||
* @brief vm area 被打开时的回调函数
|
||||
*
|
||||
*/
|
||||
void (*open)(struct vm_area_struct *area);
|
||||
/**
|
||||
* @brief vm area将要被移除的时候,将会调用该回调函数
|
||||
*
|
||||
*/
|
||||
void (*close)(struct vm_area_struct *area);
|
||||
};
|
||||
|
||||
extern struct memory_desc memory_management_struct;
|
||||
|
||||
// 导出内核程序的几个段的起止地址
|
||||
extern char _text;
|
||||
extern char _etext;
|
||||
extern char _data;
|
||||
extern char _edata;
|
||||
extern char _rodata;
|
||||
extern char _erodata;
|
||||
extern char _bss;
|
||||
extern char _ebss;
|
||||
extern char _end;
|
||||
|
||||
// 每个区域的索引
|
||||
|
||||
int ZONE_DMA_INDEX = 0;
|
||||
int ZONE_NORMAL_INDEX = 0;
|
||||
int ZONE_UNMAPPED_INDEX = 0;
|
||||
|
||||
// 初始化内存管理单元
|
||||
void mm_init();
|
||||
|
||||
/**
|
||||
* @brief 初始化内存页
|
||||
*
|
||||
* @param page 内存页结构体
|
||||
* @param flags 标志位
|
||||
* 本函数只负责初始化内存页,允许对同一页面进行多次初始化
|
||||
* 而维护计数器及置位bmp标志位的功能,应当在分配页面的时候手动完成
|
||||
* @return unsigned long
|
||||
*/
|
||||
unsigned long page_init(struct Page *page, ul flags);
|
||||
|
||||
/**
|
||||
* @brief 读取CR3寄存器的值(存储了页目录的基地址)
|
||||
*
|
||||
* @return unsigned* cr3的值的指针
|
||||
*/
|
||||
unsigned long *get_CR3()
|
||||
{
|
||||
ul *tmp;
|
||||
__asm__ __volatile__(
|
||||
"movq %%cr3, %0\n\t"
|
||||
: "=r"(tmp)::"memory");
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从已初始化的页结构中搜索符合申请条件的、连续num个struct page
|
||||
*
|
||||
* @param zone_select 选择内存区域, 可选项:dma, mapped in pgt(normal), unmapped in pgt
|
||||
* @param num 需要申请的内存页的数量 num<64
|
||||
* @param flags 将页面属性设置成flag
|
||||
* @return struct Page*
|
||||
*/
|
||||
struct Page *alloc_pages(unsigned int zone_select, int num, ul flags);
|
||||
|
||||
/**
|
||||
* @brief 清除页面的引用计数, 计数为0时清空除页表已映射以外的所有属性
|
||||
*
|
||||
* @param p 物理页结构体
|
||||
* @return unsigned long
|
||||
*/
|
||||
unsigned long page_clean(struct Page *page);
|
||||
|
||||
/**
|
||||
* @brief 释放连续number个内存页
|
||||
*
|
||||
* @param page 第一个要被释放的页面的结构体
|
||||
* @param number 要释放的内存页数量 number<64
|
||||
*/
|
||||
void free_pages(struct Page *page, int number);
|
||||
|
||||
/**
|
||||
* @brief Get the page's attr
|
||||
*
|
||||
* @param page 内存页结构体
|
||||
* @return ul 属性
|
||||
*/
|
||||
ul get_page_attr(struct Page *page);
|
||||
|
||||
/**
|
||||
* @brief Set the page's attr
|
||||
*
|
||||
* @param page 内存页结构体
|
||||
* @param flags 属性
|
||||
* @return ul 错误码
|
||||
*/
|
||||
ul set_page_attr(struct Page *page, ul flags);
|
||||
|
||||
#define mk_pml4t(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr))
|
||||
/**
|
||||
* @brief 设置pml4页表的页表项
|
||||
* @param pml4tptr pml4页表项的地址
|
||||
* @param pml4val pml4页表项的值
|
||||
*/
|
||||
#define set_pml4t(pml4tptr, pml4tval) (*(pml4tptr) = (pml4tval))
|
||||
|
||||
#define mk_pdpt(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr))
|
||||
#define set_pdpt(pdptptr, pdptval) (*(pdptptr) = (pdptval))
|
||||
|
||||
#define mk_pdt(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr))
|
||||
#define set_pdt(pdtptr, pdtval) (*(pdtptr) = (pdtval))
|
||||
|
||||
#define mk_pt(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr))
|
||||
#define set_pt(ptptr, ptval) (*(ptptr) = (ptval))
|
||||
|
||||
/*
|
||||
* vm_area_struct中的vm_flags的可选值
|
||||
* 对应的结构体请见mm-types.h
|
||||
*/
|
||||
#define VM_NONE 0
|
||||
#define VM_READ (1 << 0)
|
||||
#define VM_WRITE (1 << 1)
|
||||
#define VM_EXEC (1 << 2)
|
||||
#define VM_SHARED (1 << 3)
|
||||
#define VM_IO (1 << 4) // MMIO的内存区域
|
||||
#define VM_SOFTDIRTY (1 << 5)
|
||||
#define VM_MAYSHARE (1 << 6) // 该vma可被共享
|
||||
#define VM_USER (1 << 7) // 该vma可被用户态访问
|
||||
#define VM_DONTCOPY (1 << 8) // 当fork的时候不拷贝该虚拟内存区域
|
||||
|
||||
/* VMA basic access permission flags */
|
||||
#define VM_ACCESS_FLAGS (VM_READ | VM_WRITE | VM_EXEC)
|
||||
|
||||
/**
|
||||
* @brief 初始化虚拟内存区域结构体
|
||||
*
|
||||
* @param vma
|
||||
* @param mm
|
||||
*/
|
||||
static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm)
|
||||
{
|
||||
memset(vma, 0, sizeof(struct vm_area_struct));
|
||||
vma->vm_mm = mm;
|
||||
vma->vm_prev = vma->vm_next = NULL;
|
||||
vma->vm_ops = NULL;
|
||||
list_init(&vma->anon_vma_list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 判断给定的vma是否为当前进程所属的vma
|
||||
*
|
||||
* @param vma 给定的vma结构体
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
static inline bool vma_is_foreign(struct vm_area_struct *vma)
|
||||
{
|
||||
if (current_pcb->mm == NULL)
|
||||
return true;
|
||||
if (current_pcb->mm != vma->vm_mm)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool vma_is_accessible(struct vm_area_struct *vma)
|
||||
{
|
||||
return vma->vm_flags & VM_ACCESS_FLAGS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取一块新的vma结构体,并将其与指定的mm进行绑定
|
||||
*
|
||||
* @param mm 与VMA绑定的内存空间分布结构体
|
||||
* @return struct vm_area_struct* 新的VMA
|
||||
*/
|
||||
struct vm_area_struct *vm_area_alloc(struct mm_struct *mm);
|
||||
|
||||
/**
|
||||
* @brief 释放vma结构体
|
||||
*
|
||||
* @param vma 待释放的vma结构体
|
||||
*/
|
||||
void vm_area_free(struct vm_area_struct *vma);
|
||||
|
||||
/**
|
||||
* @brief 从链表中删除指定的vma结构体
|
||||
*
|
||||
* @param vma
|
||||
*/
|
||||
void vm_area_del(struct vm_area_struct *vma);
|
||||
|
||||
/**
|
||||
* @brief 查找第一个符合“addr < vm_end”条件的vma
|
||||
*
|
||||
* @param mm 内存空间分布结构体
|
||||
* @param addr 虚拟地址
|
||||
* @return struct vm_area_struct* 符合条件的vma
|
||||
*/
|
||||
struct vm_area_struct *vma_find(struct mm_struct *mm, uint64_t addr);
|
||||
|
||||
/**
|
||||
* @brief 插入vma
|
||||
*
|
||||
* @param mm
|
||||
* @param vma
|
||||
* @return int
|
||||
*/
|
||||
int vma_insert(struct mm_struct *mm, struct vm_area_struct *vma);
|
||||
|
||||
/**
|
||||
* @brief 重新初始化页表的函数
|
||||
* 将所有物理页映射到线性地址空间
|
||||
*/
|
||||
void page_table_init();
|
||||
|
||||
/**
|
||||
* @brief 将物理地址映射到页表的函数
|
||||
*
|
||||
* @param virt_addr_start 要映射到的虚拟地址的起始位置
|
||||
* @param phys_addr_start 物理地址的起始位置
|
||||
* @param length 要映射的区域的长度(字节)
|
||||
* @param flags 标志位
|
||||
* @param use4k 是否使用4k页
|
||||
*/
|
||||
int mm_map_phys_addr(ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool use4k);
|
||||
|
||||
/**
|
||||
* @brief 将将物理地址填写到进程的页表的函数
|
||||
*
|
||||
* @param proc_page_table_addr 页表的基地址
|
||||
* @param is_phys 页表的基地址是否为物理地址
|
||||
* @param virt_addr_start 要映射到的虚拟地址的起始位置
|
||||
* @param phys_addr_start 物理地址的起始位置
|
||||
* @param length 要映射的区域的长度(字节)
|
||||
* @param user 用户态是否可访问
|
||||
* @param flush 是否刷新tlb
|
||||
* @param use4k 是否使用4k页
|
||||
*/
|
||||
int mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool user, bool flush, bool use4k);
|
||||
|
||||
int mm_map_phys_addr_user(ul virt_addr_start, ul phys_addr_start, ul length, ul flags);
|
||||
|
||||
/**
|
||||
* @brief 从页表中清除虚拟地址的映射
|
||||
*
|
||||
* @param proc_page_table_addr 页表的地址
|
||||
* @param is_phys 页表地址是否为物理地址
|
||||
* @param virt_addr_start 要清除的虚拟地址的起始地址
|
||||
* @param length 要清除的区域的长度
|
||||
*/
|
||||
void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul length);
|
||||
|
||||
/**
|
||||
* @brief 取消当前进程的页表中的虚拟地址映射
|
||||
*
|
||||
* @param virt_addr 虚拟地址
|
||||
* @param length 地址长度
|
||||
*/
|
||||
#define mm_unmap_addr(virt_addr, length) ({ \
|
||||
mm_unmap_proc_table((uint64_t)get_CR3(), true, virt_addr, length); \
|
||||
})
|
||||
|
||||
/**
|
||||
* @brief 创建VMA
|
||||
*
|
||||
* @param mm 要绑定的内存空间分布结构体
|
||||
* @param vaddr 起始虚拟地址
|
||||
* @param length 长度(字节)
|
||||
* @param vm_flags vma的标志
|
||||
* @param vm_ops vma的操作接口
|
||||
* @param res_vma 返回的vma指针
|
||||
* @return int 错误码
|
||||
*/
|
||||
int mm_create_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, vm_flags_t vm_flags, struct vm_operations_t *vm_ops, struct vm_area_struct **res_vma);
|
||||
|
||||
/**
|
||||
* @brief 将指定的物理地址映射到指定的vma处
|
||||
*
|
||||
* @param vma 要进行映射的VMA结构体
|
||||
* @param paddr 起始物理地址
|
||||
* @param offset 要映射的起始位置在vma中的偏移量
|
||||
* @param length 要映射的长度
|
||||
* @return int 错误码
|
||||
*/
|
||||
int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr, uint64_t offset, uint64_t length);
|
||||
|
||||
/**
|
||||
* @brief 在页表中映射物理地址到指定的虚拟地址(需要页表中已存在对应的vma)
|
||||
*
|
||||
* @param mm 内存管理结构体
|
||||
* @param vaddr 虚拟地址
|
||||
* @param length 长度(字节)
|
||||
* @param paddr 物理地址
|
||||
* @return int 返回码
|
||||
*/
|
||||
int mm_map(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t paddr);
|
||||
|
||||
/**
|
||||
* @brief 在页表中取消指定的vma的映射
|
||||
*
|
||||
* @param mm 指定的mm
|
||||
* @param vma 待取消映射的vma
|
||||
* @param paddr 返回的被取消映射的起始物理地址
|
||||
* @return int 返回码
|
||||
*/
|
||||
int mm_unmap_vma(struct mm_struct *mm, struct vm_area_struct *vma, uint64_t *paddr);
|
||||
|
||||
/**
|
||||
* @brief 解除一段虚拟地址的映射(这些地址必须在vma中存在)
|
||||
*
|
||||
* @param mm 内存空间结构体
|
||||
* @param vaddr 起始地址
|
||||
* @param length 结束地址
|
||||
* @param destroy 是否释放vma结构体
|
||||
* @return int 错误码
|
||||
*/
|
||||
int mm_unmap(struct mm_struct *mm, uint64_t vaddr, uint64_t length, bool destroy);
|
||||
|
||||
/**
|
||||
* @brief 检测是否为有效的2M页(物理内存页)
|
||||
*
|
||||
* @param paddr 物理地址
|
||||
* @return int8_t 是 -> 1
|
||||
* 否 -> 0
|
||||
*/
|
||||
int8_t mm_is_2M_page(uint64_t paddr);
|
||||
|
||||
/**
|
||||
* @brief 检查页表是否存在不为0的页表项
|
||||
*
|
||||
* @param ptr 页表基指针
|
||||
* @return int8_t 存在 -> 1
|
||||
* 不存在 -> 0
|
||||
*/
|
||||
int8_t mm_check_page_table(uint64_t *ptr);
|
||||
|
||||
/**
|
||||
* @brief 调整堆区域的大小(暂时只能增加堆区域)
|
||||
*
|
||||
* @todo 缩小堆区域
|
||||
* @param old_brk_end_addr 原本的堆内存区域的结束地址
|
||||
* @param offset 新的地址相对于原地址的偏移量
|
||||
* @return uint64_t
|
||||
*/
|
||||
uint64_t mm_do_brk(uint64_t old_brk_end_addr, int64_t offset);
|
||||
|
||||
/**
|
||||
* @brief 获取系统当前的内存信息(未上锁,不一定精准)
|
||||
*
|
||||
* @return struct mm_stat_t 内存信息结构体
|
||||
*/
|
||||
struct mm_stat_t mm_stat();
|
||||
|
||||
/**
|
||||
* @brief 检测指定地址是否已经被映射
|
||||
*
|
||||
* @param page_table_phys_addr 页表的物理地址
|
||||
* @param virt_addr 要检测的地址
|
||||
* @return true 已经被映射
|
||||
* @return false
|
||||
*/
|
||||
bool mm_check_mapped(ul page_table_phys_addr, uint64_t virt_addr);
|
582
kernel/src/mm/mmap.c
Normal file
582
kernel/src/mm/mmap.c
Normal file
@ -0,0 +1,582 @@
|
||||
#include "mm.h"
|
||||
#include "slab.h"
|
||||
#include "internal.h"
|
||||
#include <common/compiler.h>
|
||||
#include <debug/bug.h>
|
||||
|
||||
extern uint64_t mm_total_2M_pages;
|
||||
|
||||
/**
|
||||
* @brief 虚拟地址长度所需要的entry数量
|
||||
*
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int64_t num_PML4E;
|
||||
int64_t num_PDPTE;
|
||||
int64_t num_PDE;
|
||||
int64_t num_PTE;
|
||||
} mm_pgt_entry_num_t;
|
||||
|
||||
/**
|
||||
* @brief 计算虚拟地址长度对应的页表entry数量
|
||||
*
|
||||
* @param length 长度
|
||||
* @param ent 返回的entry数量结构体
|
||||
*/
|
||||
static void mm_calculate_entry_num(uint64_t length, mm_pgt_entry_num_t *ent)
|
||||
{
|
||||
if (ent == NULL)
|
||||
return;
|
||||
ent->num_PML4E = (length + (1UL << PAGE_GDT_SHIFT) - 1) >> PAGE_GDT_SHIFT;
|
||||
ent->num_PDPTE = (length + PAGE_1G_SIZE - 1) >> PAGE_1G_SHIFT;
|
||||
ent->num_PDE = (length + PAGE_2M_SIZE - 1) >> PAGE_2M_SHIFT;
|
||||
ent->num_PTE = (length + PAGE_4K_SIZE - 1) >> PAGE_4K_SHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将物理地址映射到页表的函数
|
||||
*
|
||||
* @param virt_addr_start 要映射到的虚拟地址的起始位置
|
||||
* @param phys_addr_start 物理地址的起始位置
|
||||
* @param length 要映射的区域的长度(字节)
|
||||
* @param flags 标志位
|
||||
* @param use4k 是否使用4k页
|
||||
*/
|
||||
int mm_map_phys_addr(ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool use4k)
|
||||
{
|
||||
uint64_t global_CR3 = (uint64_t)get_CR3();
|
||||
|
||||
return mm_map_proc_page_table(global_CR3, true, virt_addr_start, phys_addr_start, length, flags, false, true, use4k);
|
||||
}
|
||||
|
||||
int mm_map_phys_addr_user(ul virt_addr_start, ul phys_addr_start, ul length, ul flags)
|
||||
{
|
||||
uint64_t global_CR3 = (uint64_t)get_CR3();
|
||||
return mm_map_proc_page_table(global_CR3, true, virt_addr_start, phys_addr_start, length, flags, true, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将将物理地址填写到进程的页表的函数
|
||||
*
|
||||
* @param proc_page_table_addr 页表的基地址
|
||||
* @param is_phys 页表的基地址是否为物理地址
|
||||
* @param virt_addr_start 要映射到的虚拟地址的起始位置
|
||||
* @param phys_addr_start 物理地址的起始位置
|
||||
* @param length 要映射的区域的长度(字节)
|
||||
* @param user 用户态是否可访问
|
||||
* @param flush 是否刷新tlb
|
||||
* @param use4k 是否使用4k页
|
||||
*/
|
||||
int mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool user, bool flush, bool use4k)
|
||||
{
|
||||
|
||||
// 计算线性地址对应的pml4页表项的地址
|
||||
mm_pgt_entry_num_t pgt_num;
|
||||
mm_calculate_entry_num(length, &pgt_num);
|
||||
|
||||
// 已映射的内存大小
|
||||
uint64_t length_mapped = 0;
|
||||
|
||||
// 对user标志位进行校正
|
||||
if ((flags & PAGE_U_S) != 0)
|
||||
user = true;
|
||||
else
|
||||
user = false;
|
||||
|
||||
uint64_t pml4e_id = ((virt_addr_start >> PAGE_GDT_SHIFT) & 0x1ff);
|
||||
uint64_t *pml4_ptr;
|
||||
if (is_phys)
|
||||
pml4_ptr = phys_2_virt((ul *)((ul)proc_page_table_addr & (~0xfffUL)));
|
||||
else
|
||||
pml4_ptr = (ul *)((ul)proc_page_table_addr & (~0xfffUL));
|
||||
|
||||
// 循环填写顶层页表
|
||||
for (; (pgt_num.num_PML4E > 0) && pml4e_id < 512; ++pml4e_id)
|
||||
{
|
||||
// 剩余需要处理的pml4E -1
|
||||
--(pgt_num.num_PML4E);
|
||||
|
||||
ul *pml4e_ptr = pml4_ptr + pml4e_id;
|
||||
|
||||
// 创建新的二级页表
|
||||
if (*pml4e_ptr == 0)
|
||||
{
|
||||
ul *virt_addr = kmalloc(PAGE_4K_SIZE, 0);
|
||||
memset(virt_addr, 0, PAGE_4K_SIZE);
|
||||
set_pml4t(pml4e_ptr, mk_pml4t(virt_2_phys(virt_addr), (user ? PAGE_USER_PGT : PAGE_KERNEL_PGT)));
|
||||
}
|
||||
|
||||
uint64_t pdpte_id = (((virt_addr_start + length_mapped) >> PAGE_1G_SHIFT) & 0x1ff);
|
||||
uint64_t *pdpt_ptr = (uint64_t *)phys_2_virt(*pml4e_ptr & (~0xfffUL));
|
||||
|
||||
// 循环填写二级页表
|
||||
for (; (pgt_num.num_PDPTE > 0) && pdpte_id < 512; ++pdpte_id)
|
||||
{
|
||||
--pgt_num.num_PDPTE;
|
||||
uint64_t *pdpte_ptr = (pdpt_ptr + pdpte_id);
|
||||
|
||||
// 创建新的三级页表
|
||||
if (*pdpte_ptr == 0)
|
||||
{
|
||||
ul *virt_addr = kmalloc(PAGE_4K_SIZE, 0);
|
||||
memset(virt_addr, 0, PAGE_4K_SIZE);
|
||||
set_pdpt(pdpte_ptr, mk_pdpt(virt_2_phys(virt_addr), (user ? PAGE_USER_DIR : PAGE_KERNEL_DIR)));
|
||||
}
|
||||
|
||||
uint64_t pde_id = (((virt_addr_start + length_mapped) >> PAGE_2M_SHIFT) & 0x1ff);
|
||||
uint64_t *pd_ptr = (uint64_t *)phys_2_virt(*pdpte_ptr & (~0xfffUL));
|
||||
|
||||
// 循环填写三级页表,初始化2M物理页
|
||||
for (; (pgt_num.num_PDE > 0) && pde_id < 512; ++pde_id)
|
||||
{
|
||||
--pgt_num.num_PDE;
|
||||
// 计算当前2M物理页对应的pdt的页表项的物理地址
|
||||
ul *pde_ptr = pd_ptr + pde_id;
|
||||
// ====== 使用4k页 =======
|
||||
if (unlikely(use4k))
|
||||
{
|
||||
// kdebug("use 4k");
|
||||
if (*pde_ptr == 0)
|
||||
{
|
||||
// 创建四级页表
|
||||
uint64_t *vaddr = kmalloc(PAGE_4K_SIZE, 0);
|
||||
memset(vaddr, 0, PAGE_4K_SIZE);
|
||||
set_pdt(pde_ptr, mk_pdt(virt_2_phys(vaddr), (user ? PAGE_USER_PDE : PAGE_KERNEL_PDE)));
|
||||
}
|
||||
else if (unlikely(*pde_ptr & (1 << 7)))
|
||||
{
|
||||
// 当前页表项已经被映射了2MB物理页
|
||||
goto failed;
|
||||
}
|
||||
|
||||
uint64_t pte_id = (((virt_addr_start + length_mapped) >> PAGE_4K_SHIFT) & 0x1ff);
|
||||
uint64_t *pt_ptr = (uint64_t *)phys_2_virt(*pde_ptr & (~0xfffUL));
|
||||
|
||||
// 循环填写4级页表,初始化4K页
|
||||
for (; (pgt_num.num_PTE > 0) && pte_id < 512; ++pte_id)
|
||||
{
|
||||
--pgt_num.num_PTE;
|
||||
uint64_t *pte_ptr = pt_ptr + pte_id;
|
||||
|
||||
if (unlikely(*pte_ptr != 0))
|
||||
kwarn("pte already exists.");
|
||||
else
|
||||
set_pt(pte_ptr, mk_pt((ul)phys_addr_start + length_mapped, flags | (user ? PAGE_USER_4K_PAGE : PAGE_KERNEL_4K_PAGE)));
|
||||
length_mapped += PAGE_4K_SIZE;
|
||||
}
|
||||
}
|
||||
// ======= 使用2M页 ========
|
||||
else
|
||||
{
|
||||
if (unlikely((*pde_ptr != 0) && user == true))
|
||||
{
|
||||
// 如果是用户态可访问的页,则释放当前新获取的物理页
|
||||
if (likely((((ul)phys_addr_start + length_mapped) >> PAGE_2M_SHIFT) < mm_total_2M_pages)) // 校验是否为内存中的物理页
|
||||
free_pages(Phy_to_2M_Page((ul)phys_addr_start + length_mapped), 1);
|
||||
length_mapped += PAGE_2M_SIZE;
|
||||
continue;
|
||||
}
|
||||
// 页面写穿,禁止缓存
|
||||
set_pdt(pde_ptr, mk_pdt((ul)phys_addr_start + length_mapped, flags | (user ? PAGE_USER_PAGE : PAGE_KERNEL_PAGE)));
|
||||
length_mapped += PAGE_2M_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (likely(flush))
|
||||
flush_tlb();
|
||||
return 0;
|
||||
failed:;
|
||||
kerror("Map memory failed. use4k=%d, vaddr=%#018lx, paddr=%#018lx", use4k, virt_addr_start, phys_addr_start);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从页表中清除虚拟地址的映射
|
||||
*
|
||||
* @param proc_page_table_addr 页表的地址
|
||||
* @param is_phys 页表地址是否为物理地址
|
||||
* @param virt_addr_start 要清除的虚拟地址的起始地址
|
||||
* @param length 要清除的区域的长度
|
||||
*/
|
||||
void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul length)
|
||||
{
|
||||
|
||||
// 计算线性地址对应的pml4页表项的地址
|
||||
mm_pgt_entry_num_t pgt_num;
|
||||
mm_calculate_entry_num(length, &pgt_num);
|
||||
// 已取消映射的内存大小
|
||||
uint64_t length_unmapped = 0;
|
||||
|
||||
uint64_t pml4e_id = ((virt_addr_start >> PAGE_GDT_SHIFT) & 0x1ff);
|
||||
uint64_t *pml4_ptr;
|
||||
if (is_phys)
|
||||
pml4_ptr = phys_2_virt((ul *)((ul)proc_page_table_addr & (~0xfffUL)));
|
||||
else
|
||||
pml4_ptr = (ul *)((ul)proc_page_table_addr & (~0xfffUL));
|
||||
|
||||
// 循环填写顶层页表
|
||||
for (; (pgt_num.num_PML4E > 0) && pml4e_id < 512; ++pml4e_id)
|
||||
{
|
||||
// 剩余需要处理的pml4E -1
|
||||
--(pgt_num.num_PML4E);
|
||||
|
||||
ul *pml4e_ptr = NULL;
|
||||
pml4e_ptr = pml4_ptr + pml4e_id;
|
||||
|
||||
// 二级页表不存在
|
||||
if (*pml4e_ptr == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t pdpte_id = (((virt_addr_start + length_unmapped) >> PAGE_1G_SHIFT) & 0x1ff);
|
||||
uint64_t *pdpt_ptr = (uint64_t *)phys_2_virt(*pml4e_ptr & (~0xfffUL));
|
||||
// kdebug("pdpt_ptr=%#018lx", pdpt_ptr);
|
||||
|
||||
// 循环处理二级页表
|
||||
for (; (pgt_num.num_PDPTE > 0) && pdpte_id < 512; ++pdpte_id)
|
||||
{
|
||||
--pgt_num.num_PDPTE;
|
||||
uint64_t *pdpte_ptr = (pdpt_ptr + pdpte_id);
|
||||
// kdebug("pgt_num.num_PDPTE=%ld pdpte_ptr=%#018lx", pgt_num.num_PDPTE, pdpte_ptr);
|
||||
|
||||
// 三级页表为空
|
||||
if (*pdpte_ptr == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t pde_id = (((virt_addr_start + length_unmapped) >> PAGE_2M_SHIFT) & 0x1ff);
|
||||
uint64_t *pd_ptr = (uint64_t *)phys_2_virt(*pdpte_ptr & (~0xfffUL));
|
||||
// kdebug("pd_ptr=%#018lx, *pd_ptr=%#018lx", pd_ptr, *pd_ptr);
|
||||
|
||||
// 循环处理三级页表
|
||||
for (; (pgt_num.num_PDE > 0) && pde_id < 512; ++pde_id)
|
||||
{
|
||||
--pgt_num.num_PDE;
|
||||
// 计算当前2M物理页对应的pdt的页表项的物理地址
|
||||
ul *pde_ptr = pd_ptr + pde_id;
|
||||
|
||||
// 存在4级页表
|
||||
if (((*pde_ptr) & (1 << 7)) == 0)
|
||||
{
|
||||
// 存在4K页
|
||||
uint64_t pte_id = (((virt_addr_start + length_unmapped) >> PAGE_4K_SHIFT) & 0x1ff);
|
||||
uint64_t *pt_ptr = (uint64_t *)phys_2_virt(*pde_ptr & (~0xfffUL));
|
||||
// 循环处理4K页表
|
||||
for (; pgt_num.num_PTE > 0 && pte_id < 512; ++pte_id)
|
||||
{
|
||||
uint64_t *pte_ptr = pt_ptr + pte_id;
|
||||
--pgt_num.num_PTE;
|
||||
*pte_ptr = 0;
|
||||
length_unmapped += PAGE_4K_SIZE;
|
||||
}
|
||||
|
||||
// 4级页表已经空了,释放页表
|
||||
if (unlikely(mm_check_page_table(pt_ptr)) == 0)
|
||||
{
|
||||
*pde_ptr = 0;
|
||||
kfree(pt_ptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*pde_ptr = 0;
|
||||
length_unmapped += PAGE_2M_SIZE;
|
||||
pgt_num.num_PTE -= 512;
|
||||
}
|
||||
}
|
||||
|
||||
// 3级页表已经空了,释放页表
|
||||
if (unlikely(mm_check_page_table(pd_ptr)) == 0)
|
||||
{
|
||||
*pdpte_ptr = 0;
|
||||
kfree(pd_ptr);
|
||||
}
|
||||
}
|
||||
// 2级页表已经空了,释放页表
|
||||
if (unlikely(mm_check_page_table(pdpt_ptr)) == 0)
|
||||
{
|
||||
*pml4e_ptr = 0;
|
||||
kfree(pdpt_ptr);
|
||||
}
|
||||
}
|
||||
flush_tlb();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建VMA
|
||||
*
|
||||
* @param mm 要绑定的内存空间分布结构体
|
||||
* @param vaddr 起始虚拟地址
|
||||
* @param length 长度(字节)
|
||||
* @param vm_flags vma的标志
|
||||
* @param vm_ops vma的操作接口
|
||||
* @param res_vma 返回的vma指针
|
||||
* @return int 错误码
|
||||
*/
|
||||
int mm_create_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, vm_flags_t vm_flags, struct vm_operations_t *vm_ops, struct vm_area_struct **res_vma)
|
||||
{
|
||||
int retval = 0;
|
||||
// 输入的地址如果不是4K对齐,则报错
|
||||
if (unlikely(vaddr & (PAGE_4K_SIZE - 1)))
|
||||
return -EINVAL;
|
||||
|
||||
struct vm_area_struct *vma = vm_area_alloc(mm);
|
||||
if (unlikely(vma == NULL))
|
||||
return -ENOMEM;
|
||||
vma->vm_ops = vm_ops;
|
||||
vma->vm_flags = vm_flags;
|
||||
vma->vm_start = vaddr;
|
||||
vma->vm_end = vaddr + length;
|
||||
// 将VMA加入mm的链表
|
||||
retval = vma_insert(mm, vma);
|
||||
if (retval == -EEXIST || retval == __VMA_MERGED) // 之前已经存在了相同的vma,直接返回
|
||||
{
|
||||
*res_vma = vma_find(mm, vma->vm_start);
|
||||
kfree(vma);
|
||||
if (retval == -EEXIST)
|
||||
return -EEXIST;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (res_vma != NULL)
|
||||
*res_vma = vma;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将指定的物理地址映射到指定的vma处
|
||||
*
|
||||
* @param vma 要进行映射的VMA结构体
|
||||
* @param paddr 起始物理地址
|
||||
* @param offset 要映射的起始位置在vma中的偏移量
|
||||
* @param length 要映射的长度
|
||||
* @return int 错误码
|
||||
*/
|
||||
int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr, uint64_t offset, uint64_t length)
|
||||
{
|
||||
int retval = 0;
|
||||
uint64_t mapped = 0;
|
||||
BUG_ON((offset & (PAGE_4K_SIZE - 1)) != 0);
|
||||
length = PAGE_4K_ALIGN(length); // 将length按照4K进行对齐
|
||||
// 获取物理地址对应的页面
|
||||
struct Page *pg;
|
||||
uint64_t page_flags = 0;
|
||||
if (vma->vm_flags & VM_IO) // 对于mmio的内存,创建新的page结构体
|
||||
{
|
||||
page_flags = PAGE_PWT | PAGE_PCD;
|
||||
if (unlikely(vma->anon_vma == NULL || vma->anon_vma->page == NULL))
|
||||
pg = __create_mmio_page_struct(paddr);
|
||||
else
|
||||
pg = vma->anon_vma->page;
|
||||
}
|
||||
else
|
||||
pg = Phy_to_2M_Page(paddr);
|
||||
|
||||
if (unlikely(pg->anon_vma == NULL)) // 若页面不存在anon_vma,则为页面创建anon_vma
|
||||
{
|
||||
spin_lock(&pg->op_lock);
|
||||
if (unlikely(pg->anon_vma == NULL))
|
||||
__anon_vma_create_alloc(pg, false);
|
||||
spin_unlock(&pg->op_lock);
|
||||
}
|
||||
barrier();
|
||||
// 将anon vma与vma进行绑定
|
||||
__anon_vma_add(pg->anon_vma, vma);
|
||||
barrier();
|
||||
// 长度超过界限
|
||||
BUG_ON(vma->vm_start + offset + length > vma->vm_end);
|
||||
|
||||
/*
|
||||
todo: 限制页面的读写权限
|
||||
*/
|
||||
|
||||
// ==== 将地址映射到页表 ====
|
||||
uint64_t len_4k, len_2m;
|
||||
// 将地址使用4k页填补,使得地址按照2M对齐
|
||||
len_4k = PAGE_2M_ALIGN(vma->vm_start + offset) - (vma->vm_start + offset);
|
||||
if (len_4k > 0)
|
||||
len_4k = (len_4k > length) ? length : len_4k;
|
||||
if (len_4k)
|
||||
{
|
||||
if (vma->vm_flags & VM_USER)
|
||||
page_flags |= PAGE_USER_4K_PAGE;
|
||||
else
|
||||
page_flags |= PAGE_KERNEL_4K_PAGE;
|
||||
|
||||
// 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正
|
||||
retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + offset, paddr, len_4k, page_flags, false, false, true);
|
||||
if (unlikely(retval != 0))
|
||||
goto failed;
|
||||
|
||||
mapped += len_4k;
|
||||
length -= len_4k;
|
||||
}
|
||||
|
||||
len_4k = length % PAGE_2M_SIZE;
|
||||
len_2m = length / PAGE_2M_SIZE;
|
||||
|
||||
// 映射连续的2M页
|
||||
if (likely(len_2m > 0))
|
||||
{
|
||||
if (vma->vm_flags & VM_USER)
|
||||
page_flags |= PAGE_USER_PAGE;
|
||||
else
|
||||
page_flags |= PAGE_KERNEL_PAGE;
|
||||
// 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正
|
||||
retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + offset + mapped, paddr + mapped, len_2m, page_flags, false, false, false);
|
||||
|
||||
if (unlikely(retval != 0))
|
||||
goto failed;
|
||||
mapped += len_2m;
|
||||
}
|
||||
// 最后再使用4K页填补
|
||||
if (likely(len_4k > 0))
|
||||
{
|
||||
|
||||
if (vma->vm_flags & VM_USER)
|
||||
page_flags |= PAGE_USER_4K_PAGE;
|
||||
else
|
||||
page_flags |= PAGE_KERNEL_4K_PAGE;
|
||||
|
||||
// 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正
|
||||
retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + offset + mapped, paddr + mapped, len_4k, page_flags, false, false, true);
|
||||
|
||||
if (unlikely(retval != 0))
|
||||
goto failed;
|
||||
mapped += len_4k;
|
||||
}
|
||||
|
||||
if (vma->vm_flags & VM_IO)
|
||||
vma->page_offset = 0;
|
||||
|
||||
flush_tlb();
|
||||
return 0;
|
||||
failed:;
|
||||
kdebug("map VMA failed.");
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在页表中映射物理地址到指定的虚拟地址(需要页表中已存在对应的vma)
|
||||
*
|
||||
* @param mm 内存管理结构体
|
||||
* @param vaddr 虚拟地址
|
||||
* @param length 长度(字节)
|
||||
* @param paddr 物理地址
|
||||
* @return int 返回码
|
||||
*/
|
||||
int mm_map(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t paddr)
|
||||
{
|
||||
int retval = 0;
|
||||
uint64_t offset = 0;
|
||||
for (uint64_t mapped = 0; mapped < length;)
|
||||
{
|
||||
|
||||
struct vm_area_struct *vma = vma_find(mm, vaddr + mapped);
|
||||
if (unlikely(vma == NULL))
|
||||
{
|
||||
kerror("Map addr failed: vma not found. At address: %#018lx, pid=%ld", vaddr + mapped, current_pcb->pid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// if (unlikely(vma->vm_start != (vaddr + mapped)))
|
||||
// {
|
||||
// kerror("Map addr failed: addr_start is not equal to current: %#018lx.", vaddr + mapped);
|
||||
// return -EINVAL;
|
||||
// }
|
||||
|
||||
offset = vaddr + mapped - vma->vm_start;
|
||||
uint64_t m_len = vma->vm_end - vma->vm_start - offset;
|
||||
// kdebug("start=%#018lx, offset=%ld", vma->vm_start, offset);
|
||||
retval = mm_map_vma(vma, paddr + mapped, offset, m_len);
|
||||
if (unlikely(retval != 0))
|
||||
goto failed;
|
||||
|
||||
mapped += m_len;
|
||||
}
|
||||
return 0;
|
||||
failed:;
|
||||
kerror("Map addr failed.");
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在页表中取消指定的vma的映射
|
||||
*
|
||||
* @param mm 指定的mm
|
||||
* @param vma 待取消映射的vma
|
||||
* @param paddr 返回的被取消映射的起始物理地址
|
||||
* @return int 返回码
|
||||
*/
|
||||
int mm_unmap_vma(struct mm_struct *mm, struct vm_area_struct *vma, uint64_t *paddr)
|
||||
{
|
||||
// 确保vma对应的mm与指定的mm相一致
|
||||
if (unlikely(vma->vm_mm != mm))
|
||||
return -EINVAL;
|
||||
struct anon_vma_t *anon = vma->anon_vma;
|
||||
if (paddr != NULL)
|
||||
*paddr = __mm_get_paddr(mm, vma->vm_start);
|
||||
if (anon == NULL)
|
||||
kwarn("anon is NULL");
|
||||
semaphore_down(&anon->sem);
|
||||
|
||||
mm_unmap_proc_table((uint64_t)mm->pgd, true, vma->vm_start, vma->vm_end - vma->vm_start);
|
||||
__anon_vma_del(vma);
|
||||
/** todo: 这里应该会存在bug,应修复。
|
||||
* 若anon_vma的等待队列上有其他的进程,由于anon_vma被释放
|
||||
* 这些在等待队列上的进程将无法被唤醒。
|
||||
*/
|
||||
list_init(&vma->anon_vma_list);
|
||||
|
||||
semaphore_up(&anon->sem);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解除一段虚拟地址的映射(这些地址必须在vma中存在)
|
||||
*
|
||||
* @param mm 内存空间结构体
|
||||
* @param vaddr 起始地址
|
||||
* @param length 结束地址
|
||||
* @param destroy 是否释放vma结构体
|
||||
* @return int 错误码
|
||||
*/
|
||||
int mm_unmap(struct mm_struct *mm, uint64_t vaddr, uint64_t length, bool destroy)
|
||||
{
|
||||
int retval = 0;
|
||||
for (uint64_t unmapped = 0; unmapped < length;)
|
||||
{
|
||||
struct vm_area_struct *vma = vma_find(mm, vaddr + unmapped);
|
||||
if (unlikely(vma == NULL))
|
||||
{
|
||||
kerror("Unmap addr failed: vma not found. At address: %#018lx, pid=%ld", vaddr + unmapped, current_pcb->pid);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (unlikely(vma->vm_start != (vaddr + unmapped)))
|
||||
{
|
||||
kerror("Unmap addr failed: addr_start is not equal to current: %#018lx.", vaddr + unmapped);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (vma->anon_vma != NULL)
|
||||
mm_unmap_vma(mm, vma, NULL);
|
||||
|
||||
unmapped += vma->vm_end - vma->vm_start;
|
||||
// 释放vma结构体
|
||||
if (destroy)
|
||||
{
|
||||
vm_area_del(vma);
|
||||
vm_area_free(vma);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
failed:;
|
||||
kerror("Unmap addr failed.");
|
||||
return retval;
|
||||
}
|
258
kernel/src/mm/mmio-buddy.c
Normal file
258
kernel/src/mm/mmio-buddy.c
Normal file
@ -0,0 +1,258 @@
|
||||
#include "mmio-buddy.h"
|
||||
#include <mm/slab.h>
|
||||
|
||||
/**
|
||||
* @brief 将内存对象大小的幂转换成内存池中的数组的下标
|
||||
*
|
||||
*/
|
||||
#define __exp2index(exp) (exp - 12)
|
||||
|
||||
/**
|
||||
* @brief 计算伙伴块的内存虚拟地址
|
||||
*
|
||||
*/
|
||||
#define buddy_block_vaddr(vaddr, exp) (vaddr ^ (1UL << exp))
|
||||
|
||||
static struct mmio_buddy_mem_pool __mmio_pool; // mmio buddy内存池
|
||||
|
||||
/**
|
||||
* @brief 往指定的地址空间链表中添加一个地址区域
|
||||
*
|
||||
* @param index
|
||||
* @param region
|
||||
* @return __always_inline
|
||||
*/
|
||||
static __always_inline void __buddy_add_region_obj(int index, struct __mmio_buddy_addr_region *region)
|
||||
{
|
||||
struct __mmio_free_region_list *lst = &__mmio_pool.free_regions[index];
|
||||
list_init(®ion->list);
|
||||
list_append(&lst->list_head, ®ion->list);
|
||||
++lst->num_free;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建新的地址区域结构体
|
||||
*
|
||||
* @param vaddr 虚拟地址
|
||||
* @return 创建好的地址区域结构体
|
||||
*/
|
||||
static __always_inline struct __mmio_buddy_addr_region *__mmio_buddy_create_region(uint64_t vaddr)
|
||||
{
|
||||
// 申请内存块的空间
|
||||
struct __mmio_buddy_addr_region *region =
|
||||
(struct __mmio_buddy_addr_region *)kzalloc(sizeof(struct __mmio_buddy_addr_region), 0);
|
||||
list_init(®ion->list);
|
||||
region->vaddr = vaddr;
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将给定大小为(2^exp)的地址空间一分为二,并插入下一级的链表中
|
||||
*
|
||||
* @param region 要被分割的地址区域
|
||||
* @param exp 要被分割的地址区域的大小的幂
|
||||
*/
|
||||
static __always_inline void __buddy_split(struct __mmio_buddy_addr_region *region, int exp)
|
||||
{
|
||||
// 计算分裂出来的新的伙伴块的地址
|
||||
struct __mmio_buddy_addr_region *new_region = __mmio_buddy_create_region(buddy_block_vaddr(region->vaddr, exp - 1));
|
||||
__buddy_add_region_obj(__exp2index(exp - 1), region);
|
||||
__buddy_add_region_obj(__exp2index(exp - 1), new_region);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 合并两个伙伴块
|
||||
*
|
||||
* @param x 第一个伙伴块
|
||||
* @param y 第二个伙伴块
|
||||
* @param exp x、y大小的幂
|
||||
* @return int 错误码
|
||||
*/
|
||||
static __always_inline int __buddy_merge_blocks(struct __mmio_buddy_addr_region *x, struct __mmio_buddy_addr_region *y,
|
||||
int exp)
|
||||
{
|
||||
// 判断这两个是否是一对伙伴
|
||||
if (unlikely(x->vaddr != buddy_block_vaddr(y->vaddr, exp))) // 不是一对伙伴
|
||||
return -EINVAL;
|
||||
|
||||
// === 是一对伙伴,将他们合并
|
||||
// 减少计数的工作应在该函数外完成
|
||||
|
||||
// 释放y
|
||||
__mmio_buddy_release_addr_region(y);
|
||||
// 插入x
|
||||
__buddy_add_region_obj(__exp2index(exp + 1), x);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从空闲链表中取出指定大小的内存区域, 并从链表中删除
|
||||
*
|
||||
* @param exp 内存大小的幂
|
||||
* @return __always_inline struct* 内存区域结构体
|
||||
*/
|
||||
static __always_inline struct __mmio_buddy_addr_region *__buddy_pop_region(int exp)
|
||||
{
|
||||
if (unlikely(list_empty(&__mmio_pool.free_regions[__exp2index(exp)].list_head)))
|
||||
return NULL;
|
||||
struct __mmio_buddy_addr_region *r = container_of(list_next(&__mmio_pool.free_regions[__exp2index(exp)].list_head),
|
||||
struct __mmio_buddy_addr_region, list);
|
||||
list_del(&r->list);
|
||||
// 区域计数减1
|
||||
--__mmio_pool.free_regions[__exp2index(exp)].num_free;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 寻找给定块的伙伴块
|
||||
*
|
||||
* @param x 给定的内存块
|
||||
* @param exp 内存块大小
|
||||
* @return 伙伴块的指针
|
||||
*/
|
||||
static __always_inline struct __mmio_buddy_addr_region *__find_buddy(struct __mmio_buddy_addr_region *x, int exp)
|
||||
{
|
||||
// 当前为空
|
||||
if (unlikely(list_empty(&__mmio_pool.free_regions[__exp2index(exp)].list_head)))
|
||||
return NULL;
|
||||
// 遍历链表以寻找伙伴块
|
||||
uint64_t buddy_vaddr = buddy_block_vaddr(x->vaddr, exp);
|
||||
struct List *list = &__mmio_pool.free_regions[__exp2index(exp)].list_head;
|
||||
|
||||
do
|
||||
{
|
||||
list = list_next(list);
|
||||
struct __mmio_buddy_addr_region *bd = container_of(list, struct __mmio_buddy_addr_region, list);
|
||||
if (bd->vaddr == buddy_vaddr) // 找到了伙伴块
|
||||
return bd;
|
||||
} while (list_next(list) != &__mmio_pool.free_regions[__exp2index(exp)].list_head);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/**
|
||||
* @brief 把某个大小的伙伴块全都合并成大小为(2^(exp+1))的块
|
||||
*
|
||||
* @param exp 地址空间大小(2^exp)
|
||||
*/
|
||||
static void __buddy_merge(int exp)
|
||||
{
|
||||
struct __mmio_free_region_list *free_list = &__mmio_pool.free_regions[__exp2index(exp)];
|
||||
// 若链表为空
|
||||
if (list_empty(&free_list->list_head))
|
||||
return;
|
||||
|
||||
struct List *list = list_next(&free_list->list_head);
|
||||
|
||||
do
|
||||
{
|
||||
struct __mmio_buddy_addr_region *ptr = container_of(list, struct __mmio_buddy_addr_region, list);
|
||||
// 寻找是否有伙伴块
|
||||
struct __mmio_buddy_addr_region *bd = __find_buddy(ptr, exp);
|
||||
|
||||
// 一定要在merge之前执行,否则list就被重置了
|
||||
list = list_next(list);
|
||||
|
||||
if (bd != NULL) // 找到伙伴块
|
||||
{
|
||||
free_list->num_free -= 2;
|
||||
list_del(&ptr->list);
|
||||
list_del(&bd->list);
|
||||
__buddy_merge_blocks(ptr, bd, exp);
|
||||
}
|
||||
|
||||
} while (list != &free_list->list_head);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从buddy中申请一块指定大小的内存区域
|
||||
*
|
||||
* @param exp 内存区域的大小(2^exp)
|
||||
* @return struct __mmio_buddy_addr_region* 符合要求的内存区域。没有满足要求的时候,返回NULL
|
||||
*/
|
||||
struct __mmio_buddy_addr_region *mmio_buddy_query_addr_region(int exp)
|
||||
{
|
||||
if (unlikely(exp > MMIO_BUDDY_MAX_EXP || exp < MMIO_BUDDY_MIN_EXP))
|
||||
{
|
||||
BUG_ON(1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!list_empty(&__mmio_pool.free_regions[__exp2index(exp)].list_head))
|
||||
goto has_block;
|
||||
|
||||
// 若没有符合要求的内存块,则先尝试分裂大的块
|
||||
for (int cur_exp = exp; cur_exp <= MMIO_BUDDY_MAX_EXP; ++cur_exp)
|
||||
{
|
||||
if (unlikely(
|
||||
list_empty(&__mmio_pool.free_regions[__exp2index(cur_exp)].list_head))) // 一直寻找到有空闲空间的链表
|
||||
continue;
|
||||
|
||||
// 找到了,逐级向下split
|
||||
for (int down_exp = cur_exp; down_exp > exp; --down_exp)
|
||||
{
|
||||
// 取出一块空闲区域
|
||||
struct __mmio_buddy_addr_region *r = __buddy_pop_region(down_exp);
|
||||
__buddy_split(r, down_exp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!list_empty(&__mmio_pool.free_regions[__exp2index(exp)].list_head))
|
||||
goto has_block;
|
||||
|
||||
// 尝试合并小的伙伴块
|
||||
for (int cur_exp = MMIO_BUDDY_MIN_EXP; cur_exp < exp; ++cur_exp)
|
||||
__buddy_merge(cur_exp);
|
||||
// 再次尝试获取符合要求的内存块,若仍不成功,则说明mmio空间耗尽
|
||||
if (!list_empty(&__mmio_pool.free_regions[__exp2index(exp)].list_head))
|
||||
goto has_block;
|
||||
else
|
||||
goto failed;
|
||||
failed:;
|
||||
return NULL;
|
||||
has_block:; // 有可用的内存块,分配
|
||||
return __buddy_pop_region(exp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 归还一块内存空间到buddy
|
||||
*
|
||||
* @param vaddr 虚拟地址
|
||||
* @param exp 内存空间的大小(2^exp)
|
||||
* @return int 返回码
|
||||
*/
|
||||
int __mmio_buddy_give_back(uint64_t vaddr, int exp)
|
||||
{
|
||||
// 确保内存对齐,低位都要为0
|
||||
if (vaddr & ((1UL << exp) - 1))
|
||||
return -EINVAL;
|
||||
|
||||
struct __mmio_buddy_addr_region *region = __mmio_buddy_create_region(vaddr);
|
||||
// 加入buddy
|
||||
__buddy_add_region_obj(__exp2index(exp), region);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化mmio的伙伴系统
|
||||
*
|
||||
*/
|
||||
void mmio_buddy_init()
|
||||
{
|
||||
memset(&__mmio_pool, 0, sizeof(struct mmio_buddy_mem_pool));
|
||||
spin_init(&__mmio_pool.op_lock);
|
||||
|
||||
// 初始化各个链表的头部
|
||||
for (int i = 0; i < MMIO_BUDDY_REGION_COUNT; ++i)
|
||||
{
|
||||
list_init(&__mmio_pool.free_regions[i].list_head);
|
||||
__mmio_pool.free_regions[i].num_free = 0;
|
||||
}
|
||||
|
||||
// 创建一堆1GB的地址块
|
||||
uint32_t cnt_1g_blocks = (MMIO_TOP - MMIO_BASE) / PAGE_1G_SIZE;
|
||||
uint64_t vaddr_base = MMIO_BASE;
|
||||
for (uint32_t i = 0; i < cnt_1g_blocks; ++i, vaddr_base += PAGE_1G_SIZE)
|
||||
__mmio_buddy_give_back(vaddr_base, PAGE_1G_SHIFT);
|
||||
}
|
79
kernel/src/mm/mmio-buddy.h
Normal file
79
kernel/src/mm/mmio-buddy.h
Normal file
@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
#include <common/sys/types.h>
|
||||
#include <common/glib.h>
|
||||
#include "mm-types.h"
|
||||
#include "mm.h"
|
||||
#include "slab.h"
|
||||
|
||||
#define MMIO_BUDDY_MAX_EXP PAGE_1G_SHIFT
|
||||
#define MMIO_BUDDY_MIN_EXP PAGE_4K_SHIFT
|
||||
#define MMIO_BUDDY_REGION_COUNT (MMIO_BUDDY_MAX_EXP - MMIO_BUDDY_MIN_EXP + 1)
|
||||
|
||||
/**
|
||||
* @brief mmio伙伴系统内部的地址区域结构体
|
||||
*
|
||||
*/
|
||||
struct __mmio_buddy_addr_region
|
||||
{
|
||||
struct List list;
|
||||
uint64_t vaddr; // 该内存对象起始位置的虚拟地址
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 空闲页数组结构体
|
||||
*
|
||||
*/
|
||||
struct __mmio_free_region_list
|
||||
{
|
||||
struct List list_head;
|
||||
int64_t num_free; // 空闲页的数量
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief buddy内存池
|
||||
*
|
||||
*/
|
||||
struct mmio_buddy_mem_pool
|
||||
{
|
||||
uint64_t pool_start_addr; // 内存池的起始地址
|
||||
uint64_t pool_size; // 内存池的内存空间总大小
|
||||
spinlock_t op_lock; // 操作锁
|
||||
/**
|
||||
* @brief 空闲地址区域链表
|
||||
* 数组的第i个元素代表大小为2^(i+12)的内存区域
|
||||
*/
|
||||
struct __mmio_free_region_list free_regions[MMIO_BUDDY_REGION_COUNT];
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 释放address region结构体
|
||||
*
|
||||
* @param region 待释放的结构体
|
||||
*/
|
||||
static __always_inline void __mmio_buddy_release_addr_region(struct __mmio_buddy_addr_region *region)
|
||||
{
|
||||
kfree(region);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 归还一块内存空间到buddy
|
||||
*
|
||||
* @param vaddr 虚拟地址
|
||||
* @param exp 内存空间的大小(2^exp)
|
||||
* @return int 返回码
|
||||
*/
|
||||
int __mmio_buddy_give_back(uint64_t vaddr, int exp);
|
||||
|
||||
/**
|
||||
* @brief 初始化mmio的伙伴系统
|
||||
*
|
||||
*/
|
||||
void mmio_buddy_init();
|
||||
|
||||
/**
|
||||
* @brief 从buddy中申请一块指定大小的内存区域
|
||||
*
|
||||
* @param exp 内存区域的大小(2^exp)
|
||||
* @return struct __mmio_buddy_addr_region* 符合要求的内存区域。没有满足要求的时候,返回NULL
|
||||
*/
|
||||
struct __mmio_buddy_addr_region *mmio_buddy_query_addr_region(int exp);
|
118
kernel/src/mm/mmio.c
Normal file
118
kernel/src/mm/mmio.c
Normal file
@ -0,0 +1,118 @@
|
||||
#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;
|
||||
}
|
24
kernel/src/mm/mmio.h
Normal file
24
kernel/src/mm/mmio.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "mm.h"
|
||||
|
||||
void mmio_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);
|
||||
|
||||
/**
|
||||
* @brief 取消mmio的映射并将地址空间归还到buddy中
|
||||
*
|
||||
* @param vaddr 起始的虚拟地址
|
||||
* @param size 要归还的地址空间的长度
|
||||
* @return int 错误码
|
||||
*/
|
||||
int mmio_release(uint64_t vaddr, uint64_t size);
|
713
kernel/src/mm/slab.c
Normal file
713
kernel/src/mm/slab.c
Normal file
@ -0,0 +1,713 @@
|
||||
#include "slab.h"
|
||||
#include <common/compiler.h>
|
||||
|
||||
struct slab kmalloc_cache_group[16] =
|
||||
{
|
||||
{32, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{64, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{128, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{256, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{512, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{1024, 0, 0, NULL, NULL, NULL, NULL}, // 1KB
|
||||
{2048, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{4096, 0, 0, NULL, NULL, NULL, NULL}, // 4KB
|
||||
{8192, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{16384, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{32768, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{65536, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{131072, 0, 0, NULL, NULL, NULL, NULL}, // 128KB
|
||||
{262144, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{524288, 0, 0, NULL, NULL, NULL, NULL},
|
||||
{1048576, 0, 0, NULL, NULL, NULL, NULL}, // 1MB
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 创建一个内存池
|
||||
*
|
||||
* @param size 内存池容量大小
|
||||
* @param constructor 构造函数
|
||||
* @param destructor 析构函数
|
||||
* @param arg 参数
|
||||
* @return struct slab* 构建好的内存池对象
|
||||
*/
|
||||
struct slab *slab_create(ul size, void *(*constructor)(void *vaddr, ul arg), void *(*destructor)(void *vaddr, ul arg), ul arg)
|
||||
{
|
||||
struct slab *slab_pool = (struct slab *)kmalloc(sizeof(struct slab), 0);
|
||||
|
||||
// BUG
|
||||
if (slab_pool == NULL)
|
||||
{
|
||||
kBUG("slab_create()->kmalloc()->slab == NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(slab_pool, 0, sizeof(struct slab));
|
||||
|
||||
slab_pool->size = SIZEOF_LONG_ALIGN(size);
|
||||
slab_pool->count_total_using = 0;
|
||||
slab_pool->count_total_free = 0;
|
||||
// 直接分配cache_pool_entry结构体,避免每次访问都要检测是否为NULL,提升效率
|
||||
slab_pool->cache_pool_entry = (struct slab_obj *)kmalloc(sizeof(struct slab_obj), 0);
|
||||
|
||||
// BUG
|
||||
if (slab_pool->cache_pool_entry == NULL)
|
||||
{
|
||||
kBUG("slab_create()->kmalloc()->slab->cache_pool_entry == NULL");
|
||||
kfree(slab_pool);
|
||||
return NULL;
|
||||
}
|
||||
memset(slab_pool->cache_pool_entry, 0, sizeof(struct slab_obj));
|
||||
|
||||
// dma内存池设置为空
|
||||
slab_pool->cache_dma_pool_entry = NULL;
|
||||
|
||||
// 设置构造及析构函数
|
||||
slab_pool->constructor = constructor;
|
||||
slab_pool->destructor = destructor;
|
||||
|
||||
list_init(&slab_pool->cache_pool_entry->list);
|
||||
|
||||
// 分配属于内存池的内存页
|
||||
slab_pool->cache_pool_entry->page = alloc_pages(ZONE_NORMAL, 1, PAGE_KERNEL);
|
||||
|
||||
// BUG
|
||||
if (slab_pool->cache_pool_entry->page == NULL)
|
||||
{
|
||||
kBUG("slab_create()->kmalloc()->slab->cache_pool_entry == NULL");
|
||||
kfree(slab_pool->cache_pool_entry);
|
||||
kfree(slab_pool);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// page_init(slab_pool->cache_pool_entry->page, PAGE_KERNEL);
|
||||
|
||||
slab_pool->cache_pool_entry->count_using = 0;
|
||||
slab_pool->cache_pool_entry->count_free = PAGE_2M_SIZE / slab_pool->size;
|
||||
|
||||
slab_pool->count_total_free = slab_pool->cache_pool_entry->count_free;
|
||||
|
||||
slab_pool->cache_pool_entry->vaddr = phys_2_virt(slab_pool->cache_pool_entry->page->addr_phys);
|
||||
|
||||
// bitmap有多少有效位
|
||||
slab_pool->cache_pool_entry->bmp_count = slab_pool->cache_pool_entry->count_free;
|
||||
|
||||
// 计算位图所占的空间 占用多少byte(按unsigned long大小的上边缘对齐)
|
||||
slab_pool->cache_pool_entry->bmp_len = ((slab_pool->cache_pool_entry->bmp_count + sizeof(ul) * 8 - 1) >> 6) << 3;
|
||||
// 初始化位图
|
||||
slab_pool->cache_pool_entry->bmp = (ul *)kmalloc(slab_pool->cache_pool_entry->bmp_len, 0);
|
||||
|
||||
// BUG
|
||||
if (slab_pool->cache_pool_entry->bmp == NULL)
|
||||
{
|
||||
kBUG("slab_create()->kmalloc()->slab->cache_pool_entry == NULL");
|
||||
free_pages(slab_pool->cache_pool_entry->page, 1);
|
||||
kfree(slab_pool->cache_pool_entry);
|
||||
kfree(slab_pool);
|
||||
return NULL;
|
||||
}
|
||||
// 将位图清空
|
||||
memset(slab_pool->cache_pool_entry->bmp, 0, slab_pool->cache_pool_entry->bmp_len);
|
||||
|
||||
return slab_pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 销毁内存池
|
||||
* 只有当slab是空的时候才能销毁
|
||||
* @param slab_pool 要销毁的内存池
|
||||
* @return ul
|
||||
*
|
||||
*/
|
||||
ul slab_destroy(struct slab *slab_pool)
|
||||
{
|
||||
struct slab_obj *slab_obj_ptr = slab_pool->cache_pool_entry;
|
||||
if (slab_pool->count_total_using)
|
||||
{
|
||||
kBUG("slab_cache->count_total_using != 0");
|
||||
return ESLAB_NOTNULL;
|
||||
}
|
||||
|
||||
struct slab_obj *tmp_slab_obj = NULL;
|
||||
while (!list_empty(&slab_obj_ptr->list))
|
||||
{
|
||||
tmp_slab_obj = slab_obj_ptr;
|
||||
// 获取下一个slab_obj的起始地址
|
||||
slab_obj_ptr = container_of(list_next(&slab_obj_ptr->list), struct slab_obj, list);
|
||||
|
||||
list_del(&tmp_slab_obj->list);
|
||||
|
||||
kfree(tmp_slab_obj->bmp);
|
||||
|
||||
page_clean(tmp_slab_obj->page);
|
||||
|
||||
free_pages(tmp_slab_obj->page, 1);
|
||||
|
||||
kfree(tmp_slab_obj);
|
||||
}
|
||||
|
||||
kfree(slab_obj_ptr->bmp);
|
||||
page_clean(slab_obj_ptr->page);
|
||||
free_pages(slab_obj_ptr->page, 1);
|
||||
kfree(slab_obj_ptr);
|
||||
kfree(slab_pool);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 分配SLAB内存池中的内存对象
|
||||
*
|
||||
* @param slab_pool slab内存池
|
||||
* @param arg 传递给内存对象构造函数的参数
|
||||
* @return void* 内存空间的虚拟地址
|
||||
*/
|
||||
void *slab_malloc(struct slab *slab_pool, ul arg)
|
||||
{
|
||||
struct slab_obj *slab_obj_ptr = slab_pool->cache_pool_entry;
|
||||
struct slab_obj *tmp_slab_obj = NULL;
|
||||
|
||||
// slab内存池中已经没有空闲的内存对象,进行扩容
|
||||
if (slab_pool->count_total_free == 0)
|
||||
{
|
||||
tmp_slab_obj = (struct slab_obj *)kmalloc(sizeof(struct slab_obj), 0);
|
||||
|
||||
// BUG
|
||||
if (tmp_slab_obj == NULL)
|
||||
{
|
||||
kBUG("slab_malloc()->kmalloc()->slab->tmp_slab_obj == NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(tmp_slab_obj, 0, sizeof(struct slab_obj));
|
||||
list_init(&tmp_slab_obj->list);
|
||||
|
||||
tmp_slab_obj->page = alloc_pages(ZONE_NORMAL, 1, PAGE_KERNEL);
|
||||
|
||||
// BUG
|
||||
if (tmp_slab_obj->page == NULL)
|
||||
{
|
||||
kBUG("slab_malloc()->kmalloc()=>tmp_slab_obj->page == NULL");
|
||||
kfree(tmp_slab_obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tmp_slab_obj->count_using = 0;
|
||||
tmp_slab_obj->count_free = PAGE_2M_SIZE / slab_pool->size;
|
||||
tmp_slab_obj->vaddr = phys_2_virt(tmp_slab_obj->page->addr_phys);
|
||||
tmp_slab_obj->bmp_count = tmp_slab_obj->count_free;
|
||||
// 计算位图所占的空间 占用多少byte(按unsigned long大小的上边缘对齐)
|
||||
tmp_slab_obj->bmp_len = ((tmp_slab_obj->bmp_count + sizeof(ul) * 8 - 1) >> 6) << 3;
|
||||
tmp_slab_obj->bmp = (ul *)kmalloc(tmp_slab_obj->bmp_len, 0);
|
||||
|
||||
// BUG
|
||||
if (tmp_slab_obj->bmp == NULL)
|
||||
{
|
||||
kBUG("slab_malloc()->kmalloc()=>tmp_slab_obj->bmp == NULL");
|
||||
free_pages(tmp_slab_obj->page, 1);
|
||||
kfree(tmp_slab_obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(tmp_slab_obj->bmp, 0, tmp_slab_obj->bmp_len);
|
||||
|
||||
list_add(&slab_pool->cache_pool_entry->list, &tmp_slab_obj->list);
|
||||
|
||||
slab_pool->count_total_free += tmp_slab_obj->count_free;
|
||||
|
||||
slab_obj_ptr = tmp_slab_obj;
|
||||
}
|
||||
|
||||
// 扩容完毕或无需扩容,开始分配内存对象
|
||||
int tmp_md;
|
||||
do
|
||||
{
|
||||
if (slab_obj_ptr->count_free == 0)
|
||||
{
|
||||
slab_obj_ptr = container_of(list_next(&slab_obj_ptr->list), struct slab_obj, list);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < slab_obj_ptr->bmp_count; ++i)
|
||||
{
|
||||
// 当前bmp对应的内存对象都已经被分配
|
||||
if (*(slab_obj_ptr->bmp + (i >> 6)) == 0xffffffffffffffffUL)
|
||||
{
|
||||
i += 63;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 第i个内存对象是空闲的
|
||||
tmp_md = i % 64;
|
||||
if ((*(slab_obj_ptr->bmp + (i >> 6)) & (1UL << tmp_md)) == 0)
|
||||
{
|
||||
// 置位bmp
|
||||
*(slab_obj_ptr->bmp + (i >> 6)) |= (1UL << tmp_md);
|
||||
|
||||
// 更新当前slab对象的计数器
|
||||
++(slab_obj_ptr->count_using);
|
||||
--(slab_obj_ptr->count_free);
|
||||
// 更新slab内存池的计数器
|
||||
++(slab_pool->count_total_using);
|
||||
--(slab_pool->count_total_free);
|
||||
|
||||
if (slab_pool->constructor != NULL)
|
||||
{
|
||||
// 返回内存对象指针(要求构造函数返回内存对象指针)
|
||||
return slab_pool->constructor((char *)slab_obj_ptr->vaddr + slab_pool->size * i, arg);
|
||||
}
|
||||
// 返回内存对象指针
|
||||
else
|
||||
return (void *)((char *)slab_obj_ptr->vaddr + slab_pool->size * i);
|
||||
}
|
||||
}
|
||||
|
||||
} while (slab_obj_ptr != slab_pool->cache_pool_entry);
|
||||
|
||||
// should not be here
|
||||
|
||||
kBUG("slab_malloc() ERROR: can't malloc");
|
||||
|
||||
// 释放内存
|
||||
if (tmp_slab_obj != NULL)
|
||||
{
|
||||
list_del(&tmp_slab_obj->list);
|
||||
kfree(tmp_slab_obj->bmp);
|
||||
page_clean(tmp_slab_obj->page);
|
||||
free_pages(tmp_slab_obj->page, 1);
|
||||
kfree(tmp_slab_obj);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 回收slab内存池中的对象
|
||||
*
|
||||
* @param slab_pool 对应的内存池
|
||||
* @param addr 内存对象的虚拟地址
|
||||
* @param arg 传递给虚构函数的参数
|
||||
* @return ul
|
||||
*/
|
||||
ul slab_free(struct slab *slab_pool, void *addr, ul arg)
|
||||
{
|
||||
struct slab_obj *slab_obj_ptr = slab_pool->cache_pool_entry;
|
||||
|
||||
do
|
||||
{
|
||||
// 虚拟地址不在当前内存池对象的管理范围内
|
||||
if (!(slab_obj_ptr->vaddr <= addr && addr <= (slab_obj_ptr->vaddr + PAGE_2M_SIZE)))
|
||||
{
|
||||
slab_obj_ptr = container_of(list_next(&slab_obj_ptr->list), struct slab_obj, list);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// 计算出给定内存对象是第几个
|
||||
int index = (addr - slab_obj_ptr->vaddr) / slab_pool->size;
|
||||
|
||||
// 复位位图中对应的位
|
||||
*(slab_obj_ptr->bmp + (index >> 6)) ^= (1UL << index % 64);
|
||||
|
||||
++(slab_obj_ptr->count_free);
|
||||
--(slab_obj_ptr->count_using);
|
||||
|
||||
++(slab_pool->count_total_free);
|
||||
--(slab_pool->count_total_using);
|
||||
|
||||
// 有对应的析构函数,调用析构函数
|
||||
if (slab_pool->destructor != NULL)
|
||||
slab_pool->destructor((char *)slab_obj_ptr->vaddr + slab_pool->size * index, arg);
|
||||
|
||||
// 当前内存对象池的正在使用的内存对象为0,且内存池的空闲对象大于当前对象池的2倍,则销毁当前对象池,以减轻系统内存压力
|
||||
if ((slab_obj_ptr->count_using == 0) && ((slab_pool->count_total_free >> 1) >= slab_obj_ptr->count_free) && (slab_obj_ptr != slab_pool->cache_pool_entry))
|
||||
{
|
||||
|
||||
list_del(&slab_obj_ptr->list);
|
||||
slab_pool->count_total_free -= slab_obj_ptr->count_free;
|
||||
|
||||
kfree(slab_obj_ptr->bmp);
|
||||
page_clean(slab_obj_ptr->page);
|
||||
free_pages(slab_obj_ptr->page, 1);
|
||||
|
||||
kfree(slab_obj_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
} while (slab_obj_ptr != slab_pool->cache_pool_entry);
|
||||
|
||||
kwarn("slab_free(): address not in current slab");
|
||||
return ENOT_IN_SLAB;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化内存池组
|
||||
* 在初始化通用内存管理单元期间,尚无内存空间分配函数,需要我们手动为SLAB内存池指定存储空间
|
||||
* @return ul
|
||||
*/
|
||||
ul slab_init()
|
||||
{
|
||||
kinfo("Initializing SLAB...");
|
||||
// 将slab的内存池空间放置在mms的后方
|
||||
ul tmp_addr = memory_management_struct.end_of_struct;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
io_mfence();
|
||||
spin_init(&kmalloc_cache_group[i].lock);
|
||||
// 将slab内存池对象的空间放置在mms的后面,并且预留4个unsigned long 的空间以防止内存越界
|
||||
kmalloc_cache_group[i].cache_pool_entry = (struct slab_obj *)memory_management_struct.end_of_struct;
|
||||
|
||||
memory_management_struct.end_of_struct += sizeof(struct slab_obj) + (sizeof(ul) << 2);
|
||||
|
||||
list_init(&kmalloc_cache_group[i].cache_pool_entry->list);
|
||||
|
||||
// 初始化内存池对象
|
||||
kmalloc_cache_group[i].cache_pool_entry->count_using = 0;
|
||||
kmalloc_cache_group[i].cache_pool_entry->count_free = PAGE_2M_SIZE / kmalloc_cache_group[i].size;
|
||||
kmalloc_cache_group[i].cache_pool_entry->bmp_len = (((kmalloc_cache_group[i].cache_pool_entry->count_free + sizeof(ul) * 8 - 1) >> 6) << 3);
|
||||
kmalloc_cache_group[i].cache_pool_entry->bmp_count = kmalloc_cache_group[i].cache_pool_entry->count_free;
|
||||
|
||||
// 在slab对象后方放置bmp
|
||||
kmalloc_cache_group[i].cache_pool_entry->bmp = (ul *)memory_management_struct.end_of_struct;
|
||||
|
||||
// bmp后方预留4个unsigned long的空间防止内存越界,且按照8byte进行对齐
|
||||
memory_management_struct.end_of_struct = (ul)(memory_management_struct.end_of_struct + kmalloc_cache_group[i].cache_pool_entry->bmp_len + (sizeof(ul) << 2)) & (~(sizeof(ul) - 1));
|
||||
io_mfence();
|
||||
// @todo:此处可优化,直接把所有位设置为0,然后再对部分不存在对应的内存对象的位设置为1
|
||||
memset(kmalloc_cache_group[i].cache_pool_entry->bmp, 0xff, kmalloc_cache_group[i].cache_pool_entry->bmp_len);
|
||||
for (int j = 0; j < kmalloc_cache_group[i].cache_pool_entry->bmp_count; ++j)
|
||||
*(kmalloc_cache_group[i].cache_pool_entry->bmp + (j >> 6)) ^= 1UL << (j % 64);
|
||||
|
||||
kmalloc_cache_group[i].count_total_using = 0;
|
||||
kmalloc_cache_group[i].count_total_free = kmalloc_cache_group[i].cache_pool_entry->count_free;
|
||||
io_mfence();
|
||||
}
|
||||
|
||||
struct Page *page = NULL;
|
||||
|
||||
// 将上面初始化内存池组时,所占用的内存页进行初始化
|
||||
ul tmp_page_mms_end = virt_2_phys(memory_management_struct.end_of_struct) >> PAGE_2M_SHIFT;
|
||||
|
||||
ul page_num = 0;
|
||||
for (int i = PAGE_2M_ALIGN(virt_2_phys(tmp_addr)) >> PAGE_2M_SHIFT; i <= tmp_page_mms_end; ++i)
|
||||
{
|
||||
|
||||
page = memory_management_struct.pages_struct + i;
|
||||
page_num = page->addr_phys >> PAGE_2M_SHIFT;
|
||||
*(memory_management_struct.bmp + (page_num >> 6)) |= (1UL << (page_num % 64));
|
||||
++page->zone->count_pages_using;
|
||||
io_mfence();
|
||||
--page->zone->count_pages_free;
|
||||
page_init(page, PAGE_KERNEL_INIT | PAGE_KERNEL | PAGE_PGT_MAPPED);
|
||||
}
|
||||
io_mfence();
|
||||
|
||||
// 为slab内存池对象分配内存空间
|
||||
ul *virt = NULL;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
// 获取一个新的空页并添加到空页表,然后返回其虚拟地址
|
||||
virt = (ul *)((memory_management_struct.end_of_struct + PAGE_2M_SIZE * i + PAGE_2M_SIZE - 1) & PAGE_2M_MASK);
|
||||
|
||||
page = Virt_To_2M_Page(virt);
|
||||
|
||||
page_num = page->addr_phys >> PAGE_2M_SHIFT;
|
||||
|
||||
*(memory_management_struct.bmp + (page_num >> 6)) |= (1UL << (page_num % 64));
|
||||
|
||||
++page->zone->count_pages_using;
|
||||
io_mfence(); // 该位置必须加一个mfence,否则O3优化运行时会报错
|
||||
--page->zone->count_pages_free;
|
||||
page_init(page, PAGE_PGT_MAPPED | PAGE_KERNEL | PAGE_KERNEL_INIT);
|
||||
|
||||
kmalloc_cache_group[i].cache_pool_entry->page = page;
|
||||
|
||||
kmalloc_cache_group[i].cache_pool_entry->vaddr = virt;
|
||||
}
|
||||
|
||||
kinfo("SLAB initialized successfully!");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 在kmalloc中创建slab_obj的函数(与slab_malloc()中的类似)
|
||||
*
|
||||
* @param size
|
||||
* @return struct slab_obj* 创建好的slab_obj
|
||||
*/
|
||||
|
||||
struct slab_obj *kmalloc_create_slab_obj(ul size)
|
||||
{
|
||||
struct Page *page = alloc_pages(ZONE_NORMAL, 1, 0);
|
||||
|
||||
// BUG
|
||||
if (page == NULL)
|
||||
{
|
||||
kBUG("kmalloc_create()->alloc_pages()=>page == NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
page_init(page, PAGE_KERNEL);
|
||||
|
||||
ul *vaddr = NULL;
|
||||
ul struct_size = 0;
|
||||
struct slab_obj *slab_obj_ptr;
|
||||
|
||||
// 根据size大小,选择不同的分支来处理
|
||||
// 之所以选择512byte为分界点,是因为,此时bmp大小刚好为512byte。显而易见,选择过小的话会导致kmalloc函数与当前函数反复互相调用,最终导致栈溢出
|
||||
switch (size)
|
||||
{
|
||||
// ============ 对于size<=512byte的内存池对象,将slab_obj结构体和bmp放置在物理页的内部 ========
|
||||
// 由于这些对象的特征是,bmp占的空间大,而内存块的空间小,这样做的目的是避免再去申请一块内存来存储bmp,减少浪费。
|
||||
case 32:
|
||||
case 64:
|
||||
case 128:
|
||||
case 256:
|
||||
case 512:
|
||||
vaddr = phys_2_virt(page->addr_phys);
|
||||
// slab_obj结构体的大小 (本身的大小+bmp的大小)
|
||||
struct_size = sizeof(struct slab_obj) + PAGE_2M_SIZE / size / 8;
|
||||
// 将slab_obj放置到物理页的末尾
|
||||
slab_obj_ptr = (struct slab_obj *)((unsigned char *)vaddr + PAGE_2M_SIZE - struct_size);
|
||||
slab_obj_ptr->bmp = (void *)slab_obj_ptr + sizeof(struct slab_obj);
|
||||
|
||||
slab_obj_ptr->count_free = (PAGE_2M_SIZE - struct_size) / size;
|
||||
slab_obj_ptr->count_using = 0;
|
||||
slab_obj_ptr->bmp_count = slab_obj_ptr->count_free;
|
||||
slab_obj_ptr->vaddr = vaddr;
|
||||
slab_obj_ptr->page = page;
|
||||
|
||||
list_init(&slab_obj_ptr->list);
|
||||
|
||||
slab_obj_ptr->bmp_len = ((slab_obj_ptr->bmp_count + sizeof(ul) * 8 - 1) >> 6) << 3;
|
||||
|
||||
// @todo:此处可优化,直接把所有位设置为0,然后再对部分不存在对应的内存对象的位设置为1
|
||||
memset(slab_obj_ptr->bmp, 0xff, slab_obj_ptr->bmp_len);
|
||||
|
||||
for (int i = 0; i < slab_obj_ptr->bmp_count; ++i)
|
||||
*(slab_obj_ptr->bmp + (i >> 6)) ^= 1UL << (i % 64);
|
||||
|
||||
break;
|
||||
// ================= 较大的size时,slab_obj和bmp不再放置于当前物理页内部 ============
|
||||
// 因为在这种情况下,bmp很短,继续放置在当前物理页内部则会造成可分配的对象少,加剧了内存空间的浪费
|
||||
case 1024: // 1KB
|
||||
case 2048:
|
||||
case 4096: // 4KB
|
||||
case 8192:
|
||||
case 16384:
|
||||
case 32768:
|
||||
case 65536:
|
||||
case 131072: // 128KB
|
||||
case 262144:
|
||||
case 524288:
|
||||
case 1048576: // 1MB
|
||||
slab_obj_ptr = (struct slab_obj *)kmalloc(sizeof(struct slab_obj), 0);
|
||||
|
||||
slab_obj_ptr->count_free = PAGE_2M_SIZE / size;
|
||||
slab_obj_ptr->count_using = 0;
|
||||
slab_obj_ptr->bmp_count = slab_obj_ptr->count_free;
|
||||
|
||||
slab_obj_ptr->bmp_len = ((slab_obj_ptr->bmp_count + sizeof(ul) * 8 - 1) >> 6) << 3;
|
||||
|
||||
slab_obj_ptr->bmp = (ul *)kmalloc(slab_obj_ptr->bmp_len, 0);
|
||||
|
||||
// @todo:此处可优化,直接把所有位设置为0,然后再对部分不存在对应的内存对象的位设置为1
|
||||
memset(slab_obj_ptr->bmp, 0xff, slab_obj_ptr->bmp_len);
|
||||
for (int i = 0; i < slab_obj_ptr->bmp_count; ++i)
|
||||
*(slab_obj_ptr->bmp + (i >> 6)) ^= 1UL << (i % 64);
|
||||
|
||||
slab_obj_ptr->vaddr = phys_2_virt(page->addr_phys);
|
||||
slab_obj_ptr->page = page;
|
||||
list_init(&slab_obj_ptr->list);
|
||||
break;
|
||||
// size 错误
|
||||
default:
|
||||
kerror("kamlloc_create(): Wrong size%d", size);
|
||||
free_pages(page, 1);
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
return slab_obj_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 通用内存分配函数
|
||||
*
|
||||
* @param size 要分配的内存大小
|
||||
* @param gfp 内存的flag
|
||||
* @return void* 内核内存虚拟地址
|
||||
*/
|
||||
void *kmalloc(unsigned long size, gfp_t gfp)
|
||||
{
|
||||
void *result = NULL;
|
||||
if (size > 1048576)
|
||||
{
|
||||
kwarn("kmalloc(): Can't alloc such memory: %ld bytes, because it is too large.", size);
|
||||
return NULL;
|
||||
}
|
||||
int index;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
if (kmalloc_cache_group[i].size >= size)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 对当前内存池加锁
|
||||
spin_lock(&kmalloc_cache_group[index].lock);
|
||||
|
||||
struct slab_obj *slab_obj_ptr = kmalloc_cache_group[index].cache_pool_entry;
|
||||
|
||||
// 内存池没有可用的内存对象,需要进行扩容
|
||||
if (unlikely(kmalloc_cache_group[index].count_total_free == 0))
|
||||
{
|
||||
// 创建slab_obj
|
||||
slab_obj_ptr = kmalloc_create_slab_obj(kmalloc_cache_group[index].size);
|
||||
|
||||
// BUG
|
||||
if (unlikely(slab_obj_ptr == NULL))
|
||||
{
|
||||
kBUG("kmalloc()->kmalloc_create_slab_obj()=>slab == NULL");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
kmalloc_cache_group[index].count_total_free += slab_obj_ptr->count_free;
|
||||
list_add(&kmalloc_cache_group[index].cache_pool_entry->list, &slab_obj_ptr->list);
|
||||
}
|
||||
else // 内存对象充足
|
||||
{
|
||||
do
|
||||
{
|
||||
// 跳转到下一个内存池对象
|
||||
if (slab_obj_ptr->count_free == 0)
|
||||
slab_obj_ptr = container_of(list_next(&slab_obj_ptr->list), struct slab_obj, list);
|
||||
else
|
||||
break;
|
||||
} while (slab_obj_ptr != kmalloc_cache_group[index].cache_pool_entry);
|
||||
}
|
||||
// 寻找一块可用的内存对象
|
||||
int md;
|
||||
for (int i = 0; i < slab_obj_ptr->bmp_count; ++i)
|
||||
{
|
||||
|
||||
// 当前bmp全部被使用
|
||||
if (*(slab_obj_ptr->bmp + (i >> 6)) == 0xffffffffffffffffUL)
|
||||
{
|
||||
i += 63;
|
||||
continue;
|
||||
}
|
||||
md = i % 64;
|
||||
// 找到相应的内存对象
|
||||
if ((*(slab_obj_ptr->bmp + (i >> 6)) & (1UL << md)) == 0)
|
||||
{
|
||||
*(slab_obj_ptr->bmp + (i >> 6)) |= (1UL << md);
|
||||
++(slab_obj_ptr->count_using);
|
||||
--(slab_obj_ptr->count_free);
|
||||
|
||||
--kmalloc_cache_group[index].count_total_free;
|
||||
++kmalloc_cache_group[index].count_total_using;
|
||||
// 放锁
|
||||
spin_unlock(&kmalloc_cache_group[index].lock);
|
||||
// 返回内存对象
|
||||
result = (void *)((char *)slab_obj_ptr->vaddr + kmalloc_cache_group[index].size * i);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
goto failed;
|
||||
done:;
|
||||
if (gfp & __GFP_ZERO)
|
||||
memset(result, 0, size);
|
||||
return result;
|
||||
failed:;
|
||||
spin_unlock(&kmalloc_cache_group[index].lock);
|
||||
kerror("kmalloc(): Cannot alloc more memory: %d bytes", size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 通用内存释放函数
|
||||
*
|
||||
* @param address 要释放的内存线性地址
|
||||
* @return unsigned long
|
||||
*/
|
||||
unsigned long kfree(void *address)
|
||||
{
|
||||
if (unlikely(address == NULL))
|
||||
return 0;
|
||||
struct slab_obj *slab_obj_ptr = NULL;
|
||||
|
||||
// 将线性地址按照2M物理页对齐, 获得所在物理页的起始线性地址
|
||||
void *page_base_addr = (void *)((ul)address & PAGE_2M_MASK);
|
||||
|
||||
int index;
|
||||
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
slab_obj_ptr = kmalloc_cache_group[i].cache_pool_entry;
|
||||
|
||||
do
|
||||
{
|
||||
// 不属于当前slab_obj的管理范围
|
||||
if (likely(slab_obj_ptr->vaddr != page_base_addr))
|
||||
{
|
||||
slab_obj_ptr = container_of(list_next(&slab_obj_ptr->list), struct slab_obj, list);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 对当前内存池加锁
|
||||
spin_lock(&kmalloc_cache_group[i].lock);
|
||||
// 计算地址属于哪一个内存对象
|
||||
index = (address - slab_obj_ptr->vaddr) / kmalloc_cache_group[i].size;
|
||||
|
||||
// 复位bmp
|
||||
*(slab_obj_ptr->bmp + (index >> 6)) ^= 1UL << (index % 64);
|
||||
|
||||
++(slab_obj_ptr->count_free);
|
||||
--(slab_obj_ptr->count_using);
|
||||
++kmalloc_cache_group[i].count_total_free;
|
||||
--kmalloc_cache_group[i].count_total_using;
|
||||
|
||||
// 回收空闲的slab_obj
|
||||
// 条件:当前slab_obj_ptr的使用为0、总空闲内存对象>=当前slab_obj的总对象的2倍 且当前slab_pool不为起始slab_obj
|
||||
if ((slab_obj_ptr->count_using == 0) && (kmalloc_cache_group[i].count_total_free >= ((slab_obj_ptr->bmp_count) << 1)) && (kmalloc_cache_group[i].cache_pool_entry != slab_obj_ptr))
|
||||
{
|
||||
switch (kmalloc_cache_group[i].size)
|
||||
{
|
||||
case 32:
|
||||
case 64:
|
||||
case 128:
|
||||
case 256:
|
||||
case 512:
|
||||
// 在这种情况下,slab_obj是被安放在page内部的
|
||||
list_del(&slab_obj_ptr->list);
|
||||
|
||||
kmalloc_cache_group[i].count_total_free -= slab_obj_ptr->bmp_count;
|
||||
page_clean(slab_obj_ptr->page);
|
||||
free_pages(slab_obj_ptr->page, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
// 在这种情况下,slab_obj是被安放在额外获取的内存对象中的
|
||||
list_del(&slab_obj_ptr->list);
|
||||
kmalloc_cache_group[i].count_total_free -= slab_obj_ptr->bmp_count;
|
||||
|
||||
kfree(slab_obj_ptr->bmp);
|
||||
|
||||
page_clean(slab_obj_ptr->page);
|
||||
free_pages(slab_obj_ptr->page, 1);
|
||||
|
||||
kfree(slab_obj_ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 放锁
|
||||
spin_unlock(&kmalloc_cache_group[i].lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} while (slab_obj_ptr != kmalloc_cache_group[i].cache_pool_entry);
|
||||
}
|
||||
kBUG("kfree(): Can't free memory. address=%#018lx", address);
|
||||
return ECANNOT_FREE_MEM;
|
||||
}
|
134
kernel/src/mm/slab.h
Normal file
134
kernel/src/mm/slab.h
Normal file
@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
|
||||
#include "mm.h"
|
||||
#include <common/glib.h>
|
||||
#include <common/printk.h>
|
||||
#include <common/kprint.h>
|
||||
#include <common/spinlock.h>
|
||||
|
||||
#define SIZEOF_LONG_ALIGN(size) ((size + sizeof(long) - 1) & ~(sizeof(long) - 1))
|
||||
#define SIZEOF_INT_ALIGN(size) ((size + sizeof(int) - 1) & ~(sizeof(int) - 1))
|
||||
|
||||
// SLAB存储池count_using不为空
|
||||
#define ESLAB_NOTNULL 101
|
||||
#define ENOT_IN_SLAB 102 // 地址不在当前slab内存池中
|
||||
#define ECANNOT_FREE_MEM 103 // 无法释放内存
|
||||
|
||||
struct slab_obj
|
||||
{
|
||||
struct List list;
|
||||
// 当前slab对象所使用的内存页
|
||||
struct Page *page;
|
||||
|
||||
ul count_using;
|
||||
ul count_free;
|
||||
|
||||
// 当前页面所在的线性地址
|
||||
void *vaddr;
|
||||
|
||||
// 位图
|
||||
ul bmp_len; // 位图的长度(字节)
|
||||
ul bmp_count; // 位图的有效位数
|
||||
ul *bmp;
|
||||
};
|
||||
|
||||
// slab内存池
|
||||
struct slab
|
||||
{
|
||||
ul size; // 单位:byte
|
||||
ul count_total_using;
|
||||
ul count_total_free;
|
||||
// 内存池对象
|
||||
struct slab_obj *cache_pool_entry;
|
||||
// dma内存池对象
|
||||
struct slab_obj *cache_dma_pool_entry;
|
||||
|
||||
spinlock_t lock; // 当前内存池的操作锁
|
||||
|
||||
// 内存池的构造函数和析构函数
|
||||
void *(*constructor)(void *vaddr, ul arg);
|
||||
void *(*destructor)(void *vaddr, ul arg);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 通用内存分配函数
|
||||
*
|
||||
* @param size 要分配的内存大小
|
||||
* @param gfp 内存的flag
|
||||
* @return void* 分配得到的内存的指针
|
||||
*/
|
||||
void *kmalloc(unsigned long size, gfp_t gfp);
|
||||
|
||||
/**
|
||||
* @brief 从kmalloc申请一块内存,并将这块内存清空
|
||||
*
|
||||
* @param size 要分配的内存大小
|
||||
* @param gfp 内存的flag
|
||||
* @return void* 分配得到的内存的指针
|
||||
*/
|
||||
static __always_inline void *kzalloc(size_t size, gfp_t gfp)
|
||||
{
|
||||
return kmalloc(size, gfp | __GFP_ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 通用内存释放函数
|
||||
*
|
||||
* @param address 要释放的内存地址
|
||||
* @return unsigned long
|
||||
*/
|
||||
unsigned long kfree(void *address);
|
||||
|
||||
/**
|
||||
* @brief 创建一个内存池
|
||||
*
|
||||
* @param size 内存池容量大小
|
||||
* @param constructor 构造函数
|
||||
* @param destructor 析构函数
|
||||
* @param arg 参数
|
||||
* @return struct slab* 构建好的内存池对象
|
||||
*/
|
||||
struct slab *slab_create(ul size, void *(*constructor)(void *vaddr, ul arg), void *(*destructor)(void *vaddr, ul arg), ul arg);
|
||||
|
||||
/**
|
||||
* @brief 销毁内存池对象
|
||||
* 只有当slab对象是空的时候才能销毁
|
||||
* @param slab_pool 要销毁的内存池对象
|
||||
* @return ul
|
||||
*
|
||||
*/
|
||||
ul slab_destroy(struct slab *slab_pool);
|
||||
|
||||
/**
|
||||
* @brief 分配SLAB内存池中的内存对象
|
||||
*
|
||||
* @param slab_pool slab内存池
|
||||
* @param arg 传递给内存对象构造函数的参数
|
||||
* @return void* 内存空间的虚拟地址
|
||||
*/
|
||||
void *slab_malloc(struct slab *slab_pool, ul arg);
|
||||
|
||||
/**
|
||||
* @brief 回收slab内存池中的对象
|
||||
*
|
||||
* @param slab_pool 对应的内存池
|
||||
* @param addr 内存对象的虚拟地址
|
||||
* @param arg 传递给虚构函数的参数
|
||||
* @return ul
|
||||
*/
|
||||
ul slab_free(struct slab *slab_pool, void *addr, ul arg);
|
||||
|
||||
/**
|
||||
* @brief 在kmalloc中创建slab_obj的函数(与slab_malloc()类似)
|
||||
*
|
||||
* @param size
|
||||
* @return struct slab_obj* 创建好的slab_obj
|
||||
*/
|
||||
struct slab_obj *kmalloc_create_slab_obj(ul size);
|
||||
|
||||
/**
|
||||
* @brief 初始化内存池组
|
||||
* 在初始化通用内存管理单元期间,尚无内存空间分配函数,需要我们手动为SLAB内存池指定存储空间
|
||||
* @return ul
|
||||
*/
|
||||
ul slab_init();
|
109
kernel/src/mm/utils.c
Normal file
109
kernel/src/mm/utils.c
Normal file
@ -0,0 +1,109 @@
|
||||
#include "internal.h"
|
||||
|
||||
extern uint64_t mm_total_2M_pages;
|
||||
|
||||
/**
|
||||
* @brief 获取指定虚拟地址处映射的物理地址
|
||||
*
|
||||
* @param mm 内存空间分布结构体
|
||||
* @param vaddr 虚拟地址
|
||||
* @return uint64_t 已映射的物理地址
|
||||
*/
|
||||
uint64_t __mm_get_paddr(struct mm_struct *mm, uint64_t vaddr)
|
||||
{
|
||||
ul *tmp;
|
||||
|
||||
tmp = phys_2_virt((ul *)(((ul)mm->pgd) & (~0xfffUL)) + ((vaddr >> PAGE_GDT_SHIFT) & 0x1ff));
|
||||
|
||||
// pml4页表项为0
|
||||
if (*tmp == 0)
|
||||
return 0;
|
||||
|
||||
tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((vaddr >> PAGE_1G_SHIFT) & 0x1ff));
|
||||
|
||||
// pdpt页表项为0
|
||||
if (*tmp == 0)
|
||||
return 0;
|
||||
|
||||
// 读取pdt页表项
|
||||
tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(vaddr) >> PAGE_2M_SHIFT) & 0x1ff)));
|
||||
|
||||
// pde页表项为0
|
||||
if (*tmp == 0)
|
||||
return 0;
|
||||
|
||||
if (*tmp & (1 << 7))
|
||||
{
|
||||
// 当前为2M物理页
|
||||
return (*tmp) & (~0x1fffUL);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 存在4级页表
|
||||
tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(vaddr) >> PAGE_4K_SHIFT) & 0x1ff)));
|
||||
|
||||
return (*tmp) & (~0x1ffUL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检测指定地址是否已经被映射
|
||||
*
|
||||
* @param page_table_phys_addr 页表的物理地址
|
||||
* @param virt_addr 要检测的地址
|
||||
* @return true 已经被映射
|
||||
* @return false
|
||||
*/
|
||||
bool mm_check_mapped(ul page_table_phys_addr, uint64_t virt_addr)
|
||||
{
|
||||
ul *tmp;
|
||||
|
||||
tmp = phys_2_virt((ul *)((ul)page_table_phys_addr & (~0xfffUL)) + ((virt_addr >> PAGE_GDT_SHIFT) & 0x1ff));
|
||||
|
||||
// pml4页表项为0
|
||||
if (*tmp == 0)
|
||||
return 0;
|
||||
|
||||
tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((virt_addr >> PAGE_1G_SHIFT) & 0x1ff));
|
||||
|
||||
// pdpt页表项为0
|
||||
if (*tmp == 0)
|
||||
return 0;
|
||||
|
||||
// 读取pdt页表项
|
||||
tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr) >> PAGE_2M_SHIFT) & 0x1ff)));
|
||||
|
||||
// pde页表项为0
|
||||
if (*tmp == 0)
|
||||
return 0;
|
||||
|
||||
if (*tmp & (1 << 7))
|
||||
{
|
||||
// 当前为2M物理页
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 存在4级页表
|
||||
tmp = phys_2_virt(((ul *)(*tmp & (~0xfffUL)) + (((ul)(virt_addr) >> PAGE_4K_SHIFT) & 0x1ff)));
|
||||
if (*tmp != 0)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检测是否为有效的2M页(物理内存页)
|
||||
*
|
||||
* @param paddr 物理地址
|
||||
* @return int8_t 是 -> 1
|
||||
* 否 -> 0
|
||||
*/
|
||||
int8_t mm_is_2M_page(uint64_t paddr)
|
||||
{
|
||||
if (likely((paddr >> PAGE_2M_SHIFT) < mm_total_2M_pages))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
275
kernel/src/mm/vma.c
Normal file
275
kernel/src/mm/vma.c
Normal file
@ -0,0 +1,275 @@
|
||||
#include "mm.h"
|
||||
#include "slab.h"
|
||||
#include "internal.h"
|
||||
|
||||
/**
|
||||
* @brief 获取一块新的vma结构体,并将其与指定的mm进行绑定
|
||||
*
|
||||
* @param mm 与VMA绑定的内存空间分布结构体
|
||||
* @return struct vm_area_struct* 新的VMA
|
||||
*/
|
||||
struct vm_area_struct *vm_area_alloc(struct mm_struct *mm)
|
||||
{
|
||||
struct vm_area_struct *vma = (struct vm_area_struct *)kmalloc(sizeof(struct vm_area_struct), 0);
|
||||
if (vma)
|
||||
vma_init(vma, mm);
|
||||
return vma;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从链表中删除指定的vma结构体
|
||||
*
|
||||
* @param vma
|
||||
*/
|
||||
void vm_area_del(struct vm_area_struct *vma)
|
||||
{
|
||||
if (vma->vm_mm == NULL)
|
||||
return;
|
||||
__vma_unlink_list(vma->vm_mm, vma);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放vma结构体
|
||||
*
|
||||
* @param vma 待释放的vma结构体
|
||||
*/
|
||||
void vm_area_free(struct vm_area_struct *vma)
|
||||
{
|
||||
if (vma->vm_prev == NULL && vma->vm_next == NULL) // 如果当前是剩余的最后一个vma
|
||||
vma->vm_mm->vmas = NULL;
|
||||
kfree(vma);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将vma结构体插入mm_struct的链表之中
|
||||
*
|
||||
* @param mm 内存空间分布结构体
|
||||
* @param vma 待插入的VMA结构体
|
||||
* @param prev 链表的前一个结点
|
||||
*/
|
||||
void __vma_link_list(struct mm_struct *mm, struct vm_area_struct *vma, struct vm_area_struct *prev)
|
||||
{
|
||||
struct vm_area_struct *next = NULL;
|
||||
vma->vm_prev = prev;
|
||||
if (prev) // 若指定了前一个结点,则直接连接
|
||||
{
|
||||
next = prev->vm_next;
|
||||
prev->vm_next = vma;
|
||||
}
|
||||
else // 否则将vma直接插入到给定的mm的vma链表之中
|
||||
{
|
||||
next = mm->vmas;
|
||||
mm->vmas = vma;
|
||||
}
|
||||
|
||||
vma->vm_next = next;
|
||||
|
||||
if (next != NULL)
|
||||
next->vm_prev = vma;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将vma给定结构体从vma链表的结点之中删除
|
||||
*
|
||||
* @param mm 内存空间分布结构体
|
||||
* @param vma 待插入的VMA结构体
|
||||
*/
|
||||
void __vma_unlink_list(struct mm_struct *mm, struct vm_area_struct *vma)
|
||||
{
|
||||
struct vm_area_struct *prev, *next;
|
||||
next = vma->vm_next;
|
||||
prev = vma->vm_prev;
|
||||
if (prev)
|
||||
prev->vm_next = next;
|
||||
else // 当前vma是链表中的第一个vma
|
||||
mm->vmas = next;
|
||||
|
||||
if (next)
|
||||
next->vm_prev = prev;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查找第一个符合“addr < vm_end”条件的vma
|
||||
*
|
||||
* @param mm 内存空间分布结构体
|
||||
* @param addr 虚拟地址
|
||||
* @return struct vm_area_struct* 符合条件的vma
|
||||
*/
|
||||
struct vm_area_struct *vma_find(struct mm_struct *mm, uint64_t addr)
|
||||
{
|
||||
struct vm_area_struct *vma = mm->vmas;
|
||||
struct vm_area_struct *result = NULL;
|
||||
while (vma != NULL)
|
||||
{
|
||||
if (vma->vm_end > addr)
|
||||
{
|
||||
result = vma;
|
||||
break;
|
||||
}
|
||||
vma = vma->vm_next;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 插入vma
|
||||
*
|
||||
* @param mm
|
||||
* @param vma
|
||||
* @return int
|
||||
*/
|
||||
int vma_insert(struct mm_struct *mm, struct vm_area_struct *vma)
|
||||
{
|
||||
|
||||
struct vm_area_struct *prev;
|
||||
|
||||
prev = vma_find(mm, vma->vm_start);
|
||||
|
||||
if (prev && prev->vm_start <= vma->vm_start && prev->vm_end >= vma->vm_end)
|
||||
{
|
||||
// 已经存在了相同的vma
|
||||
return -EEXIST;
|
||||
}
|
||||
// todo: bugfix: 这里的第二种情况貌似从来不会满足
|
||||
else if (prev && ((vma->vm_start >= prev->vm_start && vma->vm_start <= prev->vm_end) || (prev->vm_start <= vma->vm_end && prev->vm_start >= vma->vm_start)))
|
||||
{
|
||||
//部分重叠
|
||||
if ((!CROSS_2M_BOUND(vma->vm_start, prev->vm_start)) && (!CROSS_2M_BOUND(vma->vm_end, prev->vm_end))&& vma->vm_end)
|
||||
{
|
||||
//合并vma 并改变链表vma的范围
|
||||
kdebug("before combining vma:vm_start = %#018lx, vm_end = %#018lx\n", vma->vm_start, vma->vm_end);
|
||||
|
||||
prev->vm_start = (vma->vm_start < prev->vm_start )? vma->vm_start : prev->vm_start;
|
||||
prev->vm_end = (vma->vm_end > prev->vm_end) ? vma->vm_end : prev->vm_end;
|
||||
// 计算page_offset
|
||||
prev->page_offset = prev->vm_start - (prev->vm_start & PAGE_2M_MASK);
|
||||
kdebug("combined vma:vm_start = %#018lx, vm_end = %#018lx\nprev:vm_start = %018lx, vm_end = %018lx\n", vma->vm_start, vma->vm_end, prev->vm_start, prev->vm_end);
|
||||
kinfo("vma has same part\n");
|
||||
return __VMA_MERGED;
|
||||
}
|
||||
}
|
||||
|
||||
// prev = vma_find(mm, vma->vm_start);
|
||||
|
||||
if (prev == NULL) // 要将当前vma插入到链表的尾部
|
||||
{
|
||||
struct vm_area_struct *ptr = mm->vmas;
|
||||
while (ptr)
|
||||
{
|
||||
if (ptr->vm_next)
|
||||
ptr = ptr->vm_next;
|
||||
else
|
||||
{
|
||||
prev = ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
prev = prev->vm_prev;
|
||||
__vma_link_list(mm, vma, prev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建anon_vma,并将其与页面结构体进行绑定
|
||||
* 若提供的页面结构体指针为NULL,则只创建,不绑定
|
||||
*
|
||||
* @param page 页面结构体的指针
|
||||
* @param lock_page 是否将页面结构体加锁
|
||||
* @return struct anon_vma_t* 创建好的anon_vma
|
||||
*/
|
||||
struct anon_vma_t *__anon_vma_create_alloc(struct Page *page, bool lock_page)
|
||||
{
|
||||
struct anon_vma_t *anon_vma = (struct anon_vma_t *)kmalloc(sizeof(struct anon_vma_t), 0);
|
||||
if (unlikely(anon_vma == NULL))
|
||||
return NULL;
|
||||
memset(anon_vma, 0, sizeof(struct anon_vma_t));
|
||||
|
||||
list_init(&anon_vma->vma_list);
|
||||
semaphore_init(&anon_vma->sem, 1);
|
||||
|
||||
// 需要和page进行绑定
|
||||
if (page != NULL)
|
||||
{
|
||||
if (lock_page == true) // 需要加锁
|
||||
{
|
||||
uint64_t rflags;
|
||||
spin_lock(&page->op_lock);
|
||||
page->anon_vma = anon_vma;
|
||||
spin_unlock(&page->op_lock);
|
||||
}
|
||||
else
|
||||
page->anon_vma = anon_vma;
|
||||
|
||||
anon_vma->page = page;
|
||||
}
|
||||
return anon_vma;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 将指定的vma加入到anon_vma的管理范围之中
|
||||
*
|
||||
* @param anon_vma 页面的anon_vma
|
||||
* @param vma 待加入的vma
|
||||
* @return int 返回码
|
||||
*/
|
||||
int __anon_vma_add(struct anon_vma_t *anon_vma, struct vm_area_struct *vma)
|
||||
{
|
||||
semaphore_down(&anon_vma->sem);
|
||||
list_add(&anon_vma->vma_list, &vma->anon_vma_list);
|
||||
vma->anon_vma = anon_vma;
|
||||
atomic_inc(&anon_vma->ref_count);
|
||||
semaphore_up(&anon_vma->sem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放anon vma结构体
|
||||
*
|
||||
* @param anon_vma 待释放的anon_vma结构体
|
||||
* @return int 返回码
|
||||
*/
|
||||
int __anon_vma_free(struct anon_vma_t *anon_vma)
|
||||
{
|
||||
if (anon_vma->page != NULL)
|
||||
{
|
||||
spin_lock(&anon_vma->page->op_lock);
|
||||
anon_vma->page->anon_vma = NULL;
|
||||
spin_unlock(&anon_vma->page->op_lock);
|
||||
}
|
||||
kfree(anon_vma);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从anon_vma的管理范围中删除指定的vma
|
||||
* (在进入这个函数之前,应该要对anon_vma加锁)
|
||||
* @param vma 将要取消对应的anon_vma管理的vma结构体
|
||||
* @return int 返回码
|
||||
*/
|
||||
int __anon_vma_del(struct vm_area_struct *vma)
|
||||
{
|
||||
// 当前vma没有绑定anon_vma
|
||||
if (vma->anon_vma == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
list_del(&vma->anon_vma_list);
|
||||
atomic_dec(&vma->anon_vma->ref_count);
|
||||
|
||||
// 若当前anon_vma的引用计数归零,则意味着可以释放内存页
|
||||
if (unlikely(atomic_read(&vma->anon_vma->ref_count) == 0)) // 应当释放该anon_vma
|
||||
{
|
||||
// 若页面结构体是mmio创建的,则释放页面结构体
|
||||
if (vma->anon_vma->page->attr & PAGE_DEVICE)
|
||||
kfree(vma->anon_vma->page);
|
||||
else
|
||||
free_pages(vma->anon_vma->page, 1);
|
||||
__anon_vma_free(vma->anon_vma);
|
||||
}
|
||||
|
||||
// 清理当前vma的关联数据
|
||||
vma->anon_vma = NULL;
|
||||
list_init(&vma->anon_vma_list);
|
||||
}
|
Reference in New Issue
Block a user