1 /* VirtualBox driver - by D.C. van Moolenbroek */ 2 /* 3 * This driver currently performs two tasks: 4 * - synchronizing to the host system time; 5 * - providing an interface for HGCM communication with the host system. 6 */ 7 #include <minix/sysutil.h> 8 #include <minix/drivers.h> 9 #include <minix/driver.h> 10 #include <minix/optset.h> 11 #include <machine/pci.h> 12 #include <sys/time.h> 13 14 #include "vmmdev.h" 15 #include "proto.h" 16 17 #define DEFAULT_INTERVAL 1 /* check host time every second */ 18 #define DEFAULT_DRIFT 2 /* update time if delta is >= 2 secs */ 19 20 static void *vir_ptr; 21 static phys_bytes phys_ptr; 22 static port_t port; 23 static u32_t ticks; 24 static int interval; 25 static int drift; 26 27 static unsigned int irq; 28 static int hook_id; 29 30 static struct optset optset_table[] = { 31 { "interval", OPT_INT, &interval, 10 }, 32 { "drift", OPT_INT, &drift, 10 }, 33 { NULL, 0, NULL, 0 } 34 }; 35 36 /*===========================================================================* 37 * vbox_request * 38 *===========================================================================*/ 39 int vbox_request(struct VMMDevRequestHeader *header, phys_bytes addr, 40 int type, size_t size) 41 { 42 /* Perform a VirtualBox backdoor request. */ 43 int r; 44 45 header->size = size; 46 header->version = VMMDEV_BACKDOOR_VERSION; 47 header->type = type; 48 header->result = VMMDEV_ERR_GENERIC; 49 50 if ((r = sys_outl(port, addr)) != OK) 51 panic("device I/O failed: %d", r); 52 53 return header->result; 54 } 55 56 /*===========================================================================* 57 * vbox_init * 58 *===========================================================================*/ 59 static int vbox_init(int UNUSED(type), sef_init_info_t *UNUSED(info)) 60 { 61 /* Initialize the device. */ 62 int devind; 63 u16_t vid, did; 64 struct VMMDevReportGuestInfo *req; 65 int r; 66 67 interval = DEFAULT_INTERVAL; 68 drift = DEFAULT_DRIFT; 69 70 if (env_argc > 1) 71 optset_parse(optset_table, env_argv[1]); 72 73 pci_init(); 74 75 r = pci_first_dev(&devind, &vid, &did); 76 77 for (;;) { 78 if (r != 1) 79 panic("backdoor device not found"); 80 81 if (vid == VMMDEV_PCI_VID && did == VMMDEV_PCI_DID) 82 break; 83 84 r = pci_next_dev(&devind, &vid, &did); 85 } 86 87 pci_reserve(devind); 88 89 port = pci_attr_r32(devind, PCI_BAR) & PCI_BAR_IO_MASK; 90 91 irq = pci_attr_r8(devind, PCI_ILR); 92 hook_id = 0; 93 94 if ((r = sys_irqsetpolicy(irq, 0 /* IRQ_REENABLE */, &hook_id)) != OK) 95 panic("unable to register IRQ: %d", r); 96 97 if ((r = sys_irqenable(&hook_id)) != OK) 98 panic("unable to enable IRQ: %d", r); 99 100 if ((vir_ptr = alloc_contig(VMMDEV_BUF_SIZE, 0, &phys_ptr)) == NULL) 101 panic("unable to allocate memory"); 102 103 req = (struct VMMDevReportGuestInfo *) vir_ptr; 104 req->add_version = VMMDEV_GUEST_VERSION; 105 req->os_type = VMMDEV_GUEST_OS_OTHER; 106 107 if ((r = vbox_request(&req->header, phys_ptr, 108 VMMDEV_REQ_REPORTGUESTINFO, sizeof(*req))) != 109 VMMDEV_ERR_OK) 110 panic("backdoor device not functioning"); 111 112 ticks = sys_hz() * interval; 113 114 sys_setalarm(ticks, 0); 115 116 return OK; 117 } 118 119 /*===========================================================================* 120 * vbox_intr * 121 *===========================================================================*/ 122 static void vbox_intr(void) 123 { 124 /* Process an interrupt. */ 125 struct VMMDevEvents *req; 126 int r; 127 128 req = (struct VMMDevEvents *) vir_ptr; 129 req->events = 0; 130 131 /* If we cannot retrieve the events mask, we cannot do anything with 132 * this or any future interrupt either, so return without reenabling 133 * interrupts. 134 */ 135 if ((r = vbox_request(&req->header, phys_ptr, 136 VMMDEV_REQ_ACKNOWLEDGEEVENTS, sizeof(*req))) != 137 VMMDEV_ERR_OK) { 138 printf("VBOX: unable to retrieve event mask (%d)\n", r); 139 140 return; 141 } 142 143 if (req->events & VMMDEV_EVENT_HGCM) 144 hgcm_intr(); 145 146 if ((r = sys_irqenable(&hook_id)) != OK) 147 panic("unable to reenable IRQ: %d", r); 148 } 149 150 /*===========================================================================* 151 * vbox_update_time * 152 *===========================================================================*/ 153 static void vbox_update_time(void) 154 { 155 /* Update the current time if it has drifted too far. */ 156 struct VMMDevReqHostTime *req; 157 time_t otime, ntime; 158 159 req = (struct VMMDevReqHostTime *) vir_ptr; 160 161 if (vbox_request(&req->header, phys_ptr, VMMDEV_REQ_HOSTTIME, 162 sizeof(*req)) == VMMDEV_ERR_OK) { 163 time(&otime); /* old time */ 164 165 ntime = req->time / 1000; /* new time */ 166 167 /* Make time go forward, if the difference exceeds the drift 168 * threshold. Never make time go backward. 169 */ 170 if ((ntime - otime) >= drift) 171 stime(&ntime); 172 } 173 174 sys_setalarm(ticks, 0); 175 } 176 177 /*===========================================================================* 178 * vbox_signal * 179 *===========================================================================*/ 180 static void vbox_signal(int signo) 181 { 182 /* Process a signal. If it is a SIGTERM, terminate immediately. */ 183 184 if (signo != SIGTERM) return; 185 186 exit(0); 187 } 188 189 /*===========================================================================* 190 * sef_local_startup * 191 *===========================================================================*/ 192 static void sef_local_startup(void) 193 { 194 /* Perform local SEF initialization. */ 195 196 sef_setcb_init_fresh(vbox_init); 197 sef_setcb_init_restart(vbox_init); 198 199 sef_setcb_signal_handler(vbox_signal); 200 201 sef_startup(); 202 } 203 204 /*===========================================================================* 205 * main * 206 *===========================================================================*/ 207 int main(int argc, char **argv) 208 { 209 /* The main message loop. */ 210 message m; 211 int r, ipc_status; 212 213 env_setargs(argc, argv); 214 sef_local_startup(); 215 216 while (TRUE) { 217 if ((r = driver_receive(ANY, &m, &ipc_status)) != OK) 218 panic("driver_receive failed: %d", r); 219 220 if (is_ipc_notify(ipc_status)) { 221 switch (m.m_source) { 222 case HARDWARE: 223 vbox_intr(); 224 225 break; 226 227 case CLOCK: 228 vbox_update_time(); 229 230 break; 231 232 default: 233 printf("VBOX: received notify from %d\n", 234 m.m_source); 235 } 236 237 continue; 238 } 239 240 hgcm_message(&m, ipc_status); 241 } 242 243 return 0; 244 } 245