1433d6423SLionel Sambuc /* VirtualBox driver - by D.C. van Moolenbroek */
2433d6423SLionel Sambuc /*
3433d6423SLionel Sambuc * This driver currently performs two tasks:
4433d6423SLionel Sambuc * - synchronizing to the host system time;
5433d6423SLionel Sambuc * - providing an interface for HGCM communication with the host system.
6433d6423SLionel Sambuc */
7433d6423SLionel Sambuc #include <minix/sysutil.h>
8433d6423SLionel Sambuc #include <minix/drivers.h>
9433d6423SLionel Sambuc #include <minix/driver.h>
10433d6423SLionel Sambuc #include <minix/optset.h>
11433d6423SLionel Sambuc #include <machine/pci.h>
12433d6423SLionel Sambuc #include <sys/time.h>
13433d6423SLionel Sambuc
14433d6423SLionel Sambuc #include "vmmdev.h"
15433d6423SLionel Sambuc #include "proto.h"
16433d6423SLionel Sambuc
17433d6423SLionel Sambuc #define DEFAULT_INTERVAL 1 /* check host time every second */
18433d6423SLionel Sambuc #define DEFAULT_DRIFT 2 /* update time if delta is >= 2 secs */
19433d6423SLionel Sambuc
20433d6423SLionel Sambuc static void *vir_ptr;
21433d6423SLionel Sambuc static phys_bytes phys_ptr;
22433d6423SLionel Sambuc static port_t port;
23433d6423SLionel Sambuc static u32_t ticks;
24433d6423SLionel Sambuc static int interval;
25433d6423SLionel Sambuc static int drift;
26433d6423SLionel Sambuc
27433d6423SLionel Sambuc static unsigned int irq;
28433d6423SLionel Sambuc static int hook_id;
29433d6423SLionel Sambuc
30433d6423SLionel Sambuc static struct optset optset_table[] = {
31433d6423SLionel Sambuc { "interval", OPT_INT, &interval, 10 },
32433d6423SLionel Sambuc { "drift", OPT_INT, &drift, 10 },
33433d6423SLionel Sambuc { NULL, 0, NULL, 0 }
34433d6423SLionel Sambuc };
35433d6423SLionel Sambuc
36433d6423SLionel Sambuc /*===========================================================================*
37433d6423SLionel Sambuc * vbox_request *
38433d6423SLionel Sambuc *===========================================================================*/
vbox_request(struct VMMDevRequestHeader * header,phys_bytes addr,int type,size_t size)39433d6423SLionel Sambuc int vbox_request(struct VMMDevRequestHeader *header, phys_bytes addr,
40433d6423SLionel Sambuc int type, size_t size)
41433d6423SLionel Sambuc {
42433d6423SLionel Sambuc /* Perform a VirtualBox backdoor request. */
43433d6423SLionel Sambuc int r;
44433d6423SLionel Sambuc
45433d6423SLionel Sambuc header->size = size;
46433d6423SLionel Sambuc header->version = VMMDEV_BACKDOOR_VERSION;
47433d6423SLionel Sambuc header->type = type;
48433d6423SLionel Sambuc header->result = VMMDEV_ERR_GENERIC;
49433d6423SLionel Sambuc
50433d6423SLionel Sambuc if ((r = sys_outl(port, addr)) != OK)
51433d6423SLionel Sambuc panic("device I/O failed: %d", r);
52433d6423SLionel Sambuc
53433d6423SLionel Sambuc return header->result;
54433d6423SLionel Sambuc }
55433d6423SLionel Sambuc
56433d6423SLionel Sambuc /*===========================================================================*
57*3e1f70dbSDavid van Moolenbroek * vbox_update_time *
58*3e1f70dbSDavid van Moolenbroek *===========================================================================*/
vbox_update_time(void)59*3e1f70dbSDavid van Moolenbroek static void vbox_update_time(void)
60*3e1f70dbSDavid van Moolenbroek {
61*3e1f70dbSDavid van Moolenbroek /* Update the current time if it has drifted too far. */
62*3e1f70dbSDavid van Moolenbroek struct VMMDevReqHostTime *req;
63*3e1f70dbSDavid van Moolenbroek time_t otime, ntime;
64*3e1f70dbSDavid van Moolenbroek
65*3e1f70dbSDavid van Moolenbroek req = (struct VMMDevReqHostTime *) vir_ptr;
66*3e1f70dbSDavid van Moolenbroek
67*3e1f70dbSDavid van Moolenbroek if (vbox_request(&req->header, phys_ptr, VMMDEV_REQ_HOSTTIME,
68*3e1f70dbSDavid van Moolenbroek sizeof(*req)) == VMMDEV_ERR_OK) {
69*3e1f70dbSDavid van Moolenbroek time(&otime); /* old time */
70*3e1f70dbSDavid van Moolenbroek
71*3e1f70dbSDavid van Moolenbroek ntime = req->time / 1000; /* new time */
72*3e1f70dbSDavid van Moolenbroek
73*3e1f70dbSDavid van Moolenbroek /* Make time go forward, if the difference exceeds the drift
74*3e1f70dbSDavid van Moolenbroek * threshold. Never make time go backward.
75*3e1f70dbSDavid van Moolenbroek */
76*3e1f70dbSDavid van Moolenbroek if ((ntime - otime) >= drift)
77*3e1f70dbSDavid van Moolenbroek stime(&ntime);
78*3e1f70dbSDavid van Moolenbroek }
79*3e1f70dbSDavid van Moolenbroek
80*3e1f70dbSDavid van Moolenbroek sys_setalarm(ticks, 0);
81*3e1f70dbSDavid van Moolenbroek }
82*3e1f70dbSDavid van Moolenbroek
83*3e1f70dbSDavid van Moolenbroek /*===========================================================================*
84433d6423SLionel Sambuc * vbox_init *
85433d6423SLionel Sambuc *===========================================================================*/
vbox_init(int UNUSED (type),sef_init_info_t * UNUSED (info))86433d6423SLionel Sambuc static int vbox_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
87433d6423SLionel Sambuc {
88433d6423SLionel Sambuc /* Initialize the device. */
89433d6423SLionel Sambuc int devind;
90433d6423SLionel Sambuc u16_t vid, did;
91433d6423SLionel Sambuc struct VMMDevReportGuestInfo *req;
92433d6423SLionel Sambuc int r;
93433d6423SLionel Sambuc
94433d6423SLionel Sambuc interval = DEFAULT_INTERVAL;
95433d6423SLionel Sambuc drift = DEFAULT_DRIFT;
96433d6423SLionel Sambuc
97433d6423SLionel Sambuc if (env_argc > 1)
98433d6423SLionel Sambuc optset_parse(optset_table, env_argv[1]);
99433d6423SLionel Sambuc
100433d6423SLionel Sambuc pci_init();
101433d6423SLionel Sambuc
102433d6423SLionel Sambuc r = pci_first_dev(&devind, &vid, &did);
103433d6423SLionel Sambuc
104433d6423SLionel Sambuc for (;;) {
105433d6423SLionel Sambuc if (r != 1)
106433d6423SLionel Sambuc panic("backdoor device not found");
107433d6423SLionel Sambuc
108433d6423SLionel Sambuc if (vid == VMMDEV_PCI_VID && did == VMMDEV_PCI_DID)
109433d6423SLionel Sambuc break;
110433d6423SLionel Sambuc
111433d6423SLionel Sambuc r = pci_next_dev(&devind, &vid, &did);
112433d6423SLionel Sambuc }
113433d6423SLionel Sambuc
114433d6423SLionel Sambuc pci_reserve(devind);
115433d6423SLionel Sambuc
116433d6423SLionel Sambuc port = pci_attr_r32(devind, PCI_BAR) & PCI_BAR_IO_MASK;
117433d6423SLionel Sambuc
118433d6423SLionel Sambuc irq = pci_attr_r8(devind, PCI_ILR);
119433d6423SLionel Sambuc hook_id = 0;
120433d6423SLionel Sambuc
121433d6423SLionel Sambuc if ((r = sys_irqsetpolicy(irq, 0 /* IRQ_REENABLE */, &hook_id)) != OK)
122433d6423SLionel Sambuc panic("unable to register IRQ: %d", r);
123433d6423SLionel Sambuc
124433d6423SLionel Sambuc if ((r = sys_irqenable(&hook_id)) != OK)
125433d6423SLionel Sambuc panic("unable to enable IRQ: %d", r);
126433d6423SLionel Sambuc
127433d6423SLionel Sambuc if ((vir_ptr = alloc_contig(VMMDEV_BUF_SIZE, 0, &phys_ptr)) == NULL)
128433d6423SLionel Sambuc panic("unable to allocate memory");
129433d6423SLionel Sambuc
130433d6423SLionel Sambuc req = (struct VMMDevReportGuestInfo *) vir_ptr;
131433d6423SLionel Sambuc req->add_version = VMMDEV_GUEST_VERSION;
132433d6423SLionel Sambuc req->os_type = VMMDEV_GUEST_OS_OTHER;
133433d6423SLionel Sambuc
134433d6423SLionel Sambuc if ((r = vbox_request(&req->header, phys_ptr,
135433d6423SLionel Sambuc VMMDEV_REQ_REPORTGUESTINFO, sizeof(*req))) !=
136433d6423SLionel Sambuc VMMDEV_ERR_OK)
137433d6423SLionel Sambuc panic("backdoor device not functioning");
138433d6423SLionel Sambuc
139433d6423SLionel Sambuc ticks = sys_hz() * interval;
140433d6423SLionel Sambuc
141*3e1f70dbSDavid van Moolenbroek /*
142*3e1f70dbSDavid van Moolenbroek * Do the first time update immediately. If the time changes
143*3e1f70dbSDavid van Moolenbroek * significantly, there may otherwise be interference with rc scripts.
144*3e1f70dbSDavid van Moolenbroek */
145*3e1f70dbSDavid van Moolenbroek vbox_update_time();
146433d6423SLionel Sambuc
147433d6423SLionel Sambuc return OK;
148433d6423SLionel Sambuc }
149433d6423SLionel Sambuc
150433d6423SLionel Sambuc /*===========================================================================*
151433d6423SLionel Sambuc * vbox_intr *
152433d6423SLionel Sambuc *===========================================================================*/
vbox_intr(void)153433d6423SLionel Sambuc static void vbox_intr(void)
154433d6423SLionel Sambuc {
155433d6423SLionel Sambuc /* Process an interrupt. */
156433d6423SLionel Sambuc struct VMMDevEvents *req;
157433d6423SLionel Sambuc int r;
158433d6423SLionel Sambuc
159433d6423SLionel Sambuc req = (struct VMMDevEvents *) vir_ptr;
160433d6423SLionel Sambuc req->events = 0;
161433d6423SLionel Sambuc
162433d6423SLionel Sambuc /* If we cannot retrieve the events mask, we cannot do anything with
163433d6423SLionel Sambuc * this or any future interrupt either, so return without reenabling
164433d6423SLionel Sambuc * interrupts.
165433d6423SLionel Sambuc */
166433d6423SLionel Sambuc if ((r = vbox_request(&req->header, phys_ptr,
167433d6423SLionel Sambuc VMMDEV_REQ_ACKNOWLEDGEEVENTS, sizeof(*req))) !=
168433d6423SLionel Sambuc VMMDEV_ERR_OK) {
169433d6423SLionel Sambuc printf("VBOX: unable to retrieve event mask (%d)\n", r);
170433d6423SLionel Sambuc
171433d6423SLionel Sambuc return;
172433d6423SLionel Sambuc }
173433d6423SLionel Sambuc
174433d6423SLionel Sambuc if (req->events & VMMDEV_EVENT_HGCM)
175433d6423SLionel Sambuc hgcm_intr();
176433d6423SLionel Sambuc
177433d6423SLionel Sambuc if ((r = sys_irqenable(&hook_id)) != OK)
178433d6423SLionel Sambuc panic("unable to reenable IRQ: %d", r);
179433d6423SLionel Sambuc }
180433d6423SLionel Sambuc
181433d6423SLionel Sambuc /*===========================================================================*
182433d6423SLionel Sambuc * vbox_signal *
183433d6423SLionel Sambuc *===========================================================================*/
vbox_signal(int signo)184433d6423SLionel Sambuc static void vbox_signal(int signo)
185433d6423SLionel Sambuc {
186433d6423SLionel Sambuc /* Process a signal. If it is a SIGTERM, terminate immediately. */
187433d6423SLionel Sambuc
188433d6423SLionel Sambuc if (signo != SIGTERM) return;
189433d6423SLionel Sambuc
190433d6423SLionel Sambuc exit(0);
191433d6423SLionel Sambuc }
192433d6423SLionel Sambuc
193433d6423SLionel Sambuc /*===========================================================================*
194433d6423SLionel Sambuc * sef_local_startup *
195433d6423SLionel Sambuc *===========================================================================*/
sef_local_startup(void)196433d6423SLionel Sambuc static void sef_local_startup(void)
197433d6423SLionel Sambuc {
198433d6423SLionel Sambuc /* Perform local SEF initialization. */
199433d6423SLionel Sambuc
200433d6423SLionel Sambuc sef_setcb_init_fresh(vbox_init);
201433d6423SLionel Sambuc sef_setcb_init_restart(vbox_init);
202433d6423SLionel Sambuc
203433d6423SLionel Sambuc sef_setcb_signal_handler(vbox_signal);
204433d6423SLionel Sambuc
205433d6423SLionel Sambuc sef_startup();
206433d6423SLionel Sambuc }
207433d6423SLionel Sambuc
208433d6423SLionel Sambuc /*===========================================================================*
209433d6423SLionel Sambuc * main *
210433d6423SLionel Sambuc *===========================================================================*/
main(int argc,char ** argv)211433d6423SLionel Sambuc int main(int argc, char **argv)
212433d6423SLionel Sambuc {
213433d6423SLionel Sambuc /* The main message loop. */
214433d6423SLionel Sambuc message m;
215433d6423SLionel Sambuc int r, ipc_status;
216433d6423SLionel Sambuc
217433d6423SLionel Sambuc env_setargs(argc, argv);
218433d6423SLionel Sambuc sef_local_startup();
219433d6423SLionel Sambuc
220433d6423SLionel Sambuc while (TRUE) {
221433d6423SLionel Sambuc if ((r = driver_receive(ANY, &m, &ipc_status)) != OK)
222433d6423SLionel Sambuc panic("driver_receive failed: %d", r);
223433d6423SLionel Sambuc
224433d6423SLionel Sambuc if (is_ipc_notify(ipc_status)) {
225433d6423SLionel Sambuc switch (m.m_source) {
226433d6423SLionel Sambuc case HARDWARE:
227433d6423SLionel Sambuc vbox_intr();
228433d6423SLionel Sambuc
229433d6423SLionel Sambuc break;
230433d6423SLionel Sambuc
231433d6423SLionel Sambuc case CLOCK:
232433d6423SLionel Sambuc vbox_update_time();
233433d6423SLionel Sambuc
234433d6423SLionel Sambuc break;
235433d6423SLionel Sambuc
236433d6423SLionel Sambuc default:
237433d6423SLionel Sambuc printf("VBOX: received notify from %d\n",
238433d6423SLionel Sambuc m.m_source);
239433d6423SLionel Sambuc }
240433d6423SLionel Sambuc
241433d6423SLionel Sambuc continue;
242433d6423SLionel Sambuc }
243433d6423SLionel Sambuc
244433d6423SLionel Sambuc hgcm_message(&m, ipc_status);
245433d6423SLionel Sambuc }
246433d6423SLionel Sambuc
247433d6423SLionel Sambuc return 0;
248433d6423SLionel Sambuc }
249