xref: /kvm-unit-tests/lib/x86/vm.c (revision 7d36db351752e29ad27eaafe3f102de7064e429b)
1*7d36db35SAvi Kivity #include "vm.h"
2*7d36db35SAvi Kivity #include "libcflat.h"
3*7d36db35SAvi Kivity 
4*7d36db35SAvi Kivity #define PAGE_SIZE 4096ul
5*7d36db35SAvi Kivity #ifdef __x86_64__
6*7d36db35SAvi Kivity #define LARGE_PAGE_SIZE (512 * PAGE_SIZE)
7*7d36db35SAvi Kivity #else
8*7d36db35SAvi Kivity #define LARGE_PAGE_SIZE (1024 * PAGE_SIZE)
9*7d36db35SAvi Kivity #endif
10*7d36db35SAvi Kivity 
11*7d36db35SAvi Kivity #define X86_CR0_PE      0x00000001
12*7d36db35SAvi Kivity #define X86_CR0_PG      0x80000000
13*7d36db35SAvi Kivity #define X86_CR4_PSE     0x00000010
14*7d36db35SAvi Kivity static void *free = 0;
15*7d36db35SAvi Kivity static void *vfree_top = 0;
16*7d36db35SAvi Kivity 
17*7d36db35SAvi Kivity static void free_memory(void *mem, unsigned long size)
18*7d36db35SAvi Kivity {
19*7d36db35SAvi Kivity     while (size >= PAGE_SIZE) {
20*7d36db35SAvi Kivity 	*(void **)mem = free;
21*7d36db35SAvi Kivity 	free = mem;
22*7d36db35SAvi Kivity 	mem += PAGE_SIZE;
23*7d36db35SAvi Kivity 	size -= PAGE_SIZE;
24*7d36db35SAvi Kivity     }
25*7d36db35SAvi Kivity }
26*7d36db35SAvi Kivity 
27*7d36db35SAvi Kivity void *alloc_page()
28*7d36db35SAvi Kivity {
29*7d36db35SAvi Kivity     void *p;
30*7d36db35SAvi Kivity 
31*7d36db35SAvi Kivity     if (!free)
32*7d36db35SAvi Kivity 	return 0;
33*7d36db35SAvi Kivity 
34*7d36db35SAvi Kivity     p = free;
35*7d36db35SAvi Kivity     free = *(void **)free;
36*7d36db35SAvi Kivity 
37*7d36db35SAvi Kivity     return p;
38*7d36db35SAvi Kivity }
39*7d36db35SAvi Kivity 
40*7d36db35SAvi Kivity void free_page(void *page)
41*7d36db35SAvi Kivity {
42*7d36db35SAvi Kivity     *(void **)page = free;
43*7d36db35SAvi Kivity     free = page;
44*7d36db35SAvi Kivity }
45*7d36db35SAvi Kivity 
46*7d36db35SAvi Kivity extern char edata;
47*7d36db35SAvi Kivity static unsigned long end_of_memory;
48*7d36db35SAvi Kivity 
49*7d36db35SAvi Kivity #ifdef __x86_64__
50*7d36db35SAvi Kivity #define	PAGE_LEVEL	4
51*7d36db35SAvi Kivity #define	PGDIR_WIDTH	9
52*7d36db35SAvi Kivity #define	PGDIR_MASK	511
53*7d36db35SAvi Kivity #else
54*7d36db35SAvi Kivity #define	PAGE_LEVEL	2
55*7d36db35SAvi Kivity #define	PGDIR_WIDTH	10
56*7d36db35SAvi Kivity #define	PGDIR_MASK	1023
57*7d36db35SAvi Kivity #endif
58*7d36db35SAvi Kivity 
59*7d36db35SAvi Kivity void install_pte(unsigned long *cr3,
60*7d36db35SAvi Kivity 		 int pte_level,
61*7d36db35SAvi Kivity 		 void *virt,
62*7d36db35SAvi Kivity 		 unsigned long pte,
63*7d36db35SAvi Kivity 		 unsigned long *pt_page)
64*7d36db35SAvi Kivity {
65*7d36db35SAvi Kivity     int level;
66*7d36db35SAvi Kivity     unsigned long *pt = cr3;
67*7d36db35SAvi Kivity     unsigned offset;
68*7d36db35SAvi Kivity 
69*7d36db35SAvi Kivity     for (level = PAGE_LEVEL; level > pte_level; --level) {
70*7d36db35SAvi Kivity 	offset = ((unsigned long)virt >> ((level-1) * PGDIR_WIDTH + 12)) & PGDIR_MASK;
71*7d36db35SAvi Kivity 	if (!(pt[offset] & PTE_PRESENT)) {
72*7d36db35SAvi Kivity 	    unsigned long *new_pt = pt_page;
73*7d36db35SAvi Kivity             if (!new_pt)
74*7d36db35SAvi Kivity                 new_pt = alloc_page();
75*7d36db35SAvi Kivity             else
76*7d36db35SAvi Kivity                 pt_page = 0;
77*7d36db35SAvi Kivity 	    memset(new_pt, 0, PAGE_SIZE);
78*7d36db35SAvi Kivity 	    pt[offset] = virt_to_phys(new_pt) | PTE_PRESENT | PTE_WRITE;
79*7d36db35SAvi Kivity 	}
80*7d36db35SAvi Kivity 	pt = phys_to_virt(pt[offset] & 0xffffffffff000ull);
81*7d36db35SAvi Kivity     }
82*7d36db35SAvi Kivity     offset = ((unsigned long)virt >> ((level-1) * PGDIR_WIDTH + 12)) & PGDIR_MASK;
83*7d36db35SAvi Kivity     pt[offset] = pte;
84*7d36db35SAvi Kivity }
85*7d36db35SAvi Kivity 
86*7d36db35SAvi Kivity static unsigned long get_pte(unsigned long *cr3, void *virt)
87*7d36db35SAvi Kivity {
88*7d36db35SAvi Kivity     int level;
89*7d36db35SAvi Kivity     unsigned long *pt = cr3, pte;
90*7d36db35SAvi Kivity     unsigned offset;
91*7d36db35SAvi Kivity 
92*7d36db35SAvi Kivity     for (level = PAGE_LEVEL; level > 1; --level) {
93*7d36db35SAvi Kivity 	offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) & PGDIR_MASK;
94*7d36db35SAvi Kivity 	pte = pt[offset];
95*7d36db35SAvi Kivity 	if (!(pte & PTE_PRESENT))
96*7d36db35SAvi Kivity 	    return 0;
97*7d36db35SAvi Kivity 	if (level == 2 && (pte & PTE_PSE))
98*7d36db35SAvi Kivity 	    return pte;
99*7d36db35SAvi Kivity 	pt = phys_to_virt(pte & 0xffffffffff000ull);
100*7d36db35SAvi Kivity     }
101*7d36db35SAvi Kivity     offset = ((unsigned long)virt >> (((level-1) * PGDIR_WIDTH) + 12)) & PGDIR_MASK;
102*7d36db35SAvi Kivity     pte = pt[offset];
103*7d36db35SAvi Kivity     return pte;
104*7d36db35SAvi Kivity }
105*7d36db35SAvi Kivity 
106*7d36db35SAvi Kivity void install_large_page(unsigned long *cr3,
107*7d36db35SAvi Kivity                               unsigned long phys,
108*7d36db35SAvi Kivity                               void *virt)
109*7d36db35SAvi Kivity {
110*7d36db35SAvi Kivity     install_pte(cr3, 2, virt, phys | PTE_PRESENT | PTE_WRITE | PTE_PSE, 0);
111*7d36db35SAvi Kivity }
112*7d36db35SAvi Kivity 
113*7d36db35SAvi Kivity void install_page(unsigned long *cr3,
114*7d36db35SAvi Kivity                   unsigned long phys,
115*7d36db35SAvi Kivity                   void *virt)
116*7d36db35SAvi Kivity {
117*7d36db35SAvi Kivity     install_pte(cr3, 1, virt, phys | PTE_PRESENT | PTE_WRITE, 0);
118*7d36db35SAvi Kivity }
119*7d36db35SAvi Kivity 
120*7d36db35SAvi Kivity 
121*7d36db35SAvi Kivity static inline void load_gdt(unsigned long *table, int nent)
122*7d36db35SAvi Kivity {
123*7d36db35SAvi Kivity     struct descriptor_table_ptr descr;
124*7d36db35SAvi Kivity 
125*7d36db35SAvi Kivity     descr.limit = nent * 8 - 1;
126*7d36db35SAvi Kivity     descr.base = (ulong)table;
127*7d36db35SAvi Kivity     lgdt(&descr);
128*7d36db35SAvi Kivity }
129*7d36db35SAvi Kivity 
130*7d36db35SAvi Kivity #define SEG_CS_32 8
131*7d36db35SAvi Kivity #define SEG_CS_64 16
132*7d36db35SAvi Kivity 
133*7d36db35SAvi Kivity struct ljmp {
134*7d36db35SAvi Kivity     void *ofs;
135*7d36db35SAvi Kivity     unsigned short seg;
136*7d36db35SAvi Kivity };
137*7d36db35SAvi Kivity 
138*7d36db35SAvi Kivity static void setup_mmu(unsigned long len)
139*7d36db35SAvi Kivity {
140*7d36db35SAvi Kivity     unsigned long *cr3 = alloc_page();
141*7d36db35SAvi Kivity     unsigned long phys = 0;
142*7d36db35SAvi Kivity 
143*7d36db35SAvi Kivity     if (len < (1ul << 32))
144*7d36db35SAvi Kivity         len = 1ul << 32;  /* map mmio 1:1 */
145*7d36db35SAvi Kivity 
146*7d36db35SAvi Kivity     memset(cr3, 0, PAGE_SIZE);
147*7d36db35SAvi Kivity     while (phys + LARGE_PAGE_SIZE <= len) {
148*7d36db35SAvi Kivity 	install_large_page(cr3, phys, (void *)phys);
149*7d36db35SAvi Kivity 	phys += LARGE_PAGE_SIZE;
150*7d36db35SAvi Kivity     }
151*7d36db35SAvi Kivity     while (phys + PAGE_SIZE <= len) {
152*7d36db35SAvi Kivity 	install_page(cr3, phys, (void *)phys);
153*7d36db35SAvi Kivity 	phys += PAGE_SIZE;
154*7d36db35SAvi Kivity     }
155*7d36db35SAvi Kivity     write_cr3(virt_to_phys(cr3));
156*7d36db35SAvi Kivity #ifndef __x86_64__
157*7d36db35SAvi Kivity     write_cr4(X86_CR4_PSE);
158*7d36db35SAvi Kivity #endif
159*7d36db35SAvi Kivity     write_cr0(X86_CR0_PG |X86_CR0_PE);
160*7d36db35SAvi Kivity 
161*7d36db35SAvi Kivity     printf("paging enabled\n");
162*7d36db35SAvi Kivity     printf("cr0 = %x\n", read_cr0());
163*7d36db35SAvi Kivity     printf("cr3 = %x\n", read_cr3());
164*7d36db35SAvi Kivity     printf("cr4 = %x\n", read_cr4());
165*7d36db35SAvi Kivity }
166*7d36db35SAvi Kivity 
167*7d36db35SAvi Kivity static unsigned int inl(unsigned short port)
168*7d36db35SAvi Kivity {
169*7d36db35SAvi Kivity     unsigned int val;
170*7d36db35SAvi Kivity     asm volatile("inl %w1, %0" : "=a"(val) : "Nd"(port));
171*7d36db35SAvi Kivity     return val;
172*7d36db35SAvi Kivity }
173*7d36db35SAvi Kivity 
174*7d36db35SAvi Kivity void setup_vm()
175*7d36db35SAvi Kivity {
176*7d36db35SAvi Kivity     end_of_memory = inl(0xd1);
177*7d36db35SAvi Kivity     free_memory(&edata, end_of_memory - (unsigned long)&edata);
178*7d36db35SAvi Kivity     setup_mmu(end_of_memory);
179*7d36db35SAvi Kivity }
180*7d36db35SAvi Kivity 
181*7d36db35SAvi Kivity void *vmalloc(unsigned long size)
182*7d36db35SAvi Kivity {
183*7d36db35SAvi Kivity     void *mem, *p;
184*7d36db35SAvi Kivity     unsigned pages;
185*7d36db35SAvi Kivity 
186*7d36db35SAvi Kivity     size += sizeof(unsigned long);
187*7d36db35SAvi Kivity 
188*7d36db35SAvi Kivity     size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
189*7d36db35SAvi Kivity     vfree_top -= size;
190*7d36db35SAvi Kivity     mem = p = vfree_top;
191*7d36db35SAvi Kivity     pages = size / PAGE_SIZE;
192*7d36db35SAvi Kivity     while (pages--) {
193*7d36db35SAvi Kivity 	install_page(phys_to_virt(read_cr3()), virt_to_phys(alloc_page()), p);
194*7d36db35SAvi Kivity 	p += PAGE_SIZE;
195*7d36db35SAvi Kivity     }
196*7d36db35SAvi Kivity     *(unsigned long *)mem = size;
197*7d36db35SAvi Kivity     mem += sizeof(unsigned long);
198*7d36db35SAvi Kivity     return mem;
199*7d36db35SAvi Kivity }
200*7d36db35SAvi Kivity 
201*7d36db35SAvi Kivity void vfree(void *mem)
202*7d36db35SAvi Kivity {
203*7d36db35SAvi Kivity     unsigned long size = ((unsigned long *)mem)[-1];
204*7d36db35SAvi Kivity 
205*7d36db35SAvi Kivity     while (size) {
206*7d36db35SAvi Kivity 	free_page(phys_to_virt(get_pte(phys_to_virt(read_cr3()), mem) & PTE_ADDR));
207*7d36db35SAvi Kivity 	mem += PAGE_SIZE;
208*7d36db35SAvi Kivity 	size -= PAGE_SIZE;
209*7d36db35SAvi Kivity     }
210*7d36db35SAvi Kivity }
211*7d36db35SAvi Kivity 
212*7d36db35SAvi Kivity void *vmap(unsigned long long phys, unsigned long size)
213*7d36db35SAvi Kivity {
214*7d36db35SAvi Kivity     void *mem, *p;
215*7d36db35SAvi Kivity     unsigned pages;
216*7d36db35SAvi Kivity 
217*7d36db35SAvi Kivity     size = (size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
218*7d36db35SAvi Kivity     vfree_top -= size;
219*7d36db35SAvi Kivity     phys &= ~(unsigned long long)(PAGE_SIZE - 1);
220*7d36db35SAvi Kivity 
221*7d36db35SAvi Kivity     mem = p = vfree_top;
222*7d36db35SAvi Kivity     pages = size / PAGE_SIZE;
223*7d36db35SAvi Kivity     while (pages--) {
224*7d36db35SAvi Kivity 	install_page(phys_to_virt(read_cr3()), phys, p);
225*7d36db35SAvi Kivity 	phys += PAGE_SIZE;
226*7d36db35SAvi Kivity 	p += PAGE_SIZE;
227*7d36db35SAvi Kivity     }
228*7d36db35SAvi Kivity     return mem;
229*7d36db35SAvi Kivity }
230