Advertisement
Guest User

xen-access.c

a guest
Apr 4th, 2016
78
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.84 KB
  1. #include <errno.h>
  2. #include <inttypes.h>
  3. #include <stdlib.h>
  4. #include <stdarg.h>
  5. #include <stdbool.h>
  6. #include <string.h>
  7. #include <time.h>
  8. #include <signal.h>
  9. #include <unistd.h>
  10. #include <sys/mman.h>
  11. #include <sys/poll.h>
  12.  
  13. #include <xenctrl.h>
  14. #include <xen/vm_event.h>
  15.  
  16. #if defined(__arm__) || defined(__aarch64__)
  17. #include <xen/arch-arm.h>
  18. #define START_PFN (GUEST_RAM0_BASE >> 12)
  19. #elif defined(__i386__) || defined(__x86_64__)
  20. #define START_PFN 0ULL
  21. #endif
  22.  
  23. #define DPRINTF(a, b...) fprintf(stderr, a, ## b)
  24. #define ERROR(a, b...) fprintf(stderr, a "\n", ## b)
  25. #define PERROR(a, b...) fprintf(stderr, a ": %s\n", ## b, strerror(errno))
  26.  
  27. typedef struct vm_event {
  28.     domid_t domain_id;
  29.     xc_evtchn *xce_handle;
  30.     int port;
  31.     vm_event_back_ring_t back_ring;
  32.     uint32_t evtchn_port;
  33.     void *ring_page;
  34. } vm_event_t;
  35.  
  36. typedef struct xenaccess {
  37.     xc_interface *xc_handle;
  38.  
  39.     xen_pfn_t max_gpfn;
  40.  
  41.     vm_event_t vm_event;
  42. } xenaccess_t;
  43.  
  44. static int interrupted;
  45. bool evtchn_bind = 0, evtchn_open = 0, mem_access_enable = 0;
  46.  
  47. static void close_handler(int sig)
  48. {
  49.     interrupted = sig;
  50. }
  51.  
  52. int xc_wait_for_event_or_timeout(xc_interface *xch, xc_evtchn *xce, unsigned long ms)
  53. {
  54.     struct pollfd fd = { .fd = xc_evtchn_fd(xce), .events = POLLIN | POLLERR };
  55.     int port;
  56.     int rc;
  57.  
  58.     rc = poll(&fd, 1, ms);
  59.     if ( rc == -1 )
  60.     {
  61.         if (errno == EINTR)
  62.             return 0;
  63.  
  64.         ERROR("Poll exited with an error");
  65.         goto err;
  66.     }
  67.  
  68.     if ( rc == 1 )
  69.     {
  70.         port = xc_evtchn_pending(xce);
  71.         if ( port == -1 )
  72.         {
  73.             ERROR("Failed to read port from event channel");
  74.             goto err;
  75.         }
  76.  
  77.         rc = xc_evtchn_unmask(xce, port);
  78.         if ( rc != 0 )
  79.         {
  80.             ERROR("Failed to unmask event channel port");
  81.             goto err;
  82.         }
  83.     }
  84.     else
  85.         port = -1;
  86.  
  87.     return port;
  88.  
  89.  err:
  90.     return -errno;
  91. }
  92.  
  93. int xenaccess_teardown(xc_interface *xch, xenaccess_t *xenaccess)
  94. {
  95.     int rc;
  96.  
  97.     if ( xenaccess == NULL )
  98.         return 0;
  99.  
  100.     /* Tear down domain xenaccess in Xen */
  101.     if ( xenaccess->vm_event.ring_page )
  102.         munmap(xenaccess->vm_event.ring_page, XC_PAGE_SIZE);
  103.  
  104.     if ( mem_access_enable )
  105.     {
  106.         rc = xc_monitor_disable(xenaccess->xc_handle,
  107.                                 xenaccess->vm_event.domain_id);
  108.         if ( rc != 0 )
  109.         {
  110.             ERROR("Error tearing down domain xenaccess in xen");
  111.             return rc;
  112.         }
  113.     }
  114.  
  115.     /* Unbind VIRQ */
  116.     if ( evtchn_bind )
  117.     {
  118.         rc = xc_evtchn_unbind(xenaccess->vm_event.xce_handle,
  119.                               xenaccess->vm_event.port);
  120.         if ( rc != 0 )
  121.         {
  122.             ERROR("Error unbinding event port");
  123.             return rc;
  124.         }
  125.     }
  126.  
  127.     /* Close event channel */
  128.     if ( evtchn_open )
  129.     {
  130.         rc = xc_evtchn_close(xenaccess->vm_event.xce_handle);
  131.         if ( rc != 0 )
  132.         {
  133.             ERROR("Error closing event channel");
  134.             return rc;
  135.         }
  136.     }
  137.  
  138.     /* Close connection to Xen */
  139.     rc = xc_interface_close(xenaccess->xc_handle);
  140.     if ( rc != 0 )
  141.     {
  142.         ERROR("Error closing connection to xen");
  143.         return rc;
  144.     }
  145.     xenaccess->xc_handle = NULL;
  146.  
  147.     free(xenaccess);
  148.  
  149.     return 0;
  150. }
  151.  
  152. xenaccess_t *xenaccess_init(xc_interface **xch_r, domid_t domain_id)
  153. {
  154.     xenaccess_t *xenaccess = 0;
  155.     xc_interface *xch;
  156.     int rc;
  157.  
  158.     xch = xc_interface_open(NULL, NULL, 0);
  159.     if ( !xch )
  160.         goto err_iface;
  161.  
  162.     DPRINTF("xenaccess init\n");
  163.     *xch_r = xch;
  164.  
  165.     /* Allocate memory */
  166.     xenaccess = malloc(sizeof(xenaccess_t));
  167.     memset(xenaccess, 0, sizeof(xenaccess_t));
  168.  
  169.     /* Open connection to xen */
  170.     xenaccess->xc_handle = xch;
  171.  
  172.     /* Set domain id */
  173.     xenaccess->vm_event.domain_id = domain_id;
  174.  
  175.     /* Enable mem_access */
  176.     xenaccess->vm_event.ring_page =
  177.             xc_monitor_enable(xenaccess->xc_handle,
  178.                               xenaccess->vm_event.domain_id,
  179.                               &xenaccess->vm_event.evtchn_port);
  180.     if ( xenaccess->vm_event.ring_page == NULL )
  181.     {
  182.         switch ( errno ) {
  183.             case EBUSY:
  184.                 ERROR("xenaccess is (or was) active on this domain");
  185.                 break;
  186.             case ENODEV:
  187.                 ERROR("EPT not supported for this guest");
  188.                 break;
  189.             default:
  190.                 perror("Error enabling mem_access");
  191.                 break;
  192.         }
  193.         goto err;
  194.     }
  195.     mem_access_enable = 1;
  196.  
  197.     /* Open event channel */
  198.     xenaccess->vm_event.xce_handle = xc_evtchn_open(NULL, 0);
  199.     if ( xenaccess->vm_event.xce_handle == NULL )
  200.     {
  201.         ERROR("Failed to open event channel");
  202.         goto err;
  203.     }
  204.     evtchn_open = 1;
  205.  
  206.     /* Bind event notification */
  207.     rc = xc_evtchn_bind_interdomain(xenaccess->vm_event.xce_handle,
  208.                                     xenaccess->vm_event.domain_id,
  209.                                     xenaccess->vm_event.evtchn_port);
  210.     if ( rc < 0 )
  211.     {
  212.         ERROR("Failed to bind event channel");
  213.         goto err;
  214.     }
  215.     evtchn_bind = 1;
  216.     xenaccess->vm_event.port = rc;
  217.  
  218.     /* Initialise ring */
  219.     SHARED_RING_INIT((vm_event_sring_t *)xenaccess->vm_event.ring_page);
  220.     BACK_RING_INIT(&xenaccess->vm_event.back_ring,
  221.                    (vm_event_sring_t *)xenaccess->vm_event.ring_page,
  222.                    XC_PAGE_SIZE);
  223.  
  224.     /* Get max_gpfn */
  225.     rc = xc_domain_maximum_gpfn(xenaccess->xc_handle,
  226.                                 xenaccess->vm_event.domain_id,
  227.                                 &xenaccess->max_gpfn);
  228.  
  229.     if ( rc )
  230.     {
  231.         ERROR("Failed to get max gpfn");
  232.         goto err;
  233.     }
  234.  
  235.     DPRINTF("max_gpfn = %"PRI_xen_pfn"\n", xenaccess->max_gpfn);
  236.  
  237.     return xenaccess;
  238.  
  239.  err:
  240.     rc = xenaccess_teardown(xch, xenaccess);
  241.     if ( rc )
  242.     {
  243.         ERROR("Failed to teardown xenaccess structure!\n");
  244.     }
  245.  
  246.  err_iface:
  247.     return NULL;
  248. }
  249.  
  250. static inline
  251. int control_singlestep(
  252.     xc_interface *xch,
  253.     domid_t domain_id,
  254.     unsigned long vcpu,
  255.     bool enable)
  256. {
  257.     uint32_t op = enable ?
  258.         XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_ON : XEN_DOMCTL_DEBUG_OP_SINGLE_STEP_OFF;
  259.  
  260.     return xc_domain_debug_control(xch, domain_id, op, vcpu);
  261. }
  262.  
  263. /*
  264.  * Note that this function is not thread safe.
  265.  */
  266. static void get_request(vm_event_t *vm_event, vm_event_request_t *req)
  267. {
  268.     vm_event_back_ring_t *back_ring;
  269.     RING_IDX req_cons;
  270.  
  271.     back_ring = &vm_event->back_ring;
  272.     req_cons = back_ring->req_cons;
  273.  
  274.     /* Copy request */
  275.     memcpy(req, RING_GET_REQUEST(back_ring, req_cons), sizeof(*req));
  276.     req_cons++;
  277.  
  278.     /* Update ring */
  279.     back_ring->req_cons = req_cons;
  280.     back_ring->sring->req_event = req_cons + 1;
  281. }
  282.  
  283. /*
  284.  * Note that this function is not thread safe.
  285.  */
  286. static void put_response(vm_event_t *vm_event, vm_event_response_t *rsp)
  287. {
  288.     vm_event_back_ring_t *back_ring;
  289.     RING_IDX rsp_prod;
  290.  
  291.     back_ring = &vm_event->back_ring;
  292.     rsp_prod = back_ring->rsp_prod_pvt;
  293.  
  294.     /* Copy response */
  295.     memcpy(RING_GET_RESPONSE(back_ring, rsp_prod), rsp, sizeof(*rsp));
  296.     rsp_prod++;
  297.  
  298.     /* Update ring */
  299.     back_ring->rsp_prod_pvt = rsp_prod;
  300.     RING_PUSH_RESPONSES(back_ring);
  301. }
  302.  
  303. void usage(char* progname)
  304. {
  305.     fprintf(stderr, "Usage: %s [-m] <domain_id> write|exec", progname);
  306.             fprintf(stderr,
  307.             "\n"
  308.             "Logs first page writes, execs that occur on the domain.\n"
  309.             "\n"
  310.             "-m requires this program to run, or else the domain may pause\n");
  311. }
  312.  
  313. int main(int argc, char *argv[])
  314. {
  315.     struct sigaction act;
  316.     domid_t domain_id;
  317.     xenaccess_t *xenaccess;
  318.     vm_event_request_t req;
  319.     vm_event_response_t rsp;
  320.     int rc = -1;
  321.     int rc1;
  322.     xc_interface *xch;
  323.     xenmem_access_t default_access = XENMEM_access_rwx;
  324.     int required = 0;
  325.     int shutting_down = 0;
  326.     int ev_count = 0;
  327.  
  328.     char* progname = argv[0];
  329.     argv++;
  330.     argc--;
  331.  
  332.     if ( argc == 3 && argv[0][0] == '-' )
  333.     {
  334.         if ( !strcmp(argv[0], "-m") )
  335.             required = 1;
  336.         else
  337.         {
  338.             usage(progname);
  339.             return -1;
  340.         }
  341.         argv++;
  342.         argc--;
  343.     }
  344.  
  345.     if ( argc != 2 )
  346.     {
  347.         usage(progname);
  348.         return -1;
  349.     }
  350.  
  351.     domain_id = atoi(argv[0]);
  352.     argv++;
  353.     argc--;
  354.  
  355.     if ( !strcmp(argv[0], "write") )
  356.     {
  357.         default_access = XENMEM_access_rx;
  358.     }
  359.     else if ( !strcmp(argv[0], "exec") )
  360.     {
  361.         default_access = XENMEM_access_rw;
  362.     }
  363.     else
  364.     {
  365.         usage(argv[0]);
  366.         return -1;
  367.     }
  368.  
  369.     xenaccess = xenaccess_init(&xch, domain_id);
  370.     if ( xenaccess == NULL )
  371.     {
  372.         ERROR("Error initialising xenaccess");
  373.         return 1;
  374.     }
  375.  
  376.     DPRINTF("starting %s %u\n", argv[0], domain_id);
  377.  
  378.     /* ensure that if we get a signal, we'll do cleanup, then exit */
  379.     act.sa_handler = close_handler;
  380.     act.sa_flags = 0;
  381.     sigemptyset(&act.sa_mask);
  382.     sigaction(SIGHUP,  &act, NULL);
  383.     sigaction(SIGTERM, &act, NULL);
  384.     sigaction(SIGINT,  &act, NULL);
  385.     sigaction(SIGALRM, &act, NULL);
  386.  
  387.     /* Set whether the access listener is required */
  388.     rc = xc_domain_set_access_required(xch, domain_id, required);
  389.     if ( rc < 0 )
  390.     {
  391.         ERROR("Error %d setting mem_access listener required\n", rc);
  392.         goto exit;
  393.     }
  394.  
  395.    /* Set the default access type and convert all pages to it */
  396.     rc = xc_set_mem_access(xch, domain_id, default_access, ~0ull, 0);
  397.     if ( rc < 0 )
  398.     {
  399.         ERROR("Error %d setting default mem access type\n", rc);
  400.         goto exit;
  401.     }
  402.  
  403.     rc = xc_set_mem_access(xch, domain_id, default_access, START_PFN,
  404.                            (xenaccess->max_gpfn - START_PFN) );
  405.  
  406.     if ( rc < 0 )
  407.     {
  408.         ERROR("Error %d setting all memory to access type %d\n", rc,
  409.               default_access);
  410.         goto exit;
  411.     }
  412.  
  413.     /* Wait for access */
  414.     for (;;)
  415.     {
  416.         if ( interrupted )
  417.         {
  418.             /* Unregister for every event */
  419.             DPRINTF("xenaccess shutting down on signal %d\n", interrupted);
  420.  
  421.             rc = xc_set_mem_access(xch, domain_id, XENMEM_access_rwx, ~0ull, 0);
  422.             rc = xc_set_mem_access(xch, domain_id, XENMEM_access_rwx, START_PFN,
  423.                                    (xenaccess->max_gpfn - START_PFN) );
  424.  
  425.             shutting_down = 1;
  426.         }
  427.  
  428.         rc = xc_wait_for_event_or_timeout(xch, xenaccess->vm_event.xce_handle, 100);
  429.         if ( rc < -1 )
  430.         {
  431.             ERROR("Error getting event");
  432.             interrupted = -1;
  433.             continue;
  434.         }
  435.         else if ( rc != -1 )
  436.         {
  437.             // DPRINTF("Got event from Xen\n");
  438.         }
  439.  
  440.         while ( RING_HAS_UNCONSUMED_REQUESTS(&xenaccess->vm_event.back_ring) )
  441.         {
  442.             // xenmem_access_t access;
  443.             ev_count++;
  444.  
  445.             get_request(&xenaccess->vm_event, &req);
  446.  
  447.             if ( req.version != VM_EVENT_INTERFACE_VERSION )
  448.             {
  449.                 ERROR("Error: vm_event interface version mismatch!\n");
  450.                 interrupted = -1;
  451.                 continue;
  452.             }
  453.  
  454.             memset( &rsp, 0, sizeof (rsp) );
  455.             rsp.version = VM_EVENT_INTERFACE_VERSION;
  456.             rsp.vcpu_id = req.vcpu_id;
  457.             rsp.flags = req.flags;
  458.             rsp.reason = req.reason;
  459.             rsp.u.mem_access.flags = req.u.mem_access.flags;
  460.             rsp.data.regs.x86 = req.data.regs.x86;
  461.  
  462.             switch (req.reason) {
  463.             case VM_EVENT_REASON_MEM_ACCESS:
  464.                 rsp.flags |= VM_EVENT_FLAG_EMULATE;
  465.                 rsp.u.mem_access.gfn = req.u.mem_access.gfn;
  466.                 break;
  467.             default:
  468.                 fprintf(stderr, "UNKNOWN REASON CODE %d\n", req.reason);
  469.             }
  470.  
  471.             /* Put the response on the ring */
  472.             put_response(&xenaccess->vm_event, &rsp);
  473.        }
  474.  
  475.         /* Tell Xen page is ready */
  476.         rc = xc_evtchn_notify(xenaccess->vm_event.xce_handle,
  477.                               xenaccess->vm_event.port);
  478.  
  479.         if ( rc != 0 )
  480.         {
  481.             ERROR("Error resuming page");
  482.             interrupted = -1;
  483.         }
  484.  
  485.         if ( shutting_down )
  486.             break;
  487.     }
  488.  
  489.     DPRINTF("events: %d\n", ev_count);
  490.     DPRINTF("xenaccess shut down on signal %d\n", interrupted);
  491.  
  492. exit:
  493.     /* Tear down domain xenaccess */
  494.     rc1 = xenaccess_teardown(xch, xenaccess);
  495.     if ( rc1 != 0 )
  496.         ERROR("Error tearing down xenaccess");
  497.  
  498.     if ( rc == 0 )
  499.         rc = rc1;
  500.  
  501.     DPRINTF("xenaccess exit code %d\n", rc);
  502.     return rc;
  503. }
  504.  
  505.  
  506. /*
  507.  * Local variables:
  508.  * mode: C
  509.  * c-file-style: "BSD"
  510.  * c-basic-offset: 4
  511.  * indent-tabs-mode: nil
  512.  * End:
  513.  */
Advertisement
Advertisement
Advertisement
RAW Paste Data Copied
Advertisement