SimpleKernel 1.17.0
Loading...
Searching...
No Matches
VirtualMemory Class Reference

虚拟内存管理器(具体类,非抽象基类) More...

#include <virtual_memory.hpp>

Collaboration diagram for VirtualMemory:
Collaboration graph

Public Member Functions

auto InitCurrentCore () const -> void
 初始化当前核心的页表
 
auto MapMMIO (uint64_t phys_addr, size_t size, uint32_t flags=cpu_io::virtual_memory::GetKernelPagePermissions()) -> Expected< void * >
 映射设备内存 (MMIO)
 
auto MapPage (void *page_dir, void *virtual_addr, void *physical_addr, uint32_t flags) -> Expected< void >
 映射单个页面
 
auto UnmapPage (void *page_dir, void *virtual_addr) -> Expected< void >
 取消映射单个页面
 
auto GetMapping (void *page_dir, void *virtual_addr) -> Expected< void * >
 获取虚拟地址对应的物理地址映射
 
auto DestroyPageDirectory (void *page_dir, bool free_pages=false) -> void
 回收页表,释放所有映射和子页表
 
auto ClonePageDirectory (void *src_page_dir, bool copy_mappings=true) -> Expected< void * >
 复制页表
 
构造/析构函数
 VirtualMemory ()
 构造函数
 
 VirtualMemory (const VirtualMemory &)=delete
 
 VirtualMemory (VirtualMemory &&)=default
 
auto operator= (const VirtualMemory &) -> VirtualMemory &=delete
 
auto operator= (VirtualMemory &&) -> VirtualMemory &=default
 
 ~VirtualMemory ()=default
 

Private Member Functions

auto RecursiveFreePageTable (uint64_t *table, size_t level, bool free_pages) -> void
 递归释放页表
 
auto RecursiveClonePageTable (uint64_t *src_table, uint64_t *dst_table, size_t level, bool copy_mappings) -> Expected< void >
 递归复制页表
 
auto FindPageTableEntry (void *page_dir, void *virtual_addr, bool allocate=false) -> Expected< uint64_t * >
 在页表中查找虚拟地址对应的页表项
 

Private Attributes

void * kernel_page_dir_ {nullptr}
 

Static Private Attributes

static constexpr size_t kEntriesPerTable
 

Detailed Description

虚拟内存管理器(具体类,非抽象基类)

本类是内核虚拟内存子系统的唯一实现,通过 cpu_io::virtual_memory 命名空间中的架构相关辅助函数 实现跨架构(x86_64/riscv64/aarch64)的页表操作。 架构差异由 cpu_io 库在编译期分发,本类本身不使用虚函数 或条件编译。

物理页帧的分配/释放由外部分配器(bmalloc)提供, 本类通过 aligned_alloc/aligned_free 间接调用。

Note
地址操作、页表项操作等实用函数请使用 cpu_io 库提供的接口
以 VirtualMemorySingleton 全局单例方式使用

Definition at line 30 of file virtual_memory.hpp.

Constructor & Destructor Documentation

◆ VirtualMemory() [1/3]

VirtualMemory::VirtualMemory ( )

构造函数

Postcondition
内核页表目录已分配并清零
全部物理内存已映射

Definition at line 21 of file virtual_memory.cpp.

21 {
22 // 分配根页表目录
25 assert(kernel_page_dir_ != nullptr &&
26 "Failed to allocate kernel page directory");
27
28 // 清零页表目录
30
31 // 获取内核基本信息
32 const auto& basic_info = BasicInfoSingleton::instance();
33
34 // 映射全部物理内存
35 MapMMIO(basic_info.physical_memory_addr, basic_info.physical_memory_size)
36 .or_else([](auto&& err) -> Expected<void*> {
37 klog::Err("Failed to map kernel memory: {}", err.message());
38 while (true) {
40 }
41 return {};
42 });
43
44 klog::Info("Kernel memory mapped from {:#X} to {:#X}",
45 basic_info.physical_memory_addr,
46 basic_info.physical_memory_addr + basic_info.physical_memory_size);
47}
auto MapMMIO(uint64_t phys_addr, size_t size, uint32_t flags=cpu_io::virtual_memory::GetKernelPagePermissions()) -> Expected< void * >
映射设备内存 (MMIO)
std::expected< T, Error > Expected
std::expected 别名模板
Definition expected.hpp:365
static constexpr size_t kPageSize
Definition cpu_io.h:63
void Pause()
Definition cpu_io.h:20
auto Err(etl::format_string< Args... > fmt, Args &&... args) -> void
以 ERROR 级别记录日志
auto Info(etl::format_string< Args... > fmt, Args &&... args) -> void
以 INFO 级别记录日志
void * aligned_alloc(size_t alignment, size_t size)
Definition memory.cpp:58
Here is the call graph for this function:

◆ VirtualMemory() [2/3]

VirtualMemory::VirtualMemory ( const VirtualMemory )
delete

◆ VirtualMemory() [3/3]

VirtualMemory::VirtualMemory ( VirtualMemory &&  )
default

◆ ~VirtualMemory()

VirtualMemory::~VirtualMemory ( )
default

Member Function Documentation

◆ ClonePageDirectory()

auto VirtualMemory::ClonePageDirectory ( void *  src_page_dir,
bool  copy_mappings = true 
) -> Expected<void*>

复制页表

Parameters
src_page_dir源页表目录
copy_mappings是否复制映射(true:复制映射,false:仅复制页表结构)
Returns
Expected<void*> 新页表目录,失败时返回错误
Note
如果 copy_mappings 为 true,会复制所有的页表项; 如果为 false,只复制页表结构,不复制最后一级的映射

Definition at line 178 of file virtual_memory.cpp.

179 {
180 assert(src_page_dir != nullptr &&
181 "ClonePageDirectory: source page directory is nullptr");
182
183 // 创建新的页表目录
186 if (dst_page_dir == nullptr) {
187 return std::unexpected(Error(ErrorCode::kVmAllocationFailed));
188 }
189
190 // 清零新页表
191 std::memset(dst_page_dir, 0, cpu_io::virtual_memory::kPageSize);
192
193 // 递归复制页表
194 auto result = RecursiveClonePageTable(
195 reinterpret_cast<uint64_t*>(src_page_dir),
196 reinterpret_cast<uint64_t*>(dst_page_dir),
198 if (!result.has_value()) {
199 // 复制失败,清理已分配的页表
200 DestroyPageDirectory(dst_page_dir, false);
201 return std::unexpected(result.error());
202 }
203
204 klog::Debug("Cloned page directory from {:#x} to {:#x}",
205 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(src_page_dir)),
206 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(dst_page_dir)));
207 return dst_page_dir;
208}
auto DestroyPageDirectory(void *page_dir, bool free_pages=false) -> void
回收页表,释放所有映射和子页表
auto RecursiveClonePageTable(uint64_t *src_table, uint64_t *dst_table, size_t level, bool copy_mappings) -> Expected< void >
递归复制页表
@ kVmAllocationFailed
static constexpr size_t kPageTableLevels
Definition cpu_io.h:68
auto Debug(etl::format_string< Args... > fmt, Args &&... args) -> void
以 DEBUG 级别记录日志(SIMPLEKERNEL_MIN_LOG_LEVEL > 0 时编译期消除)
错误类型,用于 std::expected
Definition expected.hpp:343
Here is the call graph for this function:

◆ DestroyPageDirectory()

auto VirtualMemory::DestroyPageDirectory ( void *  page_dir,
bool  free_pages = false 
) -> void

回收页表,释放所有映射和子页表

Parameters
page_dir要回收的页表目录
free_pages是否同时释放映射的物理页
Note
此函数会递归释放所有层级的页表

Definition at line 160 of file virtual_memory.cpp.

161 {
162 if (page_dir == nullptr) {
163 return;
164 }
165
166 // 递归释放所有层级的页表
167 RecursiveFreePageTable(reinterpret_cast<uint64_t*>(page_dir),
169 free_pages);
170
171 // 释放根页表目录本身
172 aligned_free(page_dir);
173
174 klog::Debug("Destroyed page directory at address: {:#x}",
175 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(page_dir)));
176}
auto RecursiveFreePageTable(uint64_t *table, size_t level, bool free_pages) -> void
递归释放页表
void aligned_free(void *ptr)
Definition memory.cpp:65
Here is the call graph for this function:

◆ FindPageTableEntry()

auto VirtualMemory::FindPageTableEntry ( void *  page_dir,
void *  virtual_addr,
bool  allocate = false 
) -> Expected<uint64_t*>
private

在页表中查找虚拟地址对应的页表项

Parameters
page_dir页目录
virtual_addr虚拟地址
allocate如果页表项不存在是否分配新的页表
Returns
Expected<uint64_t*> 页表项指针,失败时返回错误

Definition at line 298 of file virtual_memory.cpp.

299 {
300 auto* current_table = reinterpret_cast<uint64_t*>(page_dir);
301 auto vaddr = reinterpret_cast<uint64_t>(virtual_addr);
302
303 // 遍历页表层级
304 for (size_t level = cpu_io::virtual_memory::kPageTableLevels - 1; level > 0;
305 --level) {
306 // 获取当前级别的虚拟页号
307 auto vpn = cpu_io::virtual_memory::GetVirtualPageNumber(vaddr, level);
308 auto* pte = &current_table[vpn];
310 // 页表项有效,获取下一级页表
311 current_table = reinterpret_cast<uint64_t*>(
313 } else {
314 // 页表项无效
315 if (allocate) {
318 if (new_table == nullptr) {
319 return std::unexpected(Error(ErrorCode::kVmAllocationFailed));
320 }
321 // 清零新页表
322 std::memset(new_table, 0, cpu_io::virtual_memory::kPageSize);
323
324 // 设置中间页表项
326 reinterpret_cast<uint64_t>(new_table),
328
329 current_table = reinterpret_cast<uint64_t*>(new_table);
330 } else {
331 return std::unexpected(Error(ErrorCode::kVmPageNotMapped));
332 }
333 }
334 }
335
336 // 返回最底层页表中的页表项
338
339 return &current_table[vpn];
340}
@ kVmPageNotMapped
auto GetVirtualPageNumber(uint64_t virtual_addr, size_t level) -> uint64_t
Definition cpu_io.h:151
auto PageTableEntryToPhysical(uint64_t pte) -> uint64_t
Definition cpu_io.h:174
auto IsPageTableEntryValid(uint64_t pte) -> bool
Definition cpu_io.h:170
auto GetTableEntryPermissions() -> uint64_t
Definition cpu_io.h:146
auto PhysicalToPageTableEntry(uint64_t physical_addr, uint64_t flags) -> uint64_t
Definition cpu_io.h:178
Here is the call graph for this function:

◆ GetMapping()

auto VirtualMemory::GetMapping ( void *  page_dir,
void *  virtual_addr 
) -> Expected<void*>

获取虚拟地址对应的物理地址映射

Parameters
page_dir页表目录
virtual_addr虚拟地址
Returns
Expected<void*> 物理地址,失败时返回错误

Definition at line 141 of file virtual_memory.cpp.

142 {
143 assert(page_dir != nullptr && "GetMapping: page_dir is null");
144
145 auto pte_result = FindPageTableEntry(page_dir, virtual_addr, false);
146 if (!pte_result.has_value()) {
147 return std::unexpected(Error(ErrorCode::kVmPageNotMapped));
148 }
149
150 auto pte = pte_result.value();
151
153 return std::unexpected(Error(ErrorCode::kVmPageNotMapped));
154 }
155
156 return reinterpret_cast<void*>(
158}
auto FindPageTableEntry(void *page_dir, void *virtual_addr, bool allocate=false) -> Expected< uint64_t * >
在页表中查找虚拟地址对应的页表项
Here is the call graph for this function:

◆ InitCurrentCore()

auto VirtualMemory::InitCurrentCore ( ) const -> void

初始化当前核心的页表

Precondition
内核页表目录已初始化
Postcondition
当前核心的页表目录已设置并启用分页

Definition at line 49 of file virtual_memory.cpp.

49 {
51 reinterpret_cast<uint64_t>(kernel_page_dir_));
52 // 开启分页
54}
void SetPageDirectory(uint64_t pd)
Definition cpu_io.h:120
Here is the call graph for this function:

◆ MapMMIO()

auto VirtualMemory::MapMMIO ( uint64_t  phys_addr,
size_t  size,
uint32_t  flags = cpu_io::virtual_memory::GetKernelPagePermissions() 
) -> Expected<void*>

映射设备内存 (MMIO)

Parameters
phys_addr设备物理基地址
size映射大小
flags页表属性,默认为内核设备内存属性(如果架构支持区分的话)
Returns
Expected<void*> 映射后的虚拟地址,失败时返回错误

Definition at line 56 of file virtual_memory.cpp.

57 {
58 // 计算对齐后的起始和结束页
59 auto start_page = cpu_io::virtual_memory::PageAlign(phys_addr);
60 auto end_page = cpu_io::virtual_memory::PageAlignUp(phys_addr + size);
61
62 // 遍历并映射
63 for (uint64_t addr = start_page; addr < end_page;
65 auto result = MapPage(kernel_page_dir_, reinterpret_cast<void*>(addr),
66 reinterpret_cast<void*>(addr), flags);
67 if (!result.has_value()) {
68 return std::unexpected(result.error());
69 }
70 }
71 return reinterpret_cast<void*>(phys_addr);
72}
auto MapPage(void *page_dir, void *virtual_addr, void *physical_addr, uint32_t flags) -> Expected< void >
映射单个页面
auto PageAlign(uint64_t addr) -> uint64_t
Definition cpu_io.h:157
auto PageAlignUp(uint64_t addr) -> uint64_t
Definition cpu_io.h:161
Here is the call graph for this function:
Here is the caller graph for this function:

◆ MapPage()

auto VirtualMemory::MapPage ( void *  page_dir,
void *  virtual_addr,
void *  physical_addr,
uint32_t  flags 
) -> Expected<void>

映射单个页面

Parameters
page_dir页表目录
virtual_addr虚拟地址
physical_addr物理地址
flags页表属性
Returns
Expected<void> 成功时返回 void,失败时返回错误

Definition at line 74 of file virtual_memory.cpp.

76 {
77 assert(page_dir != nullptr && "MapPage: page_dir is null");
78
79 // 查找页表项,如果不存在则分配
80 auto pte_result = FindPageTableEntry(page_dir, virtual_addr, true);
81 if (!pte_result.has_value()) {
82 return std::unexpected(Error(ErrorCode::kVmMapFailed));
83 }
84
85 auto pte = pte_result.value();
86
87 // 检查是否已经映射且标志位相同
89 // 如果物理地址和标志位都相同,则认为是重复映射(警告但不失败)
91 if (existing_pa == reinterpret_cast<uint64_t>(physical_addr) &&
92 (*pte & ((1ULL << cpu_io::virtual_memory::kPteAttributeBits) - 1)) ==
93 flags) {
95 "MapPage: duplicate va = {:#x}, pa = {:#X}, flags = {:#X}, skip",
96 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(virtual_addr)),
97 static_cast<uint64_t>(existing_pa), static_cast<uint64_t>(flags));
98 // 重复映射,但不是错误
99 return {};
100 }
102 "MapPage: remap va = {:#x} from pa = {:#X} to pa = {:#x}",
103 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(virtual_addr)),
104 static_cast<uint64_t>(existing_pa),
105 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(physical_addr)));
106 }
107
108 // 设置页表项
110 reinterpret_cast<uint64_t>(physical_addr), flags);
111 // 刷新 TLB
113
114 return {};
115}
static constexpr size_t kPteAttributeBits
Definition cpu_io.h:64
auto Warn(etl::format_string< Args... > fmt, Args &&... args) -> void
以 WARN 级别记录日志
Here is the call graph for this function:

◆ operator=() [1/2]

auto VirtualMemory::operator= ( const VirtualMemory ) -> VirtualMemory &=delete
delete

◆ operator=() [2/2]

auto VirtualMemory::operator= ( VirtualMemory &&  ) -> VirtualMemory &=default
default

◆ RecursiveClonePageTable()

auto VirtualMemory::RecursiveClonePageTable ( uint64_t *  src_table,
uint64_t *  dst_table,
size_t  level,
bool  copy_mappings 
) -> Expected<void>
private

递归复制页表

Parameters
src_table源页表
dst_table目标页表
level当前层级
copy_mappings是否复制映射
Returns
Expected<void> 成功时返回 void,失败时返回错误

Definition at line 244 of file virtual_memory.cpp.

247 {
248 assert(src_table != nullptr && "RecursiveClonePageTable: src_table is null");
249 assert(dst_table != nullptr && "RecursiveClonePageTable: dst_table is null");
250
251 for (size_t i = 0; i < kEntriesPerTable; ++i) {
252 uint64_t src_pte = src_table[i];
254 continue;
255 }
256
257 if (level > 0) {
258 // 非最后一级,需要递归复制子页表
260 auto* src_next_table = reinterpret_cast<uint64_t*>(src_pa);
261
262 // 分配新的子页表
265 if (dst_next_table == nullptr) {
266 return std::unexpected(Error(ErrorCode::kVmAllocationFailed));
267 }
268
269 // 清零新页表
270 std::memset(dst_next_table, 0, cpu_io::virtual_memory::kPageSize);
271
272 // 递归复制子页表
273 auto result = RecursiveClonePageTable(
274 src_next_table, reinterpret_cast<uint64_t*>(dst_next_table),
275 level - 1, copy_mappings);
276 if (!result.has_value()) {
277 aligned_free(dst_next_table);
278 return std::unexpected(result.error());
279 }
280
281 // 设置目标页表项指向新的子页表
283 reinterpret_cast<uint64_t>(dst_next_table),
285 } else {
286 // 最后一级页表
287 if (copy_mappings) {
288 // 直接复制页表项(共享物理页)
289 dst_table[i] = src_pte;
290 }
291 // 如果不复制映射,保持目标页表项为 0
292 }
293 }
294
295 return {};
296}
static constexpr size_t kEntriesPerTable
Here is the call graph for this function:

◆ RecursiveFreePageTable()

auto VirtualMemory::RecursiveFreePageTable ( uint64_t *  table,
size_t  level,
bool  free_pages 
) -> void
private

递归释放页表

Parameters
table当前页表
level当前层级
free_pages是否释放物理页

Definition at line 210 of file virtual_memory.cpp.

211 {
212 if (table == nullptr) {
213 return;
214 }
215
216 // 遍历页表中的所有条目
217 for (size_t i = 0; i < kEntriesPerTable; ++i) {
218 uint64_t pte = table[i];
220 continue;
221 }
222
224
225 // 如果不是最后一级,递归释放子页表
226 if (level > 0) {
227 RecursiveFreePageTable(reinterpret_cast<uint64_t*>(pa), level - 1,
228 free_pages);
229 } else if (free_pages) {
230 // 最后一级页表,释放物理页
231 aligned_free(reinterpret_cast<void*>(pa));
232 }
233
234 // 清除页表项
235 table[i] = 0;
236 }
237
238 // 如果不是根页表,释放当前页表
240 aligned_free(table);
241 }
242}
Here is the call graph for this function:

◆ UnmapPage()

auto VirtualMemory::UnmapPage ( void *  page_dir,
void *  virtual_addr 
) -> Expected<void>

取消映射单个页面

Parameters
page_dir页表目录
virtual_addr虚拟地址
Returns
Expected<void> 成功时返回 void,失败时返回错误

Definition at line 117 of file virtual_memory.cpp.

118 {
119 assert(page_dir != nullptr && "UnmapPage: page_dir is null");
120
121 auto pte_result = FindPageTableEntry(page_dir, virtual_addr, false);
122 if (!pte_result.has_value()) {
123 return std::unexpected(Error(ErrorCode::kVmPageNotMapped));
124 }
125
126 auto pte = pte_result.value();
127
129 return std::unexpected(Error(ErrorCode::kVmPageNotMapped));
130 }
131
132 // 清除页表项
133 *pte = 0;
134
135 // 刷新 TLB
137
138 return {};
139}
Here is the call graph for this function:

Member Data Documentation

◆ kEntriesPerTable

constexpr size_t VirtualMemory::kEntriesPerTable
staticconstexprprivate
Initial value:

Definition at line 119 of file virtual_memory.hpp.

◆ kernel_page_dir_

void* VirtualMemory::kernel_page_dir_ {nullptr}
private

Definition at line 117 of file virtual_memory.hpp.

117{nullptr};

The documentation for this class was generated from the following files: