#include "paging.h" #include "errno.h" #include "klibc.h" #include "mem.h" #include "stdarg.h" // In a Vaddr, 10 first bit (MSB) are the index in the Page Directory. A Page Directory Entry // point to a Page Table. The 10 next bits are then an index in this Page Table. A Page Table // Entry then point to a physical address at which is added the remaining 12 bits. So they are // 1024 entry in the PD, each of them pointing to a PT of 1024 entry. Each PTE pointing to 4K // page. First address (up to page_desc from mem.c) are mapped such as Paddr == Vaddr. To make // PD always accessible a (x86?) trick is used : The mirroring. A given entry N in the PD point // to the PD (this is possible because PDE very looks like PTE in x86). So N << (10 + 12 = 4Mo) // point to the Paddr of PD. Then, accessing N * 4Mo + I * 4Ko is accessing the PT of the Ieme // entry in the PD (as MMU take the PD pointed by the PDE number N like a PT). More // particularly, accessing N * 4Mo + N * 4ko is accessing the PD. // // PD is at Vaddr N * 4Mo and take 4ko. Each PT are allocated dynamically. // Just make sure that N have not been used by identity mapping #define PT_SHIFT 12 #define PTE_MASK 0x3ff // 10bits #define PD_SHIFT 22 #define PD_MIRROR_PAGE_IDX 1023U static unsigned long mappedPage = 0; struct pde { uint32_t present : 1; uint32_t write : 1; // 0 read - 1 RW uint32_t user : 1; // 0 supervisor - 1 user uint32_t write_through : 1; // 0 write-back - 1 write_through uint32_t cache_disable : 1; uint32_t access : 1; // have been accessed uint32_t zero : 1; // Not used uint32_t size : 1; // 0 for 4Kb 1 for 4Mb uint32_t ignored : 1; uint32_t available : 3; uint32_t pt_addr : 20; } __attribute__((packed)); struct pte { uint32_t present : 1; uint32_t write : 1; // 0 read - 1 RW uint32_t user : 1; // 0 supervisor - 1 user uint32_t write_through : 1; // 0 write-back - 1 write_through uint32_t cache_disable : 1; uint32_t access : 1; // have been accessed uint32_t dirty : 1; // if set, indicates that page has been written to. This flag is // not updated by the CPU, and once set will not unset itself. uint32_t zero : 1; // if PAT is supported, shall indicate the memory type. Otherwise, // it must be 0. uint32_t global : 1; // if set, prevents the TLB from updating the address in its cache // if CR3 is reset. Note, that the page global enable bit in CR4 // must be set to enable this feature. uint32_t available : 3; uint32_t paddr : 20; } __attribute__((packed)); struct pdbr { uint32_t zero1 : 3; // reserved uint32_t write_through : 1; // 0 write-back - 1 write-through uint32_t cache_disabled : 1; // 1=cache disabled uint32_t zero2 : 7; // reserved uint32_t pd_paddr : 20; } __attribute__((packed)); // invalidate the TLB entry for the page located at the given virtual address static inline void __native_flush_tlb_single(unsigned long addr) { asm volatile("invlpg (%0)" ::"r"(addr) : "memory"); } int pagingSetup(paddr_t lowerKernelAddr, paddr_t upperKernelAddr) { struct pdbr cr3; // x86 got 1024 of pde for 4Byte each: 4ko ! struct pde *pd = (struct pde *)allocPhyPage(1); memset(pd, 0, PAGE_SIZE); memset(&cr3, 0x0, sizeof(struct pdbr)); cr3.pd_paddr = ((paddr_t)pd) >> 12; // MMU not enabled for the moment. No need to use mirroring // Identity mapping up to upperKernelAddr for (paddr_t i = lowerKernelAddr; i < upperKernelAddr; i += PAGE_SIZE) { uint pdEntry = i >> (PD_SHIFT); uint ptEntry = (i >> PT_SHIFT) & PTE_MASK; struct pte *pt; if (pd[pdEntry].present) { pt = (struct pte *)(pd[pdEntry].pt_addr << PT_SHIFT); refPhyPage((paddr_t)pt); } else { pt = (struct pte *)allocPhyPage(1); memset(pt, 0, PAGE_SIZE); pd[pdEntry].present = 1; pd[pdEntry].write = 1; pd[pdEntry].pt_addr = ((paddr_t)pt >> PT_SHIFT); } pt[ptEntry].present = 1; pt[ptEntry].write = 1; // TODO set Kernel code as RO pt[ptEntry].paddr = i >> PAGE_SHIFT; } // Setup mirroring pd[PD_MIRROR_PAGE_IDX].present = 1; pd[PD_MIRROR_PAGE_IDX].write = 1; pd[PD_MIRROR_PAGE_IDX].pt_addr = ((paddr_t)pd >> PT_SHIFT); // Loading of the PDBR in the MMU: asm volatile("movl %0,%%cr3\n\t" "movl %%cr0,%%eax\n\t" "orl $0x80010000, %%eax\n\t" /* bit 31 | bit 16 */ "movl %%eax,%%cr0\n\t" "jmp 1f\n\t" "1:\n\t" "movl $2f, %%eax\n\t" "jmp *%%eax\n\t" "2:\n\t" ::"r"(cr3) : "memory", "eax"); return 0; } int pageMap(vaddr_t vaddr, paddr_t paddr, int flags) { uint pdEntry = vaddr >> (PD_SHIFT); uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK; // Thank to mirroring, we can access the PD struct pde *pd = (struct pde *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (PD_MIRROR_PAGE_IDX << PT_SHIFT)); struct pte *pt = (struct pte *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (pdEntry << PT_SHIFT)); if (!pd[pdEntry].present) { paddr_t ptPhy = allocPhyPage(1); if (ptPhy == (vaddr_t)NULL) return -ENOMEM; pd[pdEntry].user = (flags & PAGING_MEM_USER) ? 1 : 0; pd[pdEntry].present = 1; pd[pdEntry].write = 1; pd[pdEntry].pt_addr = (ptPhy >> PT_SHIFT); __native_flush_tlb_single((vaddr_t)pt); memset((void *)pt, 0, PAGE_SIZE); } else { // Already mapped ? Remove old mapping if (pt[ptEntry].present) { unrefPhyPage(pt[ptEntry].paddr << PAGE_SHIFT); } // PTE not already used ? We will use it ! So increase the PT ref count else { refPhyPage(pd[pdEntry].pt_addr << PAGE_SHIFT); } } pt[ptEntry].user = (flags & PAGING_MEM_USER) ? 1 : 0; pt[ptEntry].present = 1; pt[ptEntry].write = (flags & PAGING_MEM_WRITE) ? 1 : 0; pt[ptEntry].paddr = paddr >> PAGE_SHIFT; refPhyPage(paddr); __native_flush_tlb_single(vaddr); mappedPage++; return 0; } int pageUnmap(vaddr_t vaddr) { uint pdEntry = vaddr >> (PD_SHIFT); uint ptEntry = (vaddr >> PT_SHIFT) & PTE_MASK; // Thank to mirroring, we can access the PD struct pde *pd = (struct pde *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (PD_MIRROR_PAGE_IDX << PT_SHIFT)); struct pte *pt = (struct pte *)((PD_MIRROR_PAGE_IDX << PD_SHIFT) + (pdEntry << PT_SHIFT)); if (!pd[pdEntry].present) return -EINVAL; if (!pt[ptEntry].present) return -EINVAL; unrefPhyPage(pt[ptEntry].paddr << PAGE_SHIFT); pt[ptEntry].present = 0; // PTE not used. Decrease refcount on it. Is PT not used anymore ? if (unrefPhyPage(pd[pdEntry].pt_addr << PT_SHIFT) == 0) { pd[pdEntry].present = 0; __native_flush_tlb_single((vaddr_t)pt); } __native_flush_tlb_single(vaddr); mappedPage--; return 0; } unsigned long getNbMappedPage(void) { return mappedPage; }