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