OSDN Git Service

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