xref: /kvm-unit-tests/lib/x86/vm.c (revision 002d1830a7928c3cfcd35dc6607b8309c62e4730)
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