1*002d1830SGleb Natapov #include "fwcfg.h" 27d36db35SAvi Kivity #include "vm.h" 37d36db35SAvi Kivity #include "libcflat.h" 47d36db35SAvi Kivity 57d36db35SAvi Kivity #define PAGE_SIZE 4096ul 67d36db35SAvi Kivity #ifdef __x86_64__ 77d36db35SAvi Kivity #define LARGE_PAGE_SIZE (512 * PAGE_SIZE) 87d36db35SAvi Kivity #else 97d36db35SAvi Kivity #define LARGE_PAGE_SIZE (1024 * PAGE_SIZE) 107d36db35SAvi Kivity #endif 117d36db35SAvi Kivity 127d36db35SAvi Kivity #define X86_CR0_PE 0x00000001 1397011120SGleb Natapov #define X86_CR0_WP 0x00010000 147d36db35SAvi Kivity #define X86_CR0_PG 0x80000000 157d36db35SAvi Kivity #define X86_CR4_PSE 0x00000010 167d36db35SAvi Kivity static void *free = 0; 177d36db35SAvi Kivity static void *vfree_top = 0; 187d36db35SAvi Kivity 197d36db35SAvi Kivity static void free_memory(void *mem, unsigned long size) 207d36db35SAvi Kivity { 217d36db35SAvi Kivity while (size >= PAGE_SIZE) { 227d36db35SAvi Kivity *(void **)mem = free; 237d36db35SAvi Kivity free = mem; 247d36db35SAvi Kivity mem += PAGE_SIZE; 257d36db35SAvi Kivity size -= PAGE_SIZE; 267d36db35SAvi Kivity } 277d36db35SAvi Kivity } 287d36db35SAvi Kivity 297d36db35SAvi Kivity void *alloc_page() 307d36db35SAvi Kivity { 317d36db35SAvi Kivity void *p; 327d36db35SAvi Kivity 337d36db35SAvi Kivity if (!free) 347d36db35SAvi Kivity return 0; 357d36db35SAvi Kivity 367d36db35SAvi Kivity p = free; 377d36db35SAvi Kivity free = *(void **)free; 387d36db35SAvi Kivity 397d36db35SAvi Kivity return p; 407d36db35SAvi Kivity } 417d36db35SAvi Kivity 427d36db35SAvi Kivity void free_page(void *page) 437d36db35SAvi Kivity { 447d36db35SAvi Kivity *(void **)page = free; 457d36db35SAvi Kivity free = page; 467d36db35SAvi Kivity } 477d36db35SAvi Kivity 487d36db35SAvi Kivity extern char edata; 497d36db35SAvi Kivity static unsigned long end_of_memory; 507d36db35SAvi Kivity 517d36db35SAvi Kivity #ifdef __x86_64__ 527d36db35SAvi Kivity #define PAGE_LEVEL 4 537d36db35SAvi Kivity #define PGDIR_WIDTH 9 547d36db35SAvi Kivity #define PGDIR_MASK 511 557d36db35SAvi Kivity #else 567d36db35SAvi Kivity #define PAGE_LEVEL 2 577d36db35SAvi Kivity #define PGDIR_WIDTH 10 587d36db35SAvi Kivity #define PGDIR_MASK 1023 597d36db35SAvi Kivity #endif 607d36db35SAvi Kivity 617d36db35SAvi Kivity void install_pte(unsigned long *cr3, 627d36db35SAvi Kivity int pte_level, 637d36db35SAvi Kivity void *virt, 647d36db35SAvi Kivity unsigned long pte, 657d36db35SAvi Kivity unsigned long *pt_page) 667d36db35SAvi Kivity { 677d36db35SAvi Kivity int level; 687d36db35SAvi Kivity unsigned long *pt = cr3; 697d36db35SAvi Kivity unsigned offset; 707d36db35SAvi Kivity 717d36db35SAvi Kivity for (level = PAGE_LEVEL; level > pte_level; --level) { 727d36db35SAvi Kivity offset = ((unsigned long)virt >> ((level-1) * PGDIR_WIDTH + 12)) & PGDIR_MASK; 737d36db35SAvi Kivity if (!(pt[offset] & PTE_PRESENT)) { 747d36db35SAvi Kivity unsigned long *new_pt = pt_page; 757d36db35SAvi Kivity if (!new_pt) 767d36db35SAvi Kivity new_pt = alloc_page(); 777d36db35SAvi Kivity else 787d36db35SAvi Kivity pt_page = 0; 797d36db35SAvi Kivity memset(new_pt, 0, PAGE_SIZE); 807d36db35SAvi Kivity pt[offset] = virt_to_phys(new_pt) | PTE_PRESENT | PTE_WRITE; 817d36db35SAvi Kivity } 827d36db35SAvi Kivity pt = phys_to_virt(pt[offset] & 0xffffffffff000ull); 837d36db35SAvi Kivity } 847d36db35SAvi Kivity offset = ((unsigned long)virt >> ((level-1) * PGDIR_WIDTH + 12)) & PGDIR_MASK; 857d36db35SAvi Kivity pt[offset] = pte; 867d36db35SAvi Kivity } 877d36db35SAvi Kivity 887d36db35SAvi Kivity static unsigned long get_pte(unsigned long *cr3, void *virt) 897d36db35SAvi Kivity { 907d36db35SAvi Kivity int level; 917d36db35SAvi Kivity unsigned long *pt = cr3, pte; 927d36db35SAvi Kivity unsigned offset; 937d36db35SAvi Kivity 947d36db35SAvi Kivity for (level = PAGE_LEVEL; level > 1; --level) { 957d36db35SAvi Kivity offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) & PGDIR_MASK; 967d36db35SAvi Kivity pte = pt[offset]; 977d36db35SAvi Kivity if (!(pte & PTE_PRESENT)) 987d36db35SAvi Kivity return 0; 997d36db35SAvi Kivity if (level == 2 && (pte & PTE_PSE)) 1007d36db35SAvi Kivity return pte; 1017d36db35SAvi Kivity pt = phys_to_virt(pte & 0xffffffffff000ull); 1027d36db35SAvi Kivity } 1037d36db35SAvi Kivity offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) & PGDIR_MASK; 1047d36db35SAvi Kivity pte = pt[offset]; 1057d36db35SAvi Kivity return pte; 1067d36db35SAvi Kivity } 1077d36db35SAvi Kivity 1087d36db35SAvi Kivity void install_large_page(unsigned long *cr3, 1097d36db35SAvi Kivity unsigned long phys, 1107d36db35SAvi Kivity void *virt) 1117d36db35SAvi Kivity { 1127d36db35SAvi Kivity install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_PSE, 0); 1137d36db35SAvi Kivity } 1147d36db35SAvi Kivity 1157d36db35SAvi Kivity void install_page(unsigned long *cr3, 1167d36db35SAvi Kivity unsigned long phys, 1177d36db35SAvi Kivity void *virt) 1187d36db35SAvi Kivity { 1197d36db35SAvi Kivity install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE, 0); 1207d36db35SAvi Kivity } 1217d36db35SAvi Kivity 1227d36db35SAvi Kivity 1237d36db35SAvi Kivity static inline void load_gdt(unsigned long *table, int nent) 1247d36db35SAvi Kivity { 1257d36db35SAvi Kivity struct descriptor_table_ptr descr; 1267d36db35SAvi Kivity 1277d36db35SAvi Kivity descr.limit = nent * 8 - 1; 1287d36db35SAvi Kivity descr.base = (ulong)table; 1297d36db35SAvi Kivity lgdt(&descr); 1307d36db35SAvi Kivity } 1317d36db35SAvi Kivity 1327d36db35SAvi Kivity #define SEG_CS_32 8 1337d36db35SAvi Kivity #define SEG_CS_64 16 1347d36db35SAvi Kivity 1357d36db35SAvi Kivity struct ljmp { 1367d36db35SAvi Kivity void *ofs; 1377d36db35SAvi Kivity unsigned short seg; 1387d36db35SAvi Kivity }; 1397d36db35SAvi Kivity 14063254428SGleb Natapov static void setup_mmu_range(unsigned long *cr3, unsigned long start, 14163254428SGleb Natapov unsigned long len) 14263254428SGleb Natapov { 14363254428SGleb Natapov u64 max = (u64)len + (u64)start; 14463254428SGleb Natapov u64 phys = start; 14563254428SGleb Natapov 14663254428SGleb Natapov while (phys + LARGE_PAGE_SIZE <= max) { 14763254428SGleb Natapov install_large_page(cr3, phys, (void *)(ulong)phys); 14863254428SGleb Natapov phys += LARGE_PAGE_SIZE; 14963254428SGleb Natapov } 15063254428SGleb Natapov while (phys + PAGE_SIZE <= max) { 15163254428SGleb Natapov install_page(cr3, phys, (void *)(ulong)phys); 15263254428SGleb Natapov phys += PAGE_SIZE; 15363254428SGleb Natapov } 15463254428SGleb Natapov } 15563254428SGleb Natapov 1567d36db35SAvi Kivity static void setup_mmu(unsigned long len) 1577d36db35SAvi Kivity { 1587d36db35SAvi Kivity unsigned long *cr3 = alloc_page(); 1597d36db35SAvi Kivity 1607d36db35SAvi Kivity memset(cr3, 0, PAGE_SIZE); 16163254428SGleb Natapov 16263254428SGleb Natapov #ifdef __x86_64__ 16363254428SGleb Natapov if (len < (1ul << 32)) 16463254428SGleb Natapov len = (1ul << 32); /* map mmio 1:1 */ 16563254428SGleb Natapov 16663254428SGleb Natapov setup_mmu_range(cr3, 0, len); 16763254428SGleb Natapov #else 16863254428SGleb Natapov if (len > (1ul << 31)) 16963254428SGleb Natapov len = (1ul << 31); 17063254428SGleb Natapov 17163254428SGleb Natapov /* 0 - 2G memory, 2G-3G valloc area, 3G-4G mmio */ 17263254428SGleb Natapov setup_mmu_range(cr3, 0, len); 17363254428SGleb Natapov setup_mmu_range(cr3, 3ul << 30, (1ul << 30)); 17463254428SGleb Natapov vfree_top = (void*)(3ul << 30); 17563254428SGleb Natapov #endif 17663254428SGleb Natapov 1777d36db35SAvi Kivity write_cr3(virt_to_phys(cr3)); 1787d36db35SAvi Kivity #ifndef __x86_64__ 1797d36db35SAvi Kivity write_cr4(X86_CR4_PSE); 1807d36db35SAvi Kivity #endif 18197011120SGleb Natapov write_cr0(X86_CR0_PG |X86_CR0_PE | X86_CR0_WP); 1827d36db35SAvi Kivity 1837d36db35SAvi Kivity printf("paging enabled\n"); 1847d36db35SAvi Kivity printf("cr0 = %x\n", read_cr0()); 1857d36db35SAvi Kivity printf("cr3 = %x\n", read_cr3()); 1867d36db35SAvi Kivity printf("cr4 = %x\n", read_cr4()); 1877d36db35SAvi Kivity } 1887d36db35SAvi Kivity 1897d36db35SAvi Kivity void setup_vm() 1907d36db35SAvi Kivity { 191*002d1830SGleb Natapov end_of_memory = fwcfg_get_u64(FW_CFG_RAM_SIZE); 1927d36db35SAvi Kivity free_memory(&edata, end_of_memory - (unsigned long)&edata); 1937d36db35SAvi Kivity setup_mmu(end_of_memory); 1947d36db35SAvi Kivity } 1957d36db35SAvi Kivity 1967d36db35SAvi Kivity void *vmalloc(unsigned long size) 1977d36db35SAvi Kivity { 1987d36db35SAvi Kivity void *mem, *p; 1997d36db35SAvi Kivity unsigned pages; 2007d36db35SAvi Kivity 2017d36db35SAvi Kivity size += sizeof(unsigned long); 2027d36db35SAvi Kivity 2037d36db35SAvi Kivity size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); 2047d36db35SAvi Kivity vfree_top -= size; 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()), virt_to_phys(alloc_page()), p); 2097d36db35SAvi Kivity p += PAGE_SIZE; 2107d36db35SAvi Kivity } 2117d36db35SAvi Kivity *(unsigned long *)mem = size; 2127d36db35SAvi Kivity mem += sizeof(unsigned long); 2137d36db35SAvi Kivity return mem; 2147d36db35SAvi Kivity } 2157d36db35SAvi Kivity 216334cd2bfSGleb Natapov uint64_t virt_to_phys_cr3(void *mem) 217334cd2bfSGleb Natapov { 218334cd2bfSGleb Natapov return (get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR) + ((ulong)mem & (PAGE_SIZE - 1)); 219334cd2bfSGleb Natapov } 220334cd2bfSGleb Natapov 2217d36db35SAvi Kivity void vfree(void *mem) 2227d36db35SAvi Kivity { 2237d36db35SAvi Kivity unsigned long size = ((unsigned long *)mem)[-1]; 2247d36db35SAvi Kivity 2257d36db35SAvi Kivity while (size) { 2267d36db35SAvi Kivity free_page(phys_to_virt(get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR)); 2277d36db35SAvi Kivity mem += PAGE_SIZE; 2287d36db35SAvi Kivity size -= PAGE_SIZE; 2297d36db35SAvi Kivity } 2307d36db35SAvi Kivity } 2317d36db35SAvi Kivity 2327d36db35SAvi Kivity void *vmap(unsigned long long phys, unsigned long size) 2337d36db35SAvi Kivity { 2347d36db35SAvi Kivity void *mem, *p; 2357d36db35SAvi Kivity unsigned pages; 2367d36db35SAvi Kivity 2377d36db35SAvi Kivity size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); 2387d36db35SAvi Kivity vfree_top -= size; 2397d36db35SAvi Kivity phys &= ~(unsigned long long)(PAGE_SIZE - 1); 2407d36db35SAvi Kivity 2417d36db35SAvi Kivity mem = p = vfree_top; 2427d36db35SAvi Kivity pages = size / PAGE_SIZE; 2437d36db35SAvi Kivity while (pages--) { 2447d36db35SAvi Kivity install_page(phys_to_virt(read_cr3()), phys, p); 2457d36db35SAvi Kivity phys += PAGE_SIZE; 2467d36db35SAvi Kivity p += PAGE_SIZE; 2477d36db35SAvi Kivity } 2487d36db35SAvi Kivity return mem; 2497d36db35SAvi Kivity } 250a4b87a16SGleb Natapov 251524ae896SAvi Kivity void *alloc_vpages(ulong nr) 252524ae896SAvi Kivity { 253524ae896SAvi Kivity vfree_top -= PAGE_SIZE * nr; 254524ae896SAvi Kivity return vfree_top; 255524ae896SAvi Kivity } 256524ae896SAvi Kivity 257a4b87a16SGleb Natapov void *alloc_vpage(void) 258a4b87a16SGleb Natapov { 259524ae896SAvi Kivity return alloc_vpages(1); 260a4b87a16SGleb Natapov } 261