添加地址空间以及创建内核地址空间的方法

ch4
zhangxinyu 2 years ago
parent deec9b74af
commit bb03c2e3e2

@ -9,4 +9,7 @@ pub const MEMORY_END: usize = 0x80800000; // 设置我们当前操作系统最
pub const USER_STACK_SIZE: usize = 4096 * 2; // 每个应用用户态的栈大小为8kb
pub const KERNEL_STACK_SIZE: usize = 4096 * 2; // 每个应用的内核栈
pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1; // __alltraps对齐到了这里, (内核空间和用户空间这里虚拟地址都用这个)虚拟地址最高页 存放跳板, 跳板那一页, 被映射到 strampoline段(trap) 实际的物理地址, 即陷入时 cpu需要跳转的地址
pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE; // 用户 trap context开始的位置 在次高页
pub use crate::board::*;

@ -40,12 +40,16 @@ impl From<usize> for PhysPageNum {
// 把物理地址转为 usize, 直接返回即可
impl From<PhysAddr> for usize {
fn from(v: PhysAddr) -> Self { v.0 }
fn from(v: PhysAddr) -> Self {
v.0
}
}
// 把物理页结构体转为 usize类型的物理页 直接返回即可
impl From<PhysPageNum> for usize {
fn from(v: PhysPageNum) -> Self { v.0 }
fn from(v: PhysPageNum) -> Self {
v.0
}
}
// 把PhysAddr 转为 PhysPageNum
@ -67,12 +71,12 @@ impl PhysAddr {
self.0 & (PAGE_SIZE - 1)
}
// 舍去小数位, 把物理地址, 转为 物理页号
// 舍去小数位, 把物理地址, 转为 物理页号 向下取整
pub fn floor(&self) -> PhysPageNum {
PhysPageNum(self.0 / PAGE_SIZE)
}
// 有小数统一 +1, 物理地址转为物理页号
// 有小数统一 +1, 物理地址转为物理页号 向上取整
pub fn ceil(&self) -> PhysPageNum {
PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE)
}
@ -101,6 +105,23 @@ impl PhysPageNum {
}
}
impl VirtAddr{
// 舍去小数位, 把虚拟地址转为物理页号 向下取整
pub fn floor(&self) -> VirtPageNum {
VirtPageNum(self.0 / PAGE_SIZE)
}
// 有小数统一+1 虚拟地址转为虚拟页号 向上取整
pub fn ceil(&self) -> VirtPageNum {
VirtPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE)
}
// 虚拟地址得到业内偏移 只保留12个bit即可
pub fn page_offset(&self) -> usize {
self.0 & (PAGE_SIZE - 1)
}
}
// 把usize 转为 虚拟地址, 只保留39位
impl From<usize> for VirtAddr {
@ -128,6 +149,22 @@ impl From<usize> for VirtPageNum {
}
}
// 虚拟地址 转为虚拟页号 向下取整
impl From<VirtAddr> for VirtPageNum {
fn from(v: VirtAddr) -> Self {
assert_eq!(v.page_offset(), 0);
v.floor()
}
}
// 虚拟页号 转为虚拟地址 左移即可
impl From<VirtPageNum> for VirtAddr {
fn from(v: VirtPageNum) -> Self {
// 左移12位
Self(v.0 << PAGE_SIZE_BITS)
}
}
impl VirtPageNum{
// 从虚拟页号中 抽离 9 9 9 的布局
@ -140,4 +177,26 @@ impl VirtPageNum{
}
idx
}
}
// 代表一个range类型, 表示一段 虚拟地址 的区间
#[derive(Copy, Clone)]
pub struct VPNRange {
pub l: VirtPageNum,
pub r: VirtPageNum
}
impl Iterator for VPNRange {
type Item = VirtPageNum;
fn next(&mut self) -> Option<Self::Item> {
if self.l == self.r {
None
} else {
let current_vpn = self.l;
self.l = (self.l.0 + 1).into();
Some(current_vpn)
}
}
}

@ -0,0 +1,261 @@
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use bitflags::bitflags;
use crate::config::{MEMORY_END, PAGE_SIZE, TRAMPOLINE};
use crate::mm::address::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum, VPNRange};
use crate::mm::frame_allocator::{frame_alloc, FrameTracker};
use crate::mm::page_table::{PageTable, PTEFlags};
use crate::println;
// 引入外部符号进来, 下面映射地址空间的逻辑段会用到
extern "C" {
fn stext();
fn etext();
fn srodata();
fn erodata();
fn sdata();
fn edata();
fn sbss_with_stack();
fn ebss();
fn ekernel();
fn strampoline(); // 这个符号 在linker.ld 中定义
}
// 某逻辑段的映射方式
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum MapType {
Identical, // 恒等映射
Framed, // 逻辑映射
}
// 这个段的权限, 它是页表项标志位 PTEFlags 的一个子集, 仅仅保留4个标志位就足够了
bitflags! {
/// map permission corresponding to that in pte: `R W X U`
pub struct MapPermission: u8 {
const R = 1 << 1;
const W = 1 << 2;
const X = 1 << 3;
const U = 1 << 4;
}
}
// 代表一个逻辑段
pub struct MapArea {
vpn_range: VPNRange, // 逻辑段的虚拟页起始位置 和 结束位置
data_frames: BTreeMap<VirtPageNum, FrameTracker>, // 这个逻辑段 虚拟页 对应的具体的物理帧, 这里使用rust 进行管理其物理帧的释放, 自动drop进行还给物理页帧管理器
map_type: MapType,
map_perm: MapPermission,
}
impl MapArea {
// 从虚拟地址起始和结束, 创建一个逻辑段结构体
pub fn from(start_va: VirtAddr, end_va: VirtAddr, map_type: MapType, map_perm: MapPermission,) -> Self {
// 截取抛弃内存碎片
let start_vpn: VirtPageNum = start_va.floor();
let end_vpn: VirtPageNum = end_va.ceil();
Self {
vpn_range: VPNRange {l:start_vpn, r: end_vpn},
data_frames: BTreeMap::new(),
map_type,
map_perm,
}
}
}
impl MapArea {
// 把一个虚拟页号 创建一个对应的物理页帧 映射到page_table中, 并把创建的物理页帧生命周期 绑定到 self上
pub fn map_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) {
let ppn: PhysPageNum;
match self.map_type {
// 恒等映射
MapType::Identical => {
ppn = PhysPageNum(vpn.0);
}
MapType::Framed => {
// 随便创建一个 物理页帧
let frame = frame_alloc().unwrap();
ppn = frame.ppn;
// 把随便创建的物理页帧, 绑定到data_frames 中被 self管理生命周期
self.data_frames.insert(vpn, frame);
}
}
// 根据上面创建的物理页帧映射pte 到三级页表中
let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap();
page_table.map(vpn, ppn, pte_flags);
}
// 取消映射一个vpn, 并把 self管理的 物理页帧的生命周期结束
pub fn unmap_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) {
if self.map_type == MapType::Framed {
// 删除映射关系, 自动调用 drop 把vpn对应的ppn还给全局内存管理器
self.data_frames.remove(&vpn);
}
// 物理帧不存在了 把vpn 对应的pte设置为空
page_table.unmap(vpn);
}
// 映射 当前逻辑段中的每一个虚拟页, 到页表中
pub fn map(&mut self, page_table: &mut PageTable) {
for vpn in self.vpn_range {
self.map_one(page_table, vpn);
}
}
// 在页表中取消映射每一个虚拟页
pub fn unmap(&mut self, page_table: &mut PageTable) {
for vpn in self.vpn_range {
self.unmap_one(page_table, vpn);
}
}
// 拷贝外部的数据二进制 到当前的逻辑段中 按照页 为单位
pub fn copy_data(&mut self, page_table: &mut PageTable, data: &[u8]) {
assert_eq!(self.map_type, MapType::Framed);
let mut start: usize = 0;
// 当前逻辑段的起始页位置
let mut current_vpn = self.vpn_range.l;
// 需要拷贝的字节总大小
let len = data.len();
loop {
// 拷贝 每次最大拷贝1个页
let src = &data[start..len.min(start + PAGE_SIZE)];
// 得到逻辑段每个页的起始位置
let dst = &mut page_table
.get_pte(current_vpn) // 在页表中, 根据逻辑段中的vpn 得到 54bit的pte (44+10)
.unwrap()
.ppn() // 根据54bit的pte, 得到44bit 的物理页帧的位置
.get_bytes_array()[..src.len()]; // 得到物理页帧那一页的起始的地址 44bit<<12
dst.copy_from_slice(src);
start += PAGE_SIZE;
if start >= len {
break;
}
current_vpn = (current_vpn.0 + 1).into();
}
}
}
// 表示一个地址空间, 它是一个页表和 包含所有段信息的页表组成
pub struct MemorySet {
page_table: PageTable,
areas: Vec<MapArea>,
}
impl MemorySet {
pub fn new() -> Self {
Self {
page_table: PageTable::new(),
areas: Vec::new(),
}
}
}
impl MemorySet {
// 得到当前地址空间的 页表token
pub fn token(&self) -> usize {
self.page_table.token()
}
// 根据 start va 和end va 创建一个 逻辑段类型为逻辑映射, 插入到 当前地址空间
pub fn insert_framed_area(&mut self, start_va: VirtAddr, end_va: VirtAddr, permission: MapPermission) {
self.push(MapArea::from(start_va, end_va, MapType::Framed, permission), None);
}
// 把一个 逻辑段, 映射到当前页表中
fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) {
map_area.map(&mut self.page_table);
if let Some(data) = data {
map_area.copy_data(&mut self.page_table, data);
}
self.areas.push(map_area);
}
/// 创建跳板, 跳板就是 trap.S中的代码段中.text.trampoline, 由 linker.ld手动布局在代码段
/// 内核空间和用户空间,他们都会运行这个函数, 插入同样的代码, 把虚拟地址映射为同样的物理地址, 这里内核空间和用户空间执行这段代码的时候, 每个 虚拟地址对应的物理地址是相同的
/// 所以 (内核空间的 TRAMPOLINE+指令offset) == (用户空间的 TRAMPOLINE+offset) == (strampoline + offset)
/// (内核的 TRAMPOLINE) 和 (用户的 TRAMPOLINE) 和 strampoline 他们都是相等的
/// 在 trap.S 切换用户空间之后, 取值执行, 切换空间之前和切换空间之后, 他们对应的虚拟地址空间是相同的, 且都映射到了同一个物理页帧
fn map_trampoline(&mut self) {
// 直接插入到 把 linker.ld 中的符号strampoline, 映射到最高的虚拟页, 他的物理页帧是 链接脚本中我们手动内存布局的符号
// 如果是用户应用, 这个符号的最下面就是 次高页 即 trap context
self.page_table.map(
VirtAddr::from(TRAMPOLINE).into(),
PhysAddr::from(strampoline as usize).into(),
PTEFlags::R | PTEFlags::X,
);
}
// 创建一个内核用的地址空间
pub fn new_kernel() -> Self {
let mut memory_set = Self::new();
// 跳板创建
memory_set.map_trampoline();
// map kernel sections
println!(".text [{:#x}, {:#x})", stext as usize, etext as usize);
println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize);
println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize);
println!(
".bss [{:#x}, {:#x})",
sbss_with_stack as usize, ebss as usize
);
println!("mapping .text section");
memory_set.push(
MapArea::from(
(stext as usize).into(),
(etext as usize).into(),
MapType::Identical,
MapPermission::R | MapPermission::X,
),
None,
);
println!("mapping .rodata section");
memory_set.push(
MapArea::from(
(srodata as usize).into(),
(erodata as usize).into(),
MapType::Identical,
MapPermission::R,
),
None,
);
println!("mapping .data section");
memory_set.push(
MapArea::from(
(sdata as usize).into(),
(edata as usize).into(),
MapType::Identical,
MapPermission::R | MapPermission::W,
),
None,
);
println!("mapping .bss section");
memory_set.push(
MapArea::from(
(sbss_with_stack as usize).into(),
(ebss as usize).into(),
MapType::Identical,
MapPermission::R | MapPermission::W,
),
None,
);
println!("mapping physical memory");
memory_set.push(
MapArea::from(
(ekernel as usize).into(),
MEMORY_END.into(),
MapType::Identical,
MapPermission::R | MapPermission::W,
),
None,
);
memory_set
}
}

@ -2,6 +2,7 @@ pub mod heap_allocator;
pub mod address;
pub mod frame_allocator;
pub mod page_table;
pub mod memory_set;

Loading…
Cancel
Save