ROSE  0.11.145.0
ether.h
1 #ifndef ROSE_ETHER_H
2 #define ROSE_ETHER_H
3 
4 #ifdef ROSE_USE_ETHER
5 
6 extern "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 
15 class Ether {
16 private:
17  Ether() {}
18 
19 public:
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 
192 private:
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  sprintf(buf, "xc_domctl(request=%u) failed", cmd->u.ether.command_code);
296  throw Exception(buf);
297  }
298  }
299 
300 private:
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*/