Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/arch/x86/mm/hap/nested_hap.c
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * arch/x86/mm/hap/nested_hap.c
3
 *
4
 * Code for Nested Virtualization
5
 * Copyright (c) 2011 Advanced Micro Devices
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program; If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include <xen/vm_event.h>
22
#include <xen/event.h>
23
#include <public/vm_event.h>
24
#include <asm/domain.h>
25
#include <asm/page.h>
26
#include <asm/paging.h>
27
#include <asm/p2m.h>
28
#include <asm/mem_sharing.h>
29
#include <asm/hap.h>
30
#include <asm/hvm/support.h>
31
32
#include <asm/hvm/nestedhvm.h>
33
34
#include "private.h"
35
36
/* AlGORITHM for NESTED PAGE FAULT 
37
 * 
38
 * NOTATION
39
 * Levels: L0, L1, L2
40
 * Guests: L1 guest, L2 guest
41
 * Hypervisor: L0 hypervisor
42
 * Addresses: L2-GVA, L2-GPA, L1-GVA, L1-GPA, MPA
43
 *
44
 * On L0, when #NPF happens, the handler function should do:
45
 * hap_page_fault(GPA)
46
 * {
47
 *    1. If #NPF is from L1 guest, then we crash the guest VM (same as old 
48
 *       code)
49
 *    2. If #NPF is from L2 guest, then we continue from (3)
50
 *    3. Get np2m base from L1 guest. Map np2m base into L0 hypervisor address
51
 *       space.
52
 *    4. Walk the np2m's  page table
53
 *    5.    - if not present or permission check failure, then we inject #NPF
54
 *            back to L1 guest and
55
 *            re-launch L1 guest (L1 guest will either treat this #NPF as MMIO,
56
 *            or fix its p2m table for L2 guest)
57
 *    6.    - if present, then we will get the a new translated value L1-GPA 
58
 *            (points to L1 machine memory)
59
 *    7.        * Use L1-GPA to walk L0 P2M table
60
 *    8.            - if not present, then crash the guest (should not happen)
61
 *    9.            - if present, then we get a new translated value MPA 
62
 *                    (points to real machine memory)
63
 *   10.                * Finally, use GPA and MPA to walk nested_p2m 
64
 *                        and fix the bits.
65
 * }
66
 * 
67
 */
68
69
70
/********************************************/
71
/*        NESTED VIRT P2M FUNCTIONS         */
72
/********************************************/
73
/* Override macros from asm/page.h to make them work with mfn_t */
74
#undef page_to_mfn
75
#define page_to_mfn(_pg) _mfn(__page_to_mfn(_pg))
76
77
void
78
nestedp2m_write_p2m_entry(struct p2m_domain *p2m, unsigned long gfn,
79
    l1_pgentry_t *p, l1_pgentry_t new, unsigned int level)
80
0
{
81
0
    struct domain *d = p2m->domain;
82
0
    uint32_t old_flags;
83
0
84
0
    paging_lock(d);
85
0
86
0
    old_flags = l1e_get_flags(*p);
87
0
    safe_write_pte(p, new);
88
0
89
0
    if (old_flags & _PAGE_PRESENT)
90
0
        flush_tlb_mask(p2m->dirty_cpumask);
91
0
92
0
    paging_unlock(d);
93
0
}
94
95
/********************************************/
96
/*          NESTED VIRT FUNCTIONS           */
97
/********************************************/
98
static void
99
nestedhap_fix_p2m(struct vcpu *v, struct p2m_domain *p2m, 
100
                  paddr_t L2_gpa, paddr_t L0_gpa,
101
                  unsigned int page_order, p2m_type_t p2mt, p2m_access_t p2ma)
102
0
{
103
0
    int rc = 0;
104
0
    unsigned long gfn, mask;
105
0
    mfn_t mfn;
106
0
107
0
    ASSERT(p2m);
108
0
    ASSERT(p2m->set_entry);
109
0
    ASSERT(p2m_locked_by_me(p2m));
110
0
111
0
    /*
112
0
     * If this is a superpage mapping, round down both addresses to
113
0
     * the start of the superpage.
114
0
     */
115
0
    mask = ~((1UL << page_order) - 1);
116
0
    gfn = (L2_gpa >> PAGE_SHIFT) & mask;
117
0
    mfn = _mfn((L0_gpa >> PAGE_SHIFT) & mask);
118
0
119
0
    rc = p2m_set_entry(p2m, _gfn(gfn), mfn, page_order, p2mt, p2ma);
120
0
121
0
    if ( rc )
122
0
    {
123
0
        gdprintk(XENLOG_ERR,
124
0
                 "failed to set entry for %#"PRIx64" -> %#"PRIx64" rc:%d\n",
125
0
                 L2_gpa, L0_gpa, rc);
126
0
        domain_crash(p2m->domain);
127
0
    }
128
0
}
129
130
/* This function uses L2_gpa to walk the P2M page table in L1. If the
131
 * walk is successful, the translated value is returned in
132
 * L1_gpa. The result value tells what to do next.
133
 */
134
int
135
nestedhap_walk_L1_p2m(struct vcpu *v, paddr_t L2_gpa, paddr_t *L1_gpa,
136
                      unsigned int *page_order, uint8_t *p2m_acc,
137
                      bool_t access_r, bool_t access_w, bool_t access_x)
138
0
{
139
0
    ASSERT(hvm_funcs.nhvm_hap_walk_L1_p2m);
140
0
141
0
    return hvm_funcs.nhvm_hap_walk_L1_p2m(v, L2_gpa, L1_gpa, page_order,
142
0
        p2m_acc, access_r, access_w, access_x);
143
0
}
144
145
146
/* This function uses L1_gpa to walk the P2M table in L0 hypervisor. If the
147
 * walk is successful, the translated value is returned in L0_gpa. The return 
148
 * value tells the upper level what to do.
149
 */
150
static int
151
nestedhap_walk_L0_p2m(struct p2m_domain *p2m, paddr_t L1_gpa, paddr_t *L0_gpa,
152
                      p2m_type_t *p2mt, p2m_access_t *p2ma,
153
                      unsigned int *page_order,
154
                      bool_t access_r, bool_t access_w, bool_t access_x)
155
0
{
156
0
    mfn_t mfn;
157
0
    int rc;
158
0
159
0
    /* walk L0 P2M table */
160
0
    mfn = get_gfn_type_access(p2m, L1_gpa >> PAGE_SHIFT, p2mt, p2ma,
161
0
                              0, page_order);
162
0
163
0
    rc = NESTEDHVM_PAGEFAULT_DIRECT_MMIO;
164
0
    if ( *p2mt == p2m_mmio_direct )
165
0
        goto direct_mmio_out;
166
0
    rc = NESTEDHVM_PAGEFAULT_MMIO;
167
0
    if ( *p2mt == p2m_mmio_dm )
168
0
        goto out;
169
0
170
0
    rc = NESTEDHVM_PAGEFAULT_L0_ERROR;
171
0
    if ( access_w && p2m_is_readonly(*p2mt) )
172
0
        goto out;
173
0
174
0
    if ( p2m_is_paging(*p2mt) || p2m_is_shared(*p2mt) || !p2m_is_ram(*p2mt) )
175
0
        goto out;
176
0
177
0
    if ( !mfn_valid(mfn) )
178
0
        goto out;
179
0
180
0
    rc = NESTEDHVM_PAGEFAULT_DONE;
181
0
direct_mmio_out:
182
0
    *L0_gpa = (mfn_x(mfn) << PAGE_SHIFT) + (L1_gpa & ~PAGE_MASK);
183
0
out:
184
0
    __put_gfn(p2m, L1_gpa >> PAGE_SHIFT);
185
0
    return rc;
186
0
}
187
188
/*
189
 * The following function, nestedhap_page_fault(), is for steps (3)--(10).
190
 *
191
 * Returns:
192
 */
193
int
194
nestedhvm_hap_nested_page_fault(struct vcpu *v, paddr_t *L2_gpa,
195
    bool_t access_r, bool_t access_w, bool_t access_x)
196
0
{
197
0
    int rv;
198
0
    paddr_t L1_gpa, L0_gpa;
199
0
    struct domain *d = v->domain;
200
0
    struct p2m_domain *p2m, *nested_p2m;
201
0
    unsigned int page_order_21, page_order_10, page_order_20;
202
0
    p2m_type_t p2mt_10;
203
0
    p2m_access_t p2ma_10 = p2m_access_rwx;
204
0
    uint8_t p2ma_21 = p2m_access_rwx;
205
0
206
0
    p2m = p2m_get_hostp2m(d); /* L0 p2m */
207
0
208
0
    /* walk the L1 P2M table */
209
0
    rv = nestedhap_walk_L1_p2m(v, *L2_gpa, &L1_gpa, &page_order_21, &p2ma_21,
210
0
        access_r, access_w, access_x);
211
0
212
0
    /* let caller to handle these two cases */
213
0
    switch (rv) {
214
0
    case NESTEDHVM_PAGEFAULT_INJECT:
215
0
    case NESTEDHVM_PAGEFAULT_RETRY:
216
0
    case NESTEDHVM_PAGEFAULT_L1_ERROR:
217
0
        return rv;
218
0
    case NESTEDHVM_PAGEFAULT_DONE:
219
0
        break;
220
0
    default:
221
0
        BUG();
222
0
        break;
223
0
    }
224
0
225
0
    /* ==> we have to walk L0 P2M */
226
0
    rv = nestedhap_walk_L0_p2m(p2m, L1_gpa, &L0_gpa,
227
0
        &p2mt_10, &p2ma_10, &page_order_10,
228
0
        access_r, access_w, access_x);
229
0
230
0
    /* let upper level caller to handle these two cases */
231
0
    switch (rv) {
232
0
    case NESTEDHVM_PAGEFAULT_INJECT:
233
0
        return rv;
234
0
    case NESTEDHVM_PAGEFAULT_L0_ERROR:
235
0
        *L2_gpa = L1_gpa;
236
0
        return rv;
237
0
    case NESTEDHVM_PAGEFAULT_DONE:
238
0
        break;
239
0
    case NESTEDHVM_PAGEFAULT_MMIO:
240
0
        return rv;
241
0
    case NESTEDHVM_PAGEFAULT_DIRECT_MMIO:
242
0
        break;
243
0
    default:
244
0
        BUG();
245
0
        break;
246
0
    }
247
0
248
0
    page_order_20 = min(page_order_21, page_order_10);
249
0
250
0
    ASSERT(p2ma_10 <= p2m_access_n2rwx);
251
0
    /*NOTE: if assert fails, needs to handle new access type here */
252
0
253
0
    switch ( p2ma_10 )
254
0
    {
255
0
    case p2m_access_n ... p2m_access_rwx:
256
0
        break;
257
0
    case p2m_access_rx2rw:
258
0
        p2ma_10 = p2m_access_rx;
259
0
        break;
260
0
    case p2m_access_n2rwx:
261
0
        p2ma_10 = p2m_access_n;
262
0
        break;
263
0
    default:
264
0
        p2ma_10 = p2m_access_n;
265
0
        /* For safety, remove all permissions. */
266
0
        gdprintk(XENLOG_ERR, "Unhandled p2m access type:%d\n", p2ma_10);
267
0
    }
268
0
    /* Use minimal permission for nested p2m. */
269
0
    p2ma_10 &= (p2m_access_t)p2ma_21;
270
0
271
0
    /* fix p2m_get_pagetable(nested_p2m) */
272
0
    nested_p2m = p2m_get_nestedp2m_locked(v);
273
0
    nestedhap_fix_p2m(v, nested_p2m, *L2_gpa, L0_gpa, page_order_20,
274
0
        p2mt_10, p2ma_10);
275
0
    p2m_unlock(nested_p2m);
276
0
277
0
    return NESTEDHVM_PAGEFAULT_DONE;
278
0
}
279
280
/********************************************/
281
/*     NESTED VIRT INITIALIZATION FUNCS     */
282
/********************************************/
283
284
/*
285
 * Local variables:
286
 * mode: C
287
 * c-file-style: "BSD"
288
 * c-basic-offset: 4
289
 * tab-width: 4
290
 * indent-tabs-mode: nil
291
 * End:
292
 */