1 /******************************************************************************
2  *
3  * xc_vm_event.c
4  *
5  * Interface to low-level memory event functionality.
6  *
7  * Copyright (c) 2009 Citrix Systems, Inc. (Patrick Colp)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "xc_private.h"
24 
xc_vm_event_control(xc_interface * xch,uint32_t domain_id,unsigned int op,unsigned int mode,uint32_t * port)25 int xc_vm_event_control(xc_interface *xch, uint32_t domain_id, unsigned int op,
26                         unsigned int mode, uint32_t *port)
27 {
28     DECLARE_DOMCTL;
29     int rc;
30 
31     domctl.cmd = XEN_DOMCTL_vm_event_op;
32     domctl.domain = domain_id;
33     domctl.u.vm_event_op.op = op;
34     domctl.u.vm_event_op.mode = mode;
35 
36     rc = do_domctl(xch, &domctl);
37     if ( !rc && port )
38         *port = domctl.u.vm_event_op.port;
39     return rc;
40 }
41 
xc_vm_event_enable(xc_interface * xch,uint32_t domain_id,int param,uint32_t * port)42 void *xc_vm_event_enable(xc_interface *xch, uint32_t domain_id, int param,
43                          uint32_t *port)
44 {
45     void *ring_page = NULL;
46     uint64_t pfn;
47     xen_pfn_t ring_pfn, mmap_pfn;
48     unsigned int op, mode;
49     int rc1, rc2, saved_errno;
50 
51     if ( !port )
52     {
53         errno = EINVAL;
54         return NULL;
55     }
56 
57     /* Pause the domain for ring page setup */
58     rc1 = xc_domain_pause(xch, domain_id);
59     if ( rc1 != 0 )
60     {
61         PERROR("Unable to pause domain\n");
62         return NULL;
63     }
64 
65     /* Get the pfn of the ring page */
66     rc1 = xc_hvm_param_get(xch, domain_id, param, &pfn);
67     if ( rc1 != 0 )
68     {
69         PERROR("Failed to get pfn of ring page\n");
70         goto out;
71     }
72 
73     ring_pfn = pfn;
74     mmap_pfn = pfn;
75     rc1 = xc_get_pfn_type_batch(xch, domain_id, 1, &mmap_pfn);
76     if ( rc1 || mmap_pfn & XEN_DOMCTL_PFINFO_XTAB )
77     {
78         /* Page not in the physmap, try to populate it */
79         rc1 = xc_domain_populate_physmap_exact(xch, domain_id, 1, 0, 0,
80                                               &ring_pfn);
81         if ( rc1 != 0 )
82         {
83             PERROR("Failed to populate ring pfn\n");
84             goto out;
85         }
86     }
87 
88     mmap_pfn = ring_pfn;
89     ring_page = xc_map_foreign_pages(xch, domain_id, PROT_READ | PROT_WRITE,
90                                          &mmap_pfn, 1);
91     if ( !ring_page )
92     {
93         PERROR("Could not map the ring page\n");
94         goto out;
95     }
96 
97     switch ( param )
98     {
99     case HVM_PARAM_PAGING_RING_PFN:
100         op = XEN_VM_EVENT_ENABLE;
101         mode = XEN_DOMCTL_VM_EVENT_OP_PAGING;
102         break;
103 
104     case HVM_PARAM_MONITOR_RING_PFN:
105         op = XEN_VM_EVENT_ENABLE;
106         mode = XEN_DOMCTL_VM_EVENT_OP_MONITOR;
107         break;
108 
109     case HVM_PARAM_SHARING_RING_PFN:
110         op = XEN_VM_EVENT_ENABLE;
111         mode = XEN_DOMCTL_VM_EVENT_OP_SHARING;
112         break;
113 
114     /*
115      * This is for the outside chance that the HVM_PARAM is valid but is invalid
116      * as far as vm_event goes.
117      */
118     default:
119         errno = EINVAL;
120         rc1 = -1;
121         goto out;
122     }
123 
124     rc1 = xc_vm_event_control(xch, domain_id, op, mode, port);
125     if ( rc1 != 0 )
126     {
127         PERROR("Failed to enable vm_event\n");
128         goto out;
129     }
130 
131     /* Remove the ring_pfn from the guest's physmap */
132     rc1 = xc_domain_decrease_reservation_exact(xch, domain_id, 1, 0, &ring_pfn);
133     if ( rc1 != 0 )
134         PERROR("Failed to remove ring page from guest physmap");
135 
136  out:
137     saved_errno = errno;
138 
139     rc2 = xc_domain_unpause(xch, domain_id);
140     if ( rc1 != 0 || rc2 != 0 )
141     {
142         if ( rc2 != 0 )
143         {
144             if ( rc1 == 0 )
145                 saved_errno = errno;
146             PERROR("Unable to unpause domain");
147         }
148 
149         if ( ring_page )
150             xenforeignmemory_unmap(xch->fmem, ring_page, 1);
151         ring_page = NULL;
152 
153         errno = saved_errno;
154     }
155 
156     return ring_page;
157 }
158 
159 /*
160  * Local variables:
161  * mode: C
162  * c-file-style: "BSD"
163  * c-basic-offset: 4
164  * tab-width: 4
165  * indent-tabs-mode: nil
166  * End:
167  */
168