Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/arch/x86/domain_page.c
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * domain_page.h
3
 *
4
 * Allow temporary mapping of domain pages.
5
 *
6
 * Copyright (c) 2003-2006, Keir Fraser <keir@xensource.com>
7
 */
8
9
#include <xen/domain_page.h>
10
#include <xen/efi.h>
11
#include <xen/mm.h>
12
#include <xen/perfc.h>
13
#include <xen/pfn.h>
14
#include <xen/sched.h>
15
#include <xen/vmap.h>
16
#include <asm/current.h>
17
#include <asm/flushtlb.h>
18
#include <asm/hardirq.h>
19
#include <asm/setup.h>
20
21
static DEFINE_PER_CPU(struct vcpu *, override);
22
23
static inline struct vcpu *mapcache_current_vcpu(void)
24
43.7M
{
25
43.7M
    /* In the common case we use the mapcache of the running VCPU. */
26
43.7M
    struct vcpu *v = this_cpu(override) ?: current;
27
43.7M
28
43.7M
    /*
29
43.7M
     * When current isn't properly set up yet, this is equivalent to
30
43.7M
     * running in an idle vCPU (callers must check for NULL).
31
43.7M
     */
32
43.7M
    if ( v == INVALID_VCPU )
33
263
        return NULL;
34
43.7M
35
43.7M
    /*
36
43.7M
     * When using efi runtime page tables, we have the equivalent of the idle
37
43.7M
     * domain's page tables but current may point at another domain's VCPU.
38
43.7M
     * Return NULL as though current is not properly set up yet.
39
43.7M
     */
40
43.7M
    if ( efi_rs_using_pgtables() )
41
0
        return NULL;
42
43.7M
43
43.7M
    /*
44
43.7M
     * If guest_table is NULL, and we are running a paravirtualised guest,
45
43.7M
     * then it means we are running on the idle domain's page table and must
46
43.7M
     * therefore use its mapcache.
47
43.7M
     */
48
43.7M
    if ( unlikely(pagetable_is_null(v->arch.guest_table)) && is_pv_vcpu(v) )
49
27.1M
    {
50
27.1M
        /* If we really are idling, perform lazy context switch now. */
51
27.1M
        if ( (v = idle_vcpu[smp_processor_id()]) == current )
52
27.1M
            sync_local_execstate();
53
27.1M
        /* We must now be running on the idle page table. */
54
27.1M
        ASSERT(read_cr3() == __pa(idle_pg_table));
55
27.1M
    }
56
43.7M
57
43.7M
    return v;
58
43.7M
}
59
60
void __init mapcache_override_current(struct vcpu *v)
61
0
{
62
0
    this_cpu(override) = v;
63
0
}
64
65
#define mapcache_l2_entry(e) ((e) >> PAGETABLE_ORDER)
66
#define MAPCACHE_L2_ENTRIES (mapcache_l2_entry(MAPCACHE_ENTRIES - 1) + 1)
67
#define MAPCACHE_L1ENT(idx) \
68
    __linear_l1_table[l1_linear_offset(MAPCACHE_VIRT_START + pfn_to_paddr(idx))]
69
70
void *map_domain_page(mfn_t mfn)
71
43.7M
{
72
43.7M
    unsigned long flags;
73
43.7M
    unsigned int idx, i;
74
43.7M
    struct vcpu *v;
75
43.7M
    struct mapcache_domain *dcache;
76
43.7M
    struct mapcache_vcpu *vcache;
77
43.7M
    struct vcpu_maphash_entry *hashent;
78
43.7M
79
43.7M
#ifdef NDEBUG
80
    if ( mfn_x(mfn) <= PFN_DOWN(__pa(HYPERVISOR_VIRT_END - 1)) )
81
        return mfn_to_virt(mfn_x(mfn));
82
#endif
83
43.7M
84
43.7M
    v = mapcache_current_vcpu();
85
43.7M
    if ( !v || !is_pv_vcpu(v) )
86
16.6M
        return mfn_to_virt(mfn_x(mfn));
87
43.7M
88
27.1M
    dcache = &v->domain->arch.pv_domain.mapcache;
89
27.1M
    vcache = &v->arch.pv_vcpu.mapcache;
90
27.1M
    if ( !dcache->inuse )
91
27.1M
        return mfn_to_virt(mfn_x(mfn));
92
27.1M
93
18.4E
    perfc_incr(map_domain_page_count);
94
18.4E
95
18.4E
    local_irq_save(flags);
96
18.4E
97
18.4E
    hashent = &vcache->hash[MAPHASH_HASHFN(mfn_x(mfn))];
98
18.4E
    if ( hashent->mfn == mfn_x(mfn) )
99
0
    {
100
0
        idx = hashent->idx;
101
0
        ASSERT(idx < dcache->entries);
102
0
        hashent->refcnt++;
103
0
        ASSERT(hashent->refcnt);
104
0
        ASSERT(l1e_get_pfn(MAPCACHE_L1ENT(idx)) == mfn_x(mfn));
105
0
        goto out;
106
0
    }
107
18.4E
108
18.4E
    spin_lock(&dcache->lock);
109
18.4E
110
18.4E
    /* Has some other CPU caused a wrap? We must flush if so. */
111
18.4E
    if ( unlikely(dcache->epoch != vcache->shadow_epoch) )
112
0
    {
113
0
        vcache->shadow_epoch = dcache->epoch;
114
0
        if ( NEED_FLUSH(this_cpu(tlbflush_time), dcache->tlbflush_timestamp) )
115
0
        {
116
0
            perfc_incr(domain_page_tlb_flush);
117
0
            flush_tlb_local();
118
0
        }
119
0
    }
120
18.4E
121
18.4E
    idx = find_next_zero_bit(dcache->inuse, dcache->entries, dcache->cursor);
122
18.4E
    if ( unlikely(idx >= dcache->entries) )
123
0
    {
124
0
        unsigned long accum = 0, prev = 0;
125
0
126
0
        /* /First/, clean the garbage map and update the inuse list. */
127
0
        for ( i = 0; i < BITS_TO_LONGS(dcache->entries); i++ )
128
0
        {
129
0
            accum |= prev;
130
0
            dcache->inuse[i] &= ~xchg(&dcache->garbage[i], 0);
131
0
            prev = ~dcache->inuse[i];
132
0
        }
133
0
134
0
        if ( accum | (prev & BITMAP_LAST_WORD_MASK(dcache->entries)) )
135
0
            idx = find_first_zero_bit(dcache->inuse, dcache->entries);
136
0
        else
137
0
        {
138
0
            /* Replace a hash entry instead. */
139
0
            i = MAPHASH_HASHFN(mfn_x(mfn));
140
0
            do {
141
0
                hashent = &vcache->hash[i];
142
0
                if ( hashent->idx != MAPHASHENT_NOTINUSE && !hashent->refcnt )
143
0
                {
144
0
                    idx = hashent->idx;
145
0
                    ASSERT(l1e_get_pfn(MAPCACHE_L1ENT(idx)) == hashent->mfn);
146
0
                    l1e_write(&MAPCACHE_L1ENT(idx), l1e_empty());
147
0
                    hashent->idx = MAPHASHENT_NOTINUSE;
148
0
                    hashent->mfn = ~0UL;
149
0
                    break;
150
0
                }
151
0
                if ( ++i == MAPHASH_ENTRIES )
152
0
                    i = 0;
153
0
            } while ( i != MAPHASH_HASHFN(mfn_x(mfn)) );
154
0
        }
155
0
        BUG_ON(idx >= dcache->entries);
156
0
157
0
        /* /Second/, flush TLBs. */
158
0
        perfc_incr(domain_page_tlb_flush);
159
0
        flush_tlb_local();
160
0
        vcache->shadow_epoch = ++dcache->epoch;
161
0
        dcache->tlbflush_timestamp = tlbflush_current_time();
162
0
    }
163
18.4E
164
18.4E
    set_bit(idx, dcache->inuse);
165
18.4E
    dcache->cursor = idx + 1;
166
18.4E
167
18.4E
    spin_unlock(&dcache->lock);
168
18.4E
169
18.4E
    l1e_write(&MAPCACHE_L1ENT(idx), l1e_from_mfn(mfn, __PAGE_HYPERVISOR_RW));
170
18.4E
171
0
 out:
172
0
    local_irq_restore(flags);
173
0
    return (void *)MAPCACHE_VIRT_START + pfn_to_paddr(idx);
174
18.4E
}
175
176
void unmap_domain_page(const void *ptr)
177
43.7M
{
178
43.7M
    unsigned int idx;
179
43.7M
    struct vcpu *v;
180
43.7M
    struct mapcache_domain *dcache;
181
43.7M
    unsigned long va = (unsigned long)ptr, mfn, flags;
182
43.7M
    struct vcpu_maphash_entry *hashent;
183
43.7M
184
43.7M
    if ( va >= DIRECTMAP_VIRT_START )
185
43.7M
        return;
186
43.7M
187
18.4E
    ASSERT(va >= MAPCACHE_VIRT_START && va < MAPCACHE_VIRT_END);
188
18.4E
189
18.4E
    v = mapcache_current_vcpu();
190
18.4E
    ASSERT(v && is_pv_vcpu(v));
191
18.4E
192
18.4E
    dcache = &v->domain->arch.pv_domain.mapcache;
193
18.4E
    ASSERT(dcache->inuse);
194
18.4E
195
18.4E
    idx = PFN_DOWN(va - MAPCACHE_VIRT_START);
196
18.4E
    mfn = l1e_get_pfn(MAPCACHE_L1ENT(idx));
197
18.4E
    hashent = &v->arch.pv_vcpu.mapcache.hash[MAPHASH_HASHFN(mfn)];
198
18.4E
199
18.4E
    local_irq_save(flags);
200
18.4E
201
18.4E
    if ( hashent->idx == idx )
202
0
    {
203
0
        ASSERT(hashent->mfn == mfn);
204
0
        ASSERT(hashent->refcnt);
205
0
        hashent->refcnt--;
206
0
    }
207
18.4E
    else if ( !hashent->refcnt )
208
0
    {
209
0
        if ( hashent->idx != MAPHASHENT_NOTINUSE )
210
0
        {
211
0
            /* /First/, zap the PTE. */
212
0
            ASSERT(l1e_get_pfn(MAPCACHE_L1ENT(hashent->idx)) ==
213
0
                   hashent->mfn);
214
0
            l1e_write(&MAPCACHE_L1ENT(hashent->idx), l1e_empty());
215
0
            /* /Second/, mark as garbage. */
216
0
            set_bit(hashent->idx, dcache->garbage);
217
0
        }
218
0
219
0
        /* Add newly-freed mapping to the maphash. */
220
0
        hashent->mfn = mfn;
221
0
        hashent->idx = idx;
222
0
    }
223
18.4E
    else
224
18.4E
    {
225
18.4E
        /* /First/, zap the PTE. */
226
18.4E
        l1e_write(&MAPCACHE_L1ENT(idx), l1e_empty());
227
18.4E
        /* /Second/, mark as garbage. */
228
0
        set_bit(idx, dcache->garbage);
229
0
    }
230
18.4E
231
0
    local_irq_restore(flags);
232
0
}
233
234
int mapcache_domain_init(struct domain *d)
235
2
{
236
2
    struct mapcache_domain *dcache = &d->arch.pv_domain.mapcache;
237
2
    unsigned int bitmap_pages;
238
2
239
2
    if ( !is_pv_domain(d) || is_idle_domain(d) )
240
2
        return 0;
241
2
242
2
#ifdef NDEBUG
243
    if ( !mem_hotplug && max_page <= PFN_DOWN(__pa(HYPERVISOR_VIRT_END - 1)) )
244
        return 0;
245
#endif
246
2
247
0
    BUILD_BUG_ON(MAPCACHE_VIRT_END + PAGE_SIZE * (3 +
248
0
                 2 * PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long))) >
249
0
                 MAPCACHE_VIRT_START + (PERDOMAIN_SLOT_MBYTES << 20));
250
0
    bitmap_pages = PFN_UP(BITS_TO_LONGS(MAPCACHE_ENTRIES) * sizeof(long));
251
0
    dcache->inuse = (void *)MAPCACHE_VIRT_END + PAGE_SIZE;
252
0
    dcache->garbage = dcache->inuse +
253
0
                      (bitmap_pages + 1) * PAGE_SIZE / sizeof(long);
254
0
255
0
    spin_lock_init(&dcache->lock);
256
0
257
0
    return create_perdomain_mapping(d, (unsigned long)dcache->inuse,
258
0
                                    2 * bitmap_pages + 1,
259
0
                                    NIL(l1_pgentry_t *), NULL);
260
2
}
261
262
int mapcache_vcpu_init(struct vcpu *v)
263
24
{
264
24
    struct domain *d = v->domain;
265
24
    struct mapcache_domain *dcache = &d->arch.pv_domain.mapcache;
266
24
    unsigned long i;
267
24
    unsigned int ents = d->max_vcpus * MAPCACHE_VCPU_ENTRIES;
268
24
    unsigned int nr = PFN_UP(BITS_TO_LONGS(ents) * sizeof(long));
269
24
270
24
    if ( !is_pv_vcpu(v) || !dcache->inuse )
271
24
        return 0;
272
24
273
0
    if ( ents > dcache->entries )
274
0
    {
275
0
        /* Populate page tables. */
276
0
        int rc = create_perdomain_mapping(d, MAPCACHE_VIRT_START, ents,
277
0
                                          NIL(l1_pgentry_t *), NULL);
278
0
279
0
        /* Populate bit maps. */
280
0
        if ( !rc )
281
0
            rc = create_perdomain_mapping(d, (unsigned long)dcache->inuse,
282
0
                                          nr, NULL, NIL(struct page_info *));
283
0
        if ( !rc )
284
0
            rc = create_perdomain_mapping(d, (unsigned long)dcache->garbage,
285
0
                                          nr, NULL, NIL(struct page_info *));
286
0
287
0
        if ( rc )
288
0
            return rc;
289
0
290
0
        dcache->entries = ents;
291
0
    }
292
0
293
0
    /* Mark all maphash entries as not in use. */
294
0
    BUILD_BUG_ON(MAPHASHENT_NOTINUSE < MAPCACHE_ENTRIES);
295
0
    for ( i = 0; i < MAPHASH_ENTRIES; i++ )
296
0
    {
297
0
        struct vcpu_maphash_entry *hashent = &v->arch.pv_vcpu.mapcache.hash[i];
298
0
299
0
        hashent->mfn = ~0UL; /* never valid to map */
300
0
        hashent->idx = MAPHASHENT_NOTINUSE;
301
0
    }
302
0
303
0
    return 0;
304
0
}
305
306
void *map_domain_page_global(mfn_t mfn)
307
24
{
308
24
    ASSERT(!in_irq() &&
309
24
           ((system_state >= SYS_STATE_boot &&
310
24
             system_state < SYS_STATE_active) ||
311
24
            local_irq_is_enabled()));
312
24
313
24
#ifdef NDEBUG
314
    if ( mfn_x(mfn) <= PFN_DOWN(__pa(HYPERVISOR_VIRT_END - 1)) )
315
        return mfn_to_virt(mfn_x(mfn));
316
#endif
317
24
318
24
    return vmap(&mfn, 1);
319
24
}
320
321
void unmap_domain_page_global(const void *ptr)
322
0
{
323
0
    unsigned long va = (unsigned long)ptr;
324
0
325
0
    if ( va >= DIRECTMAP_VIRT_START )
326
0
        return;
327
0
328
0
    ASSERT(va >= VMAP_VIRT_START && va < VMAP_VIRT_END);
329
0
330
0
    vunmap(ptr);
331
0
}
332
333
/* Translate a map-domain-page'd address to the underlying MFN */
334
unsigned long domain_page_map_to_mfn(const void *ptr)
335
0
{
336
0
    unsigned long va = (unsigned long)ptr;
337
0
    const l1_pgentry_t *pl1e;
338
0
339
0
    if ( va >= DIRECTMAP_VIRT_START )
340
0
        return virt_to_mfn(ptr);
341
0
342
0
    if ( va >= VMAP_VIRT_START && va < VMAP_VIRT_END )
343
0
    {
344
0
        pl1e = virt_to_xen_l1e(va);
345
0
        BUG_ON(!pl1e);
346
0
    }
347
0
    else
348
0
    {
349
0
        ASSERT(va >= MAPCACHE_VIRT_START && va < MAPCACHE_VIRT_END);
350
0
        pl1e = &__linear_l1_table[l1_linear_offset(va)];
351
0
    }
352
0
353
0
    return l1e_get_pfn(*pl1e);
354
0
}