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