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