netbsd_pci.c revision 22df1cdf
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#ifdef HAVE_MTRR
24#include <machine/sysarch.h>
25#include <machine/mtrr.h>
26#define netbsd_set_mtrr(mr, num)	_X86_SYSARCH_L(set_mtrr)(mr, num)
27#endif
28
29#include <dev/pci/pcidevs.h>
30#include <dev/pci/pciio.h>
31#include <dev/pci/pcireg.h>
32
33#include <errno.h>
34#include <fcntl.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <unistd.h>
39
40
41#include <pci.h>
42
43#include "pciaccess.h"
44#include "pciaccess_private.h"
45
46static int pcifd;
47
48static int
49pci_read(int bus, int dev, int func, uint32_t reg, uint32_t *val)
50{
51	uint32_t rval;
52
53	if (pcibus_conf_read(pcifd, bus, dev, func, reg, &rval) == -1)
54		return (-1);
55
56	*val = rval;
57
58	return 0;
59}
60
61static int
62pci_write(int bus, int dev, int func, uint32_t reg, uint32_t val)
63{
64	return pcibus_conf_write(pcifd, bus, dev, func, reg, val);
65}
66
67static int
68pci_nfuncs(int bus, int dev)
69{
70	uint32_t hdr;
71
72	if (pci_read(bus, dev, 0, PCI_BHLC_REG, &hdr) != 0)
73		return -1;
74
75	return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1);
76}
77
78static int
79pci_device_netbsd_map_range(struct pci_device *dev,
80    struct pci_device_mapping *map)
81{
82#ifdef HAVE_MTRR
83	struct mtrr m;
84	int n = 1;
85#endif
86	int prot, fd, ret = 0;
87
88	prot = PROT_READ;
89
90	if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE)
91		prot |= PROT_WRITE;
92
93	fd = open("/dev/mem", O_RDWR);
94	if (fd == -1)
95		return errno;
96	map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base);
97	if (map->memory == MAP_FAILED)
98		return errno;
99
100#ifdef HAVE_MTRR
101	memset(&m, 0, sizeof(m));
102
103	/* No need to set an MTRR if it's the default mode. */
104	if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
105	    (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) {
106		m.base = base;
107		m.flags = MTRR_VALID | MTRR_PRIVATE;
108		m.len = size;
109		m.owner = getpid();
110		if (map->flags & PCI_DEV_MAP_FLAG_CACHEABLE)
111			m.type = MTRR_TYPE_WB;
112		if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)
113			m.type = MTRR_TYPE_WC;
114
115		if ((netbsd_set_mtrr(&m, &n)) == -1)
116			ret = errno;
117	}
118#endif
119
120	close(fd);
121
122	return ret;
123}
124
125static int
126pci_device_netbsd_unmap_range(struct pci_device *dev,
127    struct pci_device_mapping *map)
128{
129#ifdef HAVE_MTRR
130	struct mtrr m;
131	int n = 1;
132
133	memset(&m, 0, sizeof(m));
134
135	if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
136	    (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) {
137		m.base = map->base;
138		m.flags = 0;
139		m.len = size;
140		m.type = MTRR_TYPE_UC;
141		(void)netbsd_set_mtrr(&m, &n);
142	}
143#endif
144
145	return pci_device_generic_unmap_range(dev, map);
146}
147
148static int
149pci_device_netbsd_read(struct pci_device *dev, void *data,
150    pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read)
151{
152	u_int reg, rval;
153
154	*bytes_read = 0;
155	while (size > 0) {
156		int toread = MIN(size, 4 - (offset & 0x3));
157
158		reg = (offset & ~0x3);
159
160		if ((pcibus_conf_read(pcifd, dev->bus, dev->dev, dev->func,
161		    reg, &rval)) == -1)
162			return errno;
163
164		rval = htole32(rval);
165		rval >>= ((offset & 0x3) * 8);
166
167		memcpy(data, &rval, toread);
168
169		offset += toread;
170		data = (char *)data + toread;
171		size -= toread;
172		*bytes_read += toread;
173	}
174
175	return 0;
176}
177
178static int
179pci_device_netbsd_write(struct pci_device *dev, const void *data,
180    pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written)
181{
182	u_int reg, val;
183
184	if ((offset % 4) != 0 || (size % 4) != 0)
185		return EINVAL;
186
187	*bytes_written = 0;
188	while (size > 0) {
189		reg = offset;
190		memcpy(&val, data, 4);
191
192		if ((pcibus_conf_write(pcifd, dev->bus, dev->dev, dev->func,
193		   reg, val)) == -1)
194			return errno;
195
196		offset += 4;
197		data = (char *)data + 4;
198		size -= 4;
199		*bytes_written += 4;
200	}
201
202	return 0;
203}
204
205static void
206pci_system_netbsd_destroy(void)
207{
208	close(pcifd);
209	free(pci_sys);
210	pci_sys = NULL;
211}
212
213static int
214pci_device_netbsd_probe(struct pci_device *device)
215{
216	struct pci_device_private *priv = (struct pci_device_private *)device;
217	struct pci_mem_region *region;
218	uint64_t reg64, size64;
219	uint32_t bar, reg, size;
220	int bus, dev, func, err;
221
222	bus = device->bus;
223	dev = device->dev;
224	func = device->func;
225
226	err = pci_read(bus, dev, func, PCI_BHLC_REG, &reg);
227	if (err)
228		return err;
229
230	priv->header_type = PCI_HDRTYPE_TYPE(reg);
231	if (priv->header_type != 0)
232		return 0;
233
234	region = device->regions;
235	for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END;
236	     bar += sizeof(uint32_t), region++) {
237		err = pci_read(bus, dev, func, bar, &reg);
238		if (err)
239			return err;
240
241		/* Probe the size of the region. */
242		err = pci_write(bus, dev, func, bar, ~0);
243		if (err)
244			return err;
245		pci_read(bus, dev, func, bar, &size);
246		pci_write(bus, dev, func, bar, reg);
247
248		if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) {
249			region->is_IO = 1;
250			region->base_addr = PCI_MAPREG_IO_ADDR(reg);
251			region->size = PCI_MAPREG_IO_SIZE(size);
252		} else {
253			if (PCI_MAPREG_MEM_PREFETCHABLE(reg))
254				region->is_prefetchable = 1;
255			switch(PCI_MAPREG_MEM_TYPE(reg)) {
256			case PCI_MAPREG_MEM_TYPE_32BIT:
257			case PCI_MAPREG_MEM_TYPE_32BIT_1M:
258				region->base_addr = PCI_MAPREG_MEM_ADDR(reg);
259				region->size = PCI_MAPREG_MEM_SIZE(size);
260				break;
261			case PCI_MAPREG_MEM_TYPE_64BIT:
262				region->is_64 = 1;
263
264				reg64 = reg;
265				size64 = size;
266
267				bar += sizeof(uint32_t);
268
269				err = pci_read(bus, dev, func, bar, &reg);
270				if (err)
271					return err;
272				reg64 |= (uint64_t)reg << 32;
273
274				err = pci_write(bus, dev, func, bar, ~0);
275				if (err)
276					return err;
277				pci_read(bus, dev, func, bar, &size);
278				pci_write(bus, dev, func, bar, reg64 >> 32);
279				size64 |= (uint64_t)size << 32;
280
281				region->base_addr = PCI_MAPREG_MEM64_ADDR(reg64);
282				region->size = PCI_MAPREG_MEM64_SIZE(size64);
283				region++;
284				break;
285			}
286		}
287	}
288
289	return 0;
290}
291
292static const struct pci_system_methods netbsd_pci_methods = {
293	pci_system_netbsd_destroy,
294	NULL,
295	NULL,
296	pci_device_netbsd_probe,
297	pci_device_netbsd_map_range,
298	pci_device_netbsd_unmap_range,
299	pci_device_netbsd_read,
300	pci_device_netbsd_write,
301	pci_fill_capabilities_generic
302};
303
304int
305pci_system_netbsd_create(void)
306{
307	struct pci_device_private *device;
308	int bus, dev, func, ndevs, nfuncs;
309	uint32_t reg;
310
311	pcifd = open("/dev/pci0", O_RDWR);
312	if (pcifd == -1)
313		return ENXIO;
314
315	pci_sys = calloc(1, sizeof(struct pci_system));
316	if (pci_sys == NULL) {
317		close(pcifd);
318		return ENOMEM;
319	}
320
321	pci_sys->methods = &netbsd_pci_methods;
322
323	ndevs = 0;
324	for (bus = 0; bus < 256; bus++) {
325		for (dev = 0; dev < 32; dev++) {
326			nfuncs = pci_nfuncs(bus, dev);
327			for (func = 0; func < nfuncs; func++) {
328				if (pci_read(bus, dev, func, PCI_ID_REG,
329				    &reg) != 0)
330					continue;
331				if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
332				    PCI_VENDOR(reg) == 0)
333					continue;
334
335				ndevs++;
336			}
337		}
338	}
339
340	pci_sys->num_devices = ndevs;
341	pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
342	if (pci_sys->devices == NULL) {
343		free(pci_sys);
344		close(pcifd);
345		return ENOMEM;
346	}
347
348	device = pci_sys->devices;
349	for (bus = 0; bus < 256; bus++) {
350		for (dev = 0; dev < 32; dev++) {
351			nfuncs = pci_nfuncs(bus, dev);
352			for (func = 0; func < nfuncs; func++) {
353				if (pci_read(bus, dev, func, PCI_ID_REG,
354				    &reg) != 0)
355					continue;
356				if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
357				    PCI_VENDOR(reg) == 0)
358					continue;
359
360				device->base.domain = 0;
361				device->base.bus = bus;
362				device->base.dev = dev;
363				device->base.func = func;
364				device->base.vendor_id = PCI_VENDOR(reg);
365				device->base.device_id = PCI_PRODUCT(reg);
366
367				if (pci_read(bus, dev, func, PCI_CLASS_REG,
368				    &reg) != 0)
369					continue;
370
371				device->base.device_class =
372				    PCI_INTERFACE(reg) | PCI_CLASS(reg) << 16 |
373				    PCI_SUBCLASS(reg) << 8;
374				device->base.revision = PCI_REVISION(reg);
375
376				if (pci_read(bus, dev, func, PCI_SUBSYS_ID_REG,
377				    &reg) != 0)
378					continue;
379
380				device->base.subvendor_id = PCI_VENDOR(reg);
381				device->base.subdevice_id = PCI_PRODUCT(reg);
382
383				device++;
384			}
385		}
386	}
387
388	return 0;
389}
390