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