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 *===========================================================================*/
vbox_request(struct VMMDevRequestHeader * header,phys_bytes addr,int type,size_t size)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_update_time *
58 *===========================================================================*/
vbox_update_time(void)59 static void vbox_update_time(void)
60 {
61 /* Update the current time if it has drifted too far. */
62 struct VMMDevReqHostTime *req;
63 time_t otime, ntime;
64
65 req = (struct VMMDevReqHostTime *) vir_ptr;
66
67 if (vbox_request(&req->header, phys_ptr, VMMDEV_REQ_HOSTTIME,
68 sizeof(*req)) == VMMDEV_ERR_OK) {
69 time(&otime); /* old time */
70
71 ntime = req->time / 1000; /* new time */
72
73 /* Make time go forward, if the difference exceeds the drift
74 * threshold. Never make time go backward.
75 */
76 if ((ntime - otime) >= drift)
77 stime(&ntime);
78 }
79
80 sys_setalarm(ticks, 0);
81 }
82
83 /*===========================================================================*
84 * vbox_init *
85 *===========================================================================*/
vbox_init(int UNUSED (type),sef_init_info_t * UNUSED (info))86 static int vbox_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
87 {
88 /* Initialize the device. */
89 int devind;
90 u16_t vid, did;
91 struct VMMDevReportGuestInfo *req;
92 int r;
93
94 interval = DEFAULT_INTERVAL;
95 drift = DEFAULT_DRIFT;
96
97 if (env_argc > 1)
98 optset_parse(optset_table, env_argv[1]);
99
100 pci_init();
101
102 r = pci_first_dev(&devind, &vid, &did);
103
104 for (;;) {
105 if (r != 1)
106 panic("backdoor device not found");
107
108 if (vid == VMMDEV_PCI_VID && did == VMMDEV_PCI_DID)
109 break;
110
111 r = pci_next_dev(&devind, &vid, &did);
112 }
113
114 pci_reserve(devind);
115
116 port = pci_attr_r32(devind, PCI_BAR) & PCI_BAR_IO_MASK;
117
118 irq = pci_attr_r8(devind, PCI_ILR);
119 hook_id = 0;
120
121 if ((r = sys_irqsetpolicy(irq, 0 /* IRQ_REENABLE */, &hook_id)) != OK)
122 panic("unable to register IRQ: %d", r);
123
124 if ((r = sys_irqenable(&hook_id)) != OK)
125 panic("unable to enable IRQ: %d", r);
126
127 if ((vir_ptr = alloc_contig(VMMDEV_BUF_SIZE, 0, &phys_ptr)) == NULL)
128 panic("unable to allocate memory");
129
130 req = (struct VMMDevReportGuestInfo *) vir_ptr;
131 req->add_version = VMMDEV_GUEST_VERSION;
132 req->os_type = VMMDEV_GUEST_OS_OTHER;
133
134 if ((r = vbox_request(&req->header, phys_ptr,
135 VMMDEV_REQ_REPORTGUESTINFO, sizeof(*req))) !=
136 VMMDEV_ERR_OK)
137 panic("backdoor device not functioning");
138
139 ticks = sys_hz() * interval;
140
141 /*
142 * Do the first time update immediately. If the time changes
143 * significantly, there may otherwise be interference with rc scripts.
144 */
145 vbox_update_time();
146
147 return OK;
148 }
149
150 /*===========================================================================*
151 * vbox_intr *
152 *===========================================================================*/
vbox_intr(void)153 static void vbox_intr(void)
154 {
155 /* Process an interrupt. */
156 struct VMMDevEvents *req;
157 int r;
158
159 req = (struct VMMDevEvents *) vir_ptr;
160 req->events = 0;
161
162 /* If we cannot retrieve the events mask, we cannot do anything with
163 * this or any future interrupt either, so return without reenabling
164 * interrupts.
165 */
166 if ((r = vbox_request(&req->header, phys_ptr,
167 VMMDEV_REQ_ACKNOWLEDGEEVENTS, sizeof(*req))) !=
168 VMMDEV_ERR_OK) {
169 printf("VBOX: unable to retrieve event mask (%d)\n", r);
170
171 return;
172 }
173
174 if (req->events & VMMDEV_EVENT_HGCM)
175 hgcm_intr();
176
177 if ((r = sys_irqenable(&hook_id)) != OK)
178 panic("unable to reenable IRQ: %d", r);
179 }
180
181 /*===========================================================================*
182 * vbox_signal *
183 *===========================================================================*/
vbox_signal(int signo)184 static void vbox_signal(int signo)
185 {
186 /* Process a signal. If it is a SIGTERM, terminate immediately. */
187
188 if (signo != SIGTERM) return;
189
190 exit(0);
191 }
192
193 /*===========================================================================*
194 * sef_local_startup *
195 *===========================================================================*/
sef_local_startup(void)196 static void sef_local_startup(void)
197 {
198 /* Perform local SEF initialization. */
199
200 sef_setcb_init_fresh(vbox_init);
201 sef_setcb_init_restart(vbox_init);
202
203 sef_setcb_signal_handler(vbox_signal);
204
205 sef_startup();
206 }
207
208 /*===========================================================================*
209 * main *
210 *===========================================================================*/
main(int argc,char ** argv)211 int main(int argc, char **argv)
212 {
213 /* The main message loop. */
214 message m;
215 int r, ipc_status;
216
217 env_setargs(argc, argv);
218 sef_local_startup();
219
220 while (TRUE) {
221 if ((r = driver_receive(ANY, &m, &ipc_status)) != OK)
222 panic("driver_receive failed: %d", r);
223
224 if (is_ipc_notify(ipc_status)) {
225 switch (m.m_source) {
226 case HARDWARE:
227 vbox_intr();
228
229 break;
230
231 case CLOCK:
232 vbox_update_time();
233
234 break;
235
236 default:
237 printf("VBOX: received notify from %d\n",
238 m.m_source);
239 }
240
241 continue;
242 }
243
244 hgcm_message(&m, ipc_status);
245 }
246
247 return 0;
248 }
249