SimpleKernel 1.17.0
Loading...
Searching...
No Matches
virtual_memory.cpp
Go to the documentation of this file.
1
5#include "virtual_memory.hpp"
6
7#include <cpu_io.h>
8
9#include <bmalloc.hpp>
10#include <cassert>
11#include <cstddef>
12#include <cstdint>
13#include <cstring>
14
15#include "basic_info.hpp"
16#include "expected.hpp"
17#include "kernel.h"
18#include "kernel_log.hpp"
19#include "sk_stdlib.h"
20
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}
48
49auto VirtualMemory::InitCurrentCore() const -> void {
51 reinterpret_cast<uint64_t>(kernel_page_dir_));
52 // 开启分页
54}
55
56auto VirtualMemory::MapMMIO(uint64_t phys_addr, size_t size, uint32_t flags)
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}
73
74auto VirtualMemory::MapPage(void* page_dir, void* virtual_addr,
75 void* physical_addr, uint32_t flags)
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}
116
117auto VirtualMemory::UnmapPage(void* page_dir, void* virtual_addr)
118 -> Expected<void> {
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}
140
141auto VirtualMemory::GetMapping(void* page_dir, void* virtual_addr)
142 -> Expected<void*> {
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}
159
160auto VirtualMemory::DestroyPageDirectory(void* page_dir, bool free_pages)
161 -> void {
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}
177
178auto VirtualMemory::ClonePageDirectory(void* src_page_dir, bool copy_mappings)
179 -> Expected<void*> {
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}
209
210auto VirtualMemory::RecursiveFreePageTable(uint64_t* table, size_t level,
211 bool free_pages) -> void {
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}
243
245 uint64_t* dst_table, size_t level,
246 bool copy_mappings)
247 -> Expected<void> {
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}
297
298auto VirtualMemory::FindPageTableEntry(void* page_dir, void* virtual_addr,
299 bool allocate) -> Expected<uint64_t*> {
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}
VirtualMemory()
构造函数
auto MapMMIO(uint64_t phys_addr, size_t size, uint32_t flags=cpu_io::virtual_memory::GetKernelPagePermissions()) -> Expected< void * >
映射设备内存 (MMIO)
auto DestroyPageDirectory(void *page_dir, bool free_pages=false) -> void
回收页表,释放所有映射和子页表
auto GetMapping(void *page_dir, void *virtual_addr) -> Expected< void * >
获取虚拟地址对应的物理地址映射
auto InitCurrentCore() const -> void
初始化当前核心的页表
auto FindPageTableEntry(void *page_dir, void *virtual_addr, bool allocate=false) -> Expected< uint64_t * >
在页表中查找虚拟地址对应的页表项
auto RecursiveFreePageTable(uint64_t *table, size_t level, bool free_pages) -> void
递归释放页表
auto MapPage(void *page_dir, void *virtual_addr, void *physical_addr, uint32_t flags) -> Expected< void >
映射单个页面
auto RecursiveClonePageTable(uint64_t *src_table, uint64_t *dst_table, size_t level, bool copy_mappings) -> Expected< void >
递归复制页表
auto ClonePageDirectory(void *src_page_dir, bool copy_mappings=true) -> Expected< void * >
复制页表
auto UnmapPage(void *page_dir, void *virtual_addr) -> Expected< void >
取消映射单个页面
@ kVmPageNotMapped
@ kVmAllocationFailed
std::expected< T, Error > Expected
std::expected 别名模板
Definition expected.hpp:365
static constexpr size_t kPageSize
Definition cpu_io.h:63
void SetPageDirectory(uint64_t pd)
Definition cpu_io.h:120
static constexpr size_t kPteAttributeBits
Definition cpu_io.h:64
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 PageAlign(uint64_t addr) -> uint64_t
Definition cpu_io.h:157
auto IsPageTableEntryValid(uint64_t pte) -> bool
Definition cpu_io.h:170
auto GetTableEntryPermissions() -> uint64_t
Definition cpu_io.h:146
static constexpr size_t kPageTableLevels
Definition cpu_io.h:68
auto PhysicalToPageTableEntry(uint64_t physical_addr, uint64_t flags) -> uint64_t
Definition cpu_io.h:178
auto PageAlignUp(uint64_t addr) -> uint64_t
Definition cpu_io.h:161
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 级别记录日志
auto Debug(etl::format_string< Args... > fmt, Args &&... args) -> void
以 DEBUG 级别记录日志(SIMPLEKERNEL_MIN_LOG_LEVEL > 0 时编译期消除)
auto Warn(etl::format_string< Args... > fmt, Args &&... args) -> void
以 WARN 级别记录日志
void aligned_free(void *ptr)
Definition memory.cpp:65
void * aligned_alloc(size_t alignment, size_t size)
Definition memory.cpp:58
错误类型,用于 std::expected
Definition expected.hpp:343