netbsd_pci.c revision 9e884b7e
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
46#define PCIR_COMMAND    0x04
47#define PCIM_CMD_PORTEN         0x0001
48#define PCIM_CMD_MEMEN          0x0002
49#define PCIR_BIOS	0x30
50#define PCIM_BIOS_ENABLE	0x01
51#define PCIM_BIOS_ADDR_MASK	0xfffff800
52
53static int pcifd;
54
55static int
56pci_read(int bus, int dev, int func, uint32_t reg, uint32_t *val)
57{
58	uint32_t rval;
59
60	if (pcibus_conf_read(pcifd, (unsigned int)bus, (unsigned int)dev,
61	    (unsigned int)func, reg, &rval) == -1)
62		return (-1);
63
64	*val = rval;
65
66	return 0;
67}
68
69static int
70pci_write(int bus, int dev, int func, uint32_t reg, uint32_t val)
71{
72	return pcibus_conf_write(pcifd, (unsigned int)bus, (unsigned int)dev,
73	    (unsigned int)func, reg, val);
74}
75
76static int
77pci_nfuncs(int bus, int dev)
78{
79	uint32_t hdr;
80
81	if (pci_read(bus, dev, 0, PCI_BHLC_REG, &hdr) != 0)
82		return -1;
83
84	return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1);
85}
86
87/*ARGSUSED*/
88static int
89pci_device_netbsd_map_range(struct pci_device *dev,
90    struct pci_device_mapping *map)
91{
92#ifdef HAVE_MTRR
93	struct mtrr m;
94	int n = 1;
95#endif
96	int prot, fd, ret = 0;
97
98	prot = PROT_READ;
99
100	if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE)
101		prot |= PROT_WRITE;
102
103	fd = open("/dev/mem", O_RDWR);
104	if (fd == -1)
105		return errno;
106	map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd,
107	    (off_t)map->base);
108	if (map->memory == MAP_FAILED)
109		return errno;
110
111#ifdef HAVE_MTRR
112	memset(&m, 0, sizeof(m));
113
114	/* No need to set an MTRR if it's the default mode. */
115	if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
116	    (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) {
117		m.base = base;
118		m.flags = MTRR_VALID | MTRR_PRIVATE;
119		m.len = size;
120		m.owner = getpid();
121		if (map->flags & PCI_DEV_MAP_FLAG_CACHEABLE)
122			m.type = MTRR_TYPE_WB;
123		if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)
124			m.type = MTRR_TYPE_WC;
125
126		if ((netbsd_set_mtrr(&m, &n)) == -1)
127			ret = errno;
128	}
129#endif
130
131	close(fd);
132
133	return ret;
134}
135
136static int
137pci_device_netbsd_unmap_range(struct pci_device *dev,
138    struct pci_device_mapping *map)
139{
140#ifdef HAVE_MTRR
141	struct mtrr m;
142	int n = 1;
143
144	memset(&m, 0, sizeof(m));
145
146	if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
147	    (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) {
148		m.base = map->base;
149		m.flags = 0;
150		m.len = size;
151		m.type = MTRR_TYPE_UC;
152		(void)netbsd_set_mtrr(&m, &n);
153	}
154#endif
155
156	return pci_device_generic_unmap_range(dev, map);
157}
158
159static int
160pci_device_netbsd_read(struct pci_device *dev, void *data,
161    pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read)
162{
163	u_int reg, rval;
164
165	*bytes_read = 0;
166	while (size > 0) {
167		size_t toread = MIN(size, 4 - (offset & 0x3));
168
169		reg = (u_int)(offset & ~0x3);
170
171		if ((pcibus_conf_read(pcifd, (unsigned int)dev->bus,
172		    (unsigned int)dev->dev, (unsigned int)dev->func,
173		    reg, &rval)) == -1)
174			return errno;
175
176		rval = htole32(rval);
177		rval >>= ((offset & 0x3) * 8);
178
179		memcpy(data, &rval, toread);
180
181		offset += toread;
182		data = (char *)data + toread;
183		size -= toread;
184		*bytes_read += toread;
185	}
186
187	return 0;
188}
189
190static int
191pci_device_netbsd_write(struct pci_device *dev, const void *data,
192    pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written)
193{
194	u_int reg, val;
195
196	if ((offset % 4) != 0 || (size % 4) != 0)
197		return EINVAL;
198
199	*bytes_written = 0;
200	while (size > 0) {
201		reg = (u_int)offset;
202		memcpy(&val, data, 4);
203
204		if ((pcibus_conf_write(pcifd, (unsigned int)dev->bus,
205		    (unsigned int)dev->dev, (unsigned int)dev->func,
206		   reg, val)) == -1)
207			return errno;
208
209		offset += 4;
210		data = (const char *)data + 4;
211		size -= 4;
212		*bytes_written += 4;
213	}
214
215	return 0;
216}
217
218static void
219pci_system_netbsd_destroy(void)
220{
221	close(pcifd);
222	free(pci_sys);
223	pci_sys = NULL;
224}
225
226static int
227pci_device_netbsd_probe(struct pci_device *device)
228{
229	struct pci_device_private *priv =
230	    (struct pci_device_private *)(void *)device;
231	struct pci_mem_region *region;
232	uint64_t reg64, size64;
233	uint32_t bar, reg, size;
234	int bus, dev, func, err;
235
236	bus = device->bus;
237	dev = device->dev;
238	func = device->func;
239
240	err = pci_read(bus, dev, func, PCI_BHLC_REG, &reg);
241	if (err)
242		return err;
243
244	priv->header_type = PCI_HDRTYPE_TYPE(reg);
245	if (priv->header_type != 0)
246		return 0;
247
248	region = device->regions;
249	for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END;
250	     bar += sizeof(uint32_t), region++) {
251		err = pci_read(bus, dev, func, bar, &reg);
252		if (err)
253			return err;
254
255		/* Probe the size of the region. */
256		err = pci_write(bus, dev, func, bar, (unsigned int)~0);
257		if (err)
258			return err;
259		pci_read(bus, dev, func, bar, &size);
260		pci_write(bus, dev, func, bar, reg);
261
262		if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) {
263			region->is_IO = 1;
264			region->base_addr = PCI_MAPREG_IO_ADDR(reg);
265			region->size = PCI_MAPREG_IO_SIZE(size);
266		} else {
267			if (PCI_MAPREG_MEM_PREFETCHABLE(reg))
268				region->is_prefetchable = 1;
269			switch(PCI_MAPREG_MEM_TYPE(reg)) {
270			case PCI_MAPREG_MEM_TYPE_32BIT:
271			case PCI_MAPREG_MEM_TYPE_32BIT_1M:
272				region->base_addr = PCI_MAPREG_MEM_ADDR(reg);
273				region->size = PCI_MAPREG_MEM_SIZE(size);
274				break;
275			case PCI_MAPREG_MEM_TYPE_64BIT:
276				region->is_64 = 1;
277
278				reg64 = reg;
279				size64 = size;
280
281				bar += sizeof(uint32_t);
282
283				err = pci_read(bus, dev, func, bar, &reg);
284				if (err)
285					return err;
286				reg64 |= (uint64_t)reg << 32;
287
288				err = pci_write(bus, dev, func, bar,
289				    (unsigned int)~0);
290				if (err)
291					return err;
292				pci_read(bus, dev, func, bar, &size);
293				pci_write(bus, dev, func, bar,
294				    (unsigned int)(reg64 >> 32));
295				size64 |= (uint64_t)size << 32;
296
297				region->base_addr =
298				    (unsigned long)PCI_MAPREG_MEM64_ADDR(reg64);
299				region->size =
300				    (unsigned long)PCI_MAPREG_MEM64_SIZE(size64);
301				region++;
302				break;
303			}
304		}
305	}
306
307	return 0;
308}
309
310/**
311 * Read a VGA rom using the 0xc0000 mapping.
312 *
313 * This function should be extended to handle access through PCI resources,
314 * which should be more reliable when available.
315 */
316static int
317pci_device_netbsd_read_rom(struct pci_device *dev, void *buffer)
318{
319    struct pci_device_private *priv = (struct pci_device_private *)(void *)dev;
320    void *bios;
321    pciaddr_t rom_base;
322    size_t rom_size;
323    uint32_t bios_val, command_val;
324    int pci_rom, memfd;
325
326    if (((priv->base.device_class >> 16) & 0xff) != PCI_CLASS_DISPLAY ||
327	((priv->base.device_class >> 8) & 0xff) != PCI_SUBCLASS_DISPLAY_VGA)
328	return ENOSYS;
329
330    if (priv->rom_base == 0) {
331#if defined(__amd64__) || defined(__i386__)
332	rom_base = 0xc0000;
333	rom_size = 0x10000;
334	pci_rom = 0;
335#else
336	return ENOSYS;
337#endif
338    } else {
339	rom_base = priv->rom_base;
340	rom_size = dev->rom_size;
341	pci_rom = 1;
342	if ((pcibus_conf_read(pcifd, (unsigned int)dev->bus,
343	    (unsigned int)dev->dev, (unsigned int)dev->func,
344	    PCIR_COMMAND, &command_val)) == -1)
345	    return errno;
346	if ((command_val & PCIM_CMD_MEMEN) == 0) {
347	    if ((pcibus_conf_write(pcifd, (unsigned int)dev->bus,
348		(unsigned int)dev->dev, (unsigned int)dev->func,
349		PCIR_COMMAND, command_val | PCIM_CMD_MEMEN)) == -1)
350		return errno;
351	}
352	if ((pcibus_conf_read(pcifd, (unsigned int)dev->bus,
353	    (unsigned int)dev->dev, (unsigned int)dev->func,
354	    PCIR_BIOS, &bios_val)) == -1)
355	    return errno;
356	if ((bios_val & PCIM_BIOS_ENABLE) == 0) {
357	    if ((pcibus_conf_write(pcifd, (unsigned int)dev->bus,
358		(unsigned int)dev->dev, (unsigned int)dev->func,
359		PCIR_BIOS, bios_val | PCIM_BIOS_ENABLE)) == -1)
360		return errno;
361	}
362    }
363
364    fprintf(stderr, "Using rom_base = 0x%lx (pci_rom=%d)\n", (long)rom_base,
365	pci_rom);
366    memfd = open("/dev/mem", O_RDONLY);
367    if (memfd == -1)
368	return errno;
369
370    bios = mmap(NULL, rom_size, PROT_READ, 0, memfd, (off_t)rom_base);
371    if (bios == MAP_FAILED) {
372	int serrno = errno;
373	close(memfd);
374	return serrno;
375    }
376
377    memcpy(buffer, bios, rom_size);
378
379    munmap(bios, rom_size);
380    close(memfd);
381
382    if (pci_rom) {
383	if ((command_val & PCIM_CMD_MEMEN) == 0) {
384	    if ((pcibus_conf_write(pcifd, (unsigned int)dev->bus,
385		(unsigned int)dev->dev, (unsigned int)dev->func,
386		PCIR_COMMAND, command_val)) == -1)
387		return errno;
388	}
389	if ((bios_val & PCIM_BIOS_ENABLE) == 0) {
390	    if ((pcibus_conf_write(pcifd, (unsigned int)dev->bus,
391		(unsigned int)dev->dev, (unsigned int)dev->func,
392		PCIR_BIOS, bios_val)) == -1)
393		return errno;
394	}
395    }
396
397    return 0;
398}
399
400static const struct pci_system_methods netbsd_pci_methods = {
401	.destroy = pci_system_netbsd_destroy,
402	.destroy_device = NULL,
403	.read_rom = pci_device_netbsd_read_rom,
404	.probe = pci_device_netbsd_probe,
405	.map_range = pci_device_netbsd_map_range,
406	.unmap_range = pci_device_netbsd_unmap_range,
407	.read = pci_device_netbsd_read,
408	.write = pci_device_netbsd_write,
409	.fill_capabilities = pci_fill_capabilities_generic
410};
411
412int
413pci_system_netbsd_create(void)
414{
415	struct pci_device_private *device;
416	int bus, dev, func, ndevs, nfuncs;
417	uint32_t reg;
418
419	pcifd = open("/dev/pci0", O_RDWR);
420	if (pcifd == -1)
421		return ENXIO;
422
423	pci_sys = calloc(1, sizeof(struct pci_system));
424	if (pci_sys == NULL) {
425		close(pcifd);
426		return ENOMEM;
427	}
428
429	pci_sys->methods = &netbsd_pci_methods;
430
431	ndevs = 0;
432	for (bus = 0; bus < 256; bus++) {
433		for (dev = 0; dev < 32; dev++) {
434			nfuncs = pci_nfuncs(bus, dev);
435			for (func = 0; func < nfuncs; func++) {
436				if (pci_read(bus, dev, func, PCI_ID_REG,
437				    &reg) != 0)
438					continue;
439				if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
440				    PCI_VENDOR(reg) == 0)
441					continue;
442
443				ndevs++;
444			}
445		}
446	}
447
448	pci_sys->num_devices = ndevs;
449	pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
450	if (pci_sys->devices == NULL) {
451		free(pci_sys);
452		close(pcifd);
453		return ENOMEM;
454	}
455
456	device = pci_sys->devices;
457	for (bus = 0; bus < 256; bus++) {
458		for (dev = 0; dev < 32; dev++) {
459			nfuncs = pci_nfuncs(bus, dev);
460			for (func = 0; func < nfuncs; func++) {
461				if (pci_read(bus, dev, func, PCI_ID_REG,
462				    &reg) != 0)
463					continue;
464				if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
465				    PCI_VENDOR(reg) == 0)
466					continue;
467
468				device->base.domain = 0;
469				device->base.bus = bus;
470				device->base.dev = dev;
471				device->base.func = func;
472				device->base.vendor_id = PCI_VENDOR(reg);
473				device->base.device_id = PCI_PRODUCT(reg);
474
475				if (pci_read(bus, dev, func, PCI_CLASS_REG,
476				    &reg) != 0)
477					continue;
478
479				device->base.device_class =
480				    PCI_INTERFACE(reg) | PCI_CLASS(reg) << 16 |
481				    PCI_SUBCLASS(reg) << 8;
482				device->base.revision = PCI_REVISION(reg);
483
484				if (pci_read(bus, dev, func, PCI_SUBSYS_ID_REG,
485				    &reg) != 0)
486					continue;
487
488				device->base.subvendor_id = PCI_VENDOR(reg);
489				device->base.subdevice_id = PCI_PRODUCT(reg);
490
491				device++;
492			}
493		}
494	}
495
496	return 0;
497}
498