OSDN Git Service

Merge remote branch 'main/master' into vga-arbiter
[android-x86/external-libpciaccess.git] / src / netbsd_pci.c
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
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
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
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
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
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
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
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
226 pci_system_netbsd_destroy(void)
227 {
228         close(pcifd);
229         free(pci_sys);
230         pci_sys = NULL;
231 }
232
233 static int
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
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 }