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 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 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 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 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 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 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 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 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 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 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 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 299 pvbus_hyperv_print(struct pvbus_hv *hv) 300 { 301 printf(" %u.%u", hv->hv_major, hv->hv_minor); 302 } 303 304 void 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 323 pvbus_xen_print(struct pvbus_hv *hv) 324 { 325 printf(" %u.%u", hv->hv_major, hv->hv_minor); 326 } 327 328 int 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 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 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 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 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