ROSE 0.11.145.147
ether.h
1#ifndef ROSE_ETHER_H
2#define ROSE_ETHER_H
3
4#ifdef ROSE_USE_ETHER
5
6extern "C" {
7#include <sys/mman.h>
8#include <xenctrl.h>
9#include <xen/domctl.h>
10#include <xen/hvm/ether.h>
11}
12
13#include <sys/mman.h>
14
15class Ether {
16private:
17 Ether() {}
18
19public:
20 struct Exception {
21 Exception(const std::string &mesg)
22 : mesg(mesg) {}
23 std::string mesg;
24 };
25
27 Ether(domid_t dom)
28 : xc_iface(-1), xc_event_iface(-1), dom(0), shared_page_mfn(0), shared_page(NULL), event_port(-1) {
29
30 if ((xc_iface = xc_interface_open())<0)
31 throw Exception("cannot contact Xen hypervisor");
32 if ((xc_event_iface = xc_evtchn_open())<0)
33 throw Exception("cannot open event channel");
34
35 CmdInit cmd;
36 domctl(cmd, dom);
37 this->dom = dom;
38 shared_page_mfn = cmd.u.ether.comm.shared_page_mfn;
39
40 /* The hypervisor allocated a page of memory that we now map into our own address space. We should call munmap()
41 * during destruction. The hypervisor will initialize the page to all zeros. */
42 shared_page = (volatile unsigned char*)xc_map_foreign_range(xc_iface, DOMID_XEN, getpagesize(),
43 PROT_READ|PROT_WRITE, shared_page_mfn);
44 if (!shared_page)
45 throw("cannot map shared page");
46
47 /* The Xen hypervisor allocated an unbound port whose number was returned. This event channel will be used to notify
48 * us when Ether-related things happen in the domU. So now we create the local end of the channel and bind it with
49 * the remote end. */
50 event_port = xc_evtchn_bind_interdomain(xc_event_iface, DOMID_SELF, cmd.u.ether.comm.event_channel_port);
51 if (event_port<0)
52 throw("cannot bind ether event port");
53
54 /* DEBUG */
55 printf("After init:\n");
56 printf(" domain: %d\n", dom);
57 printf(" xc_iface: %d\n", xc_iface);
58 printf(" xc_event_iface: %d\n", xc_event_iface);
59 printf(" shared_page: %p\n", shared_page);
60 printf(" shared_page_mfn: 0x%lx\n", shared_page_mfn);
61 printf(" event_channel_port: %d\n", cmd.u.ether.comm.event_channel_port);
62 printf(" event_port: %u\n", event_port);
63 }
64
66 ~Ether() {
67 if (shared_page!=NULL) {
68 domctl(CmdTerminate());
69 xc_domain_unpause(xc_iface, dom);
70 }
71 if (event_port>=0) {
72 xc_evtchn_unbind(xc_event_iface, event_port);
73 event_port = -1;
74 }
75 if (shared_page!=NULL) {
76 if (is_lock_held())
77 release_lock();
78 munmap((void*)shared_page, getpagesize());
79 shared_page = NULL;
80 shared_page_mfn = 0;
81 }
82 if (xc_event_iface>=0) {
83 xc_evtchn_close(xc_event_iface);
84 xc_event_iface = -1;
85 }
86 if (xc_iface) {
87 xc_interface_close(xc_iface);
88 xc_iface = -1;
89 }
90 }
91
93 void readguest(rose_addr_t va, unsigned char *buffer, size_t size) {
94 domctl(CmdReadGuest(va, buffer, size));
95 }
96
99 void set_single_step(bool status) {
100 domctl(CmdSingleStep(status));
101 }
102
105 void set_single_step_notify(bool status) {
106 domctl(CmdSingleStepNotify(status));
107 }
108
110 void set_memwrite_notify(bool status) {
111 domctl(CmdMemwriteNotify(status));
112 }
113
115 void set_unpack_notify(bool status) {
116 domctl(CmdUnpackNotify(status));
117 }
118
122 void set_sysenter(bool new_eip) {
123 domctl(CmdSysenter(new_eip));
124 }
125
127 void add_name_filter(const std::string &name) {
128 domctl(CmdAddName(name));
129 }
130
131
132 void add_cr3_filter(rose_addr_t cr3) {
133 domctl(CmdAddCR3(cr3));
134 }
135
138 bool is_lock_held() {
139 if (shared_page==NULL)
140 throw Exception("no shared page");
141 return *(volatile uint32_t*)shared_page ? true : false;
142 }
143
147 void release_lock() {
148 if (shared_page==NULL)
149 throw Exception("no shared page");
150 if (!is_lock_held())
151 throw Exception("shared page lock is not set\n");
152 *(volatile uint32_t*)shared_page = 0;
153 }
154
158 int next_event() {
159 if (shared_page==NULL)
160 throw Exception("no shared page");
161 while (1) {
162 if (xc_evtchn_pending(xc_event_iface)!=event_port) {
163 xc_domain_unpause(xc_iface, dom);
164 } else if (xc_evtchn_unmask(xc_event_iface, event_port)<0) {
165 throw Exception("cannot unmask ether event");
166 } else if (!is_lock_held()) {
167 xc_domain_unpause(xc_iface, dom);
168 throw Exception("ether event but shared page lock is clear");
169 }
170
171 return ((volatile uint32_t*)shared_page)[1];
172 }
173 }
174
176 const void *event_data() {
177 if (!is_lock_held())
178 throw Exception("shared page lock is not set");
179 if (!shared_page)
180 throw Exception("no shared page");
181 return (const void*)(shared_page+8);
182 }
183
186 void resume() {
187 if (is_lock_held()) /*perhaps this should be an exception instead!*/
188 release_lock();
189 xc_domain_unpause(xc_iface, dom); /*not always necessary, but doesn't hurt*/
190 }
191
192private:
193 struct CmdInit: public xen_domctl {
194 CmdInit() {
195 u.ether.command_code = XEN_DOMCTL_ETHER_INIT;
196 memset(&u.ether.comm, 0, sizeof(u.ether.comm));
197 }
198 };
199
200 struct CmdTerminate: public xen_domctl {
201 CmdTerminate() {
202 u.ether.command_code = XEN_DOMCTL_ETHER_TERMINATE;
203 }
204 };
205
206 struct CmdReadGuest: public xen_domctl {
207 CmdReadGuest(rose_addr_t va, void *buffer, size_t size) {
208 u.ether.command_code = XEN_DOMCTL_ETHER_READ_GUEST;
209 u.ether.guest_va = va;
210 u.ether.guest_buffer = (unsigned char*)buffer;
211 u.ether.data_length = size;
212 /* This is actually necessary to get this memory paged in so the hypervisor can write to it. */
213 memset(buffer, 0, size);
214 }
215 };
216
217 struct CmdSingleStep: public xen_domctl {
218 CmdSingleStep(bool status) {
219 u.ether.command_code = XEN_DOMCTL_ETHER_SINGLE_STEP;
220 u.ether.on_or_off = status;
221 }
222 };
223
224 struct CmdSingleStepNotify: public xen_domctl {
225 CmdSingleStepNotify(bool status) {
226 u.ether.command_code = XEN_DOMCTL_ETHER_SS_DETECT;
227 u.ether.on_or_off = status;
228 }
229 };
230
231 struct CmdMemwriteNotify: public xen_domctl {
232 CmdMemwriteNotify(bool status) {
233 u.ether.command_code = XEN_DOMCTL_ETHER_MEMWRITE;
234 u.ether.on_or_off = status;
235 }
236 };
237
238 struct CmdUnpackNotify: public xen_domctl {
239 CmdUnpackNotify(bool status) {
240 u.ether.command_code = XEN_DOMCTL_ETHER_UNPACK;
241 u.ether.on_or_off = status;
242 }
243 };
244
245 struct CmdSysenter: public xen_domctl {
246 CmdSysenter(rose_addr_t eip) {
247 u.ether.command_code = XEN_DOMCTL_ETHER_SET_SYSENTER;
248 u.ether.sysenter_cs = 0;
249 u.ether.sysenter_eip = eip;
250 }
251 };
252
253 struct CmdAddCR3: public xen_domctl {
254 CmdAddCR3(rose_addr_t cr3) {
255 u.ether.command_code = XEN_DOMCTL_ETHER_ADD_CR3;
256 u.ether.cr3_value = cr3;
257 }
258 };
259
260 struct CmdAddName: public xen_domctl {
261 CmdAddName(const std::string &name) {
262 u.ether.command_code = XEN_DOMCTL_ETHER_ADD_NAME;
263 u.ether.data_length = name.size() + 1;
264 u.ether.guest_buffer = (unsigned char*)(name.c_str());
265 }
266 };
267
268 bool connected() {
269 return xc_iface>=0 && dom>0;
270 }
271
272 /* Send an ether command to the hypervisor. The command should have been constructed with one of the Cmd* constructors in
273 * order to initialize it properly. The @p dom parameter is normally only specified by the constructor in order
274 * to send a message to the hypervisor before this object is connected. */
275 void domctl(const xen_domctl &cmd, domid_t dom=0) {
276 domctl(const_cast<xen_domctl*>(&cmd), dom);
277 }
278 void domctl(xen_domctl &cmd, domid_t dom=0) {
279 domctl(&cmd, dom);
280 }
281 void domctl(xen_domctl *cmd, domid_t dom=0) {
282 if (connected()) {
283 if (dom>0 && dom!=this->dom)
284 throw Exception("contradicting domain IDs");
285 dom = this->dom;
286 } else if (dom<=0) {
287 throw Exception("not connected");
288 }
289
290 cmd->domain = dom;
291 cmd->cmd = XEN_DOMCTL_ether;
292 int result = xc_domctl(xc_iface, cmd);
293 if (result<0) {
294 char buf[256];
295 snprintf(buf, sizeof(buf), "xc_domctl(request=%u) failed", cmd->u.ether.command_code);
296 throw Exception(buf);
297 }
298 }
299
300private:
301 int xc_iface;
302 int xc_event_iface;
303 domid_t dom;
305 rose_addr_t shared_page_mfn;
306 volatile unsigned char *shared_page;
308 evtchn_port_t event_port;
309};
310
311#endif /*ROSE_USE_ETHER*/
312#endif /*ROSE_ETHER_H*/