Coverage Report

Created: 2017-10-25 09:10

/root/src/xen/xen/arch/x86/pv/callback.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * pv/callback.c
3
 *
4
 * hypercall handles and helper functions for guest callback
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms and conditions of the GNU General Public
8
 * License, version 2, as published by the Free Software Foundation.
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
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public
16
 * License along with this program; If not, see <http://www.gnu.org/licenses/>.
17
 */
18
19
#include <xen/event.h>
20
#include <xen/hypercall.h>
21
#include <xen/guest_access.h>
22
#include <xen/lib.h>
23
#include <xen/sched.h>
24
#include <compat/callback.h>
25
#include <compat/nmi.h>
26
27
#include <asm/current.h>
28
#include <asm/nmi.h>
29
#include <asm/shared.h>
30
#include <asm/traps.h>
31
32
#include <public/callback.h>
33
34
/* Override macros from asm/page.h to make them work with mfn_t */
35
#undef mfn_to_page
36
#define mfn_to_page(mfn) __mfn_to_page(mfn_x(mfn))
37
#undef page_to_mfn
38
#define page_to_mfn(pg) _mfn(__page_to_mfn(pg))
39
40
static int register_guest_nmi_callback(unsigned long address)
41
0
{
42
0
    struct vcpu *curr = current;
43
0
    struct domain *d = curr->domain;
44
0
    struct trap_info *t = &curr->arch.pv_vcpu.trap_ctxt[TRAP_nmi];
45
0
46
0
    if ( !is_canonical_address(address) )
47
0
        return -EINVAL;
48
0
49
0
    t->vector  = TRAP_nmi;
50
0
    t->flags   = 0;
51
0
    t->cs      = (is_pv_32bit_domain(d) ?
52
0
                  FLAT_COMPAT_KERNEL_CS : FLAT_KERNEL_CS);
53
0
    t->address = address;
54
0
    TI_SET_IF(t, 1);
55
0
56
0
    /*
57
0
     * If no handler was registered we can 'lose the NMI edge'. Re-assert it
58
0
     * now.
59
0
     */
60
0
    if ( curr->vcpu_id == 0 && arch_get_nmi_reason(d) != 0 )
61
0
        curr->nmi_pending = 1;
62
0
63
0
    return 0;
64
0
}
65
66
static void unregister_guest_nmi_callback(void)
67
0
{
68
0
    struct vcpu *curr = current;
69
0
    struct trap_info *t = &curr->arch.pv_vcpu.trap_ctxt[TRAP_nmi];
70
0
71
0
    memset(t, 0, sizeof(*t));
72
0
}
73
74
static long register_guest_callback(struct callback_register *reg)
75
0
{
76
0
    long ret = 0;
77
0
    struct vcpu *curr = current;
78
0
79
0
    if ( !is_canonical_address(reg->address) )
80
0
        return -EINVAL;
81
0
82
0
    switch ( reg->type )
83
0
    {
84
0
    case CALLBACKTYPE_event:
85
0
        curr->arch.pv_vcpu.event_callback_eip    = reg->address;
86
0
        break;
87
0
88
0
    case CALLBACKTYPE_failsafe:
89
0
        curr->arch.pv_vcpu.failsafe_callback_eip = reg->address;
90
0
        if ( reg->flags & CALLBACKF_mask_events )
91
0
            set_bit(_VGCF_failsafe_disables_events,
92
0
                    &curr->arch.vgc_flags);
93
0
        else
94
0
            clear_bit(_VGCF_failsafe_disables_events,
95
0
                      &curr->arch.vgc_flags);
96
0
        break;
97
0
98
0
    case CALLBACKTYPE_syscall:
99
0
        curr->arch.pv_vcpu.syscall_callback_eip  = reg->address;
100
0
        if ( reg->flags & CALLBACKF_mask_events )
101
0
            set_bit(_VGCF_syscall_disables_events,
102
0
                    &curr->arch.vgc_flags);
103
0
        else
104
0
            clear_bit(_VGCF_syscall_disables_events,
105
0
                      &curr->arch.vgc_flags);
106
0
        break;
107
0
108
0
    case CALLBACKTYPE_syscall32:
109
0
        curr->arch.pv_vcpu.syscall32_callback_eip = reg->address;
110
0
        curr->arch.pv_vcpu.syscall32_disables_events =
111
0
            !!(reg->flags & CALLBACKF_mask_events);
112
0
        break;
113
0
114
0
    case CALLBACKTYPE_sysenter:
115
0
        curr->arch.pv_vcpu.sysenter_callback_eip = reg->address;
116
0
        curr->arch.pv_vcpu.sysenter_disables_events =
117
0
            !!(reg->flags & CALLBACKF_mask_events);
118
0
        break;
119
0
120
0
    case CALLBACKTYPE_nmi:
121
0
        ret = register_guest_nmi_callback(reg->address);
122
0
        break;
123
0
124
0
    default:
125
0
        ret = -ENOSYS;
126
0
        break;
127
0
    }
128
0
129
0
    return ret;
130
0
}
131
132
static long unregister_guest_callback(struct callback_unregister *unreg)
133
0
{
134
0
    long ret;
135
0
136
0
    switch ( unreg->type )
137
0
    {
138
0
    case CALLBACKTYPE_event:
139
0
    case CALLBACKTYPE_failsafe:
140
0
    case CALLBACKTYPE_syscall:
141
0
    case CALLBACKTYPE_syscall32:
142
0
    case CALLBACKTYPE_sysenter:
143
0
        ret = -EINVAL;
144
0
        break;
145
0
146
0
    case CALLBACKTYPE_nmi:
147
0
        unregister_guest_nmi_callback();
148
0
        ret = 0;
149
0
        break;
150
0
151
0
    default:
152
0
        ret = -ENOSYS;
153
0
        break;
154
0
    }
155
0
156
0
    return ret;
157
0
}
158
159
long do_callback_op(int cmd, XEN_GUEST_HANDLE_PARAM(const_void) arg)
160
0
{
161
0
    long ret;
162
0
163
0
    switch ( cmd )
164
0
    {
165
0
    case CALLBACKOP_register:
166
0
    {
167
0
        struct callback_register reg;
168
0
169
0
        ret = -EFAULT;
170
0
        if ( copy_from_guest(&reg, arg, 1) )
171
0
            break;
172
0
173
0
        ret = register_guest_callback(&reg);
174
0
    }
175
0
    break;
176
0
177
0
    case CALLBACKOP_unregister:
178
0
    {
179
0
        struct callback_unregister unreg;
180
0
181
0
        ret = -EFAULT;
182
0
        if ( copy_from_guest(&unreg, arg, 1) )
183
0
            break;
184
0
185
0
        ret = unregister_guest_callback(&unreg);
186
0
    }
187
0
    break;
188
0
189
0
    default:
190
0
        ret = -ENOSYS;
191
0
        break;
192
0
    }
193
0
194
0
    return ret;
195
0
}
196
197
long do_set_callbacks(unsigned long event_address,
198
                      unsigned long failsafe_address,
199
                      unsigned long syscall_address)
200
0
{
201
0
    struct callback_register event = {
202
0
        .type = CALLBACKTYPE_event,
203
0
        .address = event_address,
204
0
    };
205
0
    struct callback_register failsafe = {
206
0
        .type = CALLBACKTYPE_failsafe,
207
0
        .address = failsafe_address,
208
0
    };
209
0
    struct callback_register syscall = {
210
0
        .type = CALLBACKTYPE_syscall,
211
0
        .address = syscall_address,
212
0
    };
213
0
214
0
    register_guest_callback(&event);
215
0
    register_guest_callback(&failsafe);
216
0
    register_guest_callback(&syscall);
217
0
218
0
    return 0;
219
0
}
220
221
static long compat_register_guest_callback(struct compat_callback_register *reg)
222
0
{
223
0
    long ret = 0;
224
0
    struct vcpu *curr = current;
225
0
226
0
    fixup_guest_code_selector(curr->domain, reg->address.cs);
227
0
228
0
    switch ( reg->type )
229
0
    {
230
0
    case CALLBACKTYPE_event:
231
0
        curr->arch.pv_vcpu.event_callback_cs     = reg->address.cs;
232
0
        curr->arch.pv_vcpu.event_callback_eip    = reg->address.eip;
233
0
        break;
234
0
235
0
    case CALLBACKTYPE_failsafe:
236
0
        curr->arch.pv_vcpu.failsafe_callback_cs  = reg->address.cs;
237
0
        curr->arch.pv_vcpu.failsafe_callback_eip = reg->address.eip;
238
0
        if ( reg->flags & CALLBACKF_mask_events )
239
0
            set_bit(_VGCF_failsafe_disables_events,
240
0
                    &curr->arch.vgc_flags);
241
0
        else
242
0
            clear_bit(_VGCF_failsafe_disables_events,
243
0
                      &curr->arch.vgc_flags);
244
0
        break;
245
0
246
0
    case CALLBACKTYPE_syscall32:
247
0
        curr->arch.pv_vcpu.syscall32_callback_cs     = reg->address.cs;
248
0
        curr->arch.pv_vcpu.syscall32_callback_eip    = reg->address.eip;
249
0
        curr->arch.pv_vcpu.syscall32_disables_events =
250
0
            (reg->flags & CALLBACKF_mask_events) != 0;
251
0
        break;
252
0
253
0
    case CALLBACKTYPE_sysenter:
254
0
        curr->arch.pv_vcpu.sysenter_callback_cs     = reg->address.cs;
255
0
        curr->arch.pv_vcpu.sysenter_callback_eip    = reg->address.eip;
256
0
        curr->arch.pv_vcpu.sysenter_disables_events =
257
0
            (reg->flags & CALLBACKF_mask_events) != 0;
258
0
        break;
259
0
260
0
    case CALLBACKTYPE_nmi:
261
0
        ret = register_guest_nmi_callback(reg->address.eip);
262
0
        break;
263
0
264
0
    default:
265
0
        ret = -ENOSYS;
266
0
        break;
267
0
    }
268
0
269
0
    return ret;
270
0
}
271
272
static long compat_unregister_guest_callback(
273
    struct compat_callback_unregister *unreg)
274
0
{
275
0
    long ret;
276
0
277
0
    switch ( unreg->type )
278
0
    {
279
0
    case CALLBACKTYPE_event:
280
0
    case CALLBACKTYPE_failsafe:
281
0
    case CALLBACKTYPE_syscall32:
282
0
    case CALLBACKTYPE_sysenter:
283
0
        ret = -EINVAL;
284
0
        break;
285
0
286
0
    case CALLBACKTYPE_nmi:
287
0
        unregister_guest_nmi_callback();
288
0
        ret = 0;
289
0
        break;
290
0
291
0
    default:
292
0
        ret = -ENOSYS;
293
0
        break;
294
0
    }
295
0
296
0
    return ret;
297
0
}
298
299
long compat_callback_op(int cmd, XEN_GUEST_HANDLE(void) arg)
300
0
{
301
0
    long ret;
302
0
303
0
    switch ( cmd )
304
0
    {
305
0
    case CALLBACKOP_register:
306
0
    {
307
0
        struct compat_callback_register reg;
308
0
309
0
        ret = -EFAULT;
310
0
        if ( copy_from_guest(&reg, arg, 1) )
311
0
            break;
312
0
313
0
        ret = compat_register_guest_callback(&reg);
314
0
    }
315
0
    break;
316
0
317
0
    case CALLBACKOP_unregister:
318
0
    {
319
0
        struct compat_callback_unregister unreg;
320
0
321
0
        ret = -EFAULT;
322
0
        if ( copy_from_guest(&unreg, arg, 1) )
323
0
            break;
324
0
325
0
        ret = compat_unregister_guest_callback(&unreg);
326
0
    }
327
0
    break;
328
0
329
0
    default:
330
0
        ret = -EINVAL;
331
0
        break;
332
0
    }
333
0
334
0
    return ret;
335
0
}
336
337
long compat_set_callbacks(unsigned long event_selector,
338
                          unsigned long event_address,
339
                          unsigned long failsafe_selector,
340
                          unsigned long failsafe_address)
341
0
{
342
0
    struct compat_callback_register event = {
343
0
        .type = CALLBACKTYPE_event,
344
0
        .address = {
345
0
            .cs = event_selector,
346
0
            .eip = event_address
347
0
        }
348
0
    };
349
0
    struct compat_callback_register failsafe = {
350
0
        .type = CALLBACKTYPE_failsafe,
351
0
        .address = {
352
0
            .cs = failsafe_selector,
353
0
            .eip = failsafe_address
354
0
        }
355
0
    };
356
0
357
0
    compat_register_guest_callback(&event);
358
0
    compat_register_guest_callback(&failsafe);
359
0
360
0
    return 0;
361
0
}
362
363
long do_set_trap_table(XEN_GUEST_HANDLE_PARAM(const_trap_info_t) traps)
364
0
{
365
0
    struct trap_info cur;
366
0
    struct vcpu *curr = current;
367
0
    struct trap_info *dst = curr->arch.pv_vcpu.trap_ctxt;
368
0
    long rc = 0;
369
0
370
0
    /* If no table is presented then clear the entire virtual IDT. */
371
0
    if ( guest_handle_is_null(traps) )
372
0
    {
373
0
        memset(dst, 0, NR_VECTORS * sizeof(*dst));
374
0
        init_int80_direct_trap(curr);
375
0
        return 0;
376
0
    }
377
0
378
0
    for ( ; ; )
379
0
    {
380
0
        if ( copy_from_guest(&cur, traps, 1) )
381
0
        {
382
0
            rc = -EFAULT;
383
0
            break;
384
0
        }
385
0
386
0
        if ( cur.address == 0 )
387
0
            break;
388
0
389
0
        if ( !is_canonical_address(cur.address) )
390
0
            return -EINVAL;
391
0
392
0
        fixup_guest_code_selector(curr->domain, cur.cs);
393
0
394
0
        memcpy(&dst[cur.vector], &cur, sizeof(cur));
395
0
396
0
        if ( cur.vector == 0x80 )
397
0
            init_int80_direct_trap(curr);
398
0
399
0
        guest_handle_add_offset(traps, 1);
400
0
401
0
        if ( hypercall_preempt_check() )
402
0
        {
403
0
            rc = hypercall_create_continuation(
404
0
                __HYPERVISOR_set_trap_table, "h", traps);
405
0
            break;
406
0
        }
407
0
    }
408
0
409
0
    return rc;
410
0
}
411
412
int compat_set_trap_table(XEN_GUEST_HANDLE(trap_info_compat_t) traps)
413
0
{
414
0
    struct vcpu *curr = current;
415
0
    struct compat_trap_info cur;
416
0
    struct trap_info *dst = curr->arch.pv_vcpu.trap_ctxt;
417
0
    long rc = 0;
418
0
419
0
    /* If no table is presented then clear the entire virtual IDT. */
420
0
    if ( guest_handle_is_null(traps) )
421
0
    {
422
0
        memset(dst, 0, NR_VECTORS * sizeof(*dst));
423
0
        init_int80_direct_trap(curr);
424
0
        return 0;
425
0
    }
426
0
427
0
    for ( ; ; )
428
0
    {
429
0
        if ( copy_from_guest(&cur, traps, 1) )
430
0
        {
431
0
            rc = -EFAULT;
432
0
            break;
433
0
        }
434
0
435
0
        if ( cur.address == 0 )
436
0
            break;
437
0
438
0
        fixup_guest_code_selector(curr->domain, cur.cs);
439
0
440
0
        XLAT_trap_info(dst + cur.vector, &cur);
441
0
442
0
        if ( cur.vector == 0x80 )
443
0
            init_int80_direct_trap(curr);
444
0
445
0
        guest_handle_add_offset(traps, 1);
446
0
447
0
        if ( hypercall_preempt_check() )
448
0
        {
449
0
            rc = hypercall_create_continuation(
450
0
                __HYPERVISOR_set_trap_table, "h", traps);
451
0
            break;
452
0
        }
453
0
    }
454
0
455
0
    return rc;
456
0
}
457
458
long do_nmi_op(unsigned int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
459
0
{
460
0
    struct xennmi_callback cb;
461
0
    long rc = 0;
462
0
463
0
    switch ( cmd )
464
0
    {
465
0
    case XENNMI_register_callback:
466
0
        rc = -EFAULT;
467
0
        if ( copy_from_guest(&cb, arg, 1) )
468
0
            break;
469
0
        rc = register_guest_nmi_callback(cb.handler_address);
470
0
        break;
471
0
    case XENNMI_unregister_callback:
472
0
        unregister_guest_nmi_callback();
473
0
        rc = 0;
474
0
        break;
475
0
    default:
476
0
        rc = -ENOSYS;
477
0
        break;
478
0
    }
479
0
480
0
    return rc;
481
0
}
482
483
int compat_nmi_op(unsigned int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
484
0
{
485
0
    struct compat_nmi_callback cb;
486
0
    int rc = 0;
487
0
488
0
    switch ( cmd )
489
0
    {
490
0
    case XENNMI_register_callback:
491
0
        rc = -EFAULT;
492
0
        if ( copy_from_guest(&cb, arg, 1) )
493
0
            break;
494
0
        rc = register_guest_nmi_callback(cb.handler_address);
495
0
        break;
496
0
    case XENNMI_unregister_callback:
497
0
        unregister_guest_nmi_callback();
498
0
        rc = 0;
499
0
        break;
500
0
    default:
501
0
        rc = -ENOSYS;
502
0
        break;
503
0
    }
504
0
505
0
    return rc;
506
0
}
507
508
/*
509
 * Local variables:
510
 * mode: C
511
 * c-file-style: "BSD"
512
 * c-basic-offset: 4
513
 * tab-width: 4
514
 * indent-tabs-mode: nil
515
 * End:
516
 */