xref: /freebsd/sys/dev/proto/proto_core.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1 /*-
2  * Copyright (c) 2014, 2015, 2019 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/bus.h>
30 #include <sys/conf.h>
31 #include <sys/cons.h>
32 #include <sys/fcntl.h>
33 #include <sys/interrupt.h>
34 #include <sys/kdb.h>
35 #include <sys/kernel.h>
36 #include <sys/malloc.h>
37 #include <sys/mman.h>
38 #include <sys/proc.h>
39 #include <sys/queue.h>
40 #include <sys/reboot.h>
41 #include <machine/bus.h>
42 #include <sys/rman.h>
43 #include <sys/uio.h>
44 #include <machine/resource.h>
45 #include <machine/stdarg.h>
46 
47 #include <dev/pci/pcivar.h>
48 
49 #include <dev/proto/proto.h>
50 #include <dev/proto/proto_dev.h>
51 #include <dev/proto/proto_busdma.h>
52 
53 CTASSERT(SYS_RES_IRQ != PROTO_RES_UNUSED &&
54     SYS_RES_DRQ != PROTO_RES_UNUSED &&
55     SYS_RES_MEMORY != PROTO_RES_UNUSED &&
56     SYS_RES_IOPORT != PROTO_RES_UNUSED);
57 CTASSERT(SYS_RES_IRQ != PROTO_RES_PCICFG &&
58     SYS_RES_DRQ != PROTO_RES_PCICFG &&
59     SYS_RES_MEMORY != PROTO_RES_PCICFG &&
60     SYS_RES_IOPORT != PROTO_RES_PCICFG);
61 CTASSERT(SYS_RES_IRQ != PROTO_RES_BUSDMA &&
62     SYS_RES_DRQ != PROTO_RES_BUSDMA &&
63     SYS_RES_MEMORY != PROTO_RES_BUSDMA &&
64     SYS_RES_IOPORT != PROTO_RES_BUSDMA);
65 
66 char proto_driver_name[] = "proto";
67 
68 static d_open_t proto_open;
69 static d_close_t proto_close;
70 static d_read_t proto_read;
71 static d_write_t proto_write;
72 static d_ioctl_t proto_ioctl;
73 static d_mmap_t proto_mmap;
74 
75 struct cdevsw proto_devsw = {
76 	.d_version = D_VERSION,
77 	.d_flags = 0,
78 	.d_name = proto_driver_name,
79 	.d_open = proto_open,
80 	.d_close = proto_close,
81 	.d_read = proto_read,
82 	.d_write = proto_write,
83 	.d_ioctl = proto_ioctl,
84 	.d_mmap = proto_mmap,
85 };
86 
87 static MALLOC_DEFINE(M_PROTO, "PROTO", "PROTO driver");
88 
89 int
90 proto_add_resource(struct proto_softc *sc, int type, int rid,
91     struct resource *res)
92 {
93 	struct proto_res *r;
94 
95 	if (type == PROTO_RES_UNUSED)
96 		return (EINVAL);
97 	if (sc->sc_rescnt == PROTO_RES_MAX)
98 		return (ENOSPC);
99 
100 	r = sc->sc_res + sc->sc_rescnt++;
101 	r->r_type = type;
102 	r->r_rid = rid;
103 	r->r_d.res = res;
104 	return (0);
105 }
106 
107 #ifdef notyet
108 static int
109 proto_intr(void *arg)
110 {
111 	struct proto_softc *sc = arg;
112 
113 	/* XXX TODO */
114 	return (FILTER_HANDLED);
115 }
116 #endif
117 
118 int
119 proto_probe(device_t dev, const char *prefix, char ***devnamesp)
120 {
121 	char **devnames = *devnamesp;
122 	const char *dn, *ep, *ev;
123 	size_t pfxlen;
124 	int idx, names;
125 
126 	if (devnames == NULL) {
127 		pfxlen = strlen(prefix);
128 		names = 1;	/* NULL pointer */
129 		ev = kern_getenv("hw.proto.attach");
130 		if (ev != NULL) {
131 			dn = ev;
132 			while (*dn != '\0') {
133 				ep = dn;
134 				while (*ep != ',' && *ep != '\0')
135 					ep++;
136 				if ((ep - dn) > pfxlen &&
137 				    strncmp(dn, prefix, pfxlen) == 0)
138 					names++;
139 				dn = (*ep == ',') ? ep + 1 : ep;
140 			}
141 		}
142 		devnames = malloc(names * sizeof(caddr_t), M_DEVBUF,
143 		    M_WAITOK | M_ZERO);
144 		*devnamesp = devnames;
145 		if (ev != NULL) {
146 			dn = ev;
147 			idx = 0;
148 			while (*dn != '\0') {
149 				ep = dn;
150 				while (*ep != ',' && *ep != '\0')
151 					ep++;
152 				if ((ep - dn) > pfxlen &&
153 				    strncmp(dn, prefix, pfxlen) == 0) {
154 					devnames[idx] = malloc(ep - dn + 1,
155 					    M_DEVBUF, M_WAITOK | M_ZERO);
156 					memcpy(devnames[idx], dn, ep - dn);
157 					idx++;
158 				}
159 				dn = (*ep == ',') ? ep + 1 : ep;
160 			}
161 			freeenv(__DECONST(char *, ev));
162 		}
163 	}
164 
165 	dn = device_get_desc(dev);
166 	while (*devnames != NULL) {
167 		if (strcmp(dn, *devnames) == 0)
168 			return (BUS_PROBE_SPECIFIC);
169 		devnames++;
170 	}
171 	return (BUS_PROBE_HOOVER);
172 }
173 
174 int
175 proto_attach(device_t dev)
176 {
177 	struct proto_softc *sc;
178 	struct proto_res *r;
179 	u_int res;
180 
181 	sc = device_get_softc(dev);
182 	sc->sc_dev = dev;
183 	mtx_init(&sc->sc_mtx, "proto-softc", NULL, MTX_DEF);
184 
185 	for (res = 0; res < sc->sc_rescnt; res++) {
186 		r = sc->sc_res + res;
187 		switch (r->r_type) {
188 		case SYS_RES_IRQ:
189 			/* XXX TODO */
190 			break;
191 		case SYS_RES_DRQ:
192 			break;
193 		case SYS_RES_MEMORY:
194 		case SYS_RES_IOPORT:
195 			r->r_size = rman_get_size(r->r_d.res);
196 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600,
197 			    "proto/%s/%02x.%s", device_get_desc(dev), r->r_rid,
198 			    (r->r_type == SYS_RES_IOPORT) ? "io" : "mem");
199 			r->r_u.cdev->si_drv1 = sc;
200 			r->r_u.cdev->si_drv2 = r;
201 			break;
202 		case PROTO_RES_PCICFG:
203 			r->r_size = 4096;
204 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600,
205 			    "proto/%s/pcicfg", device_get_desc(dev));
206 			r->r_u.cdev->si_drv1 = sc;
207 			r->r_u.cdev->si_drv2 = r;
208 			break;
209 		case PROTO_RES_BUSDMA:
210 			r->r_d.busdma = proto_busdma_attach(sc);
211 			r->r_size = 0;	/* no read(2) nor write(2) */
212 			r->r_u.cdev = make_dev(&proto_devsw, res, 0, 0, 0600,
213 			    "proto/%s/busdma", device_get_desc(dev));
214 			r->r_u.cdev->si_drv1 = sc;
215 			r->r_u.cdev->si_drv2 = r;
216 			break;
217 		}
218 	}
219 	return (0);
220 }
221 
222 int
223 proto_detach(device_t dev)
224 {
225 	struct proto_softc *sc;
226 	struct proto_res *r;
227 	u_int res;
228 
229 	sc = device_get_softc(dev);
230 
231 	mtx_lock(&sc->sc_mtx);
232 	if (sc->sc_opencnt == 0)
233 		sc->sc_opencnt = -1;
234 	mtx_unlock(&sc->sc_mtx);
235 	if (sc->sc_opencnt > 0)
236 		return (EBUSY);
237 
238 	for (res = 0; res < sc->sc_rescnt; res++) {
239 		r = sc->sc_res + res;
240 
241 		switch (r->r_type) {
242 		case SYS_RES_IRQ:
243 			/* XXX TODO */
244 			bus_release_resource(dev, r->r_type, r->r_rid,
245 			    r->r_d.res);
246 			break;
247 		case SYS_RES_DRQ:
248 			bus_release_resource(dev, r->r_type, r->r_rid,
249 			    r->r_d.res);
250 			break;
251 		case SYS_RES_MEMORY:
252 		case SYS_RES_IOPORT:
253 			destroy_dev(r->r_u.cdev);
254 			bus_release_resource(dev, r->r_type, r->r_rid,
255 			    r->r_d.res);
256 			break;
257 		case PROTO_RES_PCICFG:
258 			destroy_dev(r->r_u.cdev);
259 			break;
260 		case PROTO_RES_BUSDMA:
261 			destroy_dev(r->r_u.cdev);
262 			proto_busdma_detach(sc, r->r_d.busdma);
263 			break;
264 		}
265 		r->r_type = PROTO_RES_UNUSED;
266 	}
267 	mtx_lock(&sc->sc_mtx);
268 	sc->sc_rescnt = 0;
269 	sc->sc_opencnt = 0;
270 	mtx_unlock(&sc->sc_mtx);
271 	mtx_destroy(&sc->sc_mtx);
272 	return (0);
273 }
274 
275 /*
276  * Device functions
277  */
278 
279 static int
280 proto_open(struct cdev *cdev, int oflags, int devtype, struct thread *td)
281 {
282 	struct proto_res *r;
283 	struct proto_softc *sc;
284 	int error;
285 
286 	sc = cdev->si_drv1;
287 	mtx_lock(&sc->sc_mtx);
288 	if (sc->sc_opencnt >= 0) {
289 		r = cdev->si_drv2;
290 		if (!r->r_opened) {
291 			r->r_opened = 1;
292 			sc->sc_opencnt++;
293 			error = 0;
294 		} else
295 			error = EBUSY;
296 	} else
297 		error = ENXIO;
298 	mtx_unlock(&sc->sc_mtx);
299 	return (error);
300 }
301 
302 static int
303 proto_close(struct cdev *cdev, int fflag, int devtype, struct thread *td)
304 {
305 	struct proto_res *r;
306 	struct proto_softc *sc;
307 	int error;
308 
309 	sc = cdev->si_drv1;
310 	mtx_lock(&sc->sc_mtx);
311 	if (sc->sc_opencnt > 0) {
312 		r = cdev->si_drv2;
313 		if (r->r_opened) {
314 			if (r->r_type == PROTO_RES_BUSDMA)
315 				proto_busdma_cleanup(sc, r->r_d.busdma);
316 			r->r_opened = 0;
317 			sc->sc_opencnt--;
318 			error = 0;
319 		} else
320 			error = ENXIO;
321 	} else
322 		error = ENXIO;
323 	mtx_unlock(&sc->sc_mtx);
324 	return (error);
325 }
326 
327 static int
328 proto_read(struct cdev *cdev, struct uio *uio, int ioflag)
329 {
330 	union {
331 		uint8_t	x1[8];
332 		uint16_t x2[4];
333 		uint32_t x4[2];
334 		uint64_t x8[1];
335 	} buf;
336 	struct proto_softc *sc;
337 	struct proto_res *r;
338 	device_t dev;
339 	off_t ofs;
340 	u_long width;
341 	int error;
342 
343 	sc = cdev->si_drv1;
344 	dev = sc->sc_dev;
345 	r = cdev->si_drv2;
346 
347 	width = uio->uio_resid;
348 	if (width < 1 || width > 8 || bitcount16(width) > 1)
349 		return (EIO);
350 	ofs = uio->uio_offset;
351 	if (ofs + width > r->r_size)
352 		return (EIO);
353 
354 	switch (width) {
355 	case 1:
356 		buf.x1[0] = (r->r_type == PROTO_RES_PCICFG) ?
357 		    pci_read_config(dev, ofs, 1) : bus_read_1(r->r_d.res, ofs);
358 		break;
359 	case 2:
360 		buf.x2[0] = (r->r_type == PROTO_RES_PCICFG) ?
361 		    pci_read_config(dev, ofs, 2) : bus_read_2(r->r_d.res, ofs);
362 		break;
363 	case 4:
364 		buf.x4[0] = (r->r_type == PROTO_RES_PCICFG) ?
365 		    pci_read_config(dev, ofs, 4) : bus_read_4(r->r_d.res, ofs);
366 		break;
367 #ifndef __i386__
368 	case 8:
369 		if (r->r_type == PROTO_RES_PCICFG)
370 			return (EINVAL);
371 		buf.x8[0] = bus_read_8(r->r_d.res, ofs);
372 		break;
373 #endif
374 	default:
375 		return (EIO);
376 	}
377 
378 	error = uiomove(&buf, width, uio);
379 	return (error);
380 }
381 
382 static int
383 proto_write(struct cdev *cdev, struct uio *uio, int ioflag)
384 {
385 	union {
386 		uint8_t	x1[8];
387 		uint16_t x2[4];
388 		uint32_t x4[2];
389 		uint64_t x8[1];
390 	} buf;
391 	struct proto_softc *sc;
392 	struct proto_res *r;
393 	device_t dev;
394 	off_t ofs;
395 	u_long width;
396 	int error;
397 
398 	sc = cdev->si_drv1;
399 	dev = sc->sc_dev;
400 	r = cdev->si_drv2;
401 
402 	width = uio->uio_resid;
403 	if (width < 1 || width > 8 || bitcount16(width) > 1)
404 		return (EIO);
405 	ofs = uio->uio_offset;
406 	if (ofs + width > r->r_size)
407 		return (EIO);
408 
409 	error = uiomove(&buf, width, uio);
410 	if (error)
411 		return (error);
412 
413 	switch (width) {
414 	case 1:
415 		if (r->r_type == PROTO_RES_PCICFG)
416 			pci_write_config(dev, ofs, buf.x1[0], 1);
417 		else
418 			bus_write_1(r->r_d.res, ofs, buf.x1[0]);
419 		break;
420 	case 2:
421 		if (r->r_type == PROTO_RES_PCICFG)
422 			pci_write_config(dev, ofs, buf.x2[0], 2);
423 		else
424 			bus_write_2(r->r_d.res, ofs, buf.x2[0]);
425 		break;
426 	case 4:
427 		if (r->r_type == PROTO_RES_PCICFG)
428 			pci_write_config(dev, ofs, buf.x4[0], 4);
429 		else
430 			bus_write_4(r->r_d.res, ofs, buf.x4[0]);
431 		break;
432 #ifndef __i386__
433 	case 8:
434 		if (r->r_type == PROTO_RES_PCICFG)
435 			return (EINVAL);
436 		bus_write_8(r->r_d.res, ofs, buf.x8[0]);
437 		break;
438 #endif
439 	default:
440 		return (EIO);
441 	}
442 
443 	return (0);
444 }
445 
446 static int
447 proto_ioctl(struct cdev *cdev, u_long cmd, caddr_t data, int fflag,
448     struct thread *td)
449 {
450 	struct proto_ioc_region *region;
451 	struct proto_ioc_busdma *busdma;
452 	struct proto_res *r;
453 	struct proto_softc *sc;
454 	int error;
455 
456 	sc = cdev->si_drv1;
457 	r = cdev->si_drv2;
458 
459 	error = 0;
460 	switch (cmd) {
461 	case PROTO_IOC_REGION:
462 		if (r->r_type == PROTO_RES_BUSDMA) {
463 			error = EINVAL;
464 			break;
465 		}
466 		region = (struct proto_ioc_region *)data;
467 		region->size = r->r_size;
468 		if (r->r_type == PROTO_RES_PCICFG)
469 			region->address = 0;
470 		else
471 			region->address = rman_get_start(r->r_d.res);
472 		break;
473 	case PROTO_IOC_BUSDMA:
474 		if (r->r_type != PROTO_RES_BUSDMA) {
475 			error = EINVAL;
476 			break;
477 		}
478 		busdma = (struct proto_ioc_busdma *)data;
479 		error = proto_busdma_ioctl(sc, r->r_d.busdma, busdma, td);
480 		break;
481 	default:
482 		error = ENOIOCTL;
483 		break;
484 	}
485 	return (error);
486 }
487 
488 static int
489 proto_mmap(struct cdev *cdev, vm_ooffset_t offset, vm_paddr_t *paddr,
490     int prot, vm_memattr_t *memattr)
491 {
492 	struct proto_res *r;
493 
494 	if (offset & PAGE_MASK)
495 		return (EINVAL);
496 	if (prot & PROT_EXEC)
497 		return (EACCES);
498 
499 	r = cdev->si_drv2;
500 
501 	switch (r->r_type) {
502 	case SYS_RES_MEMORY:
503 		if (offset >= r->r_size)
504 			return (EINVAL);
505 		*paddr = rman_get_start(r->r_d.res) + offset;
506 		*memattr = VM_MEMATTR_UNCACHEABLE;
507 		break;
508 	case PROTO_RES_BUSDMA:
509 		if (!proto_busdma_mmap_allowed(r->r_d.busdma, offset))
510 			return (EINVAL);
511 		*paddr = offset;
512 		break;
513 	default:
514 		return (ENXIO);
515 	}
516 	return (0);
517 }
518