1 /*
2 * optionroms.c: Option ROM loading support.
3 *
4 * Leendert van Doorn, leendert@watson.ibm.com
5 * Copyright (c) 2005, International Business Machines Corporation.
6 *
7 * Copyright (c) 2006, Keir Fraser, XenSource Inc.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms and conditions of the GNU General Public License,
11 * version 2, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program; If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23 #include "option_rom.h"
24 #include "util.h"
25 #include "pci_regs.h"
26
27 /*
28 * Scan the list of Option ROMs at @roms for one which supports
29 * PCI (@vendor_id, @device_id) found at slot @devfn. If one is found,
30 * copy it to @dest and return its size rounded up to a multiple 2kB. This
31 * function will not copy ROMs beyond address option_rom_end.
32 */
scan_option_rom(unsigned int option_rom_end,uint8_t devfn,uint16_t vendor_id,uint16_t device_id,void * roms,uint32_t dest)33 static int scan_option_rom(
34 unsigned int option_rom_end,
35 uint8_t devfn, uint16_t vendor_id, uint16_t device_id,
36 void *roms, uint32_t dest)
37 {
38 struct option_rom_header *rom;
39 struct option_rom_pnp_header *pnph;
40 struct option_rom_pci_header *pcih;
41 uint8_t csum;
42 int i;
43
44 static uint32_t orom_ids[64];
45 static int nr_roms;
46
47 /* Avoid duplicate ROMs. */
48 for ( i = 0; i < nr_roms; i++ )
49 if ( orom_ids[i] == (vendor_id | ((uint32_t)device_id << 16)) )
50 return 0;
51
52 rom = roms;
53 for ( ; ; )
54 {
55 /* Invalid signature means we're out of option ROMs. */
56 if ( strncmp((char *)rom->signature, "\x55\xaa", 2) ||
57 (rom->rom_size == 0) )
58 break;
59
60 /* Invalid checksum means we're out of option ROMs. */
61 csum = 0;
62 for ( i = 0; i < (rom->rom_size * 512); i++ )
63 csum += ((uint8_t *)rom)[i];
64 if ( csum != 0 )
65 break;
66
67 /* Check the PCI PnP header (if any) for a match. */
68 pcih = (struct option_rom_pci_header *)
69 ((char *)rom + rom->pci_header_offset);
70 if ( (rom->pci_header_offset != 0) &&
71 !strncmp((char *)pcih->signature, "PCIR", 4) &&
72 (pcih->vendor_id == vendor_id) &&
73 (pcih->device_id == device_id) )
74 goto found;
75
76 rom = (struct option_rom_header *)
77 ((char *)rom + rom->rom_size * 512);
78 }
79
80 return 0;
81
82 found:
83 /* Find the PnP expansion header (if any). */
84 pnph = ((rom->expansion_header_offset != 0)
85 ? ((struct option_rom_pnp_header *)
86 ((char *)rom + rom->expansion_header_offset))
87 : ((struct option_rom_pnp_header *)NULL));
88 while ( (pnph != NULL) && strncmp((char *)pnph->signature, "$PnP", 4) )
89 pnph = ((pnph->next_header_offset != 0)
90 ? ((struct option_rom_pnp_header *)
91 ((char *)rom + pnph->next_header_offset))
92 : ((struct option_rom_pnp_header *)NULL));
93
94 printf("Loading PCI Option ROM ...\n");
95 if ( (pnph != NULL) && (pnph->manufacturer_name_offset != 0) )
96 printf(" - Manufacturer: %s\n",
97 (char *)rom + pnph->manufacturer_name_offset);
98 if ( (pnph != NULL) && (pnph->product_name_offset != 0) )
99 printf(" - Product name: %s\n",
100 (char *)rom + pnph->product_name_offset);
101
102 if ( (dest + rom->rom_size * 512 + 1) > option_rom_end )
103 {
104 printf("Option ROM size %x exceeds available space\n",
105 rom->rom_size * 512);
106 return 0;
107 }
108
109 orom_ids[nr_roms++] = vendor_id | ((uint32_t)device_id << 16);
110 memcpy((void *)dest, rom, rom->rom_size * 512);
111 *(uint8_t *)(dest + rom->rom_size * 512) = devfn;
112 return round_option_rom(rom->rom_size * 512 + 1);
113 }
114
115 /*
116 * Scan the PCI bus for the first NIC supported by etherboot, and copy
117 * the corresponding rom data to *copy_rom_dest. Returns the length of the
118 * selected rom, or 0 if no NIC found.
119 */
scan_etherboot_nic(unsigned int option_rom_end,uint32_t copy_rom_dest,void * etherboot_rom)120 int scan_etherboot_nic(unsigned int option_rom_end,
121 uint32_t copy_rom_dest,
122 void *etherboot_rom)
123 {
124 uint16_t class, vendor_id, device_id, devfn;
125 int rom_size = 0;
126
127 for ( devfn = 0; (devfn < 256) && !rom_size; devfn++ )
128 {
129 class = pci_readw(devfn, PCI_CLASS_DEVICE);
130 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
131 device_id = pci_readw(devfn, PCI_DEVICE_ID);
132
133 /* We're only interested in NICs. */
134 if ( (vendor_id != 0xffff) &&
135 (device_id != 0xffff) &&
136 (class == 0x0200) )
137 rom_size = scan_option_rom(
138 option_rom_end,
139 devfn, vendor_id, device_id, etherboot_rom, copy_rom_dest);
140 }
141
142 return rom_size;
143 }
144
145 /*
146 * Scan the PCI bus for the devices that have an option ROM, and copy
147 * the corresponding rom data to rom_phys_addr.
148 */
pci_load_option_roms(unsigned int option_rom_end,uint32_t rom_base_addr)149 int pci_load_option_roms(unsigned int option_rom_end,
150 uint32_t rom_base_addr)
151 {
152 uint32_t option_rom_addr, rom_phys_addr = rom_base_addr;
153 uint16_t vendor_id, device_id, devfn, class;
154
155 for ( devfn = 0; devfn < 256; devfn++ )
156 {
157 class = pci_readb(devfn, PCI_CLASS_DEVICE + 1);
158 vendor_id = pci_readw(devfn, PCI_VENDOR_ID);
159 device_id = pci_readw(devfn, PCI_DEVICE_ID);
160
161 if ( (vendor_id == 0xffff) && (device_id == 0xffff) )
162 continue;
163
164 /*
165 * Currently only scan options from mass storage devices and serial
166 * bus controller (Fibre Channel included).
167 */
168 if ( (class != 0x1) && (class != 0xc) )
169 continue;
170
171 option_rom_addr = pci_readl(devfn, PCI_ROM_ADDRESS);
172 if ( !option_rom_addr )
173 continue;
174
175 /* Ensure Expansion Bar is enabled before copying */
176 pci_writel(devfn, PCI_ROM_ADDRESS, option_rom_addr | 0x1);
177
178 rom_phys_addr += scan_option_rom(
179 option_rom_end,
180 devfn, vendor_id, device_id,
181 (void *)(option_rom_addr & ~2047), rom_phys_addr);
182
183 /* Restore the default original value of Expansion Bar */
184 pci_writel(devfn, PCI_ROM_ADDRESS, option_rom_addr);
185 }
186
187 return rom_phys_addr - rom_base_addr;
188 }
189