xref: /qemu/target/i386/hvf/x86_mmu.c (revision ff2de1668c9e5dfa7d4305451058234a029aa6ad)
1 /*
2  * Copyright (C) 2016 Veertu Inc,
3  * Copyright (C) 2017 Google Inc,
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 #include "qemu/osdep.h"
19 
20 #include "qemu-common.h"
21 #include "cpu.h"
22 #include "x86.h"
23 #include "x86_mmu.h"
24 #include "string.h"
25 #include "vmcs.h"
26 #include "vmx.h"
27 
28 #include "memory.h"
29 #include "exec/address-spaces.h"
30 
31 #define pte_present(pte) (pte & PT_PRESENT)
32 #define pte_write_access(pte) (pte & PT_WRITE)
33 #define pte_user_access(pte) (pte & PT_USER)
34 #define pte_exec_access(pte) (!(pte & PT_NX))
35 
36 #define pte_large_page(pte) (pte & PT_PS)
37 #define pte_global_access(pte) (pte & PT_GLOBAL)
38 
39 #define PAE_CR3_MASK                (~0x1fllu)
40 #define LEGACY_CR3_MASK             (0xffffffff)
41 
42 #define LEGACY_PTE_PAGE_MASK        (0xffffffffllu << 12)
43 #define PAE_PTE_PAGE_MASK           ((-1llu << 12) & ((1llu << 52) - 1))
44 #define PAE_PTE_LARGE_PAGE_MASK     ((-1llu << (21)) & ((1llu << 52) - 1))
45 
46 struct gpt_translation {
47     target_ulong  gva;
48     uint64_t gpa;
49     int    err_code;
50     uint64_t pte[5];
51     bool write_access;
52     bool user_access;
53     bool exec_access;
54 };
55 
56 static int gpt_top_level(struct CPUState *cpu, bool pae)
57 {
58     if (!pae) {
59         return 2;
60     }
61     if (x86_is_long_mode(cpu)) {
62         return 4;
63     }
64 
65     return 3;
66 }
67 
68 static inline int gpt_entry(target_ulong addr, int level, bool pae)
69 {
70     int level_shift = pae ? 9 : 10;
71     return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1);
72 }
73 
74 static inline int pte_size(bool pae)
75 {
76     return pae ? 8 : 4;
77 }
78 
79 
80 static bool get_pt_entry(struct CPUState *cpu, struct gpt_translation *pt,
81                          int level, bool pae)
82 {
83     int index;
84     uint64_t pte = 0;
85     uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
86     uint64_t gpa = pt->pte[level] & page_mask;
87 
88     if (level == 3 && !x86_is_long_mode(cpu)) {
89         gpa = pt->pte[level];
90     }
91 
92     index = gpt_entry(pt->gva, level, pae);
93     address_space_rw(&address_space_memory, gpa + index * pte_size(pae),
94                      MEMTXATTRS_UNSPECIFIED, (uint8_t *)&pte, pte_size(pae), 0);
95 
96     pt->pte[level - 1] = pte;
97 
98     return true;
99 }
100 
101 /* test page table entry */
102 static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt,
103                           int level, bool *is_large, bool pae)
104 {
105     uint64_t pte = pt->pte[level];
106 
107     if (pt->write_access) {
108         pt->err_code |= MMU_PAGE_WT;
109     }
110     if (pt->user_access) {
111         pt->err_code |= MMU_PAGE_US;
112     }
113     if (pt->exec_access) {
114         pt->err_code |= MMU_PAGE_NX;
115     }
116 
117     if (!pte_present(pte)) {
118         return false;
119     }
120 
121     if (pae && !x86_is_long_mode(cpu) && 2 == level) {
122         goto exit;
123     }
124 
125     if (1 == level && pte_large_page(pte)) {
126         pt->err_code |= MMU_PAGE_PT;
127         *is_large = true;
128     }
129     if (!level) {
130         pt->err_code |= MMU_PAGE_PT;
131     }
132 
133     uint32_t cr0 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR0);
134     /* check protection */
135     if (cr0 & CR0_WP) {
136         if (pt->write_access && !pte_write_access(pte)) {
137             return false;
138         }
139     }
140 
141     if (pt->user_access && !pte_user_access(pte)) {
142         return false;
143     }
144 
145     if (pae && pt->exec_access && !pte_exec_access(pte)) {
146         return false;
147     }
148 
149 exit:
150     /* TODO: check reserved bits */
151     return true;
152 }
153 
154 static inline uint64_t pse_pte_to_page(uint64_t pte)
155 {
156     return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000);
157 }
158 
159 static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae)
160 {
161     VM_PANIC_ON(!pte_large_page(pt->pte[1]))
162     /* 2Mb large page  */
163     if (pae) {
164         return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff);
165     }
166 
167     /* 4Mb large page */
168     return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff);
169 }
170 
171 
172 
173 static bool walk_gpt(struct CPUState *cpu, target_ulong addr, int err_code,
174                      struct gpt_translation *pt, bool pae)
175 {
176     int top_level, level;
177     bool is_large = false;
178     target_ulong cr3 = rvmcs(cpu->hvf_fd, VMCS_GUEST_CR3);
179     uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
180 
181     memset(pt, 0, sizeof(*pt));
182     top_level = gpt_top_level(cpu, pae);
183 
184     pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK);
185     pt->gva = addr;
186     pt->user_access = (err_code & MMU_PAGE_US);
187     pt->write_access = (err_code & MMU_PAGE_WT);
188     pt->exec_access = (err_code & MMU_PAGE_NX);
189 
190     for (level = top_level; level > 0; level--) {
191         get_pt_entry(cpu, pt, level, pae);
192 
193         if (!test_pt_entry(cpu, pt, level - 1, &is_large, pae)) {
194             return false;
195         }
196 
197         if (is_large) {
198             break;
199         }
200     }
201 
202     if (!is_large) {
203         pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff);
204     } else {
205         pt->gpa = large_page_gpa(pt, pae);
206     }
207 
208     return true;
209 }
210 
211 
212 bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa)
213 {
214     bool res;
215     struct gpt_translation pt;
216     int err_code = 0;
217 
218     if (!x86_is_paging_mode(cpu)) {
219         *gpa = gva;
220         return true;
221     }
222 
223     res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu));
224     if (res) {
225         *gpa = pt.gpa;
226         return true;
227     }
228 
229     return false;
230 }
231 
232 void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes)
233 {
234     uint64_t gpa;
235 
236     while (bytes > 0) {
237         /* copy page */
238         int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
239 
240         if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
241             VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
242         } else {
243             address_space_rw(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
244                              data, copy, 1);
245         }
246 
247         bytes -= copy;
248         gva += copy;
249         data += copy;
250     }
251 }
252 
253 void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes)
254 {
255     uint64_t gpa;
256 
257     while (bytes > 0) {
258         /* copy page */
259         int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
260 
261         if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
262             VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
263         }
264         address_space_rw(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
265                          data, copy, 0);
266 
267         bytes -= copy;
268         gva += copy;
269         data += copy;
270     }
271 }
272