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