xref: /libpciaccess/src/netbsd_pci.c (revision e1a0240a)
1 /*
2  * Copyright (c) 2008 Juan Romero Pardines
3  * Copyright (c) 2008 Mark Kettenis
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/ioctl.h>
20 #include <sys/mman.h>
21 #include <sys/types.h>
22 
23 #include <machine/sysarch.h>
24 #include <machine/mtrr.h>
25 
26 #include <dev/pci/pciio.h>
27 #include <dev/pci/pcireg.h>
28 #include <dev/pci/pcidevs.h>
29 
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 
37 
38 #include "pciaccess.h"
39 #include "pciaccess_private.h"
40 
41 static int pcifd;
42 
43 static int
pci_read(int bus,int dev,int func,uint32_t reg,uint32_t * val)44 pci_read(int bus, int dev, int func, uint32_t reg, uint32_t *val)
45 {
46 	struct pciio_bdf_cfgreg io;
47 	int err;
48 
49 	bzero(&io, sizeof(io));
50 	io.bus = bus;
51 	io.device = dev;
52 	io.function = func;
53 	io.cfgreg.reg = reg;
54 
55 	err = ioctl(pcifd, PCI_IOC_BDF_CFGREAD, &io);
56 	if (err)
57 		return (err);
58 
59 	*val = io.cfgreg.val;
60 
61 	return 0;
62 }
63 
64 static int
pci_write(int bus,int dev,int func,uint32_t reg,uint32_t val)65 pci_write(int bus, int dev, int func, uint32_t reg, uint32_t val)
66 {
67 	struct pciio_bdf_cfgreg io;
68 
69 	bzero(&io, sizeof(io));
70 	io.bus = bus;
71 	io.device = dev;
72 	io.function = func;
73 	io.cfgreg.reg = reg;
74 	io.cfgreg.val = val;
75 
76 	return ioctl(pcifd, PCI_IOC_BDF_CFGWRITE, &io);
77 }
78 
79 static int
pci_nfuncs(int bus,int dev)80 pci_nfuncs(int bus, int dev)
81 {
82 	uint32_t hdr;
83 
84 	if (pci_read(bus, dev, 0, PCI_BHLC_REG, &hdr) != 0)
85 		return -1;
86 
87 	return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1);
88 }
89 
90 static int
pci_device_netbsd_map_range(struct pci_device * dev,struct pci_device_mapping * map)91 pci_device_netbsd_map_range(struct pci_device *dev,
92     struct pci_device_mapping *map)
93 {
94 	struct mtrr mtrr;
95 	int fd, error, nmtrr, prot = PROT_READ;
96 
97 	if ((fd = open("/dev/mem", O_RDWR)) == -1)
98 		return errno;
99 
100 	if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE)
101 		prot |= PROT_WRITE;
102 
103 	map->memory = mmap(NULL, map->size, prot, MAP_SHARED,
104 	    fd, map->base);
105 	if (map->memory == MAP_FAILED)
106 		return errno;
107 
108 	/* No need to set an MTRR if it's the default mode. */
109 	if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
110 	    (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) {
111 		mtrr.base = map->base;
112 		mtrr.len = map->size;
113 		mtrr.flags = MTRR_VALID;
114 
115 		if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE)
116 			mtrr.type = MTRR_TYPE_WB;
117 		if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)
118 			mtrr.type = MTRR_TYPE_WC;
119 #ifdef __i386__
120 		error = i386_set_mtrr(&mtrr, &nmtrr);
121 #endif
122 #ifdef __amd64__
123 		error = x86_64_set_mtrr(&mtrr, &nmtrr);
124 #endif
125 		if (error) {
126 			close(fd);
127 			return errno;
128 		}
129 	}
130 
131 	close(fd);
132 
133 	return 0;
134 }
135 
136 static int
pci_device_netbsd_unmap_range(struct pci_device * dev,struct pci_device_mapping * map)137 pci_device_netbsd_unmap_range(struct pci_device *dev,
138     struct pci_device_mapping *map)
139 {
140 	struct mtrr mtrr;
141 	int nmtrr, error;
142 
143 	if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
144 	    (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) {
145 		mtrr.base = map->base;
146 		mtrr.len = map->size;
147 		mtrr.type = MTRR_TYPE_UC;
148 		mtrr.flags = 0; /* clear/set MTRR */
149 #ifdef __i386__
150 		error = i386_set_mtrr(&mtrr, &nmtrr);
151 #endif
152 #ifdef __amd64__
153 		error = x86_64_set_mtrr(&mtrr, &nmtrr);
154 #endif
155 		if (error)
156 			return errno;
157 	}
158 
159 	return pci_device_generic_unmap_range(dev, map);
160 }
161 
162 static int
pci_device_netbsd_read(struct pci_device * dev,void * data,pciaddr_t offset,pciaddr_t size,pciaddr_t * bytes_read)163 pci_device_netbsd_read(struct pci_device *dev, void *data,
164     pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read)
165 {
166 	struct pciio_bdf_cfgreg io;
167 
168 	io.bus = dev->bus;
169 	io.device = dev->dev;
170 	io.function = dev->func;
171 
172 	*bytes_read = 0;
173 	while (size > 0) {
174 		int toread = MIN(size, 4 - (offset & 0x3));
175 
176 		io.cfgreg.reg = (offset & ~0x3);
177 
178 		if (ioctl(pcifd, PCI_IOC_BDF_CFGREAD, &io) == -1)
179 			return errno;
180 
181 		io.cfgreg.val = htole32(io.cfgreg.val);
182 		io.cfgreg.val >>= ((offset & 0x3) * 8);
183 
184 		memcpy(data, &io.cfgreg.val, toread);
185 
186 		offset += toread;
187 		data = (char *)data + toread;
188 		size -= toread;
189 		*bytes_read += toread;
190 	}
191 
192 	return 0;
193 }
194 
195 static int
pci_device_netbsd_write(struct pci_device * dev,const void * data,pciaddr_t offset,pciaddr_t size,pciaddr_t * bytes_written)196 pci_device_netbsd_write(struct pci_device *dev, const void *data,
197     pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written)
198 {
199 	struct pciio_bdf_cfgreg io;
200 
201 	if ((offset % 4) != 0 || (size % 4) != 0)
202 		return EINVAL;
203 
204 	io.bus = dev->bus;
205 	io.device = dev->dev;
206 	io.function = dev->func;
207 
208 	*bytes_written = 0;
209 	while (size > 0) {
210 		io.cfgreg.reg = offset;
211 		memcpy(&io.cfgreg.val, data, 4);
212 
213 		if (ioctl(pcifd, PCI_IOC_BDF_CFGWRITE, &io) == -1)
214 			return errno;
215 
216 		offset += 4;
217 		data = (char *)data + 4;
218 		size -= 4;
219 		*bytes_written += 4;
220 	}
221 
222 	return 0;
223 }
224 
225 static void
pci_system_netbsd_destroy(void)226 pci_system_netbsd_destroy(void)
227 {
228 	close(pcifd);
229 	free(pci_sys);
230 	pci_sys = NULL;
231 }
232 
233 static int
pci_device_netbsd_probe(struct pci_device * device)234 pci_device_netbsd_probe(struct pci_device *device)
235 {
236 	struct pci_device_private *priv = (struct pci_device_private *)device;
237 	struct pci_mem_region *region;
238 	uint64_t reg64, size64;
239 	uint32_t bar, reg, size;
240 	int bus, dev, func, err;
241 
242 	bus = device->bus;
243 	dev = device->dev;
244 	func = device->func;
245 
246 	err = pci_read(bus, dev, func, PCI_BHLC_REG, &reg);
247 	if (err)
248 		return err;
249 
250 	priv->header_type = PCI_HDRTYPE_TYPE(reg);
251 	if (priv->header_type != 0)
252 		return 0;
253 
254 	region = device->regions;
255 	for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END;
256 	     bar += sizeof(uint32_t), region++) {
257 		err = pci_read(bus, dev, func, bar, &reg);
258 		if (err)
259 			return err;
260 
261 		/* Probe the size of the region. */
262 		err = pci_write(bus, dev, func, bar, ~0);
263 		if (err)
264 			return err;
265 		pci_read(bus, dev, func, bar, &size);
266 		pci_write(bus, dev, func, bar, reg);
267 
268 		if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) {
269 			region->is_IO = 1;
270 			region->base_addr = PCI_MAPREG_IO_ADDR(reg);
271 			region->size = PCI_MAPREG_IO_SIZE(size);
272 		} else {
273 			if (PCI_MAPREG_MEM_PREFETCHABLE(reg))
274 				region->is_prefetchable = 1;
275 			switch(PCI_MAPREG_MEM_TYPE(reg)) {
276 			case PCI_MAPREG_MEM_TYPE_32BIT:
277 			case PCI_MAPREG_MEM_TYPE_32BIT_1M:
278 				region->base_addr = PCI_MAPREG_MEM_ADDR(reg);
279 				region->size = PCI_MAPREG_MEM_SIZE(size);
280 				break;
281 			case PCI_MAPREG_MEM_TYPE_64BIT:
282 				region->is_64 = 1;
283 
284 				reg64 = reg;
285 				size64 = size;
286 
287 				bar += sizeof(uint32_t);
288 
289 				err = pci_read(bus, dev, func, bar, &reg);
290 				if (err)
291 					return err;
292 				reg64 |= (uint64_t)reg << 32;
293 
294 				err = pci_write(bus, dev, func, bar, ~0);
295 				if (err)
296 					return err;
297 				pci_read(bus, dev, func, bar, &size);
298 				pci_write(bus, dev, func, bar, reg64 >> 32);
299 				size64 |= (uint64_t)size << 32;
300 
301 				region->base_addr = PCI_MAPREG_MEM64_ADDR(reg64);
302 				region->size = PCI_MAPREG_MEM64_SIZE(size64);
303 				region++;
304 				break;
305 			}
306 		}
307 	}
308 
309 	return 0;
310 }
311 
312 static const struct pci_system_methods netbsd_pci_methods = {
313 	pci_system_netbsd_destroy,
314 	NULL,
315 	NULL,
316 	pci_device_netbsd_probe,
317 	pci_device_netbsd_map_range,
318 	pci_device_netbsd_unmap_range,
319 	pci_device_netbsd_read,
320 	pci_device_netbsd_write,
321 	pci_fill_capabilities_generic
322 };
323 
324 int
pci_system_netbsd_create(void)325 pci_system_netbsd_create(void)
326 {
327 	struct pci_device_private *device;
328 	int bus, dev, func, ndevs, nfuncs;
329 	uint32_t reg;
330 
331 	pcifd = open("/dev/pci0", O_RDWR);
332 	if (pcifd == -1)
333 		return ENXIO;
334 
335 	pci_sys = calloc(1, sizeof(struct pci_system));
336 	if (pci_sys == NULL) {
337 		close(pcifd);
338 		return ENOMEM;
339 	}
340 
341 	pci_sys->methods = &netbsd_pci_methods;
342 
343 	ndevs = 0;
344 	for (bus = 0; bus < 256; bus++) {
345 		for (dev = 0; dev < 32; dev++) {
346 			nfuncs = pci_nfuncs(bus, dev);
347 			for (func = 0; func < nfuncs; func++) {
348 				if (pci_read(bus, dev, func, PCI_ID_REG,
349 				    &reg) != 0)
350 					continue;
351 				if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
352 				    PCI_VENDOR(reg) == 0)
353 					continue;
354 
355 				ndevs++;
356 			}
357 		}
358 	}
359 
360 	pci_sys->num_devices = ndevs;
361 	pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
362 	if (pci_sys->devices == NULL) {
363 		free(pci_sys);
364 		close(pcifd);
365 		return ENOMEM;
366 	}
367 
368 	device = pci_sys->devices;
369 	for (bus = 0; bus < 256; bus++) {
370 		for (dev = 0; dev < 32; dev++) {
371 			nfuncs = pci_nfuncs(bus, dev);
372 			for (func = 0; func < nfuncs; func++) {
373 				if (pci_read(bus, dev, func, PCI_ID_REG,
374 				    &reg) != 0)
375 					continue;
376 				if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
377 				    PCI_VENDOR(reg) == 0)
378 					continue;
379 
380 				device->base.domain = 0;
381 				device->base.bus = bus;
382 				device->base.dev = dev;
383 				device->base.func = func;
384 				device->base.vendor_id = PCI_VENDOR(reg);
385 				device->base.device_id = PCI_PRODUCT(reg);
386 
387 				if (pci_read(bus, dev, func, PCI_CLASS_REG,
388 				    &reg) != 0)
389 					continue;
390 
391 				device->base.device_class =
392 				    PCI_INTERFACE(reg) | PCI_CLASS(reg) << 16 |
393 				    PCI_SUBCLASS(reg) << 8;
394 				device->base.revision = PCI_REVISION(reg);
395 
396 				if (pci_read(bus, dev, func, PCI_SUBSYS_ID_REG,
397 				    &reg) != 0)
398 					continue;
399 
400 				device->base.subvendor_id = PCI_VENDOR(reg);
401 				device->base.subdevice_id = PCI_PRODUCT(reg);
402 
403 				device++;
404 			}
405 		}
406 	}
407 
408 	return 0;
409 }
410