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