xref: /openbsd/sys/dev/pv/pvbus.c (revision 61df4475)
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