kernel: vm kernel call can't suspend
[minix.git] / drivers / vbox / vbox.c
blob8a1a95bbb5f2a03af64f0a9da8a7b6446efbb09a
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/drivers.h>
8 #include <minix/driver.h>
9 #include <minix/optset.h>
10 #include <machine/pci.h>
11 #include <sys/time.h>
13 #include "vmmdev.h"
14 #include "proto.h"
16 #define DEFAULT_INTERVAL 1 /* check host time every second */
17 #define DEFAULT_DRIFT 2 /* update time if delta is >= 2 secs */
19 static void *vir_ptr;
20 static phys_bytes phys_ptr;
21 static port_t port;
22 static u32_t ticks;
23 static int interval;
24 static int drift;
26 static unsigned int irq;
27 static int hook_id;
29 static struct optset optset_table[] = {
30 { "interval", OPT_INT, &interval, 10 },
31 { "drift", OPT_INT, &drift, 10 },
32 { NULL, 0, NULL, 0 }
35 /*===========================================================================*
36 * vbox_request *
37 *===========================================================================*/
38 int vbox_request(struct VMMDevRequestHeader *header, phys_bytes addr,
39 int type, size_t size)
41 /* Perform a VirtualBox backdoor request. */
42 int r;
44 header->size = size;
45 header->version = VMMDEV_BACKDOOR_VERSION;
46 header->type = type;
47 header->result = VMMDEV_ERR_GENERIC;
49 if ((r = sys_outl(port, addr)) != OK)
50 panic("device I/O failed: %d", r);
52 return header->result;
55 /*===========================================================================*
56 * vbox_init *
57 *===========================================================================*/
58 static int vbox_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
60 /* Initialize the device. */
61 int devind;
62 u16_t vid, did;
63 struct VMMDevReportGuestInfo *req;
64 int r;
66 interval = DEFAULT_INTERVAL;
67 drift = DEFAULT_DRIFT;
69 if (env_argc > 1)
70 optset_parse(optset_table, env_argv[1]);
72 pci_init();
74 r = pci_first_dev(&devind, &vid, &did);
76 for (;;) {
77 if (r != 1)
78 panic("backdoor device not found");
80 if (vid == VMMDEV_PCI_VID && did == VMMDEV_PCI_DID)
81 break;
83 r = pci_next_dev(&devind, &vid, &did);
86 pci_reserve(devind);
88 port = pci_attr_r32(devind, PCI_BAR) & PCI_BAR_IO_MASK;
90 irq = pci_attr_r8(devind, PCI_ILR);
91 hook_id = 0;
93 if ((r = sys_irqsetpolicy(irq, 0 /* IRQ_REENABLE */, &hook_id)) != OK)
94 panic("unable to register IRQ: %d", r);
96 if ((r = sys_irqenable(&hook_id)) != OK)
97 panic("unable to enable IRQ: %d", r);
99 if ((vir_ptr = alloc_contig(VMMDEV_BUF_SIZE, 0, &phys_ptr)) == NULL)
100 panic("unable to allocate memory");
102 req = (struct VMMDevReportGuestInfo *) vir_ptr;
103 req->add_version = VMMDEV_GUEST_VERSION;
104 req->os_type = VMMDEV_GUEST_OS_OTHER;
106 if ((r = vbox_request(&req->header, phys_ptr,
107 VMMDEV_REQ_REPORTGUESTINFO, sizeof(*req))) !=
108 VMMDEV_ERR_OK)
109 panic("backdoor device not functioning");
111 ticks = sys_hz() * interval;
113 sys_setalarm(ticks, 0);
115 return OK;
118 /*===========================================================================*
119 * vbox_intr *
120 *===========================================================================*/
121 static void vbox_intr(void)
123 /* Process an interrupt. */
124 struct VMMDevEvents *req;
125 int r;
127 req = (struct VMMDevEvents *) vir_ptr;
128 req->events = 0;
130 /* If we cannot retrieve the events mask, we cannot do anything with
131 * this or any future interrupt either, so return without reenabling
132 * interrupts.
134 if ((r = vbox_request(&req->header, phys_ptr,
135 VMMDEV_REQ_ACKNOWLEDGEEVENTS, sizeof(*req))) !=
136 VMMDEV_ERR_OK) {
137 printf("VBOX: unable to retrieve event mask (%d)\n", r);
139 return;
142 if (req->events & VMMDEV_EVENT_HGCM)
143 hgcm_intr();
145 if ((r = sys_irqenable(&hook_id)) != OK)
146 panic("unable to reenable IRQ: %d", r);
149 /*===========================================================================*
150 * vbox_update_time *
151 *===========================================================================*/
152 static void vbox_update_time(void)
154 /* Update the current time if it has drifted too far. */
155 struct VMMDevReqHostTime *req;
156 time_t otime, ntime;
158 req = (struct VMMDevReqHostTime *) vir_ptr;
160 if (vbox_request(&req->header, phys_ptr, VMMDEV_REQ_HOSTTIME,
161 sizeof(*req)) == VMMDEV_ERR_OK) {
162 time(&otime); /* old time */
164 ntime = div64u(req->time, 1000); /* new time */
166 /* Make time go forward, if the difference exceeds the drift
167 * threshold. Never make time go backward.
169 if ((int) (ntime - otime) >= drift)
170 stime(&ntime);
173 sys_setalarm(ticks, 0);
176 /*===========================================================================*
177 * vbox_signal *
178 *===========================================================================*/
179 static void vbox_signal(int signo)
181 /* Process a signal. If it is a SIGTERM, terminate immediately. */
183 if (signo != SIGTERM) return;
185 exit(0);
188 /*===========================================================================*
189 * sef_local_startup *
190 *===========================================================================*/
191 static void sef_local_startup(void)
193 /* Perform local SEF initialization. */
195 sef_setcb_init_fresh(vbox_init);
196 sef_setcb_init_restart(vbox_init);
198 sef_setcb_signal_handler(vbox_signal);
200 sef_startup();
203 /*===========================================================================*
204 * main *
205 *===========================================================================*/
206 int main(int argc, char **argv)
208 /* The main message loop. */
209 message m;
210 int r, ipc_status;
212 env_setargs(argc, argv);
213 sef_local_startup();
215 while (TRUE) {
216 if ((r = driver_receive(ANY, &m, &ipc_status)) != OK)
217 panic("driver_receive failed: %d", r);
219 if (is_ipc_notify(ipc_status)) {
220 switch (m.m_source) {
221 case HARDWARE:
222 vbox_intr();
224 break;
226 case CLOCK:
227 vbox_update_time();
229 break;
231 default:
232 printf("VBOX: received notify from %d\n",
233 m.m_source);
236 continue;
239 hgcm_message(&m, ipc_status);
242 return 0;