xref: /qemu/target/i386/hvf/x86_mmu.c (revision 27458df871097d7fc14b19d9e01c35d29737b9b3)
1c97d6d2cSSergio Andres Gomez Del Real /*
2c97d6d2cSSergio Andres Gomez Del Real  * Copyright (C) 2016 Veertu Inc,
3c97d6d2cSSergio Andres Gomez Del Real  * Copyright (C) 2017 Google Inc,
4c97d6d2cSSergio Andres Gomez Del Real  *
5c97d6d2cSSergio Andres Gomez Del Real  * This program is free software; you can redistribute it and/or
6996feed4SSergio Andres Gomez Del Real  * modify it under the terms of the GNU Lesser General Public
7996feed4SSergio Andres Gomez Del Real  * License as published by the Free Software Foundation; either
88af82b8eSChetan Pant  * version 2.1 of the License, or (at your option) any later version.
9c97d6d2cSSergio Andres Gomez Del Real  *
10c97d6d2cSSergio Andres Gomez Del Real  * This program is distributed in the hope that it will be useful,
11c97d6d2cSSergio Andres Gomez Del Real  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12996feed4SSergio Andres Gomez Del Real  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13996feed4SSergio Andres Gomez Del Real  * Lesser General Public License for more details.
14c97d6d2cSSergio Andres Gomez Del Real  *
15996feed4SSergio Andres Gomez Del Real  * You should have received a copy of the GNU Lesser General Public
16996feed4SSergio Andres Gomez Del Real  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
17c97d6d2cSSergio Andres Gomez Del Real  */
18c97d6d2cSSergio Andres Gomez Del Real 
19d8e39b70SMarkus Armbruster #include "qemu/osdep.h"
20d8e39b70SMarkus Armbruster #include "panic.h"
21ff2de166SPaolo Bonzini #include "cpu.h"
22*27458df8SWei Liu #include "emulate/x86.h"
23c97d6d2cSSergio Andres Gomez Del Real #include "x86_mmu.h"
24c97d6d2cSSergio Andres Gomez Del Real #include "vmcs.h"
25c97d6d2cSSergio Andres Gomez Del Real #include "vmx.h"
26c97d6d2cSSergio Andres Gomez Del Real 
27c97d6d2cSSergio Andres Gomez Del Real #define pte_present(pte) (pte & PT_PRESENT)
28c97d6d2cSSergio Andres Gomez Del Real #define pte_write_access(pte) (pte & PT_WRITE)
29c97d6d2cSSergio Andres Gomez Del Real #define pte_user_access(pte) (pte & PT_USER)
30c97d6d2cSSergio Andres Gomez Del Real #define pte_exec_access(pte) (!(pte & PT_NX))
31c97d6d2cSSergio Andres Gomez Del Real 
32c97d6d2cSSergio Andres Gomez Del Real #define pte_large_page(pte) (pte & PT_PS)
33c97d6d2cSSergio Andres Gomez Del Real #define pte_global_access(pte) (pte & PT_GLOBAL)
34c97d6d2cSSergio Andres Gomez Del Real 
35c97d6d2cSSergio Andres Gomez Del Real #define PAE_CR3_MASK                (~0x1fllu)
36c97d6d2cSSergio Andres Gomez Del Real #define LEGACY_CR3_MASK             (0xffffffff)
37c97d6d2cSSergio Andres Gomez Del Real 
38c97d6d2cSSergio Andres Gomez Del Real #define LEGACY_PTE_PAGE_MASK        (0xffffffffllu << 12)
39c97d6d2cSSergio Andres Gomez Del Real #define PAE_PTE_PAGE_MASK           ((-1llu << 12) & ((1llu << 52) - 1))
40c97d6d2cSSergio Andres Gomez Del Real #define PAE_PTE_LARGE_PAGE_MASK     ((-1llu << (21)) & ((1llu << 52) - 1))
41654076bcSAlexander Graf #define PAE_PTE_SUPER_PAGE_MASK     ((-1llu << (30)) & ((1llu << 52) - 1))
42c97d6d2cSSergio Andres Gomez Del Real 
43c97d6d2cSSergio Andres Gomez Del Real struct gpt_translation {
44ff2de166SPaolo Bonzini     target_ulong  gva;
45ff2de166SPaolo Bonzini     uint64_t gpa;
46c97d6d2cSSergio Andres Gomez Del Real     int    err_code;
47c97d6d2cSSergio Andres Gomez Del Real     uint64_t pte[5];
48c97d6d2cSSergio Andres Gomez Del Real     bool write_access;
49c97d6d2cSSergio Andres Gomez Del Real     bool user_access;
50c97d6d2cSSergio Andres Gomez Del Real     bool exec_access;
51c97d6d2cSSergio Andres Gomez Del Real };
52c97d6d2cSSergio Andres Gomez Del Real 
gpt_top_level(CPUState * cpu,bool pae)53f8436a16SPhilippe Mathieu-Daudé static int gpt_top_level(CPUState *cpu, bool pae)
54c97d6d2cSSergio Andres Gomez Del Real {
55c97d6d2cSSergio Andres Gomez Del Real     if (!pae) {
56c97d6d2cSSergio Andres Gomez Del Real         return 2;
57c97d6d2cSSergio Andres Gomez Del Real     }
58c97d6d2cSSergio Andres Gomez Del Real     if (x86_is_long_mode(cpu)) {
59c97d6d2cSSergio Andres Gomez Del Real         return 4;
60c97d6d2cSSergio Andres Gomez Del Real     }
61c97d6d2cSSergio Andres Gomez Del Real 
62c97d6d2cSSergio Andres Gomez Del Real     return 3;
63c97d6d2cSSergio Andres Gomez Del Real }
64c97d6d2cSSergio Andres Gomez Del Real 
gpt_entry(target_ulong addr,int level,bool pae)65ff2de166SPaolo Bonzini static inline int gpt_entry(target_ulong addr, int level, bool pae)
66c97d6d2cSSergio Andres Gomez Del Real {
67c97d6d2cSSergio Andres Gomez Del Real     int level_shift = pae ? 9 : 10;
68c97d6d2cSSergio Andres Gomez Del Real     return (addr >> (level_shift * (level - 1) + 12)) & ((1 << level_shift) - 1);
69c97d6d2cSSergio Andres Gomez Del Real }
70c97d6d2cSSergio Andres Gomez Del Real 
pte_size(bool pae)71c97d6d2cSSergio Andres Gomez Del Real static inline int pte_size(bool pae)
72c97d6d2cSSergio Andres Gomez Del Real {
73c97d6d2cSSergio Andres Gomez Del Real     return pae ? 8 : 4;
74c97d6d2cSSergio Andres Gomez Del Real }
75c97d6d2cSSergio Andres Gomez Del Real 
76c97d6d2cSSergio Andres Gomez Del Real 
get_pt_entry(CPUState * cpu,struct gpt_translation * pt,int level,bool pae)77f8436a16SPhilippe Mathieu-Daudé static bool get_pt_entry(CPUState *cpu, struct gpt_translation *pt,
78c97d6d2cSSergio Andres Gomez Del Real                          int level, bool pae)
79c97d6d2cSSergio Andres Gomez Del Real {
80c97d6d2cSSergio Andres Gomez Del Real     int index;
81c97d6d2cSSergio Andres Gomez Del Real     uint64_t pte = 0;
82ff2de166SPaolo Bonzini     uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
83ff2de166SPaolo Bonzini     uint64_t gpa = pt->pte[level] & page_mask;
84c97d6d2cSSergio Andres Gomez Del Real 
85c97d6d2cSSergio Andres Gomez Del Real     if (level == 3 && !x86_is_long_mode(cpu)) {
86c97d6d2cSSergio Andres Gomez Del Real         gpa = pt->pte[level];
87c97d6d2cSSergio Andres Gomez Del Real     }
88c97d6d2cSSergio Andres Gomez Del Real 
89c97d6d2cSSergio Andres Gomez Del Real     index = gpt_entry(pt->gva, level, pae);
9019f70347SPeter Maydell     address_space_read(&address_space_memory, gpa + index * pte_size(pae),
9119f70347SPeter Maydell                        MEMTXATTRS_UNSPECIFIED, &pte, pte_size(pae));
92c97d6d2cSSergio Andres Gomez Del Real 
93c97d6d2cSSergio Andres Gomez Del Real     pt->pte[level - 1] = pte;
94c97d6d2cSSergio Andres Gomez Del Real 
95c97d6d2cSSergio Andres Gomez Del Real     return true;
96c97d6d2cSSergio Andres Gomez Del Real }
97c97d6d2cSSergio Andres Gomez Del Real 
98c97d6d2cSSergio Andres Gomez Del Real /* test page table entry */
test_pt_entry(CPUState * cpu,struct gpt_translation * pt,int level,int * largeness,bool pae)99f8436a16SPhilippe Mathieu-Daudé static bool test_pt_entry(CPUState *cpu, struct gpt_translation *pt,
100654076bcSAlexander Graf                           int level, int *largeness, bool pae)
101c97d6d2cSSergio Andres Gomez Del Real {
102c97d6d2cSSergio Andres Gomez Del Real     uint64_t pte = pt->pte[level];
103c97d6d2cSSergio Andres Gomez Del Real 
104c97d6d2cSSergio Andres Gomez Del Real     if (pt->write_access) {
105c97d6d2cSSergio Andres Gomez Del Real         pt->err_code |= MMU_PAGE_WT;
106c97d6d2cSSergio Andres Gomez Del Real     }
107c97d6d2cSSergio Andres Gomez Del Real     if (pt->user_access) {
108c97d6d2cSSergio Andres Gomez Del Real         pt->err_code |= MMU_PAGE_US;
109c97d6d2cSSergio Andres Gomez Del Real     }
110c97d6d2cSSergio Andres Gomez Del Real     if (pt->exec_access) {
111c97d6d2cSSergio Andres Gomez Del Real         pt->err_code |= MMU_PAGE_NX;
112c97d6d2cSSergio Andres Gomez Del Real     }
113c97d6d2cSSergio Andres Gomez Del Real 
114c97d6d2cSSergio Andres Gomez Del Real     if (!pte_present(pte)) {
115c97d6d2cSSergio Andres Gomez Del Real         return false;
116c97d6d2cSSergio Andres Gomez Del Real     }
117c97d6d2cSSergio Andres Gomez Del Real 
118c97d6d2cSSergio Andres Gomez Del Real     if (pae && !x86_is_long_mode(cpu) && 2 == level) {
119c97d6d2cSSergio Andres Gomez Del Real         goto exit;
120c97d6d2cSSergio Andres Gomez Del Real     }
121c97d6d2cSSergio Andres Gomez Del Real 
122654076bcSAlexander Graf     if (level && pte_large_page(pte)) {
123c97d6d2cSSergio Andres Gomez Del Real         pt->err_code |= MMU_PAGE_PT;
124654076bcSAlexander Graf         *largeness = level;
125c97d6d2cSSergio Andres Gomez Del Real     }
126c97d6d2cSSergio Andres Gomez Del Real     if (!level) {
127c97d6d2cSSergio Andres Gomez Del Real         pt->err_code |= MMU_PAGE_PT;
128c97d6d2cSSergio Andres Gomez Del Real     }
129c97d6d2cSSergio Andres Gomez Del Real 
1303b295bcbSPhilippe Mathieu-Daudé     uint32_t cr0 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR0);
131c97d6d2cSSergio Andres Gomez Del Real     /* check protection */
132704afe34SCameron Esfahani     if (cr0 & CR0_WP_MASK) {
133c97d6d2cSSergio Andres Gomez Del Real         if (pt->write_access && !pte_write_access(pte)) {
134c97d6d2cSSergio Andres Gomez Del Real             return false;
135c97d6d2cSSergio Andres Gomez Del Real         }
136c97d6d2cSSergio Andres Gomez Del Real     }
137c97d6d2cSSergio Andres Gomez Del Real 
138c97d6d2cSSergio Andres Gomez Del Real     if (pt->user_access && !pte_user_access(pte)) {
139c97d6d2cSSergio Andres Gomez Del Real         return false;
140c97d6d2cSSergio Andres Gomez Del Real     }
141c97d6d2cSSergio Andres Gomez Del Real 
142c97d6d2cSSergio Andres Gomez Del Real     if (pae && pt->exec_access && !pte_exec_access(pte)) {
143c97d6d2cSSergio Andres Gomez Del Real         return false;
144c97d6d2cSSergio Andres Gomez Del Real     }
145c97d6d2cSSergio Andres Gomez Del Real 
146c97d6d2cSSergio Andres Gomez Del Real exit:
147c97d6d2cSSergio Andres Gomez Del Real     /* TODO: check reserved bits */
148c97d6d2cSSergio Andres Gomez Del Real     return true;
149c97d6d2cSSergio Andres Gomez Del Real }
150c97d6d2cSSergio Andres Gomez Del Real 
pse_pte_to_page(uint64_t pte)151c97d6d2cSSergio Andres Gomez Del Real static inline uint64_t pse_pte_to_page(uint64_t pte)
152c97d6d2cSSergio Andres Gomez Del Real {
153c97d6d2cSSergio Andres Gomez Del Real     return ((pte & 0x1fe000) << 19) | (pte & 0xffc00000);
154c97d6d2cSSergio Andres Gomez Del Real }
155c97d6d2cSSergio Andres Gomez Del Real 
large_page_gpa(struct gpt_translation * pt,bool pae,int largeness)156654076bcSAlexander Graf static inline uint64_t large_page_gpa(struct gpt_translation *pt, bool pae,
157654076bcSAlexander Graf                                       int largeness)
158c97d6d2cSSergio Andres Gomez Del Real {
159654076bcSAlexander Graf     VM_PANIC_ON(!pte_large_page(pt->pte[largeness]))
160654076bcSAlexander Graf 
161654076bcSAlexander Graf     /* 1Gib large page  */
162654076bcSAlexander Graf     if (pae && largeness == 2) {
163654076bcSAlexander Graf         return (pt->pte[2] & PAE_PTE_SUPER_PAGE_MASK) | (pt->gva & 0x3fffffff);
164654076bcSAlexander Graf     }
165654076bcSAlexander Graf 
166654076bcSAlexander Graf     VM_PANIC_ON(largeness != 1)
167654076bcSAlexander Graf 
168c97d6d2cSSergio Andres Gomez Del Real     /* 2Mb large page  */
169c97d6d2cSSergio Andres Gomez Del Real     if (pae) {
170c97d6d2cSSergio Andres Gomez Del Real         return (pt->pte[1] & PAE_PTE_LARGE_PAGE_MASK) | (pt->gva & 0x1fffff);
171c97d6d2cSSergio Andres Gomez Del Real     }
172c97d6d2cSSergio Andres Gomez Del Real 
173c97d6d2cSSergio Andres Gomez Del Real     /* 4Mb large page */
174c97d6d2cSSergio Andres Gomez Del Real     return pse_pte_to_page(pt->pte[1]) | (pt->gva & 0x3fffff);
175c97d6d2cSSergio Andres Gomez Del Real }
176c97d6d2cSSergio Andres Gomez Del Real 
177c97d6d2cSSergio Andres Gomez Del Real 
178c97d6d2cSSergio Andres Gomez Del Real 
walk_gpt(CPUState * cpu,target_ulong addr,int err_code,struct gpt_translation * pt,bool pae)179f8436a16SPhilippe Mathieu-Daudé static bool walk_gpt(CPUState *cpu, target_ulong addr, int err_code,
180c97d6d2cSSergio Andres Gomez Del Real                      struct gpt_translation *pt, bool pae)
181c97d6d2cSSergio Andres Gomez Del Real {
182c97d6d2cSSergio Andres Gomez Del Real     int top_level, level;
183654076bcSAlexander Graf     int largeness = 0;
1843b295bcbSPhilippe Mathieu-Daudé     target_ulong cr3 = rvmcs(cpu->accel->fd, VMCS_GUEST_CR3);
185ff2de166SPaolo Bonzini     uint64_t page_mask = pae ? PAE_PTE_PAGE_MASK : LEGACY_PTE_PAGE_MASK;
186c97d6d2cSSergio Andres Gomez Del Real 
187c97d6d2cSSergio Andres Gomez Del Real     memset(pt, 0, sizeof(*pt));
188c97d6d2cSSergio Andres Gomez Del Real     top_level = gpt_top_level(cpu, pae);
189c97d6d2cSSergio Andres Gomez Del Real 
190c97d6d2cSSergio Andres Gomez Del Real     pt->pte[top_level] = pae ? (cr3 & PAE_CR3_MASK) : (cr3 & LEGACY_CR3_MASK);
191c97d6d2cSSergio Andres Gomez Del Real     pt->gva = addr;
192c97d6d2cSSergio Andres Gomez Del Real     pt->user_access = (err_code & MMU_PAGE_US);
193c97d6d2cSSergio Andres Gomez Del Real     pt->write_access = (err_code & MMU_PAGE_WT);
194c97d6d2cSSergio Andres Gomez Del Real     pt->exec_access = (err_code & MMU_PAGE_NX);
195c97d6d2cSSergio Andres Gomez Del Real 
196c97d6d2cSSergio Andres Gomez Del Real     for (level = top_level; level > 0; level--) {
197c97d6d2cSSergio Andres Gomez Del Real         get_pt_entry(cpu, pt, level, pae);
198c97d6d2cSSergio Andres Gomez Del Real 
199654076bcSAlexander Graf         if (!test_pt_entry(cpu, pt, level - 1, &largeness, pae)) {
200c97d6d2cSSergio Andres Gomez Del Real             return false;
201c97d6d2cSSergio Andres Gomez Del Real         }
202c97d6d2cSSergio Andres Gomez Del Real 
203654076bcSAlexander Graf         if (largeness) {
204c97d6d2cSSergio Andres Gomez Del Real             break;
205c97d6d2cSSergio Andres Gomez Del Real         }
206c97d6d2cSSergio Andres Gomez Del Real     }
207c97d6d2cSSergio Andres Gomez Del Real 
208654076bcSAlexander Graf     if (!largeness) {
209c97d6d2cSSergio Andres Gomez Del Real         pt->gpa = (pt->pte[0] & page_mask) | (pt->gva & 0xfff);
210c97d6d2cSSergio Andres Gomez Del Real     } else {
211654076bcSAlexander Graf         pt->gpa = large_page_gpa(pt, pae, largeness);
212c97d6d2cSSergio Andres Gomez Del Real     }
213c97d6d2cSSergio Andres Gomez Del Real 
214c97d6d2cSSergio Andres Gomez Del Real     return true;
215c97d6d2cSSergio Andres Gomez Del Real }
216c97d6d2cSSergio Andres Gomez Del Real 
217c97d6d2cSSergio Andres Gomez Del Real 
mmu_gva_to_gpa(CPUState * cpu,target_ulong gva,uint64_t * gpa)218f8436a16SPhilippe Mathieu-Daudé bool mmu_gva_to_gpa(CPUState *cpu, target_ulong gva, uint64_t *gpa)
219c97d6d2cSSergio Andres Gomez Del Real {
220c97d6d2cSSergio Andres Gomez Del Real     bool res;
221c97d6d2cSSergio Andres Gomez Del Real     struct gpt_translation pt;
222c97d6d2cSSergio Andres Gomez Del Real     int err_code = 0;
223c97d6d2cSSergio Andres Gomez Del Real 
224c97d6d2cSSergio Andres Gomez Del Real     if (!x86_is_paging_mode(cpu)) {
225c97d6d2cSSergio Andres Gomez Del Real         *gpa = gva;
226c97d6d2cSSergio Andres Gomez Del Real         return true;
227c97d6d2cSSergio Andres Gomez Del Real     }
228c97d6d2cSSergio Andres Gomez Del Real 
229c97d6d2cSSergio Andres Gomez Del Real     res = walk_gpt(cpu, gva, err_code, &pt, x86_is_pae_enabled(cpu));
230c97d6d2cSSergio Andres Gomez Del Real     if (res) {
231c97d6d2cSSergio Andres Gomez Del Real         *gpa = pt.gpa;
232c97d6d2cSSergio Andres Gomez Del Real         return true;
233c97d6d2cSSergio Andres Gomez Del Real     }
234c97d6d2cSSergio Andres Gomez Del Real 
235c97d6d2cSSergio Andres Gomez Del Real     return false;
236c97d6d2cSSergio Andres Gomez Del Real }
237c97d6d2cSSergio Andres Gomez Del Real 
vmx_write_mem(CPUState * cpu,target_ulong gva,void * data,int bytes)238f8436a16SPhilippe Mathieu-Daudé void vmx_write_mem(CPUState *cpu, target_ulong gva, void *data, int bytes)
239c97d6d2cSSergio Andres Gomez Del Real {
240ff2de166SPaolo Bonzini     uint64_t gpa;
241c97d6d2cSSergio Andres Gomez Del Real 
242c97d6d2cSSergio Andres Gomez Del Real     while (bytes > 0) {
243c97d6d2cSSergio Andres Gomez Del Real         /* copy page */
244c97d6d2cSSergio Andres Gomez Del Real         int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
245c97d6d2cSSergio Andres Gomez Del Real 
246c97d6d2cSSergio Andres Gomez Del Real         if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
24774682782SPaolo Bonzini             VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
248c97d6d2cSSergio Andres Gomez Del Real         } else {
24919f70347SPeter Maydell             address_space_write(&address_space_memory, gpa,
25019f70347SPeter Maydell                                 MEMTXATTRS_UNSPECIFIED, data, copy);
251c97d6d2cSSergio Andres Gomez Del Real         }
252c97d6d2cSSergio Andres Gomez Del Real 
253c97d6d2cSSergio Andres Gomez Del Real         bytes -= copy;
254c97d6d2cSSergio Andres Gomez Del Real         gva += copy;
255c97d6d2cSSergio Andres Gomez Del Real         data += copy;
256c97d6d2cSSergio Andres Gomez Del Real     }
257c97d6d2cSSergio Andres Gomez Del Real }
258c97d6d2cSSergio Andres Gomez Del Real 
vmx_read_mem(CPUState * cpu,void * data,target_ulong gva,int bytes)259f8436a16SPhilippe Mathieu-Daudé void vmx_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes)
260c97d6d2cSSergio Andres Gomez Del Real {
261ff2de166SPaolo Bonzini     uint64_t gpa;
262c97d6d2cSSergio Andres Gomez Del Real 
263c97d6d2cSSergio Andres Gomez Del Real     while (bytes > 0) {
264c97d6d2cSSergio Andres Gomez Del Real         /* copy page */
265c97d6d2cSSergio Andres Gomez Del Real         int copy = MIN(bytes, 0x1000 - (gva & 0xfff));
266c97d6d2cSSergio Andres Gomez Del Real 
267c97d6d2cSSergio Andres Gomez Del Real         if (!mmu_gva_to_gpa(cpu, gva, &gpa)) {
26874682782SPaolo Bonzini             VM_PANIC_EX("%s: mmu_gva_to_gpa %llx failed\n", __func__, gva);
269c97d6d2cSSergio Andres Gomez Del Real         }
27019f70347SPeter Maydell         address_space_read(&address_space_memory, gpa, MEMTXATTRS_UNSPECIFIED,
27119f70347SPeter Maydell                            data, copy);
272c97d6d2cSSergio Andres Gomez Del Real 
273c97d6d2cSSergio Andres Gomez Del Real         bytes -= copy;
274c97d6d2cSSergio Andres Gomez Del Real         gva += copy;
275c97d6d2cSSergio Andres Gomez Del Real         data += copy;
276c97d6d2cSSergio Andres Gomez Del Real     }
277c97d6d2cSSergio Andres Gomez Del Real }
278