17d36db35SAvi Kivity #include "vm.h" 27d36db35SAvi Kivity #include "libcflat.h" 3efd8e5aaSPaolo Bonzini #include "vmalloc.h" 45aca024eSPaolo Bonzini #include "alloc_page.h" 57d36db35SAvi Kivity 604262816SPaolo Bonzini unsigned long *install_pte(unsigned long *cr3, 77d36db35SAvi Kivity int pte_level, 87d36db35SAvi Kivity void *virt, 97d36db35SAvi Kivity unsigned long pte, 107d36db35SAvi Kivity unsigned long *pt_page) 117d36db35SAvi Kivity { 127d36db35SAvi Kivity int level; 137d36db35SAvi Kivity unsigned long *pt = cr3; 147d36db35SAvi Kivity unsigned offset; 157d36db35SAvi Kivity 167d36db35SAvi Kivity for (level = PAGE_LEVEL; level > pte_level; --level) { 179d7e08c0SPeter Xu offset = PGDIR_OFFSET((unsigned long)virt, level); 18d10d16e1SAlexander Gordeev if (!(pt[offset] & PT_PRESENT_MASK)) { 197d36db35SAvi Kivity unsigned long *new_pt = pt_page; 207d36db35SAvi Kivity if (!new_pt) 217d36db35SAvi Kivity new_pt = alloc_page(); 227d36db35SAvi Kivity else 237d36db35SAvi Kivity pt_page = 0; 247d36db35SAvi Kivity memset(new_pt, 0, PAGE_SIZE); 25d10d16e1SAlexander Gordeev pt[offset] = virt_to_phys(new_pt) | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK; 267d36db35SAvi Kivity } 27d10d16e1SAlexander Gordeev pt = phys_to_virt(pt[offset] & PT_ADDR_MASK); 287d36db35SAvi Kivity } 299d7e08c0SPeter Xu offset = PGDIR_OFFSET((unsigned long)virt, level); 307d36db35SAvi Kivity pt[offset] = pte; 3104262816SPaolo Bonzini return &pt[offset]; 327d36db35SAvi Kivity } 337d36db35SAvi Kivity 341df80b57SPeter Feiner /* 351df80b57SPeter Feiner * Finds last PTE in the mapping of @virt that's at or above @lowest_level. The 361df80b57SPeter Feiner * returned PTE isn't necessarily present, but its parent is. 371df80b57SPeter Feiner */ 381df80b57SPeter Feiner struct pte_search find_pte_level(unsigned long *cr3, void *virt, 391df80b57SPeter Feiner int lowest_level) 407d36db35SAvi Kivity { 417d36db35SAvi Kivity unsigned long *pt = cr3, pte; 427d36db35SAvi Kivity unsigned offset; 431df80b57SPeter Feiner unsigned long shift; 441df80b57SPeter Feiner struct pte_search r; 457d36db35SAvi Kivity 461df80b57SPeter Feiner assert(lowest_level >= 1 && lowest_level <= PAGE_LEVEL); 471df80b57SPeter Feiner 481df80b57SPeter Feiner for (r.level = PAGE_LEVEL;; --r.level) { 491df80b57SPeter Feiner shift = (r.level - 1) * PGDIR_WIDTH + 12; 501df80b57SPeter Feiner offset = ((unsigned long)virt >> shift) & PGDIR_MASK; 511df80b57SPeter Feiner r.pte = &pt[offset]; 521df80b57SPeter Feiner pte = *r.pte; 531df80b57SPeter Feiner 54d10d16e1SAlexander Gordeev if (!(pte & PT_PRESENT_MASK)) 551df80b57SPeter Feiner return r; 561df80b57SPeter Feiner 571df80b57SPeter Feiner if ((r.level == 2 || r.level == 3) && (pte & PT_PAGE_SIZE_MASK)) 581df80b57SPeter Feiner return r; 591df80b57SPeter Feiner 601df80b57SPeter Feiner if (r.level == lowest_level) 611df80b57SPeter Feiner return r; 621df80b57SPeter Feiner 631df80b57SPeter Feiner pt = phys_to_virt(pte & 0xffffffffff000ull); 647d36db35SAvi Kivity } 651df80b57SPeter Feiner } 661df80b57SPeter Feiner 671df80b57SPeter Feiner /* 681df80b57SPeter Feiner * Returns the leaf PTE in the mapping of @virt (i.e., 4K PTE or a present huge 691df80b57SPeter Feiner * PTE). Returns NULL if no leaf PTE exists. 701df80b57SPeter Feiner */ 711df80b57SPeter Feiner unsigned long *get_pte(unsigned long *cr3, void *virt) 721df80b57SPeter Feiner { 731df80b57SPeter Feiner struct pte_search search; 741df80b57SPeter Feiner 751df80b57SPeter Feiner search = find_pte_level(cr3, virt, 1); 761df80b57SPeter Feiner return found_leaf_pte(search) ? search.pte : NULL; 771df80b57SPeter Feiner } 781df80b57SPeter Feiner 791df80b57SPeter Feiner /* 801df80b57SPeter Feiner * Returns the PTE in the mapping of @virt at the given level @pte_level. 811df80b57SPeter Feiner * Returns NULL if the PT at @pte_level isn't present (i.e., the mapping at 821df80b57SPeter Feiner * @pte_level - 1 isn't present). 831df80b57SPeter Feiner */ 841df80b57SPeter Feiner unsigned long *get_pte_level(unsigned long *cr3, void *virt, int pte_level) 851df80b57SPeter Feiner { 861df80b57SPeter Feiner struct pte_search search; 871df80b57SPeter Feiner 881df80b57SPeter Feiner search = find_pte_level(cr3, virt, pte_level); 891df80b57SPeter Feiner return search.level == pte_level ? search.pte : NULL; 907d36db35SAvi Kivity } 917d36db35SAvi Kivity 9204262816SPaolo Bonzini unsigned long *install_large_page(unsigned long *cr3, 937d36db35SAvi Kivity unsigned long phys, 947d36db35SAvi Kivity void *virt) 957d36db35SAvi Kivity { 9604262816SPaolo Bonzini return install_pte(cr3, 2, virt, 97d10d16e1SAlexander Gordeev phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK | PT_PAGE_SIZE_MASK, 0); 987d36db35SAvi Kivity } 997d36db35SAvi Kivity 10004262816SPaolo Bonzini unsigned long *install_page(unsigned long *cr3, 1017d36db35SAvi Kivity unsigned long phys, 1027d36db35SAvi Kivity void *virt) 1037d36db35SAvi Kivity { 104d10d16e1SAlexander Gordeev return install_pte(cr3, 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK, 0); 1057d36db35SAvi Kivity } 1067d36db35SAvi Kivity 1071df80b57SPeter Feiner void install_pages(unsigned long *cr3, unsigned long phys, unsigned long len, 1081df80b57SPeter Feiner void *virt) 1091df80b57SPeter Feiner { 1101df80b57SPeter Feiner unsigned long max = (u64)len + (u64)phys; 1111df80b57SPeter Feiner assert(phys % PAGE_SIZE == 0); 1121df80b57SPeter Feiner assert((unsigned long) virt % PAGE_SIZE == 0); 1131df80b57SPeter Feiner assert(len % PAGE_SIZE == 0); 1141df80b57SPeter Feiner 1151df80b57SPeter Feiner while (phys + PAGE_SIZE <= max) { 1161df80b57SPeter Feiner install_page(cr3, phys, virt); 1171df80b57SPeter Feiner phys += PAGE_SIZE; 1181df80b57SPeter Feiner virt = (char *) virt + PAGE_SIZE; 1191df80b57SPeter Feiner } 1201df80b57SPeter Feiner } 1211df80b57SPeter Feiner 1221df80b57SPeter Feiner bool any_present_pages(unsigned long *cr3, void *virt, unsigned long len) 1231df80b57SPeter Feiner { 1241df80b57SPeter Feiner unsigned long max = (unsigned long) virt + len; 1251df80b57SPeter Feiner unsigned long curr; 1261df80b57SPeter Feiner 1271df80b57SPeter Feiner for (curr = (unsigned long) virt; curr < max; curr += PAGE_SIZE) { 1281df80b57SPeter Feiner unsigned long *ptep = get_pte(cr3, (void *) curr); 1291df80b57SPeter Feiner if (ptep && (*ptep & PT_PRESENT_MASK)) 1301df80b57SPeter Feiner return true; 1311df80b57SPeter Feiner } 1321df80b57SPeter Feiner return false; 1331df80b57SPeter Feiner } 1347d36db35SAvi Kivity 13563254428SGleb Natapov static void setup_mmu_range(unsigned long *cr3, unsigned long start, 13663254428SGleb Natapov unsigned long len) 13763254428SGleb Natapov { 13863254428SGleb Natapov u64 max = (u64)len + (u64)start; 13963254428SGleb Natapov u64 phys = start; 14063254428SGleb Natapov 14163254428SGleb Natapov while (phys + LARGE_PAGE_SIZE <= max) { 14263254428SGleb Natapov install_large_page(cr3, phys, (void *)(ulong)phys); 14363254428SGleb Natapov phys += LARGE_PAGE_SIZE; 14463254428SGleb Natapov } 1451df80b57SPeter Feiner install_pages(cr3, phys, max - phys, (void *)(ulong)phys); 14663254428SGleb Natapov } 14763254428SGleb Natapov 148*937e2392SPaolo Bonzini void *setup_mmu(phys_addr_t end_of_memory) 1497d36db35SAvi Kivity { 1507d36db35SAvi Kivity unsigned long *cr3 = alloc_page(); 1517d36db35SAvi Kivity 1527d36db35SAvi Kivity memset(cr3, 0, PAGE_SIZE); 15363254428SGleb Natapov 15463254428SGleb Natapov #ifdef __x86_64__ 155*937e2392SPaolo Bonzini if (end_of_memory < (1ul << 32)) 156*937e2392SPaolo Bonzini end_of_memory = (1ul << 32); /* map mmio 1:1 */ 15763254428SGleb Natapov 158*937e2392SPaolo Bonzini setup_mmu_range(cr3, 0, end_of_memory); 15963254428SGleb Natapov #else 160*937e2392SPaolo Bonzini if (end_of_memory > (1ul << 31)) 161*937e2392SPaolo Bonzini end_of_memory = (1ul << 31); 16263254428SGleb Natapov 16363254428SGleb Natapov /* 0 - 2G memory, 2G-3G valloc area, 3G-4G mmio */ 164*937e2392SPaolo Bonzini setup_mmu_range(cr3, 0, end_of_memory); 16563254428SGleb Natapov setup_mmu_range(cr3, 3ul << 30, (1ul << 30)); 166efd8e5aaSPaolo Bonzini init_alloc_vpage((void*)(3ul << 30)); 16763254428SGleb Natapov #endif 16863254428SGleb Natapov 1697d36db35SAvi Kivity write_cr3(virt_to_phys(cr3)); 1707d36db35SAvi Kivity #ifndef __x86_64__ 1717d36db35SAvi Kivity write_cr4(X86_CR4_PSE); 1727d36db35SAvi Kivity #endif 17397011120SGleb Natapov write_cr0(X86_CR0_PG |X86_CR0_PE | X86_CR0_WP); 1747d36db35SAvi Kivity 1757d36db35SAvi Kivity printf("paging enabled\n"); 176b006d7ebSAndrew Jones printf("cr0 = %lx\n", read_cr0()); 177b006d7ebSAndrew Jones printf("cr3 = %lx\n", read_cr3()); 178b006d7ebSAndrew Jones printf("cr4 = %lx\n", read_cr4()); 179*937e2392SPaolo Bonzini return cr3; 1807d36db35SAvi Kivity } 1817d36db35SAvi Kivity 1827d36db35SAvi Kivity void *vmalloc(unsigned long size) 1837d36db35SAvi Kivity { 1847d36db35SAvi Kivity void *mem, *p; 1857d36db35SAvi Kivity unsigned pages; 1867d36db35SAvi Kivity 1877d36db35SAvi Kivity size += sizeof(unsigned long); 1887d36db35SAvi Kivity 1897d36db35SAvi Kivity size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); 1907d36db35SAvi Kivity pages = size / PAGE_SIZE; 191efd8e5aaSPaolo Bonzini mem = p = alloc_vpages(pages); 1927d36db35SAvi Kivity while (pages--) { 1937d36db35SAvi Kivity install_page(phys_to_virt(read_cr3()), virt_to_phys(alloc_page()), p); 1947d36db35SAvi Kivity p += PAGE_SIZE; 1957d36db35SAvi Kivity } 1967d36db35SAvi Kivity *(unsigned long *)mem = size; 1977d36db35SAvi Kivity mem += sizeof(unsigned long); 1987d36db35SAvi Kivity return mem; 1997d36db35SAvi Kivity } 2007d36db35SAvi Kivity 201334cd2bfSGleb Natapov uint64_t virt_to_phys_cr3(void *mem) 202334cd2bfSGleb Natapov { 203d10d16e1SAlexander Gordeev return (*get_pte(phys_to_virt(read_cr3()), mem) & PT_ADDR_MASK) + ((ulong)mem & (PAGE_SIZE - 1)); 204334cd2bfSGleb Natapov } 205334cd2bfSGleb Natapov 2067d36db35SAvi Kivity void vfree(void *mem) 2077d36db35SAvi Kivity { 2087d36db35SAvi Kivity unsigned long size = ((unsigned long *)mem)[-1]; 2097d36db35SAvi Kivity 2107d36db35SAvi Kivity while (size) { 211d10d16e1SAlexander Gordeev free_page(phys_to_virt(*get_pte(phys_to_virt(read_cr3()), mem) & PT_ADDR_MASK)); 2127d36db35SAvi Kivity mem += PAGE_SIZE; 2137d36db35SAvi Kivity size -= PAGE_SIZE; 2147d36db35SAvi Kivity } 2157d36db35SAvi Kivity } 2167d36db35SAvi Kivity 2177d36db35SAvi Kivity void *vmap(unsigned long long phys, unsigned long size) 2187d36db35SAvi Kivity { 2197d36db35SAvi Kivity void *mem, *p; 2207d36db35SAvi Kivity unsigned pages; 2217d36db35SAvi Kivity 2227d36db35SAvi Kivity size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); 2237d36db35SAvi Kivity pages = size / PAGE_SIZE; 224efd8e5aaSPaolo Bonzini mem = p = alloc_vpages(pages); 225efd8e5aaSPaolo Bonzini 226efd8e5aaSPaolo Bonzini phys &= ~(unsigned long long)(PAGE_SIZE - 1); 2277d36db35SAvi Kivity while (pages--) { 2287d36db35SAvi Kivity install_page(phys_to_virt(read_cr3()), phys, p); 2297d36db35SAvi Kivity phys += PAGE_SIZE; 2307d36db35SAvi Kivity p += PAGE_SIZE; 2317d36db35SAvi Kivity } 2327d36db35SAvi Kivity return mem; 2337d36db35SAvi Kivity } 234