Rename __attribute__((packed)) --> __packed
[coreboot.git] / src / device / oprom / yabel / device.c
blobcd1abfb026e903de340c2a0f35c7113beed72e70
1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
11 * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer
16 * in the documentation and/or other materials provided with the
17 * distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 * Contributors:
32 * IBM Corporation - initial implementation
33 *****************************************************************************/
36 #include <compiler.h>
37 #include "device.h"
38 #include "compat/rtas.h"
39 #include <string.h>
40 #include "debug.h"
42 #include <device/device.h>
43 #include <device/pci.h>
44 #include <device/pci_ops.h>
45 #include <device/resource.h>
47 /* the device we are working with... */
48 biosemu_device_t bios_device;
49 //max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges, plus 2 "special" memory ranges
50 translate_address_t translate_address_array[13];
51 u8 taa_last_entry;
53 typedef struct {
54 u8 info;
55 u8 bus;
56 u8 devfn;
57 u8 cfg_space_offset;
58 u64 address;
59 u64 size;
60 } __packed assigned_address_t;
62 #if IS_ENABLED(CONFIG_PCI_OPTION_ROM_RUN_YABEL)
63 /* coreboot version */
65 static void
66 biosemu_dev_get_addr_info(void)
68 int taa_index = 0;
69 struct resource *r;
70 u8 bus = bios_device.dev->bus->secondary;
71 u16 devfn = bios_device.dev->path.pci.devfn;
73 bios_device.bus = bus;
74 bios_device.devfn = devfn;
76 DEBUG_PRINTF("bus: %x, devfn: %x\n", bus, devfn);
77 for (r = bios_device.dev->resource_list; r; r = r->next) {
78 translate_address_array[taa_index].info = r->flags;
79 translate_address_array[taa_index].bus = bus;
80 translate_address_array[taa_index].devfn = devfn;
81 translate_address_array[taa_index].cfg_space_offset =
82 r->index;
83 translate_address_array[taa_index].address = r->base;
84 translate_address_array[taa_index].size = r->size;
85 /* don't translate addresses... all addresses are 1:1 */
86 translate_address_array[taa_index].address_offset = 0;
87 taa_index++;
89 /* Expansion ROM */
90 translate_address_array[taa_index].info = IORESOURCE_MEM | IORESOURCE_READONLY;
91 translate_address_array[taa_index].bus = bus;
92 translate_address_array[taa_index].devfn = devfn;
93 translate_address_array[taa_index].cfg_space_offset = 0x30;
94 translate_address_array[taa_index].address = bios_device.img_addr;
95 translate_address_array[taa_index].size = 0; /* TODO: do we need the size? */
96 /* don't translate addresses... all addresses are 1:1 */
97 translate_address_array[taa_index].address_offset = 0;
98 taa_index++;
99 /* legacy ranges if its a VGA card... */
100 if ((bios_device.dev->class & 0xFF0000) == 0x030000) {
101 DEBUG_PRINTF("%s: VGA device found, adding legacy resources...\n", __func__);
102 /* I/O 0x3B0-0x3BB */
103 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_IO;
104 translate_address_array[taa_index].bus = bus;
105 translate_address_array[taa_index].devfn = devfn;
106 translate_address_array[taa_index].cfg_space_offset = 0;
107 translate_address_array[taa_index].address = 0x3b0;
108 translate_address_array[taa_index].size = 0xc;
109 /* don't translate addresses... all addresses are 1:1 */
110 translate_address_array[taa_index].address_offset = 0;
111 taa_index++;
112 /* I/O 0x3C0-0x3DF */
113 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_IO;
114 translate_address_array[taa_index].bus = bus;
115 translate_address_array[taa_index].devfn = devfn;
116 translate_address_array[taa_index].cfg_space_offset = 0;
117 translate_address_array[taa_index].address = 0x3c0;
118 translate_address_array[taa_index].size = 0x20;
119 /* don't translate addresses... all addresses are 1:1 */
120 translate_address_array[taa_index].address_offset = 0;
121 taa_index++;
122 /* Mem 0xA0000-0xBFFFF */
123 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_MEM;
124 translate_address_array[taa_index].bus = bus;
125 translate_address_array[taa_index].devfn = devfn;
126 translate_address_array[taa_index].cfg_space_offset = 0;
127 translate_address_array[taa_index].address = 0xa0000;
128 translate_address_array[taa_index].size = 0x20000;
129 /* don't translate addresses... all addresses are 1:1 */
130 translate_address_array[taa_index].address_offset = 0;
131 taa_index++;
133 // store last entry index of translate_address_array
134 taa_last_entry = taa_index - 1;
135 #if IS_ENABLED(CONFIG_X86EMU_DEBUG)
136 //dump translate_address_array
137 printf("translate_address_array:\n");
138 translate_address_t ta;
139 int i;
140 for (i = 0; i <= taa_last_entry; i++) {
141 ta = translate_address_array[i];
142 printf
143 ("%d: info: %08lx bus: %02x devfn: %02x cfg_space_offset: %02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n",
144 i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset,
145 ta.address, ta.address_offset, ta.size);
147 #endif
149 #else
150 // use translate_address_dev and get_puid from net-snk's net_support.c
151 void translate_address_dev(u64 *, phandle_t);
152 u64 get_puid(phandle_t node);
155 // scan all addresses assigned to the device ("assigned-addresses" and "reg")
156 // store in translate_address_array for faster translation using dev_translate_address
157 void
158 biosemu_dev_get_addr_info(void)
160 // get bus/dev/fn from assigned-addresses
161 int32_t len;
162 //max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges
163 assigned_address_t buf[11];
164 len =
165 of_getprop(bios_device.phandle, "assigned-addresses", buf,
166 sizeof(buf));
167 bios_device.bus = buf[0].bus;
168 bios_device.devfn = buf[0].devfn;
169 DEBUG_PRINTF("bus: %x, devfn: %x\n", bios_device.bus,
170 bios_device.devfn);
171 //store address translations for all assigned-addresses and regs in
172 //translate_address_array for faster translation later on...
173 int i = 0;
174 // index to insert data into translate_address_array
175 int taa_index = 0;
176 u64 address_offset;
177 for (i = 0; i < (len / sizeof(assigned_address_t)); i++, taa_index++) {
178 //copy all info stored in assigned-addresses
179 translate_address_array[taa_index].info = buf[i].info;
180 translate_address_array[taa_index].bus = buf[i].bus;
181 translate_address_array[taa_index].devfn = buf[i].devfn;
182 translate_address_array[taa_index].cfg_space_offset =
183 buf[i].cfg_space_offset;
184 translate_address_array[taa_index].address = buf[i].address;
185 translate_address_array[taa_index].size = buf[i].size;
186 // translate first address and store it as address_offset
187 address_offset = buf[i].address;
188 translate_address_dev(&address_offset, bios_device.phandle);
189 translate_address_array[taa_index].address_offset =
190 address_offset - buf[i].address;
192 //get "reg" property
193 len = of_getprop(bios_device.phandle, "reg", buf, sizeof(buf));
194 for (i = 0; i < (len / sizeof(assigned_address_t)); i++) {
195 if ((buf[i].size == 0) || (buf[i].cfg_space_offset != 0)) {
196 // we don't care for ranges with size 0 and
197 // BARs and Expansion ROM must be in assigned-addresses... so in reg
198 // we only look for those without config space offset set...
199 // i.e. the legacy ranges
200 continue;
202 //copy all info stored in assigned-addresses
203 translate_address_array[taa_index].info = buf[i].info;
204 translate_address_array[taa_index].bus = buf[i].bus;
205 translate_address_array[taa_index].devfn = buf[i].devfn;
206 translate_address_array[taa_index].cfg_space_offset =
207 buf[i].cfg_space_offset;
208 translate_address_array[taa_index].address = buf[i].address;
209 translate_address_array[taa_index].size = buf[i].size;
210 // translate first address and store it as address_offset
211 address_offset = buf[i].address;
212 translate_address_dev(&address_offset, bios_device.phandle);
213 translate_address_array[taa_index].address_offset =
214 address_offset - buf[i].address;
215 taa_index++;
217 // store last entry index of translate_address_array
218 taa_last_entry = taa_index - 1;
219 #if IS_ENABLED(CONFIG_X86EMU_DEBUG)
220 //dump translate_address_array
221 printf("translate_address_array:\n");
222 translate_address_t ta;
223 for (i = 0; i <= taa_last_entry; i++) {
224 ta = translate_address_array[i];
225 printf
226 ("%d: %02x%02x%02x%02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n",
227 i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset,
228 ta.address, ta.address_offset, ta.size);
230 #endif
232 #endif
234 // "special memory" is a hack to make some parts of memory fall through to real memory
235 // (ie. no translation). Necessary if option ROMs attempt DMA there, map registers or
236 // do similarly crazy things.
237 void
238 biosemu_add_special_memory(u32 start, u32 size)
240 int taa_index = ++taa_last_entry;
241 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_MEM;
242 translate_address_array[taa_index].bus = 0;
243 translate_address_array[taa_index].devfn = 0;
244 translate_address_array[taa_index].cfg_space_offset = 0;
245 translate_address_array[taa_index].address = start;
246 translate_address_array[taa_index].size = size;
247 /* don't translate addresses... all addresses are 1:1 */
248 translate_address_array[taa_index].address_offset = 0;
251 #if !IS_ENABLED(CONFIG_PCI_OPTION_ROM_RUN_YABEL)
252 // to simulate accesses to legacy VGA Memory (0xA0000-0xBFFFF)
253 // we look for the first prefetchable memory BAR, if no prefetchable BAR found,
254 // we use the first memory BAR
255 // dev_translate_addr will translate accesses to the legacy VGA Memory into the found vmem BAR
256 static void
257 biosemu_dev_find_vmem_addr(void)
259 int i = 0;
260 translate_address_t ta;
261 s8 tai_np = -1, tai_p = -1; // translate_address_array index for non-prefetchable and prefetchable memory
262 //search backwards to find first entry
263 for (i = taa_last_entry; i >= 0; i--) {
264 ta = translate_address_array[i];
265 if ((ta.cfg_space_offset >= 0x10)
266 && (ta.cfg_space_offset <= 0x24)) {
267 //only BARs
268 if ((ta.info & 0x03) >= 0x02) {
269 //32/64bit memory
270 tai_np = i;
271 if ((ta.info & 0x40) != 0) {
272 // prefetchable
273 tai_p = i;
278 if (tai_p != -1) {
279 ta = translate_address_array[tai_p];
280 bios_device.vmem_addr = ta.address;
281 bios_device.vmem_size = ta.size;
282 DEBUG_PRINTF
283 ("%s: Found prefetchable Virtual Legacy Memory BAR: %llx, size: %llx\n",
284 __func__, bios_device.vmem_addr,
285 bios_device.vmem_size);
286 } else if (tai_np != -1) {
287 ta = translate_address_array[tai_np];
288 bios_device.vmem_addr = ta.address;
289 bios_device.vmem_size = ta.size;
290 DEBUG_PRINTF
291 ("%s: Found non-prefetchable Virtual Legacy Memory BAR: %llx, size: %llx",
292 __func__, bios_device.vmem_addr,
293 bios_device.vmem_size);
295 // disable vmem
296 //bios_device.vmem_size = 0;
299 void
300 biosemu_dev_get_puid(void)
302 // get puid
303 bios_device.puid = get_puid(bios_device.phandle);
304 DEBUG_PRINTF("puid: 0x%llx\n", bios_device.puid);
306 #endif
308 static void
309 biosemu_dev_get_device_vendor_id(void)
312 u32 pci_config_0;
313 #if IS_ENABLED(CONFIG_PCI_OPTION_ROM_RUN_YABEL)
314 pci_config_0 = pci_read_config32(bios_device.dev, 0x0);
315 #else
316 pci_config_0 =
317 rtas_pci_config_read(bios_device.puid, 4, bios_device.bus,
318 bios_device.devfn, 0x0);
319 #endif
320 bios_device.pci_device_id =
321 (u16) ((pci_config_0 & 0xFFFF0000) >> 16);
322 bios_device.pci_vendor_id = (u16) (pci_config_0 & 0x0000FFFF);
323 DEBUG_PRINTF("PCI Device ID: %04x, PCI Vendor ID: %x\n",
324 bios_device.pci_device_id, bios_device.pci_vendor_id);
327 /* Check whether the device has a valid Expansion ROM and search the PCI Data
328 * Structure and any Expansion ROM Header (using dev_scan_exp_header()) for
329 * needed information. If the rom_addr parameter is != 0, it is the address of
330 * the Expansion ROM image and will be used, if it is == 0, the Expansion ROM
331 * BAR address will be used.
334 biosemu_dev_check_exprom(unsigned long rom_base_addr)
336 int i = 0;
337 translate_address_t ta;
338 u16 pci_ds_offset;
339 pci_data_struct_t pci_ds;
340 if (rom_base_addr == 0) {
341 // check for ExpROM Address (Offset 30) in taa
342 for (i = 0; i <= taa_last_entry; i++) {
343 ta = translate_address_array[i];
344 if (ta.cfg_space_offset == 0x30) {
345 //translated address
346 rom_base_addr = ta.address + ta.address_offset;
347 break;
351 /* In the ROM there could be multiple Expansion ROM Images... start
352 * searching them for an x86 image.
354 do {
355 if (rom_base_addr == 0) {
356 printf("Error: no Expansion ROM address found!\n");
357 return -1;
359 set_ci();
360 u16 rom_signature = in16le((void *) rom_base_addr);
361 clr_ci();
362 if (rom_signature != 0xaa55) {
363 printf
364 ("Error: invalid Expansion ROM signature: %02x!\n",
365 *((u16 *) rom_base_addr));
366 return -1;
368 set_ci();
369 // at offset 0x18 is the (16bit little-endian) pointer to the PCI Data Structure
370 pci_ds_offset = in16le((void *) (rom_base_addr + 0x18));
371 //copy the PCI Data Structure
372 memcpy(&pci_ds, (void *) (rom_base_addr + pci_ds_offset),
373 sizeof(pci_ds));
374 clr_ci();
375 #if IS_ENABLED(CONFIG_X86EMU_DEBUG)
376 DEBUG_PRINTF("PCI Data Structure @%lx:\n",
377 rom_base_addr + pci_ds_offset);
378 dump((void *) &pci_ds, sizeof(pci_ds));
379 #endif
380 if (strncmp((const char *) pci_ds.signature, "PCIR", 4) != 0) {
381 printf("Invalid PCI Data Structure found!\n");
382 break;
384 //little-endian conversion
385 pci_ds.vendor_id = in16le(&pci_ds.vendor_id);
386 pci_ds.device_id = in16le(&pci_ds.device_id);
387 pci_ds.img_length = in16le(&pci_ds.img_length);
388 pci_ds.pci_ds_length = in16le(&pci_ds.pci_ds_length);
389 #ifdef DO_THIS_TEST_TWICE
390 if (pci_ds.vendor_id != bios_device.pci_vendor_id) {
391 printf
392 ("Image has invalid Vendor ID: %04x, expected: %04x\n",
393 pci_ds.vendor_id, bios_device.pci_vendor_id);
394 break;
396 if (pci_ds.device_id != bios_device.pci_device_id) {
397 printf
398 ("Image has invalid Device ID: %04x, expected: %04x\n",
399 pci_ds.device_id, bios_device.pci_device_id);
400 break;
402 #endif
403 DEBUG_PRINTF("Image Length: %d\n", pci_ds.img_length * 512);
404 DEBUG_PRINTF("Image Code Type: %d\n", pci_ds.code_type);
405 if (pci_ds.code_type == 0) {
406 //x86 image
407 //store image address and image length in bios_device struct
408 bios_device.img_addr = rom_base_addr;
409 bios_device.img_size = pci_ds.img_length * 512;
410 // we found the image, exit the loop
411 break;
412 } else {
413 // no x86 image, check next image (if any)
414 rom_base_addr += pci_ds.img_length * 512;
416 if ((pci_ds.indicator & 0x80) == 0x80) {
417 //last image found, exit the loop
418 DEBUG_PRINTF("Last PCI Expansion ROM Image found.\n");
419 break;
422 while (bios_device.img_addr == 0);
423 // in case we did not find a valid x86 Expansion ROM Image
424 if (bios_device.img_addr == 0) {
425 printf("Error: no valid x86 Expansion ROM Image found!\n");
426 return -1;
428 return 0;
432 biosemu_dev_init(struct device * device)
434 u8 rval = 0;
435 //init bios_device struct
436 DEBUG_PRINTF("%s\n", __func__);
437 memset(&bios_device, 0, sizeof(bios_device));
439 #if !IS_ENABLED(CONFIG_PCI_OPTION_ROM_RUN_YABEL)
440 bios_device.ihandle = of_open(device_name);
441 if (bios_device.ihandle == 0) {
442 DEBUG_PRINTF("%s is no valid device!\n", device_name);
443 return -1;
445 bios_device.phandle = of_finddevice(device_name);
446 #else
447 bios_device.dev = device;
448 #endif
449 biosemu_dev_get_addr_info();
450 #if !IS_ENABLED(CONFIG_PCI_OPTION_ROM_RUN_YABEL)
451 biosemu_dev_find_vmem_addr();
452 biosemu_dev_get_puid();
453 #endif
454 biosemu_dev_get_device_vendor_id();
455 return rval;
458 // translate address function using translate_address_array assembled
459 // by dev_get_addr_info... MUCH faster than calling translate_address_dev
460 // and accessing client interface for every translation...
461 // returns: 0 if addr not found in translate_address_array, 1 if found.
463 biosemu_dev_translate_address(int type, unsigned long * addr)
465 int i = 0;
466 translate_address_t ta;
467 #if !IS_ENABLED(CONFIG_PCI_OPTION_ROM_RUN_YABEL)
468 /* we don't need this hack for coreboot... we can access legacy areas */
469 //check if it is an access to legacy VGA Mem... if it is, map the address
470 //to the vmem BAR and then translate it...
471 // (translation info provided by Ben Herrenschmidt)
472 // NOTE: the translation seems to only work for NVIDIA cards... but it is needed
473 // to make some NVIDIA cards work at all...
474 if ((bios_device.vmem_size > 0)
475 && ((*addr >= 0xA0000) && (*addr < 0xB8000))) {
476 *addr = (*addr - 0xA0000) * 4 + 2 + bios_device.vmem_addr;
478 if ((bios_device.vmem_size > 0)
479 && ((*addr >= 0xB8000) && (*addr < 0xC0000))) {
480 u8 shift = *addr & 1;
481 *addr &= 0xfffffffe;
482 *addr = (*addr - 0xB8000) * 4 + shift + bios_device.vmem_addr;
484 #endif
485 for (i = 0; i <= taa_last_entry; i++) {
486 ta = translate_address_array[i];
487 if ((*addr >= ta.address) && (*addr <= (ta.address + ta.size)) && (ta.info & type)) {
488 *addr += ta.address_offset;
489 return 1;
492 return 0;