Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/arch/x86/pv/grant_table.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * pv/grant_table.c
3
 *
4
 * Grant table interfaces for PV guests
5
 *
6
 * Copyright (C) 2017 Wei Liu <wei.liu2@citrix.com>
7
 *
8
 * This program is free software; you can redistribute it and/or
9
 * modify it under the terms and conditions of the GNU General Public
10
 * License, version 2, as published by the Free Software Foundation.
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 GNU
15
 * General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public
18
 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include <xen/types.h>
22
23
#include <public/grant_table.h>
24
25
#include <asm/p2m.h>
26
#include <asm/pv/mm.h>
27
28
#include "mm.h"
29
30
/* Override macros from asm/page.h to make them work with mfn_t */
31
#undef mfn_to_page
32
0
#define mfn_to_page(mfn) __mfn_to_page(mfn_x(mfn))
33
#undef page_to_mfn
34
#define page_to_mfn(pg) _mfn(__page_to_mfn(pg))
35
36
static unsigned int grant_to_pte_flags(unsigned int grant_flags,
37
                                       unsigned int cache_flags)
38
0
{
39
0
    unsigned int pte_flags =
40
0
        _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_GNTTAB | _PAGE_NX;
41
0
42
0
    if ( grant_flags & GNTMAP_application_map )
43
0
        pte_flags |= _PAGE_USER;
44
0
    if ( !(grant_flags & GNTMAP_readonly) )
45
0
        pte_flags |= _PAGE_RW;
46
0
47
0
    pte_flags |= MASK_INSR((grant_flags >> _GNTMAP_guest_avail0), _PAGE_AVAIL);
48
0
    pte_flags |= cacheattr_to_pte_flags(cache_flags >> 5);
49
0
50
0
    return pte_flags;
51
0
}
52
53
int create_grant_pv_mapping(uint64_t addr, unsigned long frame,
54
                            unsigned int flags, unsigned int cache_flags)
55
0
{
56
0
    struct vcpu *curr = current;
57
0
    struct domain *currd = curr->domain;
58
0
    l1_pgentry_t nl1e, ol1e = { }, *pl1e;
59
0
    struct page_info *page;
60
0
    mfn_t gl1mfn;
61
0
    int rc = GNTST_general_error;
62
0
63
0
    nl1e = l1e_from_pfn(frame, grant_to_pte_flags(flags, cache_flags));
64
0
    nl1e = adjust_guest_l1e(nl1e, currd);
65
0
66
0
    /*
67
0
     * The meaning of addr depends on GNTMAP_contains_pte.  It is either a
68
0
     * machine address of an L1e the guest has nominated to be altered, or a
69
0
     * linear address we need to look up the appropriate L1e for.
70
0
     */
71
0
    if ( flags & GNTMAP_contains_pte )
72
0
    {
73
0
        /* addr must be suitably aligned, or we will corrupt adjacent ptes. */
74
0
        if ( !IS_ALIGNED(addr, sizeof(nl1e)) )
75
0
        {
76
0
            gdprintk(XENLOG_WARNING,
77
0
                     "Misaligned PTE address %"PRIx64"\n", addr);
78
0
            goto out;
79
0
        }
80
0
81
0
        gl1mfn = _mfn(addr >> PAGE_SHIFT);
82
0
83
0
        if ( !get_page_from_mfn(gl1mfn, currd) )
84
0
            goto out;
85
0
86
0
        pl1e = map_domain_page(gl1mfn) + (addr & ~PAGE_MASK);
87
0
    }
88
0
    else
89
0
    {
90
0
        /* Guest trying to pass an out-of-range linear address? */
91
0
        if ( is_pv_32bit_domain(currd) && addr != (uint32_t)addr )
92
0
            goto out;
93
0
94
0
        pl1e = map_guest_l1e(addr, &gl1mfn);
95
0
96
0
        if ( !pl1e )
97
0
        {
98
0
            gdprintk(XENLOG_WARNING,
99
0
                     "Could not find L1 PTE for linear address %"PRIx64"\n",
100
0
                     addr);
101
0
            goto out;
102
0
        }
103
0
104
0
        if ( !get_page_from_mfn(gl1mfn, currd) )
105
0
            goto out_unmap;
106
0
    }
107
0
108
0
    page = mfn_to_page(gl1mfn);
109
0
    if ( !page_lock(page) )
110
0
        goto out_put;
111
0
112
0
    if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table )
113
0
        goto out_unlock;
114
0
115
0
    ol1e = *pl1e;
116
0
    if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, mfn_x(gl1mfn), curr, 0) )
117
0
        rc = GNTST_okay;
118
0
119
0
 out_unlock:
120
0
    page_unlock(page);
121
0
 out_put:
122
0
    put_page(page);
123
0
 out_unmap:
124
0
    unmap_domain_page(pl1e);
125
0
126
0
    if ( rc == GNTST_okay )
127
0
        put_page_from_l1e(ol1e, currd);
128
0
129
0
 out:
130
0
    return rc;
131
0
}
132
133
/*
134
 * This exists soley for implementing GNTABOP_unmap_and_replace, the ABI of
135
 * which is bizarre.  This GNTTABOP isn't used any more, but was used by
136
 * classic-xen kernels and PVOps Linux before the M2P_OVERRIDE infrastructure
137
 * was replaced with something which actually worked.
138
 *
139
 * Look up the L1e mapping linear, and zap it.  Return the L1e via *out.
140
 * Returns a boolean indicating success.  If success, the caller is
141
 * responsible for calling put_page_from_l1e().
142
 */
143
static bool steal_linear_address(unsigned long linear, l1_pgentry_t *out)
144
0
{
145
0
    struct vcpu *curr = current;
146
0
    struct domain *currd = curr->domain;
147
0
    l1_pgentry_t *pl1e, ol1e;
148
0
    struct page_info *page;
149
0
    mfn_t gl1mfn;
150
0
    bool okay = false;
151
0
152
0
    ASSERT(is_pv_domain(currd));
153
0
154
0
    pl1e = map_guest_l1e(linear, &gl1mfn);
155
0
    if ( !pl1e )
156
0
    {
157
0
        gdprintk(XENLOG_WARNING,
158
0
                 "Could not find L1 PTE for linear %"PRIx64"\n", linear);
159
0
        goto out;
160
0
    }
161
0
162
0
    if ( !get_page_from_mfn(gl1mfn, currd) )
163
0
        goto out_unmap;
164
0
165
0
    page = mfn_to_page(gl1mfn);
166
0
    if ( !page_lock(page) )
167
0
        goto out_put;
168
0
169
0
    if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table )
170
0
        goto out_unlock;
171
0
172
0
    ol1e = *pl1e;
173
0
    okay = UPDATE_ENTRY(l1, pl1e, ol1e, l1e_empty(), mfn_x(gl1mfn), curr, 0);
174
0
175
0
 out_unlock:
176
0
    page_unlock(page);
177
0
 out_put:
178
0
    put_page(page);
179
0
 out_unmap:
180
0
    unmap_domain_page(pl1e);
181
0
182
0
    if ( okay )
183
0
        *out = ol1e;
184
0
185
0
 out:
186
0
    return okay;
187
0
}
188
189
/*
190
 * Passing a new_addr of zero is taken to mean destroy.  Passing a non-zero
191
 * new_addr has only ever been available via GNTABOP_unmap_and_replace, and
192
 * only when !(flags & GNTMAP_contains_pte).
193
 */
194
int replace_grant_pv_mapping(uint64_t addr, unsigned long frame,
195
                             uint64_t new_addr, unsigned int flags)
196
0
{
197
0
    struct vcpu *curr = current;
198
0
    struct domain *currd = curr->domain;
199
0
    l1_pgentry_t nl1e = l1e_empty(), ol1e, *pl1e;
200
0
    struct page_info *page;
201
0
    mfn_t gl1mfn;
202
0
    int rc = GNTST_general_error;
203
0
    unsigned int grant_pte_flags = grant_to_pte_flags(flags, 0);
204
0
205
0
    /*
206
0
     * On top of the explicit settings done by create_grant_pv_mapping()
207
0
     * also open-code relevant parts of adjust_guest_l1e(). Don't mirror
208
0
     * available and cachability flags, though.
209
0
     */
210
0
    if ( !is_pv_32bit_domain(currd) )
211
0
        grant_pte_flags |= (grant_pte_flags & _PAGE_USER)
212
0
                           ? _PAGE_GLOBAL
213
0
                           : _PAGE_GUEST_KERNEL | _PAGE_USER;
214
0
215
0
    /*
216
0
     * addr comes from Xen's active_entry tracking, and was used successfully
217
0
     * to create a grant.
218
0
     *
219
0
     * The meaning of addr depends on GNTMAP_contains_pte.  It is either a
220
0
     * machine address of an L1e the guest has nominated to be altered, or a
221
0
     * linear address we need to look up the appropriate L1e for.
222
0
     */
223
0
    if ( flags & GNTMAP_contains_pte )
224
0
    {
225
0
        /* Replace not available in this addressing mode. */
226
0
        if ( new_addr )
227
0
            goto out;
228
0
229
0
        /* Sanity check that we won't clobber the pagetable. */
230
0
        if ( !IS_ALIGNED(addr, sizeof(nl1e)) )
231
0
        {
232
0
            ASSERT_UNREACHABLE();
233
0
            goto out;
234
0
        }
235
0
236
0
        gl1mfn = _mfn(addr >> PAGE_SHIFT);
237
0
238
0
        if ( !get_page_from_mfn(gl1mfn, currd) )
239
0
            goto out;
240
0
241
0
        pl1e = map_domain_page(gl1mfn) + (addr & ~PAGE_MASK);
242
0
    }
243
0
    else
244
0
    {
245
0
        if ( is_pv_32bit_domain(currd) )
246
0
        {
247
0
            if ( addr != (uint32_t)addr )
248
0
            {
249
0
                ASSERT_UNREACHABLE();
250
0
                goto out;
251
0
            }
252
0
253
0
            /* Guest trying to pass an out-of-range linear address? */
254
0
            if ( new_addr != (uint32_t)new_addr )
255
0
                goto out;
256
0
        }
257
0
258
0
        if ( new_addr && !steal_linear_address(new_addr, &nl1e) )
259
0
            goto out;
260
0
261
0
        pl1e = map_guest_l1e(addr, &gl1mfn);
262
0
263
0
        if ( !pl1e )
264
0
            goto out;
265
0
266
0
        if ( !get_page_from_mfn(gl1mfn, currd) )
267
0
            goto out_unmap;
268
0
    }
269
0
270
0
    page = mfn_to_page(gl1mfn);
271
0
272
0
    if ( !page_lock(page) )
273
0
        goto out_put;
274
0
275
0
    if ( (page->u.inuse.type_info & PGT_type_mask) != PGT_l1_page_table )
276
0
        goto out_unlock;
277
0
278
0
    ol1e = *pl1e;
279
0
280
0
    /*
281
0
     * Check that the address supplied is actually mapped to frame (with
282
0
     * appropriate permissions).
283
0
     */
284
0
    if ( unlikely(l1e_get_pfn(ol1e) != frame) ||
285
0
         unlikely((l1e_get_flags(ol1e) ^ grant_pte_flags) &
286
0
                  (_PAGE_PRESENT | _PAGE_RW)) )
287
0
    {
288
0
        gdprintk(XENLOG_ERR,
289
0
                 "PTE %"PRIpte" for %"PRIx64" doesn't match grant (%"PRIpte")\n",
290
0
                 l1e_get_intpte(ol1e), addr,
291
0
                 l1e_get_intpte(l1e_from_pfn(frame, grant_pte_flags)));
292
0
        goto out_unlock;
293
0
    }
294
0
295
0
    if ( unlikely((l1e_get_flags(ol1e) ^ grant_pte_flags) &
296
0
                  ~(_PAGE_AVAIL | PAGE_CACHE_ATTRS)) )
297
0
        gdprintk(XENLOG_WARNING,
298
0
                 "PTE flags %x for %"PRIx64" don't match grant (%x)\n",
299
0
                 l1e_get_flags(ol1e), addr, grant_pte_flags);
300
0
301
0
    if ( UPDATE_ENTRY(l1, pl1e, ol1e, nl1e, mfn_x(gl1mfn), curr, 0) )
302
0
        rc = GNTST_okay;
303
0
304
0
 out_unlock:
305
0
    page_unlock(page);
306
0
 out_put:
307
0
    put_page(page);
308
0
 out_unmap:
309
0
    unmap_domain_page(pl1e);
310
0
311
0
 out:
312
0
    /* If there was an error, we are still responsible for the stolen pte. */
313
0
    if ( rc )
314
0
        put_page_from_l1e(nl1e, currd);
315
0
316
0
    return rc;
317
0
}
318
319
/*
320
 * Local variables:
321
 * mode: C
322
 * c-file-style: "BSD"
323
 * c-basic-offset: 4
324
 * tab-width: 4
325
 * indent-tabs-mode: nil
326
 * End:
327
 */