blob: ecd2481a561fb22733d936ee74396a4c58afef75 [file] [log] [blame]
Stefan Reinauer05082732015-10-21 13:00:41 -07001/******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2008, 2009 Pattrick Hueper <phueper@hueper.net>
4 * All rights reserved.
5 * This program and the accompanying materials
6 * are made available under the terms of the BSD License
7 * which accompanies this distribution, and is available at
8 * http://www.opensource.org/licenses/bsd-license.php
9 *
10 * Contributors:
11 * IBM Corporation - initial implementation
12 *****************************************************************************/
13
14
15#include <stdtypes.h>
16
17#include <arch/byteorder.h>
18#include "device.h"
19#include "compat/rtas.h"
20#include <string.h>
21#include "debug.h"
22
23#include <device/device.h>
24#include <device/pci.h>
25#include <device/pci_ops.h>
26#include <device/resource.h>
27
28/* the device we are working with... */
29biosemu_device_t bios_device;
30//max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges, plus 2 "special" memory ranges
31translate_address_t translate_address_array[13];
32u8 taa_last_entry;
33
34typedef struct {
35 u8 info;
36 u8 bus;
37 u8 devfn;
38 u8 cfg_space_offset;
39 u64 address;
40 u64 size;
41} __attribute__ ((__packed__)) assigned_address_t;
42
43#if CONFIG_PCI_OPTION_ROM_RUN_YABEL
44/* coreboot version */
45
46static void
47biosemu_dev_get_addr_info(void)
48{
49#if 0
50 int taa_index = 0;
51 struct resource *r;
52 u8 bus = bios_device.dev->bus->secondary;
53 u16 devfn = bios_device.dev->path.pci.devfn;
54
55 bios_device.bus = bus;
56 bios_device.devfn = devfn;
57
58 DEBUG_PRINTF("bus: %x, devfn: %x\n", bus, devfn);
59 for (r = bios_device.dev->resource_list; r; r = r->next) {
60 translate_address_array[taa_index].info = r->flags;
61 translate_address_array[taa_index].bus = bus;
62 translate_address_array[taa_index].devfn = devfn;
63 translate_address_array[taa_index].cfg_space_offset =
64 r->index;
65 translate_address_array[taa_index].address = r->base;
66 translate_address_array[taa_index].size = r->size;
67 /* don't translate addresses... all addresses are 1:1 */
68 translate_address_array[taa_index].address_offset = 0;
69 taa_index++;
70 }
71 /* Expansion ROM */
72 translate_address_array[taa_index].info = IORESOURCE_MEM | IORESOURCE_READONLY;
73 translate_address_array[taa_index].bus = bus;
74 translate_address_array[taa_index].devfn = devfn;
75 translate_address_array[taa_index].cfg_space_offset = 0x30;
76 translate_address_array[taa_index].address = bios_device.img_addr;
77 translate_address_array[taa_index].size = 0; /* TODO: do we need the size? */
78 /* don't translate addresses... all addresses are 1:1 */
79 translate_address_array[taa_index].address_offset = 0;
80 taa_index++;
81 /* legacy ranges if its a VGA card... */
82 if ((bios_device.dev->class & 0xFF0000) == 0x030000) {
Elyes HAOUASb0f19882018-06-09 11:59:00 +020083 DEBUG_PRINTF("%s: VGA device found, adding legacy resources..."
84 "\n", __func__);
Stefan Reinauer05082732015-10-21 13:00:41 -070085 /* I/O 0x3B0-0x3BB */
86 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_IO;
87 translate_address_array[taa_index].bus = bus;
88 translate_address_array[taa_index].devfn = devfn;
89 translate_address_array[taa_index].cfg_space_offset = 0;
90 translate_address_array[taa_index].address = 0x3b0;
91 translate_address_array[taa_index].size = 0xc;
92 /* don't translate addresses... all addresses are 1:1 */
93 translate_address_array[taa_index].address_offset = 0;
94 taa_index++;
95 /* I/O 0x3C0-0x3DF */
96 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_IO;
97 translate_address_array[taa_index].bus = bus;
98 translate_address_array[taa_index].devfn = devfn;
99 translate_address_array[taa_index].cfg_space_offset = 0;
100 translate_address_array[taa_index].address = 0x3c0;
101 translate_address_array[taa_index].size = 0x20;
102 /* don't translate addresses... all addresses are 1:1 */
103 translate_address_array[taa_index].address_offset = 0;
104 taa_index++;
105 /* Mem 0xA0000-0xBFFFF */
106 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_MEM;
107 translate_address_array[taa_index].bus = bus;
108 translate_address_array[taa_index].devfn = devfn;
109 translate_address_array[taa_index].cfg_space_offset = 0;
110 translate_address_array[taa_index].address = 0xa0000;
111 translate_address_array[taa_index].size = 0x20000;
112 /* don't translate addresses... all addresses are 1:1 */
113 translate_address_array[taa_index].address_offset = 0;
114 taa_index++;
115 }
116 // store last entry index of translate_address_array
117 taa_last_entry = taa_index - 1;
118#if CONFIG_X86EMU_DEBUG
119 //dump translate_address_array
Elyes HAOUASb0f19882018-06-09 11:59:00 +0200120 printf("translate_address_array:\n");
Stefan Reinauer05082732015-10-21 13:00:41 -0700121 translate_address_t ta;
122 int i;
123 for (i = 0; i <= taa_last_entry; i++) {
124 ta = translate_address_array[i];
125 printf
126 ("%d: info: %08lx bus: %02x devfn: %02x cfg_space_offset: %02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n",
127 i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset,
128 ta.address, ta.address_offset, ta.size);
129 }
130#endif
131#endif
132}
133#else
134// use translate_address_dev and get_puid from net-snk's net_support.c
135void translate_address_dev(u64 *, phandle_t);
136u64 get_puid(phandle_t node);
137
138
139// scan all addresses assigned to the device ("assigned-addresses" and "reg")
140// store in translate_address_array for faster translation using dev_translate_address
141void
142biosemu_dev_get_addr_info(void)
143{
144 // get bus/dev/fn from assigned-addresses
145 int32_t len;
146 //max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges
147 assigned_address_t buf[11];
148 len =
149 of_getprop(bios_device.phandle, "assigned-addresses", buf,
150 sizeof(buf));
151 bios_device.bus = buf[0].bus;
152 bios_device.devfn = buf[0].devfn;
153 DEBUG_PRINTF("bus: %x, devfn: %x\n", bios_device.bus,
154 bios_device.devfn);
155 //store address translations for all assigned-addresses and regs in
156 //translate_address_array for faster translation later on...
157 int i = 0;
158 // index to insert data into translate_address_array
159 int taa_index = 0;
160 u64 address_offset;
161 for (i = 0; i < (len / sizeof(assigned_address_t)); i++, taa_index++) {
162 //copy all info stored in assigned-addresses
163 translate_address_array[taa_index].info = buf[i].info;
164 translate_address_array[taa_index].bus = buf[i].bus;
165 translate_address_array[taa_index].devfn = buf[i].devfn;
166 translate_address_array[taa_index].cfg_space_offset =
167 buf[i].cfg_space_offset;
168 translate_address_array[taa_index].address = buf[i].address;
169 translate_address_array[taa_index].size = buf[i].size;
170 // translate first address and store it as address_offset
171 address_offset = buf[i].address;
172 translate_address_dev(&address_offset, bios_device.phandle);
173 translate_address_array[taa_index].address_offset =
174 address_offset - buf[i].address;
175 }
176 //get "reg" property
177 len = of_getprop(bios_device.phandle, "reg", buf, sizeof(buf));
178 for (i = 0; i < (len / sizeof(assigned_address_t)); i++) {
179 if ((buf[i].size == 0) || (buf[i].cfg_space_offset != 0)) {
180 // we don't care for ranges with size 0 and
181 // BARs and Expansion ROM must be in assigned-addresses... so in reg
182 // we only look for those without config space offset set...
183 // i.e. the legacy ranges
184 continue;
185 }
186 //copy all info stored in assigned-addresses
187 translate_address_array[taa_index].info = buf[i].info;
188 translate_address_array[taa_index].bus = buf[i].bus;
189 translate_address_array[taa_index].devfn = buf[i].devfn;
190 translate_address_array[taa_index].cfg_space_offset =
191 buf[i].cfg_space_offset;
192 translate_address_array[taa_index].address = buf[i].address;
193 translate_address_array[taa_index].size = buf[i].size;
194 // translate first address and store it as address_offset
195 address_offset = buf[i].address;
196 translate_address_dev(&address_offset, bios_device.phandle);
197 translate_address_array[taa_index].address_offset =
198 address_offset - buf[i].address;
199 taa_index++;
200 }
201 // store last entry index of translate_address_array
202 taa_last_entry = taa_index - 1;
203#if CONFIG_X86EMU_DEBUG
204 //dump translate_address_array
Elyes HAOUASb0f19882018-06-09 11:59:00 +0200205 printf("translate_address_array:\n");
Stefan Reinauer05082732015-10-21 13:00:41 -0700206 translate_address_t ta;
207 for (i = 0; i <= taa_last_entry; i++) {
208 ta = translate_address_array[i];
209 printf
210 ("%d: %02x%02x%02x%02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n",
211 i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset,
212 ta.address, ta.address_offset, ta.size);
213 }
214#endif
215}
216#endif
217
218// "special memory" is a hack to make some parts of memory fall through to real memory
219// (ie. no translation). Necessary if option ROMs attempt DMA there, map registers or
220// do similarly crazy things.
221void
222biosemu_add_special_memory(u32 start, u32 size)
223{
224#if 0
225 int taa_index = ++taa_last_entry;
226 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_MEM;
227 translate_address_array[taa_index].bus = 0;
228 translate_address_array[taa_index].devfn = 0;
229 translate_address_array[taa_index].cfg_space_offset = 0;
230 translate_address_array[taa_index].address = start;
231 translate_address_array[taa_index].size = size;
232 /* don't translate addresses... all addresses are 1:1 */
233 translate_address_array[taa_index].address_offset = 0;
234#endif
235 printf("TODO: Add special memory handler for %x[%x]\n", start, size);
236}
237
238#if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
239// to simulate accesses to legacy VGA Memory (0xA0000-0xBFFFF)
240// we look for the first prefetchable memory BAR, if no prefetchable BAR found,
241// we use the first memory BAR
242// dev_translate_addr will translate accesses to the legacy VGA Memory into the found vmem BAR
243static void
244biosemu_dev_find_vmem_addr(void)
245{
246 int i = 0;
247 translate_address_t ta;
248 s8 tai_np = -1, tai_p = -1; // translate_address_array index for non-prefetchable and prefetchable memory
249 //search backwards to find first entry
250 for (i = taa_last_entry; i >= 0; i--) {
251 ta = translate_address_array[i];
252 if ((ta.cfg_space_offset >= 0x10)
253 && (ta.cfg_space_offset <= 0x24)) {
254 //only BARs
255 if ((ta.info & 0x03) >= 0x02) {
256 //32/64bit memory
257 tai_np = i;
258 if ((ta.info & 0x40) != 0) {
259 // prefetchable
260 tai_p = i;
261 }
262 }
263 }
264 }
265 if (tai_p != -1) {
266 ta = translate_address_array[tai_p];
267 bios_device.vmem_addr = ta.address;
268 bios_device.vmem_size = ta.size;
269 DEBUG_PRINTF
270 ("%s: Found prefetchable Virtual Legacy Memory BAR: %llx, size: %llx\n",
271 __func__, bios_device.vmem_addr,
272 bios_device.vmem_size);
273 } else if (tai_np != -1) {
274 ta = translate_address_array[tai_np];
275 bios_device.vmem_addr = ta.address;
276 bios_device.vmem_size = ta.size;
277 DEBUG_PRINTF
278 ("%s: Found non-prefetchable Virtual Legacy Memory BAR: %llx, size: %llx",
279 __func__, bios_device.vmem_addr,
280 bios_device.vmem_size);
281 }
282 // disable vmem
283 //bios_device.vmem_size = 0;
284}
285
286void
287biosemu_dev_get_puid(void)
288{
289 // get puid
290 bios_device.puid = get_puid(bios_device.phandle);
291 DEBUG_PRINTF("puid: 0x%llx\n", bios_device.puid);
292}
293#endif
294
295static void
296biosemu_dev_get_device_vendor_id(void)
297{
298
299 u32 pci_config_0;
300#if CONFIG_PCI_OPTION_ROM_RUN_YABEL
301 pci_config_0 = pci_read_config32(bios_device.dev, 0x0);
302#else
303 pci_config_0 =
304 rtas_pci_config_read(bios_device.puid, 4, bios_device.bus,
305 bios_device.devfn, 0x0);
306#endif
307 bios_device.pci_device_id =
308 (u16) ((pci_config_0 & 0xFFFF0000) >> 16);
309 bios_device.pci_vendor_id = (u16) (pci_config_0 & 0x0000FFFF);
310 DEBUG_PRINTF("PCI Device ID: %04x, PCI Vendor ID: %x\n",
311 bios_device.pci_device_id, bios_device.pci_vendor_id);
312}
313
314/* Check whether the device has a valid Expansion ROM and search the PCI Data
315 * Structure and any Expansion ROM Header (using dev_scan_exp_header()) for
316 * needed information. If the rom_addr parameter is != 0, it is the address of
317 * the Expansion ROM image and will be used, if it is == 0, the Expansion ROM
318 * BAR address will be used.
319 */
320u8
321biosemu_dev_check_exprom(unsigned long rom_base_addr)
322{
323#if 0
324 int i = 0;
325 translate_address_t ta;
326 u16 pci_ds_offset;
327 pci_data_struct_t pci_ds;
328 if (rom_base_addr == 0) {
329 // check for ExpROM Address (Offset 30) in taa
330 for (i = 0; i <= taa_last_entry; i++) {
331 ta = translate_address_array[i];
332 if (ta.cfg_space_offset == 0x30) {
333 //translated address
334 rom_base_addr = ta.address + ta.address_offset;
335 break;
336 }
337 }
338 }
339 /* In the ROM there could be multiple Expansion ROM Images... start
340 * searching them for an x86 image.
341 */
342 do {
343 if (rom_base_addr == 0) {
344 printf("Error: no Expansion ROM address found!\n");
345 return -1;
346 }
347 set_ci();
348 u16 rom_signature = in16le((void *) rom_base_addr);
349 clr_ci();
350 if (rom_signature != 0xaa55) {
351 printf
352 ("Error: invalid Expansion ROM signature: %02x!\n",
353 *((u16 *) rom_base_addr));
354 return -1;
355 }
356 set_ci();
357 // at offset 0x18 is the (16bit little-endian) pointer to the PCI Data Structure
358 pci_ds_offset = in16le((void *) (rom_base_addr + 0x18));
359 //copy the PCI Data Structure
360 memcpy(&pci_ds, (void *) (rom_base_addr + pci_ds_offset),
361 sizeof(pci_ds));
362 clr_ci();
363#if CONFIG_X86EMU_DEBUG
364 DEBUG_PRINTF("PCI Data Structure @%lx:\n",
365 rom_base_addr + pci_ds_offset);
366 dump((void *) &pci_ds, sizeof(pci_ds));
367#endif
368 if (strncmp((const char *) pci_ds.signature, "PCIR", 4) != 0) {
369 printf("Invalid PCI Data Structure found!\n");
370 break;
371 }
372 //little-endian conversion
373 pci_ds.vendor_id = in16le(&pci_ds.vendor_id);
374 pci_ds.device_id = in16le(&pci_ds.device_id);
375 pci_ds.img_length = in16le(&pci_ds.img_length);
376 pci_ds.pci_ds_length = in16le(&pci_ds.pci_ds_length);
377#ifdef DO_THIS_TEST_TWICE
378 if (pci_ds.vendor_id != bios_device.pci_vendor_id) {
379 printf
380 ("Image has invalid Vendor ID: %04x, expected: %04x\n",
381 pci_ds.vendor_id, bios_device.pci_vendor_id);
382 break;
383 }
384 if (pci_ds.device_id != bios_device.pci_device_id) {
385 printf
386 ("Image has invalid Device ID: %04x, expected: %04x\n",
387 pci_ds.device_id, bios_device.pci_device_id);
388 break;
389 }
390#endif
391 DEBUG_PRINTF("Image Length: %d\n", pci_ds.img_length * 512);
392 DEBUG_PRINTF("Image Code Type: %d\n", pci_ds.code_type);
393 if (pci_ds.code_type == 0) {
394 //x86 image
395 //store image address and image length in bios_device struct
396 bios_device.img_addr = rom_base_addr;
397 bios_device.img_size = pci_ds.img_length * 512;
398 // we found the image, exit the loop
399 break;
400 } else {
401 // no x86 image, check next image (if any)
402 rom_base_addr += pci_ds.img_length * 512;
403 }
404 if ((pci_ds.indicator & 0x80) == 0x80) {
405 //last image found, exit the loop
406 DEBUG_PRINTF("Last PCI Expansion ROM Image found.\n");
407 break;
408 }
409 }
410 while (bios_device.img_addr == 0);
411 // in case we did not find a valid x86 Expansion ROM Image
412 if (bios_device.img_addr == 0) {
413 printf("Error: no valid x86 Expansion ROM Image found!\n");
414 return -1;
415 }
416#endif
417 return 0;
418
419}
420
421u8
422biosemu_dev_init(struct device * device)
423{
424 u8 rval = 0;
425 //init bios_device struct
426 DEBUG_PRINTF("%s\n", __func__);
427 memset(&bios_device, 0, sizeof(bios_device));
428
429#if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
430 bios_device.ihandle = of_open(device_name);
431 if (bios_device.ihandle == 0) {
432 DEBUG_PRINTF("%s is no valid device!\n", device_name);
433 return -1;
434 }
435 bios_device.phandle = of_finddevice(device_name);
436#else
437 bios_device.dev = device;
438#endif
439 biosemu_dev_get_addr_info();
440#if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
441 biosemu_dev_find_vmem_addr();
442 biosemu_dev_get_puid();
443#endif
444 biosemu_dev_get_device_vendor_id();
445 return rval;
446}
447
448// translate address function using translate_address_array assembled
449// by dev_get_addr_info... MUCH faster than calling translate_address_dev
450// and accessing client interface for every translation...
451// returns: 0 if addr not found in translate_address_array, 1 if found.
452u8
453biosemu_dev_translate_address(int type, unsigned long * addr)
454{
455 int i = 0;
456 translate_address_t ta;
457#if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
458 /* we don't need this hack for coreboot... we can access legacy areas */
459 //check if it is an access to legacy VGA Mem... if it is, map the address
460 //to the vmem BAR and then translate it...
461 // (translation info provided by Ben Herrenschmidt)
462 // NOTE: the translation seems to only work for NVIDIA cards... but it is needed
463 // to make some NVIDIA cards work at all...
464 if ((bios_device.vmem_size > 0)
465 && ((*addr >= 0xA0000) && (*addr < 0xB8000))) {
466 *addr = (*addr - 0xA0000) * 4 + 2 + bios_device.vmem_addr;
467 }
468 if ((bios_device.vmem_size > 0)
469 && ((*addr >= 0xB8000) && (*addr < 0xC0000))) {
470 u8 shift = *addr & 1;
471 *addr &= 0xfffffffe;
472 *addr = (*addr - 0xB8000) * 4 + shift + bios_device.vmem_addr;
473 }
474#endif
475 for (i = 0; i <= taa_last_entry; i++) {
476 ta = translate_address_array[i];
477 if ((*addr >= ta.address) && (*addr <= (ta.address + ta.size)) && (ta.info & type)) {
478 *addr += ta.address_offset;
479 return 1;
480 }
481 }
482 return 0;
483}