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