Line data Source code
1 : /* $OpenBSD: pvbus.c,v 1.18 2018/01/18 11:43:20 mikeb Exp $ */
2 :
3 : /*
4 : * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
5 : *
6 : * Permission to use, copy, modify, and distribute this software for any
7 : * purpose with or without fee is hereby granted, provided that the above
8 : * copyright notice and this permission notice appear in all copies.
9 : *
10 : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 : */
18 :
19 : #if !defined(__i386__) && !defined(__amd64__)
20 : #error pvbus(4) is currently only supported on i386 and amd64
21 : #endif
22 :
23 : #include <sys/param.h>
24 : #include <sys/systm.h>
25 : #include <sys/kernel.h>
26 : #include <sys/malloc.h>
27 : #include <sys/timeout.h>
28 : #include <sys/signalvar.h>
29 : #include <sys/syslog.h>
30 : #include <sys/proc.h>
31 : #include <sys/socket.h>
32 : #include <sys/ioctl.h>
33 : #include <sys/fcntl.h>
34 :
35 : #include <machine/specialreg.h>
36 : #include <machine/cpu.h>
37 : #include <machine/conf.h>
38 : #include <machine/bus.h>
39 : #include <machine/vmmvar.h>
40 :
41 : #include <dev/rndvar.h>
42 :
43 : #include <dev/pv/pvvar.h>
44 : #include <dev/pv/pvreg.h>
45 :
46 : #include "vmt.h"
47 :
48 : int has_hv_cpuid = 0;
49 :
50 : extern char *hw_vendor;
51 : extern void rdrand(void *);
52 :
53 : int pvbus_activate(struct device *, int);
54 : int pvbus_match(struct device *, void *, void *);
55 : void pvbus_attach(struct device *, struct device *, void *);
56 : int pvbus_print(void *, const char *);
57 : int pvbus_search(struct device *, void *, void *);
58 :
59 : void pvbus_kvm(struct pvbus_hv *);
60 : void pvbus_hyperv(struct pvbus_hv *);
61 : void pvbus_hyperv_print(struct pvbus_hv *);
62 : void pvbus_xen(struct pvbus_hv *);
63 : void pvbus_xen_print(struct pvbus_hv *);
64 :
65 : int pvbus_minor(struct pvbus_softc *, dev_t);
66 : int pvbusgetstr(size_t, const char *, char **);
67 :
68 : struct cfattach pvbus_ca = {
69 : sizeof(struct pvbus_softc),
70 : pvbus_match,
71 : pvbus_attach,
72 : NULL,
73 : pvbus_activate
74 : };
75 :
76 : struct cfdriver pvbus_cd = {
77 : NULL,
78 : "pvbus",
79 : DV_DULL
80 : };
81 :
82 : struct pvbus_type {
83 : const char *signature;
84 : const char *name;
85 : void (*init)(struct pvbus_hv *);
86 : void (*print)(struct pvbus_hv *);
87 : } pvbus_types[PVBUS_MAX] = {
88 : { "KVMKVMKVM\0\0\0", "KVM", pvbus_kvm },
89 : { "Microsoft Hv", "Hyper-V", pvbus_hyperv, pvbus_hyperv_print },
90 : { "VMwareVMware", "VMware" },
91 : { "XenVMMXenVMM", "Xen", pvbus_xen, pvbus_xen_print },
92 : { "bhyve bhyve ", "bhyve" },
93 : { VMM_HV_SIGNATURE, "OpenBSD" },
94 : };
95 :
96 : struct bus_dma_tag pvbus_dma_tag = {
97 : NULL,
98 : _bus_dmamap_create,
99 : _bus_dmamap_destroy,
100 : _bus_dmamap_load,
101 : _bus_dmamap_load_mbuf,
102 : _bus_dmamap_load_uio,
103 : _bus_dmamap_load_raw,
104 : _bus_dmamap_unload,
105 : _bus_dmamap_sync,
106 : _bus_dmamem_alloc,
107 : _bus_dmamem_alloc_range,
108 : _bus_dmamem_free,
109 : _bus_dmamem_map,
110 : _bus_dmamem_unmap,
111 : _bus_dmamem_mmap,
112 : };
113 :
114 : struct pvbus_hv pvbus_hv[PVBUS_MAX];
115 : struct pvbus_softc *pvbus_softc;
116 :
117 : int
118 0 : pvbus_probe(void)
119 : {
120 : /* Must be set in identcpu */
121 0 : if (!has_hv_cpuid)
122 0 : return (0);
123 0 : return (1);
124 0 : }
125 :
126 : int
127 0 : pvbus_match(struct device *parent, void *match, void *aux)
128 : {
129 0 : const char **busname = (const char **)aux;
130 0 : return (strcmp(*busname, pvbus_cd.cd_name) == 0);
131 : }
132 :
133 : void
134 0 : pvbus_attach(struct device *parent, struct device *self, void *aux)
135 : {
136 0 : struct pvbus_softc *sc = (struct pvbus_softc *)self;
137 : int i, cnt;
138 :
139 0 : sc->pvbus_hv = pvbus_hv;
140 0 : pvbus_softc = sc;
141 :
142 0 : printf(":");
143 0 : for (i = 0, cnt = 0; i < PVBUS_MAX; i++) {
144 0 : if (pvbus_hv[i].hv_base == 0)
145 : continue;
146 0 : if (cnt++)
147 0 : printf(",");
148 0 : printf(" %s", pvbus_types[i].name);
149 0 : if (pvbus_types[i].print != NULL)
150 0 : (pvbus_types[i].print)(&pvbus_hv[i]);
151 : }
152 :
153 0 : printf("\n");
154 0 : config_search(pvbus_search, self, sc);
155 0 : }
156 :
157 : void
158 0 : pvbus_identify(void)
159 : {
160 : struct pvbus_hv *hv;
161 : uint32_t reg0, base;
162 0 : union {
163 : uint32_t regs[3];
164 : char str[CPUID_HV_SIGNATURE_STRLEN];
165 : } r;
166 : int i, cnt;
167 : const char *pv_name;
168 :
169 0 : for (base = CPUID_HV_SIGNATURE_START, cnt = 0;
170 0 : base < CPUID_HV_SIGNATURE_END;
171 0 : base += CPUID_HV_SIGNATURE_STEP) {
172 0 : CPUID(base, reg0, r.regs[0], r.regs[1], r.regs[2]);
173 0 : for (i = 0; i < 4; i++) {
174 : /*
175 : * Check if first 4 chars are printable ASCII as
176 : * minimal validity check
177 : */
178 0 : if (r.str[i] < 32 || r.str[i] > 126)
179 : goto out;
180 : }
181 :
182 0 : for (i = 0; i < PVBUS_MAX; i++) {
183 0 : if (pvbus_types[i].signature == NULL ||
184 0 : memcmp(pvbus_types[i].signature, r.str,
185 0 : CPUID_HV_SIGNATURE_STRLEN) != 0)
186 : continue;
187 0 : hv = &pvbus_hv[i];
188 0 : hv->hv_base = base;
189 0 : if (pvbus_types[i].init != NULL)
190 0 : (pvbus_types[i].init)(hv);
191 0 : if (hw_vendor == NULL) {
192 0 : pv_name = pvbus_types[i].name;
193 :
194 : /*
195 : * Use the HV name as a fallback if we didn't
196 : * get the vendor name from the firmware/BIOS.
197 : */
198 0 : if ((hw_vendor = malloc(strlen(pv_name) + 1,
199 0 : M_DEVBUF, M_NOWAIT)) != NULL) {
200 0 : strlcpy(hw_vendor, pv_name,
201 0 : strlen(pv_name) + 1);
202 0 : }
203 : }
204 0 : cnt++;
205 0 : }
206 : }
207 :
208 : out:
209 0 : if (cnt)
210 0 : has_hv_cpuid = 1;
211 0 : }
212 :
213 : void
214 0 : pvbus_init_cpu(void)
215 : {
216 : int i;
217 :
218 0 : for (i = 0; i < PVBUS_MAX; i++) {
219 0 : if (pvbus_hv[i].hv_base == 0)
220 : continue;
221 0 : if (pvbus_hv[i].hv_init_cpu != NULL)
222 0 : (pvbus_hv[i].hv_init_cpu)(&pvbus_hv[i]);
223 : }
224 0 : }
225 :
226 : int
227 0 : pvbus_activate(struct device *self, int act)
228 : {
229 : int rv = 0;
230 :
231 0 : switch (act) {
232 : case DVACT_SUSPEND:
233 0 : rv = config_activate_children(self, act);
234 0 : break;
235 : case DVACT_RESUME:
236 0 : rv = config_activate_children(self, act);
237 0 : break;
238 : case DVACT_POWERDOWN:
239 0 : rv = config_activate_children(self, act);
240 0 : break;
241 : default:
242 0 : rv = config_activate_children(self, act);
243 0 : break;
244 : }
245 :
246 0 : return (rv);
247 : }
248 :
249 : int
250 0 : pvbus_search(struct device *parent, void *arg, void *aux)
251 : {
252 0 : struct pvbus_softc *sc = (struct pvbus_softc *)aux;
253 0 : struct cfdata *cf = arg;
254 0 : struct pv_attach_args pva;
255 :
256 0 : pva.pva_busname = cf->cf_driver->cd_name;
257 0 : pva.pva_hv = sc->pvbus_hv;
258 0 : pva.pva_dmat = &pvbus_dma_tag;
259 :
260 0 : if (cf->cf_attach->ca_match(parent, cf, &pva) > 0)
261 0 : config_attach(parent, cf, &pva, pvbus_print);
262 :
263 0 : return (0);
264 0 : }
265 :
266 : int
267 0 : pvbus_print(void *aux, const char *pnp)
268 : {
269 0 : struct pv_attach_args *pva = aux;
270 0 : if (pnp)
271 0 : printf("%s at %s", pva->pva_busname, pnp);
272 0 : return (UNCONF);
273 : }
274 :
275 : void
276 0 : pvbus_shutdown(struct device *dev)
277 : {
278 0 : suspend_randomness();
279 :
280 0 : log(LOG_KERN | LOG_NOTICE, "Shutting down in response to request"
281 0 : " from %s host\n", dev->dv_xname);
282 0 : prsignal(initprocess, SIGUSR2);
283 0 : }
284 :
285 : void
286 0 : pvbus_reboot(struct device *dev)
287 : {
288 0 : suspend_randomness();
289 :
290 0 : log(LOG_KERN | LOG_NOTICE, "Rebooting in response to request"
291 0 : " from %s host\n", dev->dv_xname);
292 0 : prsignal(initprocess, SIGINT);
293 0 : }
294 :
295 : void
296 0 : pvbus_kvm(struct pvbus_hv *hv)
297 : {
298 : uint32_t regs[4];
299 :
300 0 : CPUID(hv->hv_base + CPUID_OFFSET_KVM_FEATURES,
301 : regs[0], regs[1], regs[2], regs[3]);
302 0 : hv->hv_features = regs[0];
303 0 : }
304 :
305 : void
306 0 : pvbus_hyperv(struct pvbus_hv *hv)
307 : {
308 : uint32_t regs[4];
309 :
310 0 : CPUID(hv->hv_base + CPUID_OFFSET_HYPERV_FEATURES,
311 : regs[0], regs[1], regs[2], regs[3]);
312 0 : hv->hv_features = regs[0];
313 :
314 0 : CPUID(hv->hv_base + CPUID_OFFSET_HYPERV_VERSION,
315 : regs[0], regs[1], regs[2], regs[3]);
316 0 : hv->hv_major = (regs[1] & HYPERV_VERSION_EBX_MAJOR_M) >>
317 : HYPERV_VERSION_EBX_MAJOR_S;
318 0 : hv->hv_minor = (regs[1] & HYPERV_VERSION_EBX_MINOR_M) >>
319 : HYPERV_VERSION_EBX_MINOR_S;
320 0 : }
321 :
322 : void
323 0 : pvbus_hyperv_print(struct pvbus_hv *hv)
324 : {
325 0 : printf(" %u.%u", hv->hv_major, hv->hv_minor);
326 0 : }
327 :
328 : void
329 0 : pvbus_xen(struct pvbus_hv *hv)
330 : {
331 : uint32_t regs[4];
332 :
333 0 : CPUID(hv->hv_base + CPUID_OFFSET_XEN_VERSION,
334 : regs[0], regs[1], regs[2], regs[3]);
335 0 : hv->hv_major = regs[0] >> XEN_VERSION_MAJOR_S;
336 0 : hv->hv_minor = regs[0] & XEN_VERSION_MINOR_M;
337 :
338 : /* x2apic is broken in Xen 4.2 or older */
339 0 : if ((hv->hv_major < 4) ||
340 0 : (hv->hv_major == 4 && hv->hv_minor < 3)) {
341 : /* Remove CPU flag for x2apic */
342 0 : cpu_ecxfeature &= ~CPUIDECX_X2APIC;
343 0 : }
344 0 : }
345 :
346 : void
347 0 : pvbus_xen_print(struct pvbus_hv *hv)
348 : {
349 0 : printf(" %u.%u", hv->hv_major, hv->hv_minor);
350 0 : }
351 :
352 : int
353 0 : pvbus_minor(struct pvbus_softc *sc, dev_t dev)
354 : {
355 : int hvid, cnt;
356 : struct pvbus_hv *hv;
357 :
358 0 : for (hvid = 0, cnt = 0; hvid < PVBUS_MAX; hvid++) {
359 0 : hv = &sc->pvbus_hv[hvid];
360 0 : if (hv->hv_base == 0)
361 : continue;
362 0 : if (minor(dev) == cnt++)
363 0 : return (hvid);
364 : }
365 :
366 0 : return (-1);
367 0 : }
368 :
369 : int
370 0 : pvbusopen(dev_t dev, int flags, int mode, struct proc *p)
371 : {
372 0 : if (pvbus_softc == NULL)
373 0 : return (ENODEV);
374 0 : if (pvbus_minor(pvbus_softc, dev) == -1)
375 0 : return (ENXIO);
376 0 : return (0);
377 0 : }
378 :
379 : int
380 0 : pvbusclose(dev_t dev, int flags, int mode, struct proc *p)
381 : {
382 0 : if (pvbus_softc == NULL)
383 0 : return (ENODEV);
384 0 : if (pvbus_minor(pvbus_softc, dev) == -1)
385 0 : return (ENXIO);
386 0 : return (0);
387 0 : }
388 :
389 : int
390 0 : pvbusgetstr(size_t srclen, const char *src, char **dstp)
391 : {
392 : int error = 0;
393 : char *dst;
394 :
395 : /*
396 : * Reject size that is too short or obviously too long:
397 : * - at least one byte for the nul terminator.
398 : * - PAGE_SIZE is an arbitrary value, but known pv backends seem
399 : * to have a hard (PAGE_SIZE - x) limit in their messaging.
400 : */
401 0 : if (srclen < 1)
402 0 : return (EINVAL);
403 0 : else if (srclen > PAGE_SIZE)
404 0 : return (ENAMETOOLONG);
405 :
406 0 : *dstp = dst = malloc(srclen + 1, M_TEMP|M_ZERO, M_WAITOK);
407 0 : if (src != NULL) {
408 0 : error = copyin(src, dst, srclen);
409 0 : dst[srclen] = '\0';
410 0 : }
411 :
412 0 : return (error);
413 0 : }
414 :
415 : int
416 0 : pvbusioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
417 : {
418 0 : struct pvbus_req *pvr = (struct pvbus_req *)data;
419 0 : struct pvbus_softc *sc = pvbus_softc;
420 0 : char *value = NULL, *key = NULL;
421 : const char *str = NULL;
422 : size_t valuelen = 0, keylen = 0, sz;
423 : int hvid, error = 0, op;
424 : struct pvbus_hv *hv;
425 :
426 0 : if (sc == NULL)
427 0 : return (ENODEV);
428 0 : if ((hvid = pvbus_minor(sc, dev)) == -1)
429 0 : return (ENXIO);
430 :
431 0 : switch (cmd) {
432 : case PVBUSIOC_KVWRITE:
433 0 : if ((flags & FWRITE) == 0)
434 0 : return (EPERM);
435 : case PVBUSIOC_KVREAD:
436 0 : hv = &sc->pvbus_hv[hvid];
437 0 : if (hv->hv_base == 0 || hv->hv_kvop == NULL)
438 0 : return (ENXIO);
439 : break;
440 : case PVBUSIOC_TYPE:
441 0 : str = pvbus_types[hvid].name;
442 0 : sz = strlen(str) + 1;
443 0 : if (sz > pvr->pvr_keylen)
444 0 : return (ENOMEM);
445 0 : error = copyout(str, pvr->pvr_key, sz);
446 0 : return (error);
447 : default:
448 0 : return (ENOTTY);
449 : }
450 :
451 : str = NULL;
452 : op = PVBUS_KVREAD;
453 :
454 0 : switch (cmd) {
455 : case PVBUSIOC_KVWRITE:
456 0 : str = pvr->pvr_value;
457 0 : op = PVBUS_KVWRITE;
458 :
459 : /* FALLTHROUGH */
460 : case PVBUSIOC_KVREAD:
461 0 : keylen = pvr->pvr_keylen;
462 0 : if ((error = pvbusgetstr(keylen, pvr->pvr_key, &key)) != 0)
463 : break;
464 :
465 0 : valuelen = pvr->pvr_valuelen;
466 0 : if ((error = pvbusgetstr(valuelen, str, &value)) != 0)
467 : break;
468 :
469 : /* Call driver-specific callback */
470 0 : if ((error = (hv->hv_kvop)(hv->hv_arg, op,
471 0 : key, value, valuelen)) != 0)
472 : break;
473 :
474 0 : sz = strlen(value) + 1;
475 0 : if ((error = copyout(value, pvr->pvr_value, sz)) != 0)
476 0 : break;
477 : break;
478 : default:
479 : error = ENOTTY;
480 0 : break;
481 : }
482 :
483 0 : free(key, M_TEMP, keylen + 1);
484 0 : free(value, M_TEMP, valuelen + 1);
485 :
486 0 : return (error);
487 0 : }
|