xref: /minix3/minix/drivers/vmm_guest/vbox/vbox.c (revision 3e1f70db42f099696c68ae51eaf54e57bc024f02)
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