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