1 /* $OpenBSD: pvbus.c,v 1.29 2024/08/19 00:03:12 deraadt 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/malloc.h>
26 #include <sys/signalvar.h>
27 #include <sys/syslog.h>
28 #include <sys/proc.h>
29 #include <sys/ioctl.h>
30 #include <sys/fcntl.h>
31
32 #include <machine/specialreg.h>
33 #include <machine/cpu.h>
34 #include <machine/conf.h>
35 #include <machine/bus.h>
36 #include <machine/vmmvar.h>
37
38 #include <dev/pv/pvvar.h>
39 #include <dev/pv/pvreg.h>
40 #include <dev/pv/hypervreg.h>
41
42 #include "hyperv.h"
43
44 int has_hv_cpuid = 0;
45
46 extern void rdrand(void *);
47
48 int pvbus_match(struct device *, void *, void *);
49 void pvbus_attach(struct device *, struct device *, void *);
50 int pvbus_print(void *, const char *);
51 int pvbus_search(struct device *, void *, void *);
52
53 void pvbus_kvm(struct pvbus_hv *);
54 void pvbus_hyperv(struct pvbus_hv *);
55 void pvbus_hyperv_print(struct pvbus_hv *);
56 void pvbus_xen(struct pvbus_hv *);
57 void pvbus_xen_print(struct pvbus_hv *);
58
59 int pvbus_minor(struct pvbus_softc *, dev_t);
60 int pvbusgetstr(size_t, const char *, char **);
61
62 const struct cfattach pvbus_ca = {
63 sizeof(struct pvbus_softc),
64 pvbus_match,
65 pvbus_attach,
66 };
67
68 struct cfdriver pvbus_cd = {
69 NULL,
70 "pvbus",
71 DV_DULL
72 };
73
74 struct pvbus_type {
75 const char *signature;
76 const char *name;
77 void (*init)(struct pvbus_hv *);
78 void (*print)(struct pvbus_hv *);
79 } pvbus_types[PVBUS_MAX] = {
80 { "KVMKVMKVM\0\0\0", "KVM", pvbus_kvm },
81 { "Microsoft Hv", "Hyper-V", pvbus_hyperv, pvbus_hyperv_print },
82 { "VMwareVMware", "VMware" },
83 { "XenVMMXenVMM", "Xen", pvbus_xen, pvbus_xen_print },
84 { "bhyve bhyve ", "bhyve" },
85 { VMM_HV_SIGNATURE, "OpenBSD", pvbus_kvm },
86 };
87
88 struct bus_dma_tag pvbus_dma_tag = {
89 NULL,
90 _bus_dmamap_create,
91 _bus_dmamap_destroy,
92 _bus_dmamap_load,
93 _bus_dmamap_load_mbuf,
94 _bus_dmamap_load_uio,
95 _bus_dmamap_load_raw,
96 _bus_dmamap_unload,
97 _bus_dmamap_sync,
98 _bus_dmamem_alloc,
99 _bus_dmamem_alloc_range,
100 _bus_dmamem_free,
101 _bus_dmamem_map,
102 _bus_dmamem_unmap,
103 _bus_dmamem_mmap,
104 };
105
106 struct pvbus_hv pvbus_hv[PVBUS_MAX];
107 struct pvbus_softc *pvbus_softc;
108
109 int
pvbus_probe(void)110 pvbus_probe(void)
111 {
112 /* Must be set in identcpu */
113 if (!has_hv_cpuid)
114 return (0);
115 return (1);
116 }
117
118 int
pvbus_match(struct device * parent,void * match,void * aux)119 pvbus_match(struct device *parent, void *match, void *aux)
120 {
121 const char **busname = (const char **)aux;
122 return (strcmp(*busname, pvbus_cd.cd_name) == 0);
123 }
124
125 void
pvbus_attach(struct device * parent,struct device * self,void * aux)126 pvbus_attach(struct device *parent, struct device *self, void *aux)
127 {
128 struct pvbus_softc *sc = (struct pvbus_softc *)self;
129 int i, cnt;
130
131 sc->pvbus_hv = pvbus_hv;
132 pvbus_softc = sc;
133
134 printf(":");
135 for (i = 0, cnt = 0; i < PVBUS_MAX; i++) {
136 if (pvbus_hv[i].hv_base == 0)
137 continue;
138 if (cnt++)
139 printf(",");
140 printf(" %s", pvbus_types[i].name);
141 if (pvbus_types[i].print != NULL)
142 (pvbus_types[i].print)(&pvbus_hv[i]);
143 }
144
145 printf("\n");
146 config_search(pvbus_search, self, sc);
147 }
148
149 void
pvbus_identify(void)150 pvbus_identify(void)
151 {
152 struct pvbus_hv *hv;
153 uint32_t reg0, base;
154 union {
155 uint32_t regs[3];
156 char str[CPUID_HV_SIGNATURE_STRLEN];
157 } r;
158 int i, cnt;
159 const char *pv_name;
160
161 for (base = CPUID_HV_SIGNATURE_START, cnt = 0;
162 base < CPUID_HV_SIGNATURE_END;
163 base += CPUID_HV_SIGNATURE_STEP) {
164 CPUID(base, reg0, r.regs[0], r.regs[1], r.regs[2]);
165 for (i = 0; i < 4; i++) {
166 /*
167 * Check if first 4 chars are printable ASCII as
168 * minimal validity check
169 */
170 if (r.str[i] < 32 || r.str[i] > 126)
171 goto out;
172 }
173
174 for (i = 0; i < PVBUS_MAX; i++) {
175 if (pvbus_types[i].signature == NULL ||
176 memcmp(pvbus_types[i].signature, r.str,
177 CPUID_HV_SIGNATURE_STRLEN) != 0)
178 continue;
179 hv = &pvbus_hv[i];
180 hv->hv_base = base;
181 if (pvbus_types[i].init != NULL)
182 (pvbus_types[i].init)(hv);
183 if (hw_vendor == NULL) {
184 pv_name = pvbus_types[i].name;
185
186 /*
187 * Use the HV name as a fallback if we didn't
188 * get the vendor name from the firmware/BIOS.
189 */
190 if ((hw_vendor = malloc(strlen(pv_name) + 1,
191 M_DEVBUF, M_NOWAIT)) != NULL) {
192 strlcpy(hw_vendor, pv_name,
193 strlen(pv_name) + 1);
194 }
195 }
196 cnt++;
197 }
198 }
199
200 out:
201 if (cnt)
202 has_hv_cpuid = 1;
203 }
204
205 void
pvbus_init_cpu(void)206 pvbus_init_cpu(void)
207 {
208 int i;
209
210 for (i = 0; i < PVBUS_MAX; i++) {
211 if (pvbus_hv[i].hv_base == 0)
212 continue;
213 if (pvbus_hv[i].hv_init_cpu != NULL)
214 (pvbus_hv[i].hv_init_cpu)(&pvbus_hv[i]);
215 }
216 }
217
218 int
pvbus_search(struct device * parent,void * arg,void * aux)219 pvbus_search(struct device *parent, void *arg, void *aux)
220 {
221 struct pvbus_softc *sc = (struct pvbus_softc *)aux;
222 struct cfdata *cf = arg;
223 struct pv_attach_args pva;
224
225 pva.pva_busname = cf->cf_driver->cd_name;
226 pva.pva_hv = sc->pvbus_hv;
227 pva.pva_dmat = &pvbus_dma_tag;
228
229 if (cf->cf_attach->ca_match(parent, cf, &pva) > 0)
230 config_attach(parent, cf, &pva, pvbus_print);
231
232 return (0);
233 }
234
235 int
pvbus_print(void * aux,const char * pnp)236 pvbus_print(void *aux, const char *pnp)
237 {
238 struct pv_attach_args *pva = aux;
239 if (pnp)
240 printf("%s at %s", pva->pva_busname, pnp);
241 return (UNCONF);
242 }
243
244 void
pvbus_shutdown(struct device * dev)245 pvbus_shutdown(struct device *dev)
246 {
247 suspend_randomness();
248
249 log(LOG_KERN | LOG_NOTICE, "Shutting down in response to request"
250 " from %s host\n", dev->dv_xname);
251 prsignal(initprocess, SIGUSR2);
252 }
253
254 void
pvbus_reboot(struct device * dev)255 pvbus_reboot(struct device *dev)
256 {
257 suspend_randomness();
258
259 log(LOG_KERN | LOG_NOTICE, "Rebooting in response to request"
260 " from %s host\n", dev->dv_xname);
261 prsignal(initprocess, SIGINT);
262 }
263
264 void
pvbus_kvm(struct pvbus_hv * hv)265 pvbus_kvm(struct pvbus_hv *hv)
266 {
267 uint32_t regs[4];
268
269 CPUID(hv->hv_base + CPUID_OFFSET_KVM_FEATURES,
270 regs[0], regs[1], regs[2], regs[3]);
271 hv->hv_features = regs[0];
272 }
273
274 extern void hv_delay(int usecs);
275
276 void
pvbus_hyperv(struct pvbus_hv * hv)277 pvbus_hyperv(struct pvbus_hv *hv)
278 {
279 uint32_t regs[4];
280
281 CPUID(hv->hv_base + CPUID_OFFSET_HYPERV_FEATURES,
282 regs[0], regs[1], regs[2], regs[3]);
283 hv->hv_features = regs[0];
284
285 CPUID(hv->hv_base + CPUID_OFFSET_HYPERV_VERSION,
286 regs[0], regs[1], regs[2], regs[3]);
287 hv->hv_major = (regs[1] & HYPERV_VERSION_EBX_MAJOR_M) >>
288 HYPERV_VERSION_EBX_MAJOR_S;
289 hv->hv_minor = (regs[1] & HYPERV_VERSION_EBX_MINOR_M) >>
290 HYPERV_VERSION_EBX_MINOR_S;
291
292 #if NHYPERV > 0
293 if (hv->hv_features & CPUID_HV_MSR_TIME_REFCNT)
294 delay_init(hv_delay, 4000);
295 #endif
296 }
297
298 void
pvbus_hyperv_print(struct pvbus_hv * hv)299 pvbus_hyperv_print(struct pvbus_hv *hv)
300 {
301 printf(" %u.%u", hv->hv_major, hv->hv_minor);
302 }
303
304 void
pvbus_xen(struct pvbus_hv * hv)305 pvbus_xen(struct pvbus_hv *hv)
306 {
307 uint32_t regs[4];
308
309 CPUID(hv->hv_base + CPUID_OFFSET_XEN_VERSION,
310 regs[0], regs[1], regs[2], regs[3]);
311 hv->hv_major = regs[0] >> XEN_VERSION_MAJOR_S;
312 hv->hv_minor = regs[0] & XEN_VERSION_MINOR_M;
313
314 /* x2apic is broken in Xen 4.2 or older */
315 if ((hv->hv_major < 4) ||
316 (hv->hv_major == 4 && hv->hv_minor < 3)) {
317 /* Remove CPU flag for x2apic */
318 cpu_ecxfeature &= ~CPUIDECX_X2APIC;
319 }
320 }
321
322 void
pvbus_xen_print(struct pvbus_hv * hv)323 pvbus_xen_print(struct pvbus_hv *hv)
324 {
325 printf(" %u.%u", hv->hv_major, hv->hv_minor);
326 }
327
328 int
pvbus_minor(struct pvbus_softc * sc,dev_t dev)329 pvbus_minor(struct pvbus_softc *sc, dev_t dev)
330 {
331 int hvid, cnt;
332 struct pvbus_hv *hv;
333
334 for (hvid = 0, cnt = 0; hvid < PVBUS_MAX; hvid++) {
335 hv = &sc->pvbus_hv[hvid];
336 if (hv->hv_base == 0)
337 continue;
338 if (minor(dev) == cnt++)
339 return (hvid);
340 }
341
342 return (-1);
343 }
344
345 int
pvbusopen(dev_t dev,int flags,int mode,struct proc * p)346 pvbusopen(dev_t dev, int flags, int mode, struct proc *p)
347 {
348 if (pvbus_softc == NULL)
349 return (ENODEV);
350 if (pvbus_minor(pvbus_softc, dev) == -1)
351 return (ENXIO);
352 return (0);
353 }
354
355 int
pvbusclose(dev_t dev,int flags,int mode,struct proc * p)356 pvbusclose(dev_t dev, int flags, int mode, struct proc *p)
357 {
358 if (pvbus_softc == NULL)
359 return (ENODEV);
360 if (pvbus_minor(pvbus_softc, dev) == -1)
361 return (ENXIO);
362 return (0);
363 }
364
365 int
pvbusgetstr(size_t srclen,const char * src,char ** dstp)366 pvbusgetstr(size_t srclen, const char *src, char **dstp)
367 {
368 int error = 0;
369 char *dst;
370
371 /*
372 * Reject size that is too short or obviously too long:
373 * - Known pv backends other than vmware have a hard limit smaller than
374 * PVBUS_KVOP_MAXSIZE in their messaging. vmware has a software
375 * limit at 1MB, but current open-vm-tools has a limit at 64KB
376 * (=PVBUS_KVOP_MAXSIZE).
377 */
378 if (srclen < 1)
379 return (EINVAL);
380 else if (srclen > PVBUS_KVOP_MAXSIZE)
381 return (ENAMETOOLONG);
382
383 *dstp = dst = malloc(srclen + 1, M_TEMP, M_WAITOK | M_ZERO);
384 if (src != NULL) {
385 error = copyin(src, dst, srclen);
386 dst[srclen] = '\0';
387 }
388
389 return (error);
390 }
391
392 int
pvbusioctl(dev_t dev,u_long cmd,caddr_t data,int flags,struct proc * p)393 pvbusioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
394 {
395 struct pvbus_req *pvr = (struct pvbus_req *)data;
396 struct pvbus_softc *sc = pvbus_softc;
397 char *value = NULL, *key = NULL;
398 const char *str = NULL;
399 size_t valuelen = 0, keylen = 0, sz;
400 int hvid, error = 0, op;
401 struct pvbus_hv *hv;
402
403 if (sc == NULL)
404 return (ENODEV);
405 if ((hvid = pvbus_minor(sc, dev)) == -1)
406 return (ENXIO);
407
408 switch (cmd) {
409 case PVBUSIOC_KVWRITE:
410 if ((flags & FWRITE) == 0)
411 return (EPERM);
412 case PVBUSIOC_KVREAD:
413 hv = &sc->pvbus_hv[hvid];
414 if (hv->hv_base == 0 || hv->hv_kvop == NULL)
415 return (ENXIO);
416 break;
417 case PVBUSIOC_TYPE:
418 str = pvbus_types[hvid].name;
419 sz = strlen(str) + 1;
420 if (sz > pvr->pvr_keylen)
421 return (ENOMEM);
422 error = copyout(str, pvr->pvr_key, sz);
423 return (error);
424 default:
425 return (ENOTTY);
426 }
427
428 str = NULL;
429 op = PVBUS_KVREAD;
430
431 switch (cmd) {
432 case PVBUSIOC_KVWRITE:
433 str = pvr->pvr_value;
434 op = PVBUS_KVWRITE;
435
436 /* FALLTHROUGH */
437 case PVBUSIOC_KVREAD:
438 keylen = pvr->pvr_keylen;
439 if ((error = pvbusgetstr(keylen, pvr->pvr_key, &key)) != 0)
440 break;
441
442 valuelen = pvr->pvr_valuelen;
443 if ((error = pvbusgetstr(valuelen, str, &value)) != 0)
444 break;
445
446 /* Call driver-specific callback */
447 if ((error = (hv->hv_kvop)(hv->hv_arg, op,
448 key, value, valuelen)) != 0)
449 break;
450
451 sz = strlen(value) + 1;
452 if ((error = copyout(value, pvr->pvr_value, sz)) != 0)
453 break;
454 break;
455 default:
456 error = ENOTTY;
457 break;
458 }
459
460 free(key, M_TEMP, keylen + 1);
461 free(value, M_TEMP, valuelen + 1);
462
463 return (error);
464 }
465