SimpleKernel 1.17.0
Loading...
Searching...
No Matches
virtual_memory_test.cpp
Go to the documentation of this file.
1
5#include "virtual_memory.hpp"
6
7#include <gmock/gmock.h>
8#include <gtest/gtest.h>
9
10#include <cstdint>
11#include <cstring>
12#include <thread>
13#include <unordered_map>
14#include <vector>
15
16#include "basic_info.hpp"
18
19namespace {
20
21// Mock allocator for testing
22class MockAllocator {
23 public:
24 static auto GetInstance() -> MockAllocator& {
25 static MockAllocator instance;
26 return instance;
27 }
28
29 auto AlignedAlloc(size_t alignment, size_t size) -> void* {
30 void* ptr = nullptr;
31 if (posix_memalign(&ptr, alignment, size) == 0) {
32 allocated_blocks_[ptr] = size;
33 return ptr;
34 }
35 return nullptr;
36 }
37
38 void Free(void* ptr) {
39 if (ptr == nullptr) {
40 return;
41 }
42 auto it = allocated_blocks_.find(ptr);
43 if (it != allocated_blocks_.end()) {
44 allocated_blocks_.erase(it);
45 free(ptr);
46 }
47 }
48
49 void Reset() {
50 for (auto& [ptr, size] : allocated_blocks_) {
51 free(ptr);
52 }
53 allocated_blocks_.clear();
54 }
55
56 [[nodiscard]] auto GetAllocatedCount() const -> size_t {
57 return allocated_blocks_.size();
58 }
59
60 private:
61 MockAllocator() = default;
62 std::unordered_map<void*, size_t> allocated_blocks_;
63};
64
65extern "C" void* aligned_alloc(size_t alignment, size_t size) {
66 return MockAllocator::GetInstance().AlignedAlloc(alignment, size);
67}
68
69extern "C" void aligned_free(void* ptr) {
70 MockAllocator::GetInstance().Free(ptr);
71}
72
73class VirtualMemoryTest : public ::testing::Test {
74 protected:
75 void SetUp() override {
76 BasicInfoSingleton::create();
77 BasicInfoSingleton::instance().physical_memory_addr = 0x80000000;
78 BasicInfoSingleton::instance().physical_memory_size = 0x10000000;
79 MockAllocator::GetInstance().Reset();
80
81 env_state_.InitializeCores(1);
82 env_state_.SetCurrentThreadEnvironment();
83 env_state_.BindThreadToCore(std::this_thread::get_id(), 0);
84 }
85
86 void TearDown() override {
87 MockAllocator::GetInstance().Reset();
88 env_state_.ClearCurrentThreadEnvironment();
89 BasicInfoSingleton::destroy();
90 }
91
93};
94
95TEST_F(VirtualMemoryTest, MapPageBasic) {
97
100 ASSERT_NE(page_dir, nullptr);
101 std::memset(page_dir, 0, cpu_io::virtual_memory::kPageSize);
102
103 void* virt_addr = reinterpret_cast<void*>(0x1000);
104 void* phys_addr = reinterpret_cast<void*>(0x80001000);
105
106 // 映射页面
107 auto result = vm.MapPage(page_dir, virt_addr, phys_addr,
109
110 EXPECT_TRUE(result.has_value());
111
112 // 验证映射
113 auto mapped = vm.GetMapping(page_dir, virt_addr);
114 EXPECT_TRUE(mapped.has_value());
115 if (mapped) {
116 EXPECT_EQ(*mapped, phys_addr);
117 }
118}
119
120TEST_F(VirtualMemoryTest, UnmapPage) {
121 VirtualMemory vm;
122
125 ASSERT_NE(page_dir, nullptr);
126 std::memset(page_dir, 0, cpu_io::virtual_memory::kPageSize);
127
128 void* virt_addr = reinterpret_cast<void*>(0x1000);
129 void* phys_addr = reinterpret_cast<void*>(0x80001000);
130
131 // 映射页面
132 (void)vm.MapPage(page_dir, virt_addr, phys_addr,
134
135 // 取消映射
136 auto result = vm.UnmapPage(page_dir, virt_addr);
137 EXPECT_TRUE(result.has_value());
138
139 // 验证取消映射
140 auto mapped = vm.GetMapping(page_dir, virt_addr);
141 EXPECT_FALSE(mapped.has_value());
142}
143
144TEST_F(VirtualMemoryTest, UnmapNonExistentPage) {
145 VirtualMemory vm;
146
149 ASSERT_NE(page_dir, nullptr);
150 std::memset(page_dir, 0, cpu_io::virtual_memory::kPageSize);
151
152 void* virt_addr = reinterpret_cast<void*>(0x1000);
153
154 // 取消映射不存在的页面
155 auto result = vm.UnmapPage(page_dir, virt_addr);
156 EXPECT_FALSE(result.has_value());
157}
158
159TEST_F(VirtualMemoryTest, GetMappingNonExistent) {
160 VirtualMemory vm;
161
164 ASSERT_NE(page_dir, nullptr);
165 std::memset(page_dir, 0, cpu_io::virtual_memory::kPageSize);
166
167 void* virt_addr = reinterpret_cast<void*>(0x1000);
168
169 auto mapped = vm.GetMapping(page_dir, virt_addr);
170 EXPECT_FALSE(mapped.has_value());
171}
172
173TEST_F(VirtualMemoryTest, MapMultiplePages) {
174 VirtualMemory vm;
175
178 ASSERT_NE(page_dir, nullptr);
179 std::memset(page_dir, 0, cpu_io::virtual_memory::kPageSize);
180
181 constexpr size_t kNumPages = 10;
182 constexpr size_t kPageSize = 0x1000;
183
184 // 映射多个页面
185 for (size_t i = 0; i < kNumPages; ++i) {
186 void* virt_addr = reinterpret_cast<void*>(0x10000 + i * kPageSize);
187 void* phys_addr = reinterpret_cast<void*>(0x80000000 + i * kPageSize);
188
189 auto result = vm.MapPage(page_dir, virt_addr, phys_addr,
191 EXPECT_TRUE(result.has_value());
192 }
193
194 // 验证所有映射
195 for (size_t i = 0; i < kNumPages; ++i) {
196 void* virt_addr = reinterpret_cast<void*>(0x10000 + i * kPageSize);
197 void* phys_addr = reinterpret_cast<void*>(0x80000000 + i * kPageSize);
198
199 auto mapped = vm.GetMapping(page_dir, virt_addr);
200 EXPECT_TRUE(mapped.has_value());
201 if (mapped) {
202 EXPECT_EQ(*mapped, phys_addr);
203 }
204 }
205}
206
207TEST_F(VirtualMemoryTest, RemapPage) {
208 VirtualMemory vm;
209
212 ASSERT_NE(page_dir, nullptr);
213 std::memset(page_dir, 0, cpu_io::virtual_memory::kPageSize);
214
215 void* virt_addr = reinterpret_cast<void*>(0x1000);
216 void* phys_addr1 = reinterpret_cast<void*>(0x80001000);
217 void* phys_addr2 = reinterpret_cast<void*>(0x80002000);
218
219 // 第一次映射
220 (void)vm.MapPage(page_dir, virt_addr, phys_addr1,
222
223 // 重新映射到不同的物理地址
224 auto result = vm.MapPage(page_dir, virt_addr, phys_addr2,
226 EXPECT_TRUE(result.has_value());
227
228 // 验证新映射
229 auto mapped = vm.GetMapping(page_dir, virt_addr);
230 EXPECT_TRUE(mapped.has_value());
231 if (mapped) {
232 EXPECT_EQ(*mapped, phys_addr2);
233 }
234}
235
236TEST_F(VirtualMemoryTest, DestroyPageDirectoryWithoutFreePages) {
237 VirtualMemory vm;
238
241 ASSERT_NE(page_dir, nullptr);
242 std::memset(page_dir, 0, cpu_io::virtual_memory::kPageSize);
243
244 // 映射一些页面
245 constexpr size_t kNumPages = 5;
246 constexpr size_t kPageSize = 0x1000;
247
248 for (size_t i = 0; i < kNumPages; ++i) {
249 void* virt_addr = reinterpret_cast<void*>(0x10000 + i * kPageSize);
250 void* phys_addr = reinterpret_cast<void*>(0x80000000 + i * kPageSize);
251 (void)vm.MapPage(page_dir, virt_addr, phys_addr,
253 }
254
255 size_t allocated_before = MockAllocator::GetInstance().GetAllocatedCount();
256
257 // 销毁页表(不释放物理页)
258 vm.DestroyPageDirectory(page_dir, false);
259
260 size_t allocated_after = MockAllocator::GetInstance().GetAllocatedCount();
261
262 // 验证页表已释放
263 EXPECT_LT(allocated_after, allocated_before);
264}
265
266TEST_F(VirtualMemoryTest, ClonePageDirectoryWithMappings) {
267 VirtualMemory vm;
268
271 ASSERT_NE(src_page_dir, nullptr);
272 std::memset(src_page_dir, 0, cpu_io::virtual_memory::kPageSize);
273
274 // 在源页表中映射页面
275 constexpr size_t kNumPages = 5;
276 constexpr size_t kPageSize = 0x1000;
277
278 for (size_t i = 0; i < kNumPages; ++i) {
279 void* virt_addr = reinterpret_cast<void*>(0x10000 + i * kPageSize);
280 void* phys_addr = reinterpret_cast<void*>(0x80000000 + i * kPageSize);
281 (void)vm.MapPage(src_page_dir, virt_addr, phys_addr,
283 }
284
285 // 克隆页表(复制映射)
286 auto clone_result = vm.ClonePageDirectory(src_page_dir, true);
287 ASSERT_TRUE(clone_result.has_value());
288 auto* dst_page_dir = clone_result.value();
289 EXPECT_NE(dst_page_dir, src_page_dir);
290
291 // 验证克隆的页表具有相同的映射
292 for (size_t i = 0; i < kNumPages; ++i) {
293 void* virt_addr = reinterpret_cast<void*>(0x10000 + i * kPageSize);
294 void* phys_addr = reinterpret_cast<void*>(0x80000000 + i * kPageSize);
295
296 auto src_mapped = vm.GetMapping(src_page_dir, virt_addr);
297 auto dst_mapped = vm.GetMapping(dst_page_dir, virt_addr);
298
299 EXPECT_TRUE(src_mapped.has_value());
300 EXPECT_TRUE(dst_mapped.has_value());
301
302 if (src_mapped && dst_mapped) {
303 EXPECT_EQ(*src_mapped, phys_addr);
304 EXPECT_EQ(*dst_mapped, phys_addr);
305 EXPECT_EQ(*src_mapped, *dst_mapped);
306 }
307 }
308
309 // 清理
310 vm.DestroyPageDirectory(src_page_dir, false);
311 vm.DestroyPageDirectory(dst_page_dir, false);
312}
313
314TEST_F(VirtualMemoryTest, ClonePageDirectoryWithoutMappings) {
315 VirtualMemory vm;
316
319 ASSERT_NE(src_page_dir, nullptr);
320 std::memset(src_page_dir, 0, cpu_io::virtual_memory::kPageSize);
321
322 // 在源页表中映射页面
323 constexpr size_t kNumPages = 3;
324 constexpr size_t kPageSize = 0x1000;
325
326 for (size_t i = 0; i < kNumPages; ++i) {
327 void* virt_addr = reinterpret_cast<void*>(0x10000 + i * kPageSize);
328 void* phys_addr = reinterpret_cast<void*>(0x80000000 + i * kPageSize);
329 (void)vm.MapPage(src_page_dir, virt_addr, phys_addr,
331 }
332
333 // 克隆页表(不复制映射)
334 auto clone_result = vm.ClonePageDirectory(src_page_dir, false);
335 ASSERT_TRUE(clone_result.has_value());
336 auto* dst_page_dir = clone_result.value();
337 EXPECT_NE(dst_page_dir, src_page_dir);
338
339 // 验证克隆的页表没有映射
340 for (size_t i = 0; i < kNumPages; ++i) {
341 void* virt_addr = reinterpret_cast<void*>(0x10000 + i * kPageSize);
342
343 auto src_mapped = vm.GetMapping(src_page_dir, virt_addr);
344 auto dst_mapped = vm.GetMapping(dst_page_dir, virt_addr);
345
346 EXPECT_TRUE(src_mapped.has_value());
347 EXPECT_FALSE(dst_mapped.has_value());
348 }
349
350 // 清理
351 vm.DestroyPageDirectory(src_page_dir, false);
352 vm.DestroyPageDirectory(dst_page_dir, false);
353}
354
355TEST_F(VirtualMemoryTest, CloneNullPageDirectory) {
356 // 注意:根据重构后的代码,ClonePageDirectory 对 nullptr 输入会触发断言
357 // 因此该测试在使用 sk_assert_msg 的情况下无法正常进行
358 // 这里保留测试结构,但在实际运行时断言会触发
359 GTEST_SKIP() << "ClonePageDirectory with nullptr triggers assertion";
360}
361
362TEST_F(VirtualMemoryTest, DestroyNullPageDirectory) {
363 VirtualMemory vm;
364
365 // 应该不会崩溃
366 vm.DestroyPageDirectory(nullptr, false);
367 vm.DestroyPageDirectory(nullptr, true);
368}
369
370TEST_F(VirtualMemoryTest, MemoryLeakCheck) {
371 VirtualMemory vm;
372
379
380 ASSERT_NE(page_dir1, nullptr);
381 ASSERT_NE(page_dir2, nullptr);
382 ASSERT_NE(page_dir3, nullptr);
383 std::memset(page_dir1, 0, cpu_io::virtual_memory::kPageSize);
384 std::memset(page_dir2, 0, cpu_io::virtual_memory::kPageSize);
385 std::memset(page_dir3, 0, cpu_io::virtual_memory::kPageSize);
386
387 size_t allocated_before = MockAllocator::GetInstance().GetAllocatedCount();
388
389 // 映射一些页面
390 for (size_t i = 0; i < 10; ++i) {
391 void* virt_addr = reinterpret_cast<void*>(0x10000 + i * 0x1000);
392 void* phys_addr = reinterpret_cast<void*>(0x80000000 + i * 0x1000);
393 (void)vm.MapPage(page_dir1, virt_addr, phys_addr,
395 }
396
397 // 克隆
398 auto clone_result = vm.ClonePageDirectory(page_dir1, true);
399 ASSERT_TRUE(clone_result.has_value());
400 auto* cloned = clone_result.value();
401
402 // 清理所有页表
403 vm.DestroyPageDirectory(page_dir1, false);
404 vm.DestroyPageDirectory(page_dir2, false);
405 vm.DestroyPageDirectory(page_dir3, false);
406 vm.DestroyPageDirectory(cloned, false);
407
408 // 应该释放了大部分页表内存(除了内核页表)
409 size_t allocated_after = MockAllocator::GetInstance().GetAllocatedCount();
410 EXPECT_LT(allocated_after, allocated_before);
411}
412
413} // namespace
虚拟内存管理器(具体类,非抽象基类)
auto DestroyPageDirectory(void *page_dir, bool free_pages=false) -> void
回收页表,释放所有映射和子页表
auto GetMapping(void *page_dir, void *virtual_addr) -> Expected< void * >
获取虚拟地址对应的物理地址映射
auto MapPage(void *page_dir, void *virtual_addr, void *physical_addr, uint32_t flags) -> Expected< void >
映射单个页面
auto ClonePageDirectory(void *src_page_dir, bool copy_mappings=true) -> Expected< void * >
复制页表
auto UnmapPage(void *page_dir, void *virtual_addr) -> Expected< void >
取消映射单个页面
TEST_F(KernelFdtTest, ConstructorTest)
static constexpr size_t kPageSize
Definition cpu_io.h:63
auto GetUserPagePermissions(bool readable=true, bool writable=false, bool executable=false, bool global=false) -> uint64_t
Definition cpu_io.h:79
void aligned_free(void *ptr)
Definition memory.cpp:65
void free(void *ptr)
Definition memory.cpp:38
void * aligned_alloc(size_t alignment, size_t size)
Definition memory.cpp:58
#define EXPECT_TRUE(cond, msg)
#define EXPECT_NE(val1, val2, msg)
#define EXPECT_LT(val1, val2, msg)
#define EXPECT_FALSE(cond, msg)
#define EXPECT_EQ(val1, val2, msg)