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