1 /*
2 * xen-access.c
3 *
4 * Exercises the basic per-page access mechanisms
5 *
6 * Copyright (c) 2011 Virtuata, Inc.
7 * Copyright (c) 2009 by Citrix Systems, Inc. (Patrick Colp), based on
8 * xenpaging.c
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to
12 * deal in the Software without restriction, including without limitation the
13 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
14 * sell copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 */
28
29 #include <errno.h>
30 #include <inttypes.h>
31 #include <stdlib.h>
32 #include <stdarg.h>
33 #include <stdbool.h>
34 #include <string.h>
35 #include <time.h>
36 #include <signal.h>
37 #include <unistd.h>
38 #include <sys/mman.h>
39 #include <poll.h>
40
41 #include <xenctrl.h>
42 #include <xenevtchn.h>
43 #include <xen/vm_event.h>
44
45 #include <xen-tools/libs.h>
46
47 #if defined(__arm__) || defined(__aarch64__)
48 #include <xen/arch-arm.h>
49 #define START_PFN (GUEST_RAM0_BASE >> 12)
50 #elif defined(__i386__) || defined(__x86_64__)
51 #define START_PFN 0ULL
52 #endif
53
54 #define DPRINTF(a, b...) fprintf(stderr, a, ## b)
55 #define ERROR(a, b...) fprintf(stderr, a "\n", ## b)
56 #define PERROR(a, b...) fprintf(stderr, a ": %s\n", ## b, strerror(errno))
57
58 /* From xen/include/asm-x86/processor.h */
59 #define X86_TRAP_DEBUG 1
60 #define X86_TRAP_INT3 3
61
62 /* From xen/include/asm-x86/x86-defns.h */
63 #define X86_CR4_PGE 0x00000080 /* enable global pages */
64
65 typedef struct vm_event {
66 domid_t domain_id;
67 xenevtchn_handle *xce_handle;
68 int port;
69 vm_event_back_ring_t back_ring;
70 uint32_t evtchn_port;
71 void *ring_page;
72 } vm_event_t;
73
74 typedef struct xenaccess {
75 xc_interface *xc_handle;
76
77 xen_pfn_t max_gpfn;
78
79 vm_event_t vm_event;
80 } xenaccess_t;
81
82 static int interrupted;
83 bool evtchn_bind = 0, evtchn_open = 0, mem_access_enable = 0;
84
close_handler(int sig)85 static void close_handler(int sig)
86 {
87 interrupted = sig;
88 }
89
xc_wait_for_event_or_timeout(xc_interface * xch,xenevtchn_handle * xce,unsigned long ms)90 int xc_wait_for_event_or_timeout(xc_interface *xch, xenevtchn_handle *xce, unsigned long ms)
91 {
92 struct pollfd fd = { .fd = xenevtchn_fd(xce), .events = POLLIN | POLLERR };
93 int port;
94 int rc;
95
96 rc = poll(&fd, 1, ms);
97 if ( rc == -1 )
98 {
99 if (errno == EINTR)
100 return 0;
101
102 ERROR("Poll exited with an error");
103 goto err;
104 }
105
106 if ( rc == 1 )
107 {
108 port = xenevtchn_pending(xce);
109 if ( port == -1 )
110 {
111 ERROR("Failed to read port from event channel");
112 goto err;
113 }
114
115 rc = xenevtchn_unmask(xce, port);
116 if ( rc != 0 )
117 {
118 ERROR("Failed to unmask event channel port");
119 goto err;
120 }
121 }
122 else
123 port = -1;
124
125 return port;
126
127 err:
128 return -errno;
129 }
130
xenaccess_teardown(xc_interface * xch,xenaccess_t * xenaccess)131 int xenaccess_teardown(xc_interface *xch, xenaccess_t *xenaccess)
132 {
133 int rc;
134
135 if ( xenaccess == NULL )
136 return 0;
137
138 /* Tear down domain xenaccess in Xen */
139 if ( xenaccess->vm_event.ring_page )
140 munmap(xenaccess->vm_event.ring_page, XC_PAGE_SIZE);
141
142 if ( mem_access_enable )
143 {
144 rc = xc_monitor_disable(xenaccess->xc_handle,
145 xenaccess->vm_event.domain_id);
146 if ( rc != 0 )
147 {
148 ERROR("Error tearing down domain xenaccess in xen");
149 return rc;
150 }
151 }
152
153 /* Unbind VIRQ */
154 if ( evtchn_bind )
155 {
156 rc = xenevtchn_unbind(xenaccess->vm_event.xce_handle,
157 xenaccess->vm_event.port);
158 if ( rc != 0 )
159 {
160 ERROR("Error unbinding event port");
161 return rc;
162 }
163 }
164
165 /* Close event channel */
166 if ( evtchn_open )
167 {
168 rc = xenevtchn_close(xenaccess->vm_event.xce_handle);
169 if ( rc != 0 )
170 {
171 ERROR("Error closing event channel");
172 return rc;
173 }
174 }
175
176 /* Close connection to Xen */
177 rc = xc_interface_close(xenaccess->xc_handle);
178 if ( rc != 0 )
179 {
180 ERROR("Error closing connection to xen");
181 return rc;
182 }
183 xenaccess->xc_handle = NULL;
184
185 free(xenaccess);
186
187 return 0;
188 }
189
xenaccess_init(xc_interface ** xch_r,domid_t domain_id)190 xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t domain_id)
191 {
192 xenaccess_t *xenaccess = 0;
193 xc_interface *xch;
194 int rc;
195
196 xch = xc_interface_open(NULL, NULL, 0);
197 if ( !xch )
198 goto err_iface;
199
200 DPRINTF("xenaccess init\n");
201 *xch_r = xch;
202
203 /* Allocate memory */
204 xenaccess = malloc(sizeof(xenaccess_t));
205 memset(xenaccess, 0, sizeof(xenaccess_t));
206
207 /* Open connection to xen */
208 xenaccess->xc_handle = xch;
209
210 /* Set domain id */
211 xenaccess->vm_event.domain_id = domain_id;
212
213 /* Enable mem_access */
214 xenaccess->vm_event.ring_page =
215 xc_monitor_enable(xenaccess->xc_handle,
216 xenaccess->vm_event.domain_id,
217 &xenaccess->vm_event.evtchn_port);
218 if ( xenaccess->vm_event.ring_page == NULL )
219 {
220 switch ( errno ) {
221 case EBUSY:
222 ERROR("xenaccess is (or was) active on this domain");
223 break;
224 case ENODEV:
225 ERROR("EPT not supported for this guest");
226 break;
227 default:
228 perror("Error enabling mem_access");
229 break;
230 }
231 goto err;
232 }
233 mem_access_enable = 1;
234
235 /* Open event channel */
236 xenaccess->vm_event.xce_handle = xenevtchn_open(NULL, 0);
237 if ( xenaccess->vm_event.xce_handle == NULL )
238 {
239 ERROR("Failed to open event channel");
240 goto err;
241 }
242 evtchn_open = 1;
243
244 /* Bind event notification */
245 rc = xenevtchn_bind_interdomain(xenaccess->vm_event.xce_handle,
246 xenaccess->vm_event.domain_id,
247 xenaccess->vm_event.evtchn_port);
248 if ( rc < 0 )
249 {
250 ERROR("Failed to bind event channel");
251 goto err;
252 }
253 evtchn_bind = 1;
254 xenaccess->vm_event.port = rc;
255
256 /* Initialise ring */
257 SHARED_RING_INIT((vm_event_sring_t *)xenaccess->vm_event.ring_page);
258 BACK_RING_INIT(&xenaccess->vm_event.back_ring,
259 (vm_event_sring_t *)xenaccess->vm_event.ring_page,
260 XC_PAGE_SIZE);
261
262 /* Get max_gpfn */
263 rc = xc_domain_maximum_gpfn(xenaccess->xc_handle,
264 xenaccess->vm_event.domain_id,
265 &xenaccess->max_gpfn);
266
267 if ( rc )
268 {
269 ERROR("Failed to get max gpfn");
270 goto err;
271 }
272
273 DPRINTF("max_gpfn = %"PRI_xen_pfn"\n", xenaccess->max_gpfn);
274
275 return xenaccess;
276
277 err:
278 rc = xenaccess_teardown(xch, xenaccess);
279 if ( rc )
280 {
281 ERROR("Failed to teardown xenaccess structure!\n");
282 }
283
284 err_iface:
285 return NULL;
286 }
287
288 static inline
control_singlestep(xc_interface * xch,domid_t domain_id,unsigned long vcpu,bool enable)289 int control_singlestep(
290 xc_interface *xch,
291 domid_t domain_id,
292 unsigned long vcpu,
293 bool enable)
294 {
295 uint32_t op = enable ?
296 XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_ON : XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_OFF;
297
298 return xc_domain_debug_control(xch, domain_id, op, vcpu);
299 }
300
301 /*
302 * Note that this function is not thread safe.
303 */
get_request(vm_event_t * vm_event,vm_event_request_t * req)304 static void get_request(vm_event_t *vm_event, vm_event_request_t *req)
305 {
306 vm_event_back_ring_t *back_ring;
307 RING_IDX req_cons;
308
309 back_ring = &vm_event->back_ring;
310 req_cons = back_ring->req_cons;
311
312 /* Copy request */
313 memcpy(req, RING_GET_REQUEST(back_ring, req_cons), sizeof(*req));
314 req_cons++;
315
316 /* Update ring */
317 back_ring->req_cons = req_cons;
318 back_ring->sring->req_event = req_cons + 1;
319 }
320
321 /*
322 * X86 control register names
323 */
get_x86_ctrl_reg_name(uint32_t index)324 static const char* get_x86_ctrl_reg_name(uint32_t index)
325 {
326 static const char* names[] = {
327 [VM_EVENT_X86_CR0] = "CR0",
328 [VM_EVENT_X86_CR3] = "CR3",
329 [VM_EVENT_X86_CR4] = "CR4",
330 [VM_EVENT_X86_XCR0] = "XCR0",
331 };
332
333 if ( index >= ARRAY_SIZE(names) || names[index] == NULL )
334 return "";
335
336 return names[index];
337 }
338
339 /*
340 * Note that this function is not thread safe.
341 */
put_response(vm_event_t * vm_event,vm_event_response_t * rsp)342 static void put_response(vm_event_t *vm_event, vm_event_response_t *rsp)
343 {
344 vm_event_back_ring_t *back_ring;
345 RING_IDX rsp_prod;
346
347 back_ring = &vm_event->back_ring;
348 rsp_prod = back_ring->rsp_prod_pvt;
349
350 /* Copy response */
351 memcpy(RING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp));
352 rsp_prod++;
353
354 /* Update ring */
355 back_ring->rsp_prod_pvt = rsp_prod;
356 RING_PUSH_RESPONSES(back_ring);
357 }
358
usage(char * progname)359 void usage(char* progname)
360 {
361 fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec", progname);
362 #if defined(__i386__) || defined(__x86_64__)
363 fprintf(stderr, "|breakpoint|altp2m_write|altp2m_exec|debug|cpuid|desc_access|write_ctrlreg_cr4|altp2m_write_no_gpt");
364 #elif defined(__arm__) || defined(__aarch64__)
365 fprintf(stderr, "|privcall");
366 #endif
367 fprintf(stderr,
368 "\n"
369 "Logs first page writes, execs, or breakpoint traps that occur on the domain.\n"
370 "\n"
371 "-m requires this program to run, or else the domain may pause\n");
372 }
373
main(int argc,char * argv[])374 int main(int argc, char *argv[])
375 {
376 struct sigaction act;
377 domid_t domain_id;
378 xenaccess_t *xenaccess;
379 vm_event_request_t req;
380 vm_event_response_t rsp;
381 int rc = -1;
382 int rc1;
383 xc_interface *xch;
384 xenmem_access_t default_access = XENMEM_access_rwx;
385 xenmem_access_t after_first_access = XENMEM_access_rwx;
386 int memaccess = 0;
387 int required = 0;
388 int breakpoint = 0;
389 int shutting_down = 0;
390 int privcall = 0;
391 int altp2m = 0;
392 int debug = 0;
393 int cpuid = 0;
394 int desc_access = 0;
395 int write_ctrlreg_cr4 = 0;
396 int altp2m_write_no_gpt = 0;
397 uint16_t altp2m_view_id = 0;
398
399 char* progname = argv[0];
400 argv++;
401 argc--;
402
403 if ( argc == 3 && argv[0][0] == '-' )
404 {
405 if ( !strcmp(argv[0], "-m") )
406 required = 1;
407 else
408 {
409 usage(progname);
410 return -1;
411 }
412 argv++;
413 argc--;
414 }
415
416 if ( argc != 2 )
417 {
418 usage(progname);
419 return -1;
420 }
421
422 domain_id = atoi(argv[0]);
423 argv++;
424 argc--;
425
426 if ( !strcmp(argv[0], "write") )
427 {
428 default_access = XENMEM_access_rx;
429 after_first_access = XENMEM_access_rwx;
430 memaccess = 1;
431 }
432 else if ( !strcmp(argv[0], "exec") )
433 {
434 default_access = XENMEM_access_rw;
435 after_first_access = XENMEM_access_rwx;
436 memaccess = 1;
437 }
438 #if defined(__i386__) || defined(__x86_64__)
439 else if ( !strcmp(argv[0], "breakpoint") )
440 {
441 breakpoint = 1;
442 }
443 else if ( !strcmp(argv[0], "altp2m_write") )
444 {
445 default_access = XENMEM_access_rx;
446 altp2m = 1;
447 memaccess = 1;
448 }
449 else if ( !strcmp(argv[0], "altp2m_exec") )
450 {
451 default_access = XENMEM_access_rw;
452 altp2m = 1;
453 memaccess = 1;
454 }
455 else if ( !strcmp(argv[0], "altp2m_write_no_gpt") )
456 {
457 default_access = XENMEM_access_rw;
458 altp2m_write_no_gpt = 1;
459 memaccess = 1;
460 altp2m = 1;
461 }
462 else if ( !strcmp(argv[0], "debug") )
463 {
464 debug = 1;
465 }
466 else if ( !strcmp(argv[0], "cpuid") )
467 {
468 cpuid = 1;
469 }
470 else if ( !strcmp(argv[0], "desc_access") )
471 {
472 desc_access = 1;
473 }
474 else if ( !strcmp(argv[0], "write_ctrlreg_cr4") )
475 {
476 write_ctrlreg_cr4 = 1;
477 }
478 #elif defined(__arm__) || defined(__aarch64__)
479 else if ( !strcmp(argv[0], "privcall") )
480 {
481 privcall = 1;
482 }
483 #endif
484 else
485 {
486 usage(argv[0]);
487 return -1;
488 }
489
490 xenaccess = xenaccess_init(&xch, domain_id);
491 if ( xenaccess == NULL )
492 {
493 ERROR("Error initialising xenaccess");
494 return 1;
495 }
496
497 DPRINTF("starting %s %u\n", argv[0], domain_id);
498
499 /* ensure that if we get a signal, we'll do cleanup, then exit */
500 act.sa_handler = close_handler;
501 act.sa_flags = 0;
502 sigemptyset(&act.sa_mask);
503 sigaction(SIGHUP, &act, NULL);
504 sigaction(SIGTERM, &act, NULL);
505 sigaction(SIGINT, &act, NULL);
506 sigaction(SIGALRM, &act, NULL);
507
508 /* Set whether the access listener is required */
509 rc = xc_domain_set_access_required(xch, domain_id, required);
510 if ( rc < 0 )
511 {
512 ERROR("Error %d setting mem_access listener required\n", rc);
513 goto exit;
514 }
515
516 /* With altp2m we just create a new, restricted view of the memory */
517 if ( memaccess && altp2m )
518 {
519 xen_pfn_t gfn = 0;
520 unsigned long perm_set = 0;
521
522 if( altp2m_write_no_gpt )
523 {
524 rc = xc_monitor_inguest_pagefault(xch, domain_id, 1);
525 if ( rc < 0 )
526 {
527 ERROR("Error %d setting inguest pagefault\n", rc);
528 goto exit;
529 }
530 rc = xc_monitor_emul_unimplemented(xch, domain_id, 1);
531 if ( rc < 0 )
532 {
533 ERROR("Error %d failed to enable emul unimplemented\n", rc);
534 goto exit;
535 }
536 }
537
538 rc = xc_altp2m_set_domain_state( xch, domain_id, 1 );
539 if ( rc < 0 )
540 {
541 ERROR("Error %d enabling altp2m on domain!\n", rc);
542 goto exit;
543 }
544
545 rc = xc_altp2m_create_view( xch, domain_id, default_access, &altp2m_view_id );
546 if ( rc < 0 )
547 {
548 ERROR("Error %d creating altp2m view!\n", rc);
549 goto exit;
550 }
551
552 DPRINTF("altp2m view created with id %u\n", altp2m_view_id);
553 DPRINTF("Setting altp2m mem_access permissions.. ");
554
555 for(; gfn < xenaccess->max_gpfn; ++gfn)
556 {
557 rc = xc_altp2m_set_mem_access( xch, domain_id, altp2m_view_id, gfn,
558 default_access);
559 if ( !rc )
560 perm_set++;
561 }
562
563 DPRINTF("done! Permissions set on %lu pages.\n", perm_set);
564
565 rc = xc_altp2m_switch_to_view( xch, domain_id, altp2m_view_id );
566 if ( rc < 0 )
567 {
568 ERROR("Error %d switching to altp2m view!\n", rc);
569 goto exit;
570 }
571
572 rc = xc_monitor_singlestep( xch, domain_id, 1 );
573 if ( rc < 0 )
574 {
575 ERROR("Error %d failed to enable singlestep monitoring!\n", rc);
576 goto exit;
577 }
578 }
579
580 if ( memaccess && !altp2m )
581 {
582 /* Set the default access type and convert all pages to it */
583 rc = xc_set_mem_access(xch, domain_id, default_access, ~0ull, 0);
584 if ( rc < 0 )
585 {
586 ERROR("Error %d setting default mem access type\n", rc);
587 goto exit;
588 }
589
590 rc = xc_set_mem_access(xch, domain_id, default_access, START_PFN,
591 (xenaccess->max_gpfn - START_PFN) );
592
593 if ( rc < 0 )
594 {
595 ERROR("Error %d setting all memory to access type %d\n", rc,
596 default_access);
597 goto exit;
598 }
599 }
600
601 if ( breakpoint )
602 {
603 rc = xc_monitor_software_breakpoint(xch, domain_id, 1);
604 if ( rc < 0 )
605 {
606 ERROR("Error %d setting breakpoint trapping with vm_event\n", rc);
607 goto exit;
608 }
609 }
610
611 if ( debug )
612 {
613 rc = xc_monitor_debug_exceptions(xch, domain_id, 1, 1);
614 if ( rc < 0 )
615 {
616 ERROR("Error %d setting debug exception listener with vm_event\n", rc);
617 goto exit;
618 }
619 }
620
621 if ( cpuid )
622 {
623 rc = xc_monitor_cpuid(xch, domain_id, 1);
624 if ( rc < 0 )
625 {
626 ERROR("Error %d setting cpuid listener with vm_event\n", rc);
627 goto exit;
628 }
629 }
630
631 if ( desc_access )
632 {
633 rc = xc_monitor_descriptor_access(xch, domain_id, 1);
634 if ( rc < 0 )
635 {
636 ERROR("Error %d setting descriptor access listener with vm_event\n", rc);
637 goto exit;
638 }
639 }
640
641 if ( privcall )
642 {
643 rc = xc_monitor_privileged_call(xch, domain_id, 1);
644 if ( rc < 0 )
645 {
646 ERROR("Error %d setting privileged call trapping with vm_event\n", rc);
647 goto exit;
648 }
649 }
650
651 if ( write_ctrlreg_cr4 )
652 {
653 /* Mask the CR4.PGE bit so no events will be generated for global TLB flushes. */
654 rc = xc_monitor_write_ctrlreg(xch, domain_id, VM_EVENT_X86_CR4, 1, 1,
655 X86_CR4_PGE, 1);
656 if ( rc < 0 )
657 {
658 ERROR("Error %d setting write control register trapping with vm_event\n", rc);
659 goto exit;
660 }
661 }
662
663 /* Wait for access */
664 for (;;)
665 {
666 if ( interrupted )
667 {
668 /* Unregister for every event */
669 DPRINTF("xenaccess shutting down on signal %d\n", interrupted);
670
671 if ( breakpoint )
672 rc = xc_monitor_software_breakpoint(xch, domain_id, 0);
673 if ( debug )
674 rc = xc_monitor_debug_exceptions(xch, domain_id, 0, 0);
675 if ( cpuid )
676 rc = xc_monitor_cpuid(xch, domain_id, 0);
677 if ( desc_access )
678 rc = xc_monitor_descriptor_access(xch, domain_id, 0);
679 if ( write_ctrlreg_cr4 )
680 rc = xc_monitor_write_ctrlreg(xch, domain_id, VM_EVENT_X86_CR4, 0, 0, 0, 0);
681
682 if ( privcall )
683 rc = xc_monitor_privileged_call(xch, domain_id, 0);
684
685 if ( altp2m )
686 {
687 rc = xc_altp2m_switch_to_view( xch, domain_id, 0 );
688 rc = xc_altp2m_destroy_view(xch, domain_id, altp2m_view_id);
689 rc = xc_altp2m_set_domain_state(xch, domain_id, 0);
690 rc = xc_monitor_singlestep(xch, domain_id, 0);
691 } else {
692 rc = xc_set_mem_access(xch, domain_id, XENMEM_access_rwx, ~0ull, 0);
693 rc = xc_set_mem_access(xch, domain_id, XENMEM_access_rwx, START_PFN,
694 (xenaccess->max_gpfn - START_PFN) );
695 }
696
697 shutting_down = 1;
698 }
699
700 rc = xc_wait_for_event_or_timeout(xch, xenaccess->vm_event.xce_handle, 100);
701 if ( rc < -1 )
702 {
703 ERROR("Error getting event");
704 interrupted = -1;
705 continue;
706 }
707 else if ( rc != -1 )
708 {
709 DPRINTF("Got event from Xen\n");
710 }
711
712 while ( RING_HAS_UNCONSUMED_REQUESTS(&xenaccess->vm_event.back_ring) )
713 {
714 get_request(&xenaccess->vm_event, &req);
715
716 if ( req.version != VM_EVENT_INTERFACE_VERSION )
717 {
718 ERROR("Error: vm_event interface version mismatch!\n");
719 interrupted = -1;
720 continue;
721 }
722
723 memset( &rsp, 0, sizeof (rsp) );
724 rsp.version = VM_EVENT_INTERFACE_VERSION;
725 rsp.vcpu_id = req.vcpu_id;
726 rsp.flags = (req.flags & VM_EVENT_FLAG_VCPU_PAUSED);
727 rsp.reason = req.reason;
728
729 switch (req.reason) {
730 case VM_EVENT_REASON_MEM_ACCESS:
731 if ( !shutting_down )
732 {
733 /*
734 * This serves no other purpose here then demonstrating the use of the API.
735 * At shutdown we have already reset all the permissions so really no use getting it again.
736 */
737 xenmem_access_t access;
738 rc = xc_get_mem_access(xch, domain_id, req.u.mem_access.gfn, &access);
739 if (rc < 0)
740 {
741 ERROR("Error %d getting mem_access event\n", rc);
742 interrupted = -1;
743 continue;
744 }
745 }
746
747 printf("PAGE ACCESS: %c%c%c for GFN %"PRIx64" (offset %06"
748 PRIx64") gla %016"PRIx64" (valid: %c; fault in gpt: %c; fault with gla: %c) (vcpu %u [%c], altp2m view %u)\n",
749 (req.u.mem_access.flags & MEM_ACCESS_R) ? 'r' : '-',
750 (req.u.mem_access.flags & MEM_ACCESS_W) ? 'w' : '-',
751 (req.u.mem_access.flags & MEM_ACCESS_X) ? 'x' : '-',
752 req.u.mem_access.gfn,
753 req.u.mem_access.offset,
754 req.u.mem_access.gla,
755 (req.u.mem_access.flags & MEM_ACCESS_GLA_VALID) ? 'y' : 'n',
756 (req.u.mem_access.flags & MEM_ACCESS_FAULT_IN_GPT) ? 'y' : 'n',
757 (req.u.mem_access.flags & MEM_ACCESS_FAULT_WITH_GLA) ? 'y': 'n',
758 req.vcpu_id,
759 (req.flags & VM_EVENT_FLAG_VCPU_PAUSED) ? 'p' : 'r',
760 req.altp2m_idx);
761
762 if ( altp2m && req.flags & VM_EVENT_FLAG_ALTERNATE_P2M)
763 {
764 DPRINTF("\tSwitching back to default view!\n");
765
766 rsp.flags |= (VM_EVENT_FLAG_ALTERNATE_P2M | VM_EVENT_FLAG_TOGGLE_SINGLESTEP);
767 rsp.altp2m_idx = 0;
768 }
769 else if ( default_access != after_first_access )
770 {
771 rc = xc_set_mem_access(xch, domain_id, after_first_access,
772 req.u.mem_access.gfn, 1);
773 if (rc < 0)
774 {
775 ERROR("Error %d setting gfn to access_type %d\n", rc,
776 after_first_access);
777 interrupted = -1;
778 continue;
779 }
780 }
781
782 rsp.u.mem_access = req.u.mem_access;
783 break;
784 case VM_EVENT_REASON_SOFTWARE_BREAKPOINT:
785 printf("Breakpoint: rip=%016"PRIx64", gfn=%"PRIx64" (vcpu %d)\n",
786 req.data.regs.x86.rip,
787 req.u.software_breakpoint.gfn,
788 req.vcpu_id);
789
790 /* Reinject */
791 rc = xc_hvm_inject_trap(xch, domain_id, req.vcpu_id,
792 X86_TRAP_INT3,
793 req.u.software_breakpoint.type, -1,
794 req.u.software_breakpoint.insn_length, 0);
795 if (rc < 0)
796 {
797 ERROR("Error %d injecting breakpoint\n", rc);
798 interrupted = -1;
799 continue;
800 }
801 break;
802 case VM_EVENT_REASON_PRIVILEGED_CALL:
803 printf("Privileged call: pc=%"PRIx64" (vcpu %d)\n",
804 req.data.regs.arm.pc,
805 req.vcpu_id);
806
807 rsp.data.regs.arm = req.data.regs.arm;
808 rsp.data.regs.arm.pc += 4;
809 rsp.flags |= VM_EVENT_FLAG_SET_REGISTERS;
810 break;
811 case VM_EVENT_REASON_SINGLESTEP:
812 printf("Singlestep: rip=%016"PRIx64", vcpu %d, altp2m %u\n",
813 req.data.regs.x86.rip,
814 req.vcpu_id,
815 req.altp2m_idx);
816
817 if ( altp2m )
818 {
819 printf("\tSwitching altp2m to view %u!\n", altp2m_view_id);
820
821 rsp.flags |= VM_EVENT_FLAG_ALTERNATE_P2M;
822 rsp.altp2m_idx = altp2m_view_id;
823 }
824
825 rsp.flags |= VM_EVENT_FLAG_TOGGLE_SINGLESTEP;
826
827 break;
828 case VM_EVENT_REASON_DEBUG_EXCEPTION:
829 printf("Debug exception: rip=%016"PRIx64", vcpu %d. Type: %u. Length: %u. Pending dbg 0x%08"PRIx64"\n",
830 req.data.regs.x86.rip,
831 req.vcpu_id,
832 req.u.debug_exception.type,
833 req.u.debug_exception.insn_length,
834 req.u.debug_exception.pending_dbg);
835
836 /* Reinject */
837 rc = xc_hvm_inject_trap(xch, domain_id, req.vcpu_id,
838 X86_TRAP_DEBUG,
839 req.u.debug_exception.type, -1,
840 req.u.debug_exception.insn_length,
841 req.u.debug_exception.pending_dbg);
842 if (rc < 0)
843 {
844 ERROR("Error %d injecting breakpoint\n", rc);
845 interrupted = -1;
846 continue;
847 }
848
849 break;
850 case VM_EVENT_REASON_CPUID:
851 printf("CPUID executed: rip=%016"PRIx64", vcpu %d. Insn length: %"PRIu32" " \
852 "0x%"PRIx32" 0x%"PRIx32": EAX=0x%"PRIx64" EBX=0x%"PRIx64" ECX=0x%"PRIx64" EDX=0x%"PRIx64"\n",
853 req.data.regs.x86.rip,
854 req.vcpu_id,
855 req.u.cpuid.insn_length,
856 req.u.cpuid.leaf,
857 req.u.cpuid.subleaf,
858 req.data.regs.x86.rax,
859 req.data.regs.x86.rbx,
860 req.data.regs.x86.rcx,
861 req.data.regs.x86.rdx);
862 rsp.flags |= VM_EVENT_FLAG_SET_REGISTERS;
863 rsp.data = req.data;
864 rsp.data.regs.x86.rip += req.u.cpuid.insn_length;
865 break;
866 case VM_EVENT_REASON_DESCRIPTOR_ACCESS:
867 printf("Descriptor access: rip=%016"PRIx64", vcpu %d: "\
868 "VMExit info=0x%"PRIx32", descriptor=%d, is write=%d\n",
869 req.data.regs.x86.rip,
870 req.vcpu_id,
871 req.u.desc_access.arch.vmx.instr_info,
872 req.u.desc_access.descriptor,
873 req.u.desc_access.is_write);
874 rsp.flags |= VM_EVENT_FLAG_EMULATE;
875 break;
876 case VM_EVENT_REASON_WRITE_CTRLREG:
877 printf("Control register written: rip=%016"PRIx64", vcpu %d: "
878 "reg=%s, old_value=%016"PRIx64", new_value=%016"PRIx64"\n",
879 req.data.regs.x86.rip,
880 req.vcpu_id,
881 get_x86_ctrl_reg_name(req.u.write_ctrlreg.index),
882 req.u.write_ctrlreg.old_value,
883 req.u.write_ctrlreg.new_value);
884 break;
885 case VM_EVENT_REASON_EMUL_UNIMPLEMENTED:
886 if ( altp2m_write_no_gpt && req.flags & VM_EVENT_FLAG_ALTERNATE_P2M )
887 {
888 DPRINTF("\tSwitching back to default view!\n");
889
890 rsp.flags |= (VM_EVENT_FLAG_ALTERNATE_P2M |
891 VM_EVENT_FLAG_TOGGLE_SINGLESTEP);
892 rsp.altp2m_idx = 0;
893 }
894 break;
895 default:
896 fprintf(stderr, "UNKNOWN REASON CODE %d\n", req.reason);
897 }
898
899 /* Put the response on the ring */
900 put_response(&xenaccess->vm_event, &rsp);
901 }
902
903 /* Tell Xen page is ready */
904 rc = xenevtchn_notify(xenaccess->vm_event.xce_handle,
905 xenaccess->vm_event.port);
906
907 if ( rc != 0 )
908 {
909 ERROR("Error resuming page");
910 interrupted = -1;
911 }
912
913 if ( shutting_down )
914 break;
915 }
916 DPRINTF("xenaccess shut down on signal %d\n", interrupted);
917
918 exit:
919 if ( altp2m )
920 {
921 uint32_t vcpu_id;
922 for ( vcpu_id = 0; vcpu_id<XEN_LEGACY_MAX_VCPUS; vcpu_id++)
923 rc = control_singlestep(xch, domain_id, vcpu_id, 0);
924 }
925
926 /* Tear down domain xenaccess */
927 rc1 = xenaccess_teardown(xch, xenaccess);
928 if ( rc1 != 0 )
929 ERROR("Error tearing down xenaccess");
930
931 if ( rc == 0 )
932 rc = rc1;
933
934 DPRINTF("xenaccess exit code %d\n", rc);
935 return rc;
936 }
937
938
939 /*
940 * Local variables:
941 * mode: C
942 * c-file-style: "BSD"
943 * c-basic-offset: 4
944 * indent-tabs-mode: nil
945 * End:
946 */
947