1002d1830SGleb Natapov #include "fwcfg.h" 27d36db35SAvi Kivity #include "vm.h" 37d36db35SAvi Kivity #include "libcflat.h" 47d36db35SAvi Kivity 57d36db35SAvi Kivity static void *free = 0; 67d36db35SAvi Kivity static void *vfree_top = 0; 77d36db35SAvi Kivity 87d36db35SAvi Kivity static void free_memory(void *mem, unsigned long size) 97d36db35SAvi Kivity { 107d36db35SAvi Kivity while (size >= PAGE_SIZE) { 117d36db35SAvi Kivity *(void **)mem = free; 127d36db35SAvi Kivity free = mem; 137d36db35SAvi Kivity mem += PAGE_SIZE; 147d36db35SAvi Kivity size -= PAGE_SIZE; 157d36db35SAvi Kivity } 167d36db35SAvi Kivity } 177d36db35SAvi Kivity 187d36db35SAvi Kivity void *alloc_page() 197d36db35SAvi Kivity { 207d36db35SAvi Kivity void *p; 217d36db35SAvi Kivity 227d36db35SAvi Kivity if (!free) 237d36db35SAvi Kivity return 0; 247d36db35SAvi Kivity 257d36db35SAvi Kivity p = free; 267d36db35SAvi Kivity free = *(void **)free; 277d36db35SAvi Kivity 287d36db35SAvi Kivity return p; 297d36db35SAvi Kivity } 307d36db35SAvi Kivity 317d36db35SAvi Kivity void free_page(void *page) 327d36db35SAvi Kivity { 337d36db35SAvi Kivity *(void **)page = free; 347d36db35SAvi Kivity free = page; 357d36db35SAvi Kivity } 367d36db35SAvi Kivity 377d36db35SAvi Kivity extern char edata; 387d36db35SAvi Kivity static unsigned long end_of_memory; 397d36db35SAvi Kivity 4004262816SPaolo Bonzini unsigned long *install_pte(unsigned long *cr3, 417d36db35SAvi Kivity int pte_level, 427d36db35SAvi Kivity void *virt, 437d36db35SAvi Kivity unsigned long pte, 447d36db35SAvi Kivity unsigned long *pt_page) 457d36db35SAvi Kivity { 467d36db35SAvi Kivity int level; 477d36db35SAvi Kivity unsigned long *pt = cr3; 487d36db35SAvi Kivity unsigned offset; 497d36db35SAvi Kivity 507d36db35SAvi Kivity for (level = PAGE_LEVEL; level > pte_level; --level) { 517d36db35SAvi Kivity offset = ((unsigned long)virt >> ((level-1) * PGDIR_WIDTH + 12)) & PGDIR_MASK; 52d10d16e1SAlexander Gordeev if (!(pt[offset] & PT_PRESENT_MASK)) { 537d36db35SAvi Kivity unsigned long *new_pt = pt_page; 547d36db35SAvi Kivity if (!new_pt) 557d36db35SAvi Kivity new_pt = alloc_page(); 567d36db35SAvi Kivity else 577d36db35SAvi Kivity pt_page = 0; 587d36db35SAvi Kivity memset(new_pt, 0, PAGE_SIZE); 59d10d16e1SAlexander Gordeev pt[offset] = virt_to_phys(new_pt) | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK; 607d36db35SAvi Kivity } 61d10d16e1SAlexander Gordeev pt = phys_to_virt(pt[offset] & PT_ADDR_MASK); 627d36db35SAvi Kivity } 637d36db35SAvi Kivity offset = ((unsigned long)virt >> ((level-1) * PGDIR_WIDTH + 12)) & PGDIR_MASK; 647d36db35SAvi Kivity pt[offset] = pte; 6504262816SPaolo Bonzini return &pt[offset]; 667d36db35SAvi Kivity } 677d36db35SAvi Kivity 6804262816SPaolo Bonzini unsigned long *get_pte(unsigned long *cr3, void *virt) 697d36db35SAvi Kivity { 707d36db35SAvi Kivity int level; 717d36db35SAvi Kivity unsigned long *pt = cr3, pte; 727d36db35SAvi Kivity unsigned offset; 737d36db35SAvi Kivity 747d36db35SAvi Kivity for (level = PAGE_LEVEL; level > 1; --level) { 757d36db35SAvi Kivity offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) & PGDIR_MASK; 767d36db35SAvi Kivity pte = pt[offset]; 77d10d16e1SAlexander Gordeev if (!(pte & PT_PRESENT_MASK)) 7804262816SPaolo Bonzini return NULL; 79d10d16e1SAlexander Gordeev if (level == 2 && (pte & PT_PAGE_SIZE_MASK)) 8004262816SPaolo Bonzini return &pt[offset]; 81d10d16e1SAlexander Gordeev pt = phys_to_virt(pte & PT_ADDR_MASK); 827d36db35SAvi Kivity } 837d36db35SAvi Kivity offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) & PGDIR_MASK; 8404262816SPaolo Bonzini return &pt[offset]; 857d36db35SAvi Kivity } 867d36db35SAvi Kivity 8704262816SPaolo Bonzini unsigned long *install_large_page(unsigned long *cr3, 887d36db35SAvi Kivity unsigned long phys, 897d36db35SAvi Kivity void *virt) 907d36db35SAvi Kivity { 9104262816SPaolo Bonzini return install_pte(cr3, 2, virt, 92d10d16e1SAlexander Gordeev phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK | PT_PAGE_SIZE_MASK, 0); 937d36db35SAvi Kivity } 947d36db35SAvi Kivity 9504262816SPaolo Bonzini unsigned long *install_page(unsigned long *cr3, 967d36db35SAvi Kivity unsigned long phys, 977d36db35SAvi Kivity void *virt) 987d36db35SAvi Kivity { 99d10d16e1SAlexander Gordeev return install_pte(cr3, 1, virt, phys | PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK, 0); 1007d36db35SAvi Kivity } 1017d36db35SAvi Kivity 1027d36db35SAvi Kivity 10363254428SGleb Natapov static void setup_mmu_range(unsigned long *cr3, unsigned long start, 10463254428SGleb Natapov unsigned long len) 10563254428SGleb Natapov { 10663254428SGleb Natapov u64 max = (u64)len + (u64)start; 10763254428SGleb Natapov u64 phys = start; 10863254428SGleb Natapov 10963254428SGleb Natapov while (phys + LARGE_PAGE_SIZE <= max) { 11063254428SGleb Natapov install_large_page(cr3, phys, (void *)(ulong)phys); 11163254428SGleb Natapov phys += LARGE_PAGE_SIZE; 11263254428SGleb Natapov } 11363254428SGleb Natapov while (phys + PAGE_SIZE <= max) { 11463254428SGleb Natapov install_page(cr3, phys, (void *)(ulong)phys); 11563254428SGleb Natapov phys += PAGE_SIZE; 11663254428SGleb Natapov } 11763254428SGleb Natapov } 11863254428SGleb Natapov 1197d36db35SAvi Kivity static void setup_mmu(unsigned long len) 1207d36db35SAvi Kivity { 1217d36db35SAvi Kivity unsigned long *cr3 = alloc_page(); 1227d36db35SAvi Kivity 1237d36db35SAvi Kivity memset(cr3, 0, PAGE_SIZE); 12463254428SGleb Natapov 12563254428SGleb Natapov #ifdef __x86_64__ 12663254428SGleb Natapov if (len < (1ul << 32)) 12763254428SGleb Natapov len = (1ul << 32); /* map mmio 1:1 */ 12863254428SGleb Natapov 12963254428SGleb Natapov setup_mmu_range(cr3, 0, len); 13063254428SGleb Natapov #else 13163254428SGleb Natapov if (len > (1ul << 31)) 13263254428SGleb Natapov len = (1ul << 31); 13363254428SGleb Natapov 13463254428SGleb Natapov /* 0 - 2G memory, 2G-3G valloc area, 3G-4G mmio */ 13563254428SGleb Natapov setup_mmu_range(cr3, 0, len); 13663254428SGleb Natapov setup_mmu_range(cr3, 3ul << 30, (1ul << 30)); 13763254428SGleb Natapov vfree_top = (void*)(3ul << 30); 13863254428SGleb Natapov #endif 13963254428SGleb Natapov 1407d36db35SAvi Kivity write_cr3(virt_to_phys(cr3)); 1417d36db35SAvi Kivity #ifndef __x86_64__ 1427d36db35SAvi Kivity write_cr4(X86_CR4_PSE); 1437d36db35SAvi Kivity #endif 14497011120SGleb Natapov write_cr0(X86_CR0_PG |X86_CR0_PE | X86_CR0_WP); 1457d36db35SAvi Kivity 1467d36db35SAvi Kivity printf("paging enabled\n"); 147b006d7ebSAndrew Jones printf("cr0 = %lx\n", read_cr0()); 148b006d7ebSAndrew Jones printf("cr3 = %lx\n", read_cr3()); 149b006d7ebSAndrew Jones printf("cr4 = %lx\n", read_cr4()); 1507d36db35SAvi Kivity } 1517d36db35SAvi Kivity 1527d36db35SAvi Kivity void setup_vm() 1537d36db35SAvi Kivity { 154*f7b87da6SPeter Xu assert(!end_of_memory); 155002d1830SGleb Natapov end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE); 1567d36db35SAvi Kivity free_memory(&edata, end_of_memory - (unsigned long)&edata); 1577d36db35SAvi Kivity setup_mmu(end_of_memory); 1587d36db35SAvi Kivity } 1597d36db35SAvi Kivity 1607d36db35SAvi Kivity void *vmalloc(unsigned long size) 1617d36db35SAvi Kivity { 1627d36db35SAvi Kivity void *mem, *p; 1637d36db35SAvi Kivity unsigned pages; 1647d36db35SAvi Kivity 1657d36db35SAvi Kivity size += sizeof(unsigned long); 1667d36db35SAvi Kivity 1677d36db35SAvi Kivity size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); 1687d36db35SAvi Kivity vfree_top -= size; 1697d36db35SAvi Kivity mem = p = vfree_top; 1707d36db35SAvi Kivity pages = size / PAGE_SIZE; 1717d36db35SAvi Kivity while (pages--) { 1727d36db35SAvi Kivity install_page(phys_to_virt(read_cr3()), virt_to_phys(alloc_page()), p); 1737d36db35SAvi Kivity p += PAGE_SIZE; 1747d36db35SAvi Kivity } 1757d36db35SAvi Kivity *(unsigned long *)mem = size; 1767d36db35SAvi Kivity mem += sizeof(unsigned long); 1777d36db35SAvi Kivity return mem; 1787d36db35SAvi Kivity } 1797d36db35SAvi Kivity 180334cd2bfSGleb Natapov uint64_t virt_to_phys_cr3(void *mem) 181334cd2bfSGleb Natapov { 182d10d16e1SAlexander Gordeev return (*get_pte(phys_to_virt(read_cr3()), mem) & PT_ADDR_MASK) + ((ulong)mem & (PAGE_SIZE - 1)); 183334cd2bfSGleb Natapov } 184334cd2bfSGleb Natapov 1857d36db35SAvi Kivity void vfree(void *mem) 1867d36db35SAvi Kivity { 1877d36db35SAvi Kivity unsigned long size = ((unsigned long *)mem)[-1]; 1887d36db35SAvi Kivity 1897d36db35SAvi Kivity while (size) { 190d10d16e1SAlexander Gordeev free_page(phys_to_virt(*get_pte(phys_to_virt(read_cr3()), mem) & PT_ADDR_MASK)); 1917d36db35SAvi Kivity mem += PAGE_SIZE; 1927d36db35SAvi Kivity size -= PAGE_SIZE; 1937d36db35SAvi Kivity } 1947d36db35SAvi Kivity } 1957d36db35SAvi Kivity 1967d36db35SAvi Kivity void *vmap(unsigned long long phys, unsigned long size) 1977d36db35SAvi Kivity { 1987d36db35SAvi Kivity void *mem, *p; 1997d36db35SAvi Kivity unsigned pages; 2007d36db35SAvi Kivity 2017d36db35SAvi Kivity size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); 2027d36db35SAvi Kivity vfree_top -= size; 2037d36db35SAvi Kivity phys &= ~(unsigned long long)(PAGE_SIZE - 1); 2047d36db35SAvi Kivity 2057d36db35SAvi Kivity mem = p = vfree_top; 2067d36db35SAvi Kivity pages = size / PAGE_SIZE; 2077d36db35SAvi Kivity while (pages--) { 2087d36db35SAvi Kivity install_page(phys_to_virt(read_cr3()), phys, p); 2097d36db35SAvi Kivity phys += PAGE_SIZE; 2107d36db35SAvi Kivity p += PAGE_SIZE; 2117d36db35SAvi Kivity } 2127d36db35SAvi Kivity return mem; 2137d36db35SAvi Kivity } 214a4b87a16SGleb Natapov 215524ae896SAvi Kivity void *alloc_vpages(ulong nr) 216524ae896SAvi Kivity { 217524ae896SAvi Kivity vfree_top -= PAGE_SIZE * nr; 218524ae896SAvi Kivity return vfree_top; 219524ae896SAvi Kivity } 220524ae896SAvi Kivity 221a4b87a16SGleb Natapov void *alloc_vpage(void) 222a4b87a16SGleb Natapov { 223524ae896SAvi Kivity return alloc_vpages(1); 224a4b87a16SGleb Natapov } 225