1 | /* |
---|
2 | * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. |
---|
3 | * |
---|
4 | * This program is free software; you can redistribute it and/or |
---|
5 | * modify it under the terms of the GNU General Public License as |
---|
6 | * published by the Free Software Foundation; either version 2 of the |
---|
7 | * License, or any later version. |
---|
8 | * |
---|
9 | * This program is distributed in the hope that it will be useful, but |
---|
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
12 | * General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU General Public License |
---|
15 | * along with this program; if not, write to the Free Software |
---|
16 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
---|
17 | */ |
---|
18 | |
---|
19 | FILE_LICENCE ( GPL2_OR_LATER ); |
---|
20 | |
---|
21 | #include <stdint.h> |
---|
22 | #include <stdlib.h> |
---|
23 | #include <string.h> |
---|
24 | #include <pxe.h> |
---|
25 | #include <realmode.h> |
---|
26 | #include <undirom.h> |
---|
27 | |
---|
28 | /** @file |
---|
29 | * |
---|
30 | * UNDI expansion ROMs |
---|
31 | * |
---|
32 | */ |
---|
33 | |
---|
34 | /** List of all UNDI ROMs */ |
---|
35 | static LIST_HEAD ( undiroms ); |
---|
36 | |
---|
37 | /** |
---|
38 | * Parse PXE ROM ID structure |
---|
39 | * |
---|
40 | * @v undirom UNDI ROM |
---|
41 | * @v pxeromid Offset within ROM to PXE ROM ID structure |
---|
42 | * @ret rc Return status code |
---|
43 | */ |
---|
44 | static int undirom_parse_pxeromid ( struct undi_rom *undirom, |
---|
45 | unsigned int pxeromid ) { |
---|
46 | struct undi_rom_id undi_rom_id; |
---|
47 | unsigned int undiloader; |
---|
48 | |
---|
49 | DBGC ( undirom, "UNDIROM %p has PXE ROM ID at %04x:%04x\n", undirom, |
---|
50 | undirom->rom_segment, pxeromid ); |
---|
51 | |
---|
52 | /* Read PXE ROM ID structure and verify */ |
---|
53 | copy_from_real ( &undi_rom_id, undirom->rom_segment, pxeromid, |
---|
54 | sizeof ( undi_rom_id ) ); |
---|
55 | if ( undi_rom_id.Signature != UNDI_ROM_ID_SIGNATURE ) { |
---|
56 | DBGC ( undirom, "UNDIROM %p has bad PXE ROM ID signature " |
---|
57 | "%08x\n", undirom, undi_rom_id.Signature ); |
---|
58 | return -EINVAL; |
---|
59 | } |
---|
60 | |
---|
61 | /* Check for UNDI loader */ |
---|
62 | undiloader = undi_rom_id.UNDILoader; |
---|
63 | if ( ! undiloader ) { |
---|
64 | DBGC ( undirom, "UNDIROM %p has no UNDI loader\n", undirom ); |
---|
65 | return -EINVAL; |
---|
66 | } |
---|
67 | |
---|
68 | /* Fill in UNDI ROM loader fields */ |
---|
69 | undirom->loader_entry.segment = undirom->rom_segment; |
---|
70 | undirom->loader_entry.offset = undiloader; |
---|
71 | undirom->code_size = undi_rom_id.CodeSize; |
---|
72 | undirom->data_size = undi_rom_id.DataSize; |
---|
73 | |
---|
74 | DBGC ( undirom, "UNDIROM %p has UNDI loader at %04x:%04x " |
---|
75 | "(code %04zx data %04zx)\n", undirom, |
---|
76 | undirom->loader_entry.segment, undirom->loader_entry.offset, |
---|
77 | undirom->code_size, undirom->data_size ); |
---|
78 | return 0; |
---|
79 | } |
---|
80 | |
---|
81 | /** |
---|
82 | * Parse PCI expansion header |
---|
83 | * |
---|
84 | * @v undirom UNDI ROM |
---|
85 | * @v pcirheader Offset within ROM to PCI expansion header |
---|
86 | */ |
---|
87 | static int undirom_parse_pcirheader ( struct undi_rom *undirom, |
---|
88 | unsigned int pcirheader ) { |
---|
89 | struct pcir_header pcir_header; |
---|
90 | |
---|
91 | DBGC ( undirom, "UNDIROM %p has PCI expansion header at %04x:%04x\n", |
---|
92 | undirom, undirom->rom_segment, pcirheader ); |
---|
93 | |
---|
94 | /* Read PCI expansion header and verify */ |
---|
95 | copy_from_real ( &pcir_header, undirom->rom_segment, pcirheader, |
---|
96 | sizeof ( pcir_header ) ); |
---|
97 | if ( pcir_header.signature != PCIR_SIGNATURE ) { |
---|
98 | DBGC ( undirom, "UNDIROM %p has bad PCI expansion header " |
---|
99 | "signature %08x\n", undirom, pcir_header.signature ); |
---|
100 | return -EINVAL; |
---|
101 | } |
---|
102 | |
---|
103 | /* Fill in UNDI ROM PCI device fields */ |
---|
104 | undirom->bus_type = PCI_NIC; |
---|
105 | undirom->bus_id.pci.vendor_id = pcir_header.vendor_id; |
---|
106 | undirom->bus_id.pci.device_id = pcir_header.device_id; |
---|
107 | |
---|
108 | DBGC ( undirom, "UNDIROM %p is for PCI devices %04x:%04x\n", undirom, |
---|
109 | undirom->bus_id.pci.vendor_id, undirom->bus_id.pci.device_id ); |
---|
110 | return 0; |
---|
111 | |
---|
112 | } |
---|
113 | |
---|
114 | /** |
---|
115 | * Probe UNDI ROM |
---|
116 | * |
---|
117 | * @v rom_segment ROM segment address |
---|
118 | * @ret rc Return status code |
---|
119 | */ |
---|
120 | static int undirom_probe ( unsigned int rom_segment ) { |
---|
121 | struct undi_rom *undirom = NULL; |
---|
122 | struct undi_rom_header romheader; |
---|
123 | size_t rom_len; |
---|
124 | unsigned int pxeromid; |
---|
125 | unsigned int pcirheader; |
---|
126 | int rc; |
---|
127 | |
---|
128 | /* Read expansion ROM header and verify */ |
---|
129 | copy_from_real ( &romheader, rom_segment, 0, sizeof ( romheader ) ); |
---|
130 | if ( romheader.Signature != ROM_SIGNATURE ) { |
---|
131 | rc = -EINVAL; |
---|
132 | goto err; |
---|
133 | } |
---|
134 | rom_len = ( romheader.ROMLength * 512 ); |
---|
135 | |
---|
136 | /* Allocate memory for UNDI ROM */ |
---|
137 | undirom = zalloc ( sizeof ( *undirom ) ); |
---|
138 | if ( ! undirom ) { |
---|
139 | DBG ( "Could not allocate UNDI ROM structure\n" ); |
---|
140 | rc = -ENOMEM; |
---|
141 | goto err; |
---|
142 | } |
---|
143 | DBGC ( undirom, "UNDIROM %p trying expansion ROM at %04x:0000 " |
---|
144 | "(%zdkB)\n", undirom, rom_segment, ( rom_len / 1024 ) ); |
---|
145 | undirom->rom_segment = rom_segment; |
---|
146 | |
---|
147 | /* Check for and parse PXE ROM ID */ |
---|
148 | pxeromid = romheader.PXEROMID; |
---|
149 | if ( ! pxeromid ) { |
---|
150 | DBGC ( undirom, "UNDIROM %p has no PXE ROM ID\n", undirom ); |
---|
151 | rc = -EINVAL; |
---|
152 | goto err; |
---|
153 | } |
---|
154 | if ( pxeromid > rom_len ) { |
---|
155 | DBGC ( undirom, "UNDIROM %p PXE ROM ID outside ROM\n", |
---|
156 | undirom ); |
---|
157 | rc = -EINVAL; |
---|
158 | goto err; |
---|
159 | } |
---|
160 | if ( ( rc = undirom_parse_pxeromid ( undirom, pxeromid ) ) != 0 ) |
---|
161 | goto err; |
---|
162 | |
---|
163 | /* Parse PCIR header, if present */ |
---|
164 | pcirheader = romheader.PCIRHeader; |
---|
165 | if ( pcirheader ) |
---|
166 | undirom_parse_pcirheader ( undirom, pcirheader ); |
---|
167 | |
---|
168 | /* Add to UNDI ROM list and return */ |
---|
169 | DBGC ( undirom, "UNDIROM %p registered\n", undirom ); |
---|
170 | list_add ( &undirom->list, &undiroms ); |
---|
171 | return 0; |
---|
172 | |
---|
173 | err: |
---|
174 | free ( undirom ); |
---|
175 | return rc; |
---|
176 | } |
---|
177 | |
---|
178 | /** |
---|
179 | * Create UNDI ROMs for all possible expansion ROMs |
---|
180 | * |
---|
181 | * @ret |
---|
182 | */ |
---|
183 | static void undirom_probe_all_roms ( void ) { |
---|
184 | static int probed = 0; |
---|
185 | unsigned int rom_segment; |
---|
186 | |
---|
187 | /* Perform probe only once */ |
---|
188 | if ( probed ) |
---|
189 | return; |
---|
190 | |
---|
191 | DBG ( "Scanning for PXE expansion ROMs\n" ); |
---|
192 | |
---|
193 | /* Scan through expansion ROM region at 512 byte intervals */ |
---|
194 | for ( rom_segment = 0xc000 ; rom_segment < 0x10000 ; |
---|
195 | rom_segment += 0x20 ) { |
---|
196 | undirom_probe ( rom_segment ); |
---|
197 | } |
---|
198 | |
---|
199 | probed = 1; |
---|
200 | } |
---|
201 | |
---|
202 | /** |
---|
203 | * Find UNDI ROM for PCI device |
---|
204 | * |
---|
205 | * @v vendor_id PCI vendor ID |
---|
206 | * @v device_id PCI device ID |
---|
207 | * @v rombase ROM base address, or 0 for any |
---|
208 | * @ret undirom UNDI ROM, or NULL |
---|
209 | */ |
---|
210 | struct undi_rom * undirom_find_pci ( unsigned int vendor_id, |
---|
211 | unsigned int device_id, |
---|
212 | unsigned int rombase ) { |
---|
213 | struct undi_rom *undirom; |
---|
214 | |
---|
215 | undirom_probe_all_roms(); |
---|
216 | |
---|
217 | list_for_each_entry ( undirom, &undiroms, list ) { |
---|
218 | if ( undirom->bus_type != PCI_NIC ) |
---|
219 | continue; |
---|
220 | if ( undirom->bus_id.pci.vendor_id != vendor_id ) |
---|
221 | continue; |
---|
222 | if ( undirom->bus_id.pci.device_id != device_id ) |
---|
223 | continue; |
---|
224 | if ( rombase && ( ( undirom->rom_segment << 4 ) != rombase ) ) |
---|
225 | continue; |
---|
226 | DBGC ( undirom, "UNDIROM %p matched PCI %04x:%04x (%08x)\n", |
---|
227 | undirom, vendor_id, device_id, rombase ); |
---|
228 | return undirom; |
---|
229 | } |
---|
230 | |
---|
231 | DBG ( "No UNDI ROM matched PCI %04x:%04x (%08x)\n", |
---|
232 | vendor_id, device_id, rombase ); |
---|
233 | return NULL; |
---|
234 | } |
---|