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