Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/arch/x86/pv/domain.c
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * arch/x86/pv/domain.c
3
 *
4
 * PV domain handling
5
 */
6
7
#include <xen/domain_page.h>
8
#include <xen/errno.h>
9
#include <xen/lib.h>
10
#include <xen/sched.h>
11
12
#include <asm/pv/domain.h>
13
14
/* Override macros from asm/page.h to make them work with mfn_t */
15
#undef mfn_to_page
16
#define mfn_to_page(mfn) __mfn_to_page(mfn_x(mfn))
17
#undef page_to_mfn
18
0
#define page_to_mfn(pg) _mfn(__page_to_mfn(pg))
19
20
static void noreturn continue_nonidle_domain(struct vcpu *v)
21
0
{
22
0
    check_wakeup_from_wait();
23
0
    mark_regs_dirty(guest_cpu_user_regs());
24
0
    reset_stack_and_jump(ret_from_intr);
25
0
}
26
27
static int setup_compat_l4(struct vcpu *v)
28
0
{
29
0
    struct page_info *pg;
30
0
    l4_pgentry_t *l4tab;
31
0
    mfn_t mfn;
32
0
33
0
    pg = alloc_domheap_page(v->domain, MEMF_no_owner);
34
0
    if ( pg == NULL )
35
0
        return -ENOMEM;
36
0
37
0
    mfn = page_to_mfn(pg);
38
0
    l4tab = map_domain_page(mfn);
39
0
    clear_page(l4tab);
40
0
    init_xen_l4_slots(l4tab, mfn, v->domain, INVALID_MFN, false);
41
0
    unmap_domain_page(l4tab);
42
0
43
0
    /* This page needs to look like a pagetable so that it can be shadowed */
44
0
    pg->u.inuse.type_info = PGT_l4_page_table | PGT_validated | 1;
45
0
46
0
    v->arch.guest_table = pagetable_from_page(pg);
47
0
    v->arch.guest_table_user = v->arch.guest_table;
48
0
49
0
    return 0;
50
0
}
51
52
static void release_compat_l4(struct vcpu *v)
53
0
{
54
0
    if ( !pagetable_is_null(v->arch.guest_table) )
55
0
        free_domheap_page(pagetable_get_page(v->arch.guest_table));
56
0
    v->arch.guest_table = pagetable_null();
57
0
    v->arch.guest_table_user = pagetable_null();
58
0
}
59
60
int switch_compat(struct domain *d)
61
0
{
62
0
    struct vcpu *v;
63
0
    int rc;
64
0
65
0
    if ( is_hvm_domain(d) || d->tot_pages != 0 )
66
0
        return -EACCES;
67
0
    if ( is_pv_32bit_domain(d) )
68
0
        return 0;
69
0
70
0
    d->arch.has_32bit_shinfo = 1;
71
0
    d->arch.is_32bit_pv = 1;
72
0
73
0
    for_each_vcpu( d, v )
74
0
    {
75
0
        if ( (rc = setup_compat_arg_xlat(v)) ||
76
0
             (rc = setup_compat_l4(v)) )
77
0
            goto undo_and_fail;
78
0
    }
79
0
80
0
    domain_set_alloc_bitsize(d);
81
0
    recalculate_cpuid_policy(d);
82
0
83
0
    d->arch.x87_fip_width = 4;
84
0
85
0
    return 0;
86
0
87
0
 undo_and_fail:
88
0
    d->arch.is_32bit_pv = d->arch.has_32bit_shinfo = 0;
89
0
    for_each_vcpu( d, v )
90
0
    {
91
0
        free_compat_arg_xlat(v);
92
0
        release_compat_l4(v);
93
0
    }
94
0
95
0
    return rc;
96
0
}
97
98
static int pv_create_gdt_ldt_l1tab(struct vcpu *v)
99
0
{
100
0
    return create_perdomain_mapping(v->domain, GDT_VIRT_START(v),
101
0
                                    1U << GDT_LDT_VCPU_SHIFT,
102
0
                                    v->domain->arch.pv_domain.gdt_ldt_l1tab,
103
0
                                    NULL);
104
0
}
105
106
static void pv_destroy_gdt_ldt_l1tab(struct vcpu *v)
107
0
{
108
0
    destroy_perdomain_mapping(v->domain, GDT_VIRT_START(v),
109
0
                              1U << GDT_LDT_VCPU_SHIFT);
110
0
}
111
112
void pv_vcpu_destroy(struct vcpu *v)
113
0
{
114
0
    if ( is_pv_32bit_vcpu(v) )
115
0
    {
116
0
        free_compat_arg_xlat(v);
117
0
        release_compat_l4(v);
118
0
    }
119
0
120
0
    pv_destroy_gdt_ldt_l1tab(v);
121
0
    xfree(v->arch.pv_vcpu.trap_ctxt);
122
0
    v->arch.pv_vcpu.trap_ctxt = NULL;
123
0
}
124
125
int pv_vcpu_initialise(struct vcpu *v)
126
0
{
127
0
    struct domain *d = v->domain;
128
0
    int rc;
129
0
130
0
    ASSERT(!is_idle_domain(d));
131
0
132
0
    spin_lock_init(&v->arch.pv_vcpu.shadow_ldt_lock);
133
0
134
0
    rc = pv_create_gdt_ldt_l1tab(v);
135
0
    if ( rc )
136
0
        return rc;
137
0
138
0
    BUILD_BUG_ON(NR_VECTORS * sizeof(*v->arch.pv_vcpu.trap_ctxt) >
139
0
                 PAGE_SIZE);
140
0
    v->arch.pv_vcpu.trap_ctxt = xzalloc_array(struct trap_info,
141
0
                                              NR_VECTORS);
142
0
    if ( !v->arch.pv_vcpu.trap_ctxt )
143
0
    {
144
0
        rc = -ENOMEM;
145
0
        goto done;
146
0
    }
147
0
148
0
    /* PV guests by default have a 100Hz ticker. */
149
0
    v->periodic_period = MILLISECS(10);
150
0
151
0
    v->arch.pv_vcpu.ctrlreg[4] = real_cr4_to_pv_guest_cr4(mmu_cr4_features);
152
0
153
0
    if ( is_pv_32bit_domain(d) )
154
0
    {
155
0
        if ( (rc = setup_compat_arg_xlat(v)) )
156
0
            goto done;
157
0
158
0
        if ( (rc = setup_compat_l4(v)) )
159
0
            goto done;
160
0
    }
161
0
162
0
 done:
163
0
    if ( rc )
164
0
        pv_vcpu_destroy(v);
165
0
    return rc;
166
0
}
167
168
void pv_domain_destroy(struct domain *d)
169
0
{
170
0
    destroy_perdomain_mapping(d, GDT_LDT_VIRT_START,
171
0
                              GDT_LDT_MBYTES << (20 - PAGE_SHIFT));
172
0
173
0
    xfree(d->arch.pv_domain.cpuidmasks);
174
0
    d->arch.pv_domain.cpuidmasks = NULL;
175
0
176
0
    free_xenheap_page(d->arch.pv_domain.gdt_ldt_l1tab);
177
0
    d->arch.pv_domain.gdt_ldt_l1tab = NULL;
178
0
}
179
180
181
int pv_domain_initialise(struct domain *d, unsigned int domcr_flags,
182
                         struct xen_arch_domainconfig *config)
183
0
{
184
0
    static const struct arch_csw pv_csw = {
185
0
        .from = paravirt_ctxt_switch_from,
186
0
        .to   = paravirt_ctxt_switch_to,
187
0
        .tail = continue_nonidle_domain,
188
0
    };
189
0
    int rc = -ENOMEM;
190
0
191
0
    d->arch.pv_domain.gdt_ldt_l1tab =
192
0
        alloc_xenheap_pages(0, MEMF_node(domain_to_node(d)));
193
0
    if ( !d->arch.pv_domain.gdt_ldt_l1tab )
194
0
        goto fail;
195
0
    clear_page(d->arch.pv_domain.gdt_ldt_l1tab);
196
0
197
0
    if ( levelling_caps & ~LCAP_faulting )
198
0
    {
199
0
        d->arch.pv_domain.cpuidmasks = xmalloc(struct cpuidmasks);
200
0
        if ( !d->arch.pv_domain.cpuidmasks )
201
0
            goto fail;
202
0
        *d->arch.pv_domain.cpuidmasks = cpuidmask_defaults;
203
0
    }
204
0
205
0
    rc = create_perdomain_mapping(d, GDT_LDT_VIRT_START,
206
0
                                  GDT_LDT_MBYTES << (20 - PAGE_SHIFT),
207
0
                                  NULL, NULL);
208
0
    if ( rc )
209
0
        goto fail;
210
0
211
0
    d->arch.ctxt_switch = &pv_csw;
212
0
213
0
    /* 64-bit PV guest by default. */
214
0
    d->arch.is_32bit_pv = d->arch.has_32bit_shinfo = 0;
215
0
216
0
    return 0;
217
0
218
0
  fail:
219
0
    pv_domain_destroy(d);
220
0
221
0
    return rc;
222
0
}
223
224
void toggle_guest_mode(struct vcpu *v)
225
0
{
226
0
    if ( is_pv_32bit_vcpu(v) )
227
0
        return;
228
0
229
0
    if ( cpu_has_fsgsbase )
230
0
    {
231
0
        if ( v->arch.flags & TF_kernel_mode )
232
0
            v->arch.pv_vcpu.gs_base_kernel = __rdgsbase();
233
0
        else
234
0
            v->arch.pv_vcpu.gs_base_user = __rdgsbase();
235
0
    }
236
0
    v->arch.flags ^= TF_kernel_mode;
237
0
    asm volatile ( "swapgs" );
238
0
    update_cr3(v);
239
0
    /* Don't flush user global mappings from the TLB. Don't tick TLB clock. */
240
0
    asm volatile ( "mov %0, %%cr3" : : "r" (v->arch.cr3) : "memory" );
241
0
242
0
    if ( !(v->arch.flags & TF_kernel_mode) )
243
0
        return;
244
0
245
0
    if ( v->arch.pv_vcpu.need_update_runstate_area &&
246
0
         update_runstate_area(v) )
247
0
        v->arch.pv_vcpu.need_update_runstate_area = 0;
248
0
249
0
    if ( v->arch.pv_vcpu.pending_system_time.version &&
250
0
         update_secondary_system_time(v,
251
0
                                      &v->arch.pv_vcpu.pending_system_time) )
252
0
        v->arch.pv_vcpu.pending_system_time.version = 0;
253
0
}
254
255
/*
256
 * Local variables:
257
 * mode: C
258
 * c-file-style: "BSD"
259
 * c-basic-offset: 4
260
 * tab-width: 4
261
 * indent-tabs-mode: nil
262
 * End:
263
 */