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