source: trunk/pcidev.c

Last change on this file was 1644, checked in by hailfinger, 6 months ago

Decouple BAR reading from pci device init, handle errors gracefully.

pcidev_init() now returns struct pci_device * instead of a BAR stored in
PCI config space. This allows for real error checking instead of having
exit(1) everywhere in pcidev.c.
Thanks to Niklas Söderlund for coming up with the original error
handling patch which was slightly modified and folded into this patch.
Move the declaration of struct pci_device in programmer.h before the
first user.

Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@…>
Acked-by: Stefan Tauner <stefan.tauner@…>

File size: 9.5 KB
Line 
1/*
2 * This file is part of the flashrom project.
3 *
4 * Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
5 * Copyright (C) 2010, 2011 Carl-Daniel Hailfinger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20 */
21
22#include <stdlib.h>
23#include <string.h>
24#include "flash.h"
25#include "programmer.h"
26#include "hwaccess.h"
27
28uint32_t io_base_addr;
29struct pci_access *pacc;
30
31enum pci_bartype {
32        TYPE_MEMBAR,
33        TYPE_IOBAR,
34        TYPE_ROMBAR,
35        TYPE_UNKNOWN
36};
37
38uintptr_t pcidev_readbar(struct pci_dev *dev, int bar)
39{
40        uint64_t addr;
41        uint32_t upperaddr;
42        uint8_t headertype;
43        uint16_t supported_cycles;
44        enum pci_bartype bartype = TYPE_UNKNOWN;
45
46
47        headertype = pci_read_byte(dev, PCI_HEADER_TYPE) & 0x7f;
48        msg_pspew("PCI header type 0x%02x\n", headertype);
49
50        /* Don't use dev->base_addr[x] (as value for 'bar'), won't work on older libpci. */
51        addr = pci_read_long(dev, bar);
52
53        /* Sanity checks. */
54        switch (headertype) {
55        case PCI_HEADER_TYPE_NORMAL:
56                switch (bar) {
57                case PCI_BASE_ADDRESS_0:
58                case PCI_BASE_ADDRESS_1:
59                case PCI_BASE_ADDRESS_2:
60                case PCI_BASE_ADDRESS_3:
61                case PCI_BASE_ADDRESS_4:
62                case PCI_BASE_ADDRESS_5:
63                        if ((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
64                                bartype = TYPE_IOBAR;
65                        else
66                                bartype = TYPE_MEMBAR;
67                        break;
68                case PCI_ROM_ADDRESS:
69                        bartype = TYPE_ROMBAR;
70                        break;
71                }
72                break;
73        case PCI_HEADER_TYPE_BRIDGE:
74                switch (bar) {
75                case PCI_BASE_ADDRESS_0:
76                case PCI_BASE_ADDRESS_1:
77                        if ((addr & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO)
78                                bartype = TYPE_IOBAR;
79                        else
80                                bartype = TYPE_MEMBAR;
81                        break;
82                case PCI_ROM_ADDRESS1:
83                        bartype = TYPE_ROMBAR;
84                        break;
85                }
86                break;
87        case PCI_HEADER_TYPE_CARDBUS:
88                break;
89        default:
90                msg_perr("Unknown PCI header type 0x%02x, BAR type cannot be determined reliably.\n",
91                         headertype);
92                break;
93        }
94
95        supported_cycles = pci_read_word(dev, PCI_COMMAND);
96
97        msg_pdbg("Requested BAR is ");
98        switch (bartype) {
99        case TYPE_MEMBAR:
100                msg_pdbg("MEM");
101                if (!(supported_cycles & PCI_COMMAND_MEMORY)) {
102                        msg_perr("MEM BAR access requested, but device has MEM space accesses disabled.\n");
103                        /* TODO: Abort here? */
104                }
105                msg_pdbg(", %sbit, %sprefetchable\n",
106                         ((addr & 0x6) == 0x0) ? "32" : (((addr & 0x6) == 0x4) ? "64" : "reserved"),
107                         (addr & 0x8) ? "" : "not ");
108                if ((addr & 0x6) == 0x4) {
109                        /* The spec says that a 64-bit register consumes
110                         * two subsequent dword locations.
111                         */
112                        upperaddr = pci_read_long(dev, bar + 4);
113                        if (upperaddr != 0x00000000) {
114                                /* Fun! A real 64-bit resource. */
115                                if (sizeof(uintptr_t) != sizeof(uint64_t)) {
116                                        msg_perr("BAR unreachable!");
117                                        /* TODO: Really abort here? If multiple PCI devices match,
118                                         * we might never tell the user about the other devices.
119                                         */
120                                        return 0;
121                                }
122                                addr |= (uint64_t)upperaddr << 32;
123                        }
124                }
125                addr &= PCI_BASE_ADDRESS_MEM_MASK;
126                break;
127        case TYPE_IOBAR:
128                msg_pdbg("I/O\n");
129#if __FLASHROM_HAVE_OUTB__
130                if (!(supported_cycles & PCI_COMMAND_IO)) {
131                        msg_perr("I/O BAR access requested, but device has I/O space accesses disabled.\n");
132                        /* TODO: Abort here? */
133                }
134#else
135                msg_perr("I/O BAR access requested, but flashrom does not support I/O BAR access on this "
136                         "platform (yet).\n");
137#endif
138                addr &= PCI_BASE_ADDRESS_IO_MASK;
139                break;
140        case TYPE_ROMBAR:
141                msg_pdbg("ROM\n");
142                /* Not sure if this check is needed. */
143                if (!(supported_cycles & PCI_COMMAND_MEMORY)) {
144                        msg_perr("MEM BAR access requested, but device has MEM space accesses disabled.\n");
145                        /* TODO: Abort here? */
146                }
147                addr &= PCI_ROM_ADDRESS_MASK;
148                break;
149        case TYPE_UNKNOWN:
150                msg_perr("BAR type unknown, please report a bug at flashrom@flashrom.org\n");
151        }
152
153        return (uintptr_t)addr;
154}
155
156static int pcidev_shutdown(void *data)
157{
158        if (pacc == NULL) {
159                msg_perr("%s: Tried to cleanup an invalid PCI context!\n"
160                         "Please report a bug at flashrom@flashrom.org\n", __func__);
161                return 1;
162        }
163        pci_cleanup(pacc);
164        return 0;
165}
166
167int pci_init_common(void)
168{
169        if (pacc != NULL) {
170                msg_perr("%s: Tried to allocate a new PCI context, but there is still an old one!\n"
171                         "Please report a bug at flashrom@flashrom.org\n", __func__);
172                return 1;
173        }
174        pacc = pci_alloc();     /* Get the pci_access structure */
175        pci_init(pacc);         /* Initialize the PCI library */
176        if (register_shutdown(pcidev_shutdown, NULL))
177                return 1;
178        pci_scan_bus(pacc);     /* We want to get the list of devices */
179        return 0;
180}
181
182/* pcidev_init gets an array of allowed PCI device IDs and returns a pointer to struct pci_dev iff exactly one
183 * match was found. If the "pci=bb:dd.f" programmer parameter was specified, a match is only considered if it
184 * also matches the specified bus:device.function.
185 * For convenience, this function also registers its own undo handlers.
186 */
187struct pci_dev *pcidev_init(const struct dev_entry *devs, int bar)
188{
189        struct pci_dev *dev;
190        struct pci_dev *found_dev = NULL;
191        struct pci_filter filter;
192        char *pcidev_bdf;
193        char *msg = NULL;
194        int found = 0;
195        int i;
196        uintptr_t addr = 0;
197
198        if (pci_init_common() != 0)
199                return NULL;
200        pci_filter_init(pacc, &filter);
201
202        /* Filter by bb:dd.f (if supplied by the user). */
203        pcidev_bdf = extract_programmer_param("pci");
204        if (pcidev_bdf != NULL) {
205                if ((msg = pci_filter_parse_slot(&filter, pcidev_bdf))) {
206                        msg_perr("Error: %s\n", msg);
207                        return NULL;
208                }
209        }
210        free(pcidev_bdf);
211
212        for (dev = pacc->devices; dev; dev = dev->next) {
213                if (pci_filter_match(&filter, dev)) {
214                        /* Check against list of supported devices. */
215                        for (i = 0; devs[i].device_name != NULL; i++)
216                                if ((dev->vendor_id == devs[i].vendor_id) &&
217                                    (dev->device_id == devs[i].device_id))
218                                        break;
219                        /* Not supported, try the next one. */
220                        if (devs[i].device_name == NULL)
221                                continue;
222
223                        msg_pdbg("Found \"%s %s\" (%04x:%04x, BDF %02x:%02x.%x).\n", devs[i].vendor_name,
224                                 devs[i].device_name, dev->vendor_id, dev->device_id, dev->bus, dev->dev,
225                                 dev->func);
226                        if (devs[i].status == NT)
227                                msg_pinfo("===\nThis PCI device is UNTESTED. Please report the 'flashrom -p "
228                                          "xxxx' output \n"
229                                          "to flashrom@flashrom.org if it works for you. Please add the name "
230                                          "of your\n"
231                                          "PCI device to the subject. Thank you for your help!\n===\n");
232
233                        /* FIXME: We should count all matching devices, not
234                         * just those with a valid BAR.
235                         */
236                        if ((addr = pcidev_readbar(dev, bar)) != 0) {
237                                found_dev = dev;
238                                found++;
239                        }
240                }
241        }
242
243        /* Only continue if exactly one supported PCI dev has been found. */
244        if (found == 0) {
245                msg_perr("Error: No supported PCI device found.\n");
246                return NULL;
247        } else if (found > 1) {
248                msg_perr("Error: Multiple supported PCI devices found. Use 'flashrom -p xxxx:pci=bb:dd.f' \n"
249                         "to explicitly select the card with the given BDF (PCI bus, device, function).\n");
250                return NULL;
251        }
252
253        return found_dev;
254}
255
256enum pci_write_type {
257        pci_write_type_byte,
258        pci_write_type_word,
259        pci_write_type_long,
260};
261
262struct undo_pci_write_data {
263        struct pci_dev dev;
264        int reg;
265        enum pci_write_type type;
266        union {
267                uint8_t bytedata;
268                uint16_t worddata;
269                uint32_t longdata;
270        };
271};
272
273int undo_pci_write(void *p)
274{
275        struct undo_pci_write_data *data = p;
276        if (pacc == NULL) {
277                msg_perr("%s: Tried to undo PCI writes without a valid PCI context!\n"
278                         "Please report a bug at flashrom@flashrom.org\n", __func__);
279                return 1;
280        }
281        msg_pdbg("Restoring PCI config space for %02x:%02x:%01x reg 0x%02x\n",
282                 data->dev.bus, data->dev.dev, data->dev.func, data->reg);
283        switch (data->type) {
284        case pci_write_type_byte:
285                pci_write_byte(&data->dev, data->reg, data->bytedata);
286                break;
287        case pci_write_type_word:
288                pci_write_word(&data->dev, data->reg, data->worddata);
289                break;
290        case pci_write_type_long:
291                pci_write_long(&data->dev, data->reg, data->longdata);
292                break;
293        }
294        /* p was allocated in register_undo_pci_write. */
295        free(p);
296        return 0;
297}
298
299#define register_undo_pci_write(a, b, c)                                \
300{                                                                       \
301        struct undo_pci_write_data *undo_pci_write_data;                \
302        undo_pci_write_data = malloc(sizeof(struct undo_pci_write_data)); \
303        if (!undo_pci_write_data) {                                     \
304                msg_gerr("Out of memory!\n");                           \
305                exit(1);                                                \
306        }                                                               \
307        undo_pci_write_data->dev = *a;                                  \
308        undo_pci_write_data->reg = b;                                   \
309        undo_pci_write_data->type = pci_write_type_##c;                 \
310        undo_pci_write_data->c##data = pci_read_##c(dev, reg);          \
311        register_shutdown(undo_pci_write, undo_pci_write_data);         \
312}
313
314#define register_undo_pci_write_byte(a, b) register_undo_pci_write(a, b, byte)
315#define register_undo_pci_write_word(a, b) register_undo_pci_write(a, b, word)
316#define register_undo_pci_write_long(a, b) register_undo_pci_write(a, b, long)
317
318int rpci_write_byte(struct pci_dev *dev, int reg, uint8_t data)
319{
320        register_undo_pci_write_byte(dev, reg);
321        return pci_write_byte(dev, reg, data);
322}
323 
324int rpci_write_word(struct pci_dev *dev, int reg, uint16_t data)
325{
326        register_undo_pci_write_word(dev, reg);
327        return pci_write_word(dev, reg, data);
328}
329 
330int rpci_write_long(struct pci_dev *dev, int reg, uint32_t data)
331{
332        register_undo_pci_write_long(dev, reg);
333        return pci_write_long(dev, reg, data);
334}
Note: See TracBrowser for help on using the repository browser.