1 #pragma once 2 3 #include <common/glib.h> 4 #include <common/gfp.h> 5 #include <mm/mm-types.h> 6 #include <process/process.h> 7 8 // 每个页表的项数 9 // 64位下,每个页表4k,每条页表项8B,故一个页表有512条 10 #define PTRS_PER_PGT 512 11 12 // 内核层的起始地址 13 #define PAGE_OFFSET 0xffff800000000000UL 14 #define KERNEL_BASE_LINEAR_ADDR 0xffff800000000000UL 15 #define USER_MAX_LINEAR_ADDR 0x00007fffffffffffUL 16 // MMIO虚拟地址空间:1TB 17 #define MMIO_BASE 0xffffa10000000000UL 18 #define MMIO_TOP 0xffffa20000000000UL 19 20 #define PAGE_4K_SHIFT 12 21 #define PAGE_2M_SHIFT 21 22 #define PAGE_1G_SHIFT 30 23 #define PAGE_GDT_SHIFT 39 24 25 // 不同大小的页的容量 26 #define PAGE_4K_SIZE (1UL << PAGE_4K_SHIFT) 27 #define PAGE_2M_SIZE (1UL << PAGE_2M_SHIFT) 28 #define PAGE_1G_SIZE (1UL << PAGE_1G_SHIFT) 29 30 // 屏蔽低于x的数值 31 #define PAGE_4K_MASK (~(PAGE_4K_SIZE - 1)) 32 #define PAGE_2M_MASK (~(PAGE_2M_SIZE - 1)) 33 34 // 将addr按照x的上边界对齐 35 #define PAGE_4K_ALIGN(addr) (((unsigned long)(addr) + PAGE_4K_SIZE - 1) & PAGE_4K_MASK) 36 #define PAGE_2M_ALIGN(addr) (((unsigned long)(addr) + PAGE_2M_SIZE - 1) & PAGE_2M_MASK) 37 38 // 虚拟地址与物理地址转换 39 #define virt_2_phys(addr) ((unsigned long)(addr)-PAGE_OFFSET) 40 #define phys_2_virt(addr) ((unsigned long *)((unsigned long)(addr) + PAGE_OFFSET)) 41 // 获取对应的页结构体 42 #define Virt_To_2M_Page(kaddr) (memory_management_struct.pages_struct + (virt_2_phys(kaddr) >> PAGE_2M_SHIFT)) 43 #define Phy_to_2M_Page(kaddr) (memory_management_struct.pages_struct + ((unsigned long)(kaddr) >> PAGE_2M_SHIFT)) 44 45 // 在这个地址以上的虚拟空间,用来进行特殊的映射 46 #define SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE 0xffffa00000000000UL 47 #define FRAME_BUFFER_MAPPING_OFFSET 0x3000000UL 48 #define IO_APIC_MAPPING_OFFSET 0xfec00000UL 49 #define LOCAL_APIC_MAPPING_OFFSET 0xfee00000UL 50 #define AHCI_MAPPING_OFFSET 0xff200000UL // AHCI 映射偏移量,之后使用了4M的地址 51 #define XHCI_MAPPING_OFFSET 0x100000000 // XHCI控制器映射偏移量(后方请预留1GB的虚拟空间来映射不同的controller) 52 53 // ===== 内存区域属性 ===== 54 // DMA区域 55 #define ZONE_DMA (1 << 0) 56 // 已在页表中映射的区域 57 #define ZONE_NORMAL (1 << 1) 58 // 未在页表中映射的区域 59 #define ZONE_UNMAPPED_IN_PGT (1 << 2) 60 61 // ===== 页面属性 ===== 62 // 页面在页表中已被映射 mapped=1 unmapped=0 63 #define PAGE_PGT_MAPPED (1 << 0) 64 65 // 内核初始化所占用的页 init-code=1 normal-code/data=0 66 #define PAGE_KERNEL_INIT (1 << 1) 67 68 // 1=设备MMIO映射的内存 0=物理内存 69 #define PAGE_DEVICE (1 << 2) 70 71 // 内核层页 kernel=1 memory=0 72 #define PAGE_KERNEL (1 << 3) 73 74 // 共享的页 shared=1 single-use=0 75 #define PAGE_SHARED (1 << 4) 76 77 // =========== 页表项权限 ======== 78 79 // bit 63 Execution Disable: 80 #define PAGE_XD (1UL << 63) 81 82 // bit 12 Page Attribute Table 83 #define PAGE_PAT (1UL << 12) 84 // 对于PTE而言,第7位是PAT 85 #define PAGE_4K_PAT (1UL << 7) 86 87 // bit 8 Global Page:1,global;0,part 88 #define PAGE_GLOBAL (1UL << 8) 89 90 // bit 7 Page Size:1,big page;0,small page; 91 #define PAGE_PS (1UL << 7) 92 93 // bit 6 Dirty:1,dirty;0,clean; 94 #define PAGE_DIRTY (1UL << 6) 95 96 // bit 5 Accessed:1,visited;0,unvisited; 97 #define PAGE_ACCESSED (1UL << 5) 98 99 // bit 4 Page Level Cache Disable 100 #define PAGE_PCD (1UL << 4) 101 102 // bit 3 Page Level Write Through 103 #define PAGE_PWT (1UL << 3) 104 105 // bit 2 User Supervisor:1,user and supervisor;0,supervisor; 106 #define PAGE_U_S (1UL << 2) 107 108 // bit 1 Read Write:1,read and write;0,read; 109 #define PAGE_R_W (1UL << 1) 110 111 // bit 0 Present:1,present;0,no present; 112 #define PAGE_PRESENT (1UL << 0) 113 114 // 1,0 115 #define PAGE_KERNEL_PGT (PAGE_R_W | PAGE_PRESENT) 116 117 // 1,0 118 #define PAGE_KERNEL_DIR (PAGE_R_W | PAGE_PRESENT) 119 120 // 1,0 (4级页表在3级页表中的页表项的属性) 121 #define PAGE_KERNEL_PDE (PAGE_R_W | PAGE_PRESENT) 122 123 // 7,1,0 124 #define PAGE_KERNEL_PAGE (PAGE_PS | PAGE_R_W | PAGE_PRESENT) 125 126 #define PAGE_KERNEL_4K_PAGE (PAGE_R_W | PAGE_PRESENT) 127 128 #define PAGE_USER_PGT (PAGE_U_S | PAGE_R_W | PAGE_PRESENT) 129 130 // 2,1,0 131 #define PAGE_USER_DIR (PAGE_U_S | PAGE_R_W | PAGE_PRESENT) 132 133 // 1,0 (4级页表在3级页表中的页表项的属性) 134 #define PAGE_USER_PDE (PAGE_U_S | PAGE_R_W | PAGE_PRESENT) 135 // 7,2,1,0 136 #define PAGE_USER_PAGE (PAGE_PS | PAGE_U_S | PAGE_R_W | PAGE_PRESENT) 137 138 #define PAGE_USER_4K_PAGE (PAGE_U_S | PAGE_R_W | PAGE_PRESENT) 139 140 // ===== 错误码定义 ==== 141 // 物理页结构体为空 142 #define EPAGE_NULL 1 143 144 /** 145 * @brief 刷新TLB的宏定义 146 * 由于任何写入cr3的操作都会刷新TLB,因此这个宏定义可以刷新TLB 147 */ 148 #define flush_tlb() \ 149 do \ 150 { \ 151 ul tmp; \ 152 io_mfence(); \ 153 __asm__ __volatile__( \ 154 "movq %%cr3, %0\n\t" \ 155 "movq %0, %%cr3\n\t" \ 156 : "=r"(tmp)::"memory"); \ 157 \ 158 } while (0); 159 160 /** 161 * @brief 系统内存信息结构体(单位:字节) 162 * 163 */ 164 struct mm_stat_t 165 { 166 uint64_t total; // 计算机的总内存数量大小 167 uint64_t used; // 已使用的内存大小 168 uint64_t free; // 空闲物理页所占的内存大小 169 uint64_t shared; // 共享的内存大小 170 uint64_t cache_used; // 位于slab缓冲区中的已使用的内存大小 171 uint64_t cache_free; // 位于slab缓冲区中的空闲的内存大小 172 uint64_t available; // 系统总空闲内存大小(包括kmalloc缓冲区) 173 }; 174 175 /** 176 * @brief 虚拟内存区域的操作方法的结构体 177 * 178 */ 179 struct vm_operations_t 180 { 181 /** 182 * @brief vm area 被打开时的回调函数 183 * 184 */ 185 void (*open)(struct vm_area_struct *area); 186 /** 187 * @brief vm area将要被移除的时候,将会调用该回调函数 188 * 189 */ 190 void (*close)(struct vm_area_struct *area); 191 }; 192 193 extern struct memory_desc memory_management_struct; 194 195 // 导出内核程序的几个段的起止地址 196 extern char _text; 197 extern char _etext; 198 extern char _data; 199 extern char _edata; 200 extern char _rodata; 201 extern char _erodata; 202 extern char _bss; 203 extern char _ebss; 204 extern char _end; 205 206 // 每个区域的索引 207 208 int ZONE_DMA_INDEX = 0; 209 int ZONE_NORMAL_INDEX = 0; 210 int ZONE_UNMAPPED_INDEX = 0; 211 212 // 初始化内存管理单元 213 void mm_init(); 214 215 /** 216 * @brief 初始化内存页 217 * 218 * @param page 内存页结构体 219 * @param flags 标志位 220 * 本函数只负责初始化内存页,允许对同一页面进行多次初始化 221 * 而维护计数器及置位bmp标志位的功能,应当在分配页面的时候手动完成 222 * @return unsigned long 223 */ 224 unsigned long page_init(struct Page *page, ul flags); 225 226 /** 227 * @brief 读取CR3寄存器的值(存储了页目录的基地址) 228 * 229 * @return unsigned* cr3的值的指针 230 */ 231 unsigned long *get_CR3() 232 { 233 ul *tmp; 234 __asm__ __volatile__( 235 "movq %%cr3, %0\n\t" 236 : "=r"(tmp)::"memory"); 237 return tmp; 238 } 239 240 /** 241 * @brief 从已初始化的页结构中搜索符合申请条件的、连续num个struct page 242 * 243 * @param zone_select 选择内存区域, 可选项:dma, mapped in pgt(normal), unmapped in pgt 244 * @param num 需要申请的内存页的数量 num<64 245 * @param flags 将页面属性设置成flag 246 * @return struct Page* 247 */ 248 struct Page *alloc_pages(unsigned int zone_select, int num, ul flags); 249 250 /** 251 * @brief 清除页面的引用计数, 计数为0时清空除页表已映射以外的所有属性 252 * 253 * @param p 物理页结构体 254 * @return unsigned long 255 */ 256 unsigned long page_clean(struct Page *page); 257 258 /** 259 * @brief 释放连续number个内存页 260 * 261 * @param page 第一个要被释放的页面的结构体 262 * @param number 要释放的内存页数量 number<64 263 */ 264 void free_pages(struct Page *page, int number); 265 266 /** 267 * @brief Get the page's attr 268 * 269 * @param page 内存页结构体 270 * @return ul 属性 271 */ 272 ul get_page_attr(struct Page *page); 273 274 /** 275 * @brief Set the page's attr 276 * 277 * @param page 内存页结构体 278 * @param flags 属性 279 * @return ul 错误码 280 */ 281 ul set_page_attr(struct Page *page, ul flags); 282 283 #define mk_pml4t(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr)) 284 /** 285 * @brief 设置pml4页表的页表项 286 * @param pml4tptr pml4页表项的地址 287 * @param pml4val pml4页表项的值 288 */ 289 #define set_pml4t(pml4tptr, pml4tval) (*(pml4tptr) = (pml4tval)) 290 291 #define mk_pdpt(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr)) 292 #define set_pdpt(pdptptr, pdptval) (*(pdptptr) = (pdptval)) 293 294 #define mk_pdt(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr)) 295 #define set_pdt(pdtptr, pdtval) (*(pdtptr) = (pdtval)) 296 297 #define mk_pt(addr, attr) ((unsigned long)(addr) | (unsigned long)(attr)) 298 #define set_pt(ptptr, ptval) (*(ptptr) = (ptval)) 299 300 /* 301 * vm_area_struct中的vm_flags的可选值 302 * 对应的结构体请见mm-types.h 303 */ 304 #define VM_NONE 0 305 #define VM_READ (1 << 0) 306 #define VM_WRITE (1 << 1) 307 #define VM_EXEC (1 << 2) 308 #define VM_SHARED (1 << 3) 309 #define VM_IO (1 << 4) // MMIO的内存区域 310 #define VM_SOFTDIRTY (1 << 5) 311 #define VM_MAYSHARE (1 << 6) // 该vma可被共享 312 #define VM_USER (1 << 7) // 该vma可被用户态访问 313 #define VM_DONTCOPY (1 << 8) // 当fork的时候不拷贝该虚拟内存区域 314 315 /* VMA basic access permission flags */ 316 #define VM_ACCESS_FLAGS (VM_READ | VM_WRITE | VM_EXEC) 317 318 /** 319 * @brief 初始化虚拟内存区域结构体 320 * 321 * @param vma 322 * @param mm 323 */ 324 static inline void vma_init(struct vm_area_struct *vma, struct mm_struct *mm) 325 { 326 memset(vma, 0, sizeof(struct vm_area_struct)); 327 vma->vm_mm = mm; 328 vma->vm_prev = vma->vm_next = NULL; 329 vma->vm_ops = NULL; 330 list_init(&vma->anon_vma_list); 331 } 332 333 /** 334 * @brief 判断给定的vma是否为当前进程所属的vma 335 * 336 * @param vma 给定的vma结构体 337 * @return true 338 * @return false 339 */ 340 static inline bool vma_is_foreign(struct vm_area_struct *vma) 341 { 342 if (current_pcb->mm == NULL) 343 return true; 344 if (current_pcb->mm != vma->vm_mm) 345 return true; 346 return false; 347 } 348 349 static inline bool vma_is_accessible(struct vm_area_struct *vma) 350 { 351 return vma->vm_flags & VM_ACCESS_FLAGS; 352 } 353 354 /** 355 * @brief 获取一块新的vma结构体,并将其与指定的mm进行绑定 356 * 357 * @param mm 与VMA绑定的内存空间分布结构体 358 * @return struct vm_area_struct* 新的VMA 359 */ 360 struct vm_area_struct *vm_area_alloc(struct mm_struct *mm); 361 362 /** 363 * @brief 释放vma结构体 364 * 365 * @param vma 待释放的vma结构体 366 */ 367 void vm_area_free(struct vm_area_struct *vma); 368 369 /** 370 * @brief 从链表中删除指定的vma结构体 371 * 372 * @param vma 373 */ 374 void vm_area_del(struct vm_area_struct *vma); 375 376 /** 377 * @brief 查找第一个符合“addr < vm_end”条件的vma 378 * 379 * @param mm 内存空间分布结构体 380 * @param addr 虚拟地址 381 * @return struct vm_area_struct* 符合条件的vma 382 */ 383 struct vm_area_struct *vma_find(struct mm_struct *mm, uint64_t addr); 384 385 /** 386 * @brief 插入vma 387 * 388 * @param mm 389 * @param vma 390 * @return int 391 */ 392 int vma_insert(struct mm_struct *mm, struct vm_area_struct *vma); 393 394 /** 395 * @brief 重新初始化页表的函数 396 * 将所有物理页映射到线性地址空间 397 */ 398 void page_table_init(); 399 400 /** 401 * @brief 将物理地址映射到页表的函数 402 * 403 * @param virt_addr_start 要映射到的虚拟地址的起始位置 404 * @param phys_addr_start 物理地址的起始位置 405 * @param length 要映射的区域的长度(字节) 406 * @param flags 标志位 407 * @param use4k 是否使用4k页 408 */ 409 int mm_map_phys_addr(ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool use4k); 410 411 /** 412 * @brief 将将物理地址填写到进程的页表的函数 413 * 414 * @param proc_page_table_addr 页表的基地址 415 * @param is_phys 页表的基地址是否为物理地址 416 * @param virt_addr_start 要映射到的虚拟地址的起始位置 417 * @param phys_addr_start 物理地址的起始位置 418 * @param length 要映射的区域的长度(字节) 419 * @param user 用户态是否可访问 420 * @param flush 是否刷新tlb 421 * @param use4k 是否使用4k页 422 */ 423 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); 424 425 int mm_map_phys_addr_user(ul virt_addr_start, ul phys_addr_start, ul length, ul flags); 426 427 /** 428 * @brief 从页表中清除虚拟地址的映射 429 * 430 * @param proc_page_table_addr 页表的地址 431 * @param is_phys 页表地址是否为物理地址 432 * @param virt_addr_start 要清除的虚拟地址的起始地址 433 * @param length 要清除的区域的长度 434 */ 435 void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul length); 436 437 /** 438 * @brief 取消当前进程的页表中的虚拟地址映射 439 * 440 * @param virt_addr 虚拟地址 441 * @param length 地址长度 442 */ 443 #define mm_unmap_addr(virt_addr, length) ({ \ 444 mm_unmap_proc_table((uint64_t)get_CR3(), true, virt_addr, length); \ 445 }) 446 447 /** 448 * @brief 创建VMA 449 * 450 * @param mm 要绑定的内存空间分布结构体 451 * @param vaddr 起始虚拟地址 452 * @param length 长度(字节) 453 * @param vm_flags vma的标志 454 * @param vm_ops vma的操作接口 455 * @param res_vma 返回的vma指针 456 * @return int 错误码 457 */ 458 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); 459 460 /** 461 * @brief 将指定的物理地址映射到指定的vma处 462 * 463 * @param vma 要进行映射的VMA结构体 464 * @param paddr 起始物理地址 465 * @param offset 要映射的起始位置在vma中的偏移量 466 * @param length 要映射的长度 467 * @return int 错误码 468 */ 469 int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr, uint64_t offset, uint64_t length); 470 471 /** 472 * @brief 在页表中映射物理地址到指定的虚拟地址(需要页表中已存在对应的vma) 473 * 474 * @param mm 内存管理结构体 475 * @param vaddr 虚拟地址 476 * @param length 长度(字节) 477 * @param paddr 物理地址 478 * @return int 返回码 479 */ 480 int mm_map(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t paddr); 481 482 /** 483 * @brief 在页表中取消指定的vma的映射 484 * 485 * @param mm 指定的mm 486 * @param vma 待取消映射的vma 487 * @param paddr 返回的被取消映射的起始物理地址 488 * @return int 返回码 489 */ 490 int mm_unmap_vma(struct mm_struct *mm, struct vm_area_struct *vma, uint64_t *paddr); 491 492 /** 493 * @brief 解除一段虚拟地址的映射(这些地址必须在vma中存在) 494 * 495 * @param mm 内存空间结构体 496 * @param vaddr 起始地址 497 * @param length 结束地址 498 * @param destroy 是否释放vma结构体 499 * @return int 错误码 500 */ 501 int mm_unmap(struct mm_struct *mm, uint64_t vaddr, uint64_t length, bool destroy); 502 503 /** 504 * @brief 检测是否为有效的2M页(物理内存页) 505 * 506 * @param paddr 物理地址 507 * @return int8_t 是 -> 1 508 * 否 -> 0 509 */ 510 int8_t mm_is_2M_page(uint64_t paddr); 511 512 /** 513 * @brief 检查页表是否存在不为0的页表项 514 * 515 * @param ptr 页表基指针 516 * @return int8_t 存在 -> 1 517 * 不存在 -> 0 518 */ 519 int8_t mm_check_page_table(uint64_t *ptr); 520 521 /** 522 * @brief 调整堆区域的大小(暂时只能增加堆区域) 523 * 524 * @todo 缩小堆区域 525 * @param old_brk_end_addr 原本的堆内存区域的结束地址 526 * @param offset 新的地址相对于原地址的偏移量 527 * @return uint64_t 528 */ 529 uint64_t mm_do_brk(uint64_t old_brk_end_addr, int64_t offset); 530 531 /** 532 * @brief 获取系统当前的内存信息(未上锁,不一定精准) 533 * 534 * @return struct mm_stat_t 内存信息结构体 535 */ 536 struct mm_stat_t mm_stat(); 537 538 /** 539 * @brief 检测指定地址是否已经被映射 540 * 541 * @param page_table_phys_addr 页表的物理地址 542 * @param virt_addr 要检测的地址 543 * @return true 已经被映射 544 * @return false 545 */ 546 bool mm_check_mapped(ul page_table_phys_addr, uint64_t virt_addr);