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