ether.h

Go to the documentation of this file.
00001 #ifndef ROSE_ETHER_H
00002 #define ROSE_ETHER_H
00003 
00004 #ifdef ROSE_USE_ETHER
00005 
00006 extern "C" {
00007 #include <sys/mman.h>
00008 #include <xenctrl.h>
00009 #include <xen/domctl.h>
00010 #include <xen/hvm/ether.h>
00011 }
00012 
00013 #include <sys/mman.h>
00014 
00015 class Ether {
00016 private:
00017     Ether() {}
00018     
00019 public:
00020     struct Exception {
00021         Exception(const std::string &mesg)
00022             : mesg(mesg) {}
00023         std::string mesg;
00024     };
00025 
00027     Ether(domid_t dom)
00028         : xc_iface(-1), xc_event_iface(-1), dom(0), shared_page_mfn(0), shared_page(NULL), event_port(-1) {
00029         
00030         if ((xc_iface = xc_interface_open())<0)
00031             throw Exception("cannot contact Xen hypervisor");
00032         if ((xc_event_iface = xc_evtchn_open())<0)
00033             throw Exception("cannot open event channel");
00034         
00035         CmdInit cmd;
00036         domctl(cmd, dom);
00037         this->dom = dom;
00038         shared_page_mfn = cmd.u.ether.comm.shared_page_mfn;
00039 
00040         /* The hypervisor allocated a page of memory that we now map into our own address space. We should call munmap()
00041          * during destruction. The hypervisor will initialize the page to all zeros. */
00042         shared_page = (volatile unsigned char*)xc_map_foreign_range(xc_iface, DOMID_XEN, getpagesize(),
00043                                                                     PROT_READ|PROT_WRITE, shared_page_mfn);
00044         if (!shared_page)
00045             throw("cannot map shared page");
00046 
00047         /* The Xen hypervisor allocated an unbound port whose number was returned. This event channel will be used to notify
00048          * us when Ether-related things happen in the domU.  So now we create the local end of the channel and bind it with
00049          * the remote end. */
00050         event_port = xc_evtchn_bind_interdomain(xc_event_iface, DOMID_SELF, cmd.u.ether.comm.event_channel_port);
00051         if (event_port<0)
00052             throw("cannot bind ether event port");
00053 
00054         /* DEBUG */
00055         printf("After init:\n");
00056         printf("    domain:             %d\n", dom);
00057         printf("    xc_iface:           %d\n", xc_iface);
00058         printf("    xc_event_iface:     %d\n", xc_event_iface);
00059         printf("    shared_page:        %p\n", shared_page);
00060         printf("    shared_page_mfn:    0x%lx\n", shared_page_mfn);
00061         printf("    event_channel_port: %d\n", cmd.u.ether.comm.event_channel_port);
00062         printf("    event_port:         %u\n", event_port);
00063     }
00064 
00066     ~Ether() {
00067         if (shared_page!=NULL) {
00068             domctl(CmdTerminate());
00069             xc_domain_unpause(xc_iface, dom);
00070         }
00071         if (event_port>=0) {
00072             xc_evtchn_unbind(xc_event_iface, event_port);
00073             event_port = -1;
00074         }
00075         if (shared_page!=NULL) {
00076             if (is_lock_held())
00077                 release_lock();
00078             munmap((void*)shared_page, getpagesize());
00079             shared_page = NULL;
00080             shared_page_mfn = 0;
00081         }
00082         if (xc_event_iface>=0) {
00083             xc_evtchn_close(xc_event_iface);
00084             xc_event_iface = -1;
00085         }
00086         if (xc_iface) {
00087             xc_interface_close(xc_iface);
00088             xc_iface = -1;
00089         }
00090     }
00091 
00093     void readguest(rose_addr_t va, unsigned char *buffer, size_t size) {
00094         domctl(CmdReadGuest(va, buffer, size));
00095     }
00096     
00099     void set_single_step(bool status) {
00100         domctl(CmdSingleStep(status));
00101     }
00102     
00105     void set_single_step_notify(bool status) {
00106         domctl(CmdSingleStepNotify(status));
00107     }
00108 
00110     void set_memwrite_notify(bool status) {
00111         domctl(CmdMemwriteNotify(status));
00112     }
00113 
00115     void set_unpack_notify(bool status) {
00116         domctl(CmdUnpackNotify(status));
00117     }
00118 
00122     void set_sysenter(bool new_eip) {
00123         domctl(CmdSysenter(new_eip));
00124     }
00125 
00127     void add_name_filter(const std::string &name) {
00128         domctl(CmdAddName(name));
00129     }
00130 
00131 
00132     void add_cr3_filter(rose_addr_t cr3) {
00133         domctl(CmdAddCR3(cr3));
00134     }
00135 
00138     bool is_lock_held() {
00139         if (shared_page==NULL)
00140             throw Exception("no shared page");
00141         return *(volatile uint32_t*)shared_page ? true : false;
00142     }
00143     
00147     void release_lock() {
00148         if (shared_page==NULL)
00149             throw Exception("no shared page");
00150         if (!is_lock_held())
00151             throw Exception("shared page lock is not set\n");
00152         *(volatile uint32_t*)shared_page = 0;
00153     }
00154 
00158     int next_event() {
00159         if (shared_page==NULL)
00160             throw Exception("no shared page");
00161         while (1) {
00162             if (xc_evtchn_pending(xc_event_iface)!=event_port) {
00163                 xc_domain_unpause(xc_iface, dom);
00164             } else if (xc_evtchn_unmask(xc_event_iface, event_port)<0) {
00165                 throw Exception("cannot unmask ether event");
00166             } else if (!is_lock_held()) {
00167                 xc_domain_unpause(xc_iface, dom);
00168                 throw Exception("ether event but shared page lock is clear");
00169             }
00170 
00171             return ((volatile uint32_t*)shared_page)[1];
00172         }
00173     }
00174 
00176     const void *event_data() {
00177         if (!is_lock_held())
00178             throw Exception("shared page lock is not set");
00179         if (!shared_page)
00180             throw Exception("no shared page");
00181         return (const void*)(shared_page+8);
00182     }
00183     
00186     void resume() {
00187         if (is_lock_held())  /*perhaps this should be an exception instead!*/
00188             release_lock();
00189         xc_domain_unpause(xc_iface, dom); /*not always necessary, but doesn't hurt*/
00190     }
00191 
00192 private:
00193     struct CmdInit: public xen_domctl {
00194         CmdInit() {
00195             u.ether.command_code = XEN_DOMCTL_ETHER_INIT;
00196             memset(&u.ether.comm, 0, sizeof(u.ether.comm));
00197         }
00198     };
00199 
00200     struct CmdTerminate: public xen_domctl {
00201         CmdTerminate() {
00202             u.ether.command_code = XEN_DOMCTL_ETHER_TERMINATE;
00203         }
00204     };
00205 
00206     struct CmdReadGuest: public xen_domctl {
00207         CmdReadGuest(rose_addr_t va, void *buffer, size_t size) {
00208             u.ether.command_code = XEN_DOMCTL_ETHER_READ_GUEST;
00209             u.ether.guest_va = va;
00210             u.ether.guest_buffer = (unsigned char*)buffer;
00211             u.ether.data_length = size;
00212             /* This is actually necessary to get this memory paged in so the hypervisor can write to it. */
00213             memset(buffer, 0, size);
00214         }
00215     };
00216 
00217     struct CmdSingleStep: public xen_domctl {
00218         CmdSingleStep(bool status) {
00219             u.ether.command_code = XEN_DOMCTL_ETHER_SINGLE_STEP;
00220             u.ether.on_or_off = status;
00221         }
00222     };
00223 
00224     struct CmdSingleStepNotify: public xen_domctl {
00225         CmdSingleStepNotify(bool status) {
00226             u.ether.command_code = XEN_DOMCTL_ETHER_SS_DETECT;
00227             u.ether.on_or_off = status;
00228         }
00229     };
00230     
00231     struct CmdMemwriteNotify: public xen_domctl {
00232         CmdMemwriteNotify(bool status) {
00233             u.ether.command_code = XEN_DOMCTL_ETHER_MEMWRITE;
00234             u.ether.on_or_off = status;
00235         }
00236     };
00237     
00238     struct CmdUnpackNotify: public xen_domctl {
00239         CmdUnpackNotify(bool status) {
00240             u.ether.command_code = XEN_DOMCTL_ETHER_UNPACK;
00241             u.ether.on_or_off = status;
00242         }
00243     };
00244 
00245     struct CmdSysenter: public xen_domctl {
00246         CmdSysenter(rose_addr_t eip) {
00247             u.ether.command_code = XEN_DOMCTL_ETHER_SET_SYSENTER;
00248             u.ether.sysenter_cs = 0;
00249             u.ether.sysenter_eip = eip;
00250         }
00251     };
00252 
00253     struct CmdAddCR3: public xen_domctl {
00254         CmdAddCR3(rose_addr_t cr3) {
00255             u.ether.command_code = XEN_DOMCTL_ETHER_ADD_CR3;
00256             u.ether.cr3_value = cr3;
00257         }
00258     };
00259 
00260     struct CmdAddName: public xen_domctl {
00261         CmdAddName(const std::string &name) {
00262             u.ether.command_code = XEN_DOMCTL_ETHER_ADD_NAME;
00263             u.ether.data_length = name.size() + 1;
00264             u.ether.guest_buffer = (unsigned char*)(name.c_str());
00265         }
00266     };
00267     
00268     bool connected() {
00269         return xc_iface>=0 && dom>0;
00270     }
00271 
00272     /* Send an ether command to the hypervisor.  The command should have been constructed with one of the Cmd* constructors in
00273      * order to initialize it properly.  The @p dom parameter is normally only specified by the constructor in order
00274      * to send a message to the hypervisor before this object is connected. */
00275     void domctl(const xen_domctl &cmd, domid_t dom=0) {
00276         domctl(const_cast<xen_domctl*>(&cmd), dom);
00277     }
00278     void domctl(xen_domctl &cmd, domid_t dom=0) {
00279         domctl(&cmd, dom);
00280     }
00281     void domctl(xen_domctl *cmd, domid_t dom=0) {
00282         if (connected()) {
00283             if (dom>0 && dom!=this->dom)
00284                 throw Exception("contradicting domain IDs");
00285             dom = this->dom;
00286         } else if (dom<=0) {
00287             throw Exception("not connected");
00288         }
00289 
00290         cmd->domain = dom;
00291         cmd->cmd = XEN_DOMCTL_ether;
00292         int result = xc_domctl(xc_iface, cmd);
00293         if (result<0) {
00294             char buf[256];
00295             sprintf(buf, "xc_domctl(request=%u) failed", cmd->u.ether.command_code);
00296             throw Exception(buf);
00297         }
00298     }
00299     
00300 private:
00301     int xc_iface;                               
00302     int xc_event_iface;                         
00303     domid_t dom;                                
00305     rose_addr_t shared_page_mfn;                
00306     volatile unsigned char *shared_page;        
00308     evtchn_port_t event_port;                   
00309 };
00310 
00311 #endif /*ROSE_USE_ETHER*/
00312 #endif /*ROSE_ETHER_H*/

Generated on Sat May 19 00:53:06 2012 for ROSE by  doxygen 1.4.7