xref: /openbmc/qemu/target/i386/hvf/x86_mmu.c (revision f101c9fe)
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.1 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 
19 #include "qemu/osdep.h"
20 #include "panic.h"
21 #include "qemu-common.h"
22 #include "cpu.h"
23 #include "x86.h"
24 #include "x86_mmu.h"
25 #include "vmcs.h"
26 #include "vmx.h"
27 
28 #define pte_present(pte) (pte & PT_PRESENT)
29 #define pte_write_access(pte) (pte & PT_WRITE)
30 #define pte_user_access(pte) (pte & PT_USER)
31 #define pte_exec_access(pte) (!(pte & PT_NX))
32 
33 #define pte_large_page(pte) (pte & PT_PS)
34 #define pte_global_access(pte) (pte & PT_GLOBAL)
35 
36 #define PAE_CR3_MASK                (~0x1fllu)
37 #define LEGACY_CR3_MASK             (0xffffffff)
38 
39 #define LEGACY_PTE_PAGE_MASK        (0xffffffffllu << 12)
40 #define PAE_PTE_PAGE_MASK           ((-1llu << 12) & ((1llu << 52) - 1))
41 #define PAE_PTE_LARGE_PAGE_MASK     ((-1llu << (21)) & ((1llu << 52) - 1))
42 
43 struct gpt_translation {
44     target_ulong  gva;
45     uint64_t gpa;
46     int    err_code;
47     uint64_t pte[5];
48     bool write_access;
49     bool user_access;
50     bool exec_access;
51 };
52 
53 static int gpt_top_level(struct CPUState *cpu, bool pae)
54 {
55     if (!pae) {
56         return 2;
57     }
58     if (x86_is_long_mode(cpu)) {
59         return 4;
60     }
61 
62     return 3;
63 }
64 
65 static inline int gpt_entry(target_ulong addr, int level, bool pae)
66 {
67     int level_shift = pae ? 9 : 10;
68     return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1);
69 }
70 
71 static inline int pte_size(bool pae)
72 {
73     return pae ? 8 : 4;
74 }
75 
76 
77 static bool get_pt_entry(struct CPUState *cpu, struct gpt_translation *pt,
78                          int level, bool pae)
79 {
80     int index;
81     uint64_t pte = 0;
82     uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
83     uint64_t gpa = pt->pte[level] & page_mask;
84 
85     if (level == 3 && !x86_is_long_mode(cpu)) {
86         gpa = pt->pte[level];
87     }
88 
89     index = gpt_entry(pt->gva, level, pae);
90     address_space_read(&address_space_memory, gpa + index * pte_size(pae),
91                        MEMTXATTRS_UNSPECIFIED, &pte, pte_size(pae));
92 
93     pt->pte[level - 1] = pte;
94 
95     return true;
96 }
97 
98 /* test page table entry */
99 static bool test_pt_entry(struct CPUState *cpu, struct gpt_translation *pt,
100                           int level, bool *is_large, bool pae)
101 {
102     uint64_t pte = pt->pte[level];
103 
104     if (pt->write_access) {
105         pt->err_code |= MMU_PAGE_WT;
106     }
107     if (pt->user_access) {
108         pt->err_code |= MMU_PAGE_US;
109     }
110     if (pt->exec_access) {
111         pt->err_code |= MMU_PAGE_NX;
112     }
113 
114     if (!pte_present(pte)) {
115         return false;
116     }
117 
118     if (pae && !x86_is_long_mode(cpu) && 2 == level) {
119         goto exit;
120     }
121 
122     if (1 == level && pte_large_page(pte)) {
123         pt->err_code |= MMU_PAGE_PT;
124         *is_large = true;
125     }
126     if (!level) {
127         pt->err_code |= MMU_PAGE_PT;
128     }
129 
130     uint32_t cr0 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR0);
131     /* check protection */
132     if (cr0 & CR0_WP) {
133         if (pt->write_access && !pte_write_access(pte)) {
134             return false;
135         }
136     }
137 
138     if (pt->user_access && !pte_user_access(pte)) {
139         return false;
140     }
141 
142     if (pae && pt->exec_access && !pte_exec_access(pte)) {
143         return false;
144     }
145 
146 exit:
147     /* TODO: check reserved bits */
148     return true;
149 }
150 
151 static inline uint64_t pse_pte_to_page(uint64_t pte)
152 {
153     return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000);
154 }
155 
156 static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae)
157 {
158     VM_PANIC_ON(!pte_large_page(pt->pte[1]))
159     /* 2Mb large page  */
160     if (pae) {
161         return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff);
162     }
163 
164     /* 4Mb large page */
165     return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff);
166 }
167 
168 
169 
170 static bool walk_gpt(struct CPUState *cpu, target_ulong addr, int err_code,
171                      struct gpt_translation *pt, bool pae)
172 {
173     int top_level, level;
174     bool is_large = false;
175     target_ulong cr3 = rvmcs(cpu->hvf->fd, VMCS_GUEST_CR3);
176     uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
177 
178     memset(pt, 0, sizeof(*pt));
179     top_level = gpt_top_level(cpu, pae);
180 
181     pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK);
182     pt->gva = addr;
183     pt->user_access = (err_code & MMU_PAGE_US);
184     pt->write_access = (err_code & MMU_PAGE_WT);
185     pt->exec_access = (err_code & MMU_PAGE_NX);
186 
187     for (level = top_level; level > 0; level--) {
188         get_pt_entry(cpu, pt, level, pae);
189 
190         if (!test_pt_entry(cpu, pt, level - 1, &is_large, pae)) {
191             return false;
192         }
193 
194         if (is_large) {
195             break;
196         }
197     }
198 
199     if (!is_large) {
200         pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff);
201     } else {
202         pt->gpa = large_page_gpa(pt, pae);
203     }
204 
205     return true;
206 }
207 
208 
209 bool mmu_gva_to_gpa(struct CPUState *cpu, target_ulong gva, uint64_t *gpa)
210 {
211     bool res;
212     struct gpt_translation pt;
213     int err_code = 0;
214 
215     if (!x86_is_paging_mode(cpu)) {
216         *gpa = gva;
217         return true;
218     }
219 
220     res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu));
221     if (res) {
222         *gpa = pt.gpa;
223         return true;
224     }
225 
226     return false;
227 }
228 
229 void vmx_write_mem(struct CPUState *cpu, target_ulong gva, void *data, int bytes)
230 {
231     uint64_t gpa;
232 
233     while (bytes > 0) {
234         /* copy page */
235         int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
236 
237         if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
238             VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
239         } else {
240             address_space_write(&address_space_memory, gpa,
241                                 MEMTXATTRS_UNSPECIFIED, data, copy);
242         }
243 
244         bytes -= copy;
245         gva += copy;
246         data += copy;
247     }
248 }
249 
250 void vmx_read_mem(struct CPUState *cpu, void *data, target_ulong gva, int bytes)
251 {
252     uint64_t gpa;
253 
254     while (bytes > 0) {
255         /* copy page */
256         int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
257 
258         if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
259             VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
260         }
261         address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
262                            data, copy);
263 
264         bytes -= copy;
265         gva += copy;
266         data += copy;
267     }
268 }
269