xref: /kvm-unit-tests/lib/x86/vm.c (revision 9d7e08c0050c0834d5e77a1de0cfa3859d24c777)
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) {
51*9d7e08c0SPeter Xu 	offset = PGDIR_OFFSET((unsigned long)virt, level);
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     }
63*9d7e08c0SPeter Xu     offset = PGDIR_OFFSET((unsigned long)virt, level);
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 {
154f7b87da6SPeter 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