blob: 2f41847438f13af491304ebee9f65a1b0781e35e [file] [log] [blame]
Stefan Reinauer38cd29e2009-08-11 21:28:25 +00001/******************************************************************************
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 "device.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000016#include "compat/rtas.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000017#include <string.h>
18#include "debug.h"
19
20#include <device/device.h>
21#include <device/pci.h>
22#include <device/pci_ops.h>
23#include <device/resource.h>
24
25/* the device we are working with... */
26biosemu_device_t bios_device;
Patrick Georgi91443042011-01-13 11:38:46 +000027//max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges, plus 2 "special" memory ranges
28translate_address_t translate_address_array[13];
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000029u8 taa_last_entry;
30
31typedef struct {
32 u8 info;
33 u8 bus;
34 u8 devfn;
35 u8 cfg_space_offset;
36 u64 address;
37 u64 size;
38} __attribute__ ((__packed__)) assigned_address_t;
39
Stefan Reinauer1d888a92011-04-21 20:24:43 +000040#if CONFIG_PCI_OPTION_ROM_RUN_YABEL
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000041/* coreboot version */
42
Uwe Hermann01ce6012010-03-05 10:03:50 +000043static void
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000044biosemu_dev_get_addr_info(void)
45{
46 int taa_index = 0;
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000047 struct resource *r;
Myles Watsonbd61a812010-08-30 21:52:38 +000048 u8 bus = bios_device.dev->bus->secondary;
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000049 u16 devfn = bios_device.dev->path.pci.devfn;
50
51 bios_device.bus = bus;
52 bios_device.devfn = devfn;
53
54 DEBUG_PRINTF("bus: %x, devfn: %x\n", bus, devfn);
Myles Watsonc25cc112010-05-21 14:33:48 +000055 for (r = bios_device.dev->resource_list; r; r = r->next) {
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000056 translate_address_array[taa_index].info = r->flags;
57 translate_address_array[taa_index].bus = bus;
58 translate_address_array[taa_index].devfn = devfn;
59 translate_address_array[taa_index].cfg_space_offset =
60 r->index;
61 translate_address_array[taa_index].address = r->base;
62 translate_address_array[taa_index].size = r->size;
Martin Roth63373ed2013-07-08 16:24:19 -060063 /* don't translate addresses... all addresses are 1:1 */
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000064 translate_address_array[taa_index].address_offset = 0;
65 taa_index++;
66 }
67 /* Expansion ROM */
68 translate_address_array[taa_index].info = IORESOURCE_MEM | IORESOURCE_READONLY;
69 translate_address_array[taa_index].bus = bus;
70 translate_address_array[taa_index].devfn = devfn;
71 translate_address_array[taa_index].cfg_space_offset = 0x30;
Stefan Reinauer775a7662010-01-19 21:14:24 +000072 translate_address_array[taa_index].address = bios_device.img_addr;
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000073 translate_address_array[taa_index].size = 0; /* TODO: do we need the size? */
Martin Roth63373ed2013-07-08 16:24:19 -060074 /* don't translate addresses... all addresses are 1:1 */
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000075 translate_address_array[taa_index].address_offset = 0;
76 taa_index++;
77 /* legacy ranges if its a VGA card... */
78 if ((bios_device.dev->class & 0xFF0000) == 0x030000) {
79 DEBUG_PRINTF("%s: VGA device found, adding legacy resources... \n", __func__);
80 /* I/O 0x3B0-0x3BB */
81 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_IO;
82 translate_address_array[taa_index].bus = bus;
83 translate_address_array[taa_index].devfn = devfn;
84 translate_address_array[taa_index].cfg_space_offset = 0;
85 translate_address_array[taa_index].address = 0x3b0;
86 translate_address_array[taa_index].size = 0xc;
Martin Roth63373ed2013-07-08 16:24:19 -060087 /* don't translate addresses... all addresses are 1:1 */
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000088 translate_address_array[taa_index].address_offset = 0;
89 taa_index++;
90 /* I/O 0x3C0-0x3DF */
91 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_IO;
92 translate_address_array[taa_index].bus = bus;
93 translate_address_array[taa_index].devfn = devfn;
94 translate_address_array[taa_index].cfg_space_offset = 0;
95 translate_address_array[taa_index].address = 0x3c0;
96 translate_address_array[taa_index].size = 0x20;
Martin Roth63373ed2013-07-08 16:24:19 -060097 /* don't translate addresses... all addresses are 1:1 */
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000098 translate_address_array[taa_index].address_offset = 0;
99 taa_index++;
100 /* Mem 0xA0000-0xBFFFF */
101 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_MEM;
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 = 0xa0000;
106 translate_address_array[taa_index].size = 0x20000;
Martin Roth63373ed2013-07-08 16:24:19 -0600107 /* don't translate addresses... all addresses are 1:1 */
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000108 translate_address_array[taa_index].address_offset = 0;
109 taa_index++;
110 }
111 // store last entry index of translate_address_array
112 taa_last_entry = taa_index - 1;
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000113#if CONFIG_X86EMU_DEBUG
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000114 //dump translate_address_array
115 printf("translate_address_array: \n");
116 translate_address_t ta;
Stefan Reinauerc56e5ad2010-05-27 15:41:15 +0000117 int i;
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000118 for (i = 0; i <= taa_last_entry; i++) {
119 ta = translate_address_array[i];
120 printf
121 ("%d: info: %08lx bus: %02x devfn: %02x cfg_space_offset: %02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n",
122 i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset,
123 ta.address, ta.address_offset, ta.size);
124 }
125#endif
126}
127#else
128// use translate_address_dev and get_puid from net-snk's net_support.c
129void translate_address_dev(u64 *, phandle_t);
130u64 get_puid(phandle_t node);
131
132
Martin Roth63373ed2013-07-08 16:24:19 -0600133// scan all addresses assigned to the device ("assigned-addresses" and "reg")
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000134// store in translate_address_array for faster translation using dev_translate_address
135void
136biosemu_dev_get_addr_info(void)
137{
138 // get bus/dev/fn from assigned-addresses
139 int32_t len;
140 //max. 6 BARs and 1 Exp.ROM plus CfgSpace and 3 legacy ranges
141 assigned_address_t buf[11];
142 len =
143 of_getprop(bios_device.phandle, "assigned-addresses", buf,
144 sizeof(buf));
145 bios_device.bus = buf[0].bus;
146 bios_device.devfn = buf[0].devfn;
147 DEBUG_PRINTF("bus: %x, devfn: %x\n", bios_device.bus,
148 bios_device.devfn);
149 //store address translations for all assigned-addresses and regs in
150 //translate_address_array for faster translation later on...
151 int i = 0;
152 // index to insert data into translate_address_array
153 int taa_index = 0;
154 u64 address_offset;
155 for (i = 0; i < (len / sizeof(assigned_address_t)); i++, taa_index++) {
156 //copy all info stored in assigned-addresses
157 translate_address_array[taa_index].info = buf[i].info;
158 translate_address_array[taa_index].bus = buf[i].bus;
159 translate_address_array[taa_index].devfn = buf[i].devfn;
160 translate_address_array[taa_index].cfg_space_offset =
161 buf[i].cfg_space_offset;
162 translate_address_array[taa_index].address = buf[i].address;
163 translate_address_array[taa_index].size = buf[i].size;
164 // translate first address and store it as address_offset
165 address_offset = buf[i].address;
166 translate_address_dev(&address_offset, bios_device.phandle);
167 translate_address_array[taa_index].address_offset =
168 address_offset - buf[i].address;
169 }
170 //get "reg" property
171 len = of_getprop(bios_device.phandle, "reg", buf, sizeof(buf));
172 for (i = 0; i < (len / sizeof(assigned_address_t)); i++) {
173 if ((buf[i].size == 0) || (buf[i].cfg_space_offset != 0)) {
Martin Roth63373ed2013-07-08 16:24:19 -0600174 // we don't care for ranges with size 0 and
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000175 // BARs and Expansion ROM must be in assigned-addresses... so in reg
176 // we only look for those without config space offset set...
177 // i.e. the legacy ranges
178 continue;
179 }
180 //copy all info stored in assigned-addresses
181 translate_address_array[taa_index].info = buf[i].info;
182 translate_address_array[taa_index].bus = buf[i].bus;
183 translate_address_array[taa_index].devfn = buf[i].devfn;
184 translate_address_array[taa_index].cfg_space_offset =
185 buf[i].cfg_space_offset;
186 translate_address_array[taa_index].address = buf[i].address;
187 translate_address_array[taa_index].size = buf[i].size;
188 // translate first address and store it as address_offset
189 address_offset = buf[i].address;
190 translate_address_dev(&address_offset, bios_device.phandle);
191 translate_address_array[taa_index].address_offset =
192 address_offset - buf[i].address;
193 taa_index++;
194 }
195 // store last entry index of translate_address_array
196 taa_last_entry = taa_index - 1;
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000197#if CONFIG_X86EMU_DEBUG
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000198 //dump translate_address_array
199 printf("translate_address_array: \n");
200 translate_address_t ta;
201 for (i = 0; i <= taa_last_entry; i++) {
202 ta = translate_address_array[i];
203 printf
204 ("%d: %02x%02x%02x%02x\n\taddr: %016llx\n\toffs: %016llx\n\tsize: %016llx\n",
205 i, ta.info, ta.bus, ta.devfn, ta.cfg_space_offset,
206 ta.address, ta.address_offset, ta.size);
207 }
208#endif
209}
210#endif
211
Patrick Georgi91443042011-01-13 11:38:46 +0000212// "special memory" is a hack to make some parts of memory fall through to real memory
213// (ie. no translation). Necessary if option ROMs attempt DMA there, map registers or
Martin Roth63373ed2013-07-08 16:24:19 -0600214// do similarly crazy things.
Patrick Georgi91443042011-01-13 11:38:46 +0000215void
216biosemu_add_special_memory(u32 start, u32 size)
217{
218 int taa_index = ++taa_last_entry;
219 translate_address_array[taa_index].info = IORESOURCE_FIXED | IORESOURCE_MEM;
220 translate_address_array[taa_index].bus = 0;
221 translate_address_array[taa_index].devfn = 0;
222 translate_address_array[taa_index].cfg_space_offset = 0;
223 translate_address_array[taa_index].address = start;
224 translate_address_array[taa_index].size = size;
Martin Roth63373ed2013-07-08 16:24:19 -0600225 /* don't translate addresses... all addresses are 1:1 */
Patrick Georgi91443042011-01-13 11:38:46 +0000226 translate_address_array[taa_index].address_offset = 0;
227}
228
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000229#if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000230// to simulate accesses to legacy VGA Memory (0xA0000-0xBFFFF)
231// we look for the first prefetchable memory BAR, if no prefetchable BAR found,
232// we use the first memory BAR
233// dev_translate_addr will translate accesses to the legacy VGA Memory into the found vmem BAR
Uwe Hermann01ce6012010-03-05 10:03:50 +0000234static void
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000235biosemu_dev_find_vmem_addr(void)
236{
237 int i = 0;
238 translate_address_t ta;
239 s8 tai_np = -1, tai_p = -1; // translate_address_array index for non-prefetchable and prefetchable memory
240 //search backwards to find first entry
241 for (i = taa_last_entry; i >= 0; i--) {
242 ta = translate_address_array[i];
243 if ((ta.cfg_space_offset >= 0x10)
244 && (ta.cfg_space_offset <= 0x24)) {
245 //only BARs
246 if ((ta.info & 0x03) >= 0x02) {
247 //32/64bit memory
248 tai_np = i;
249 if ((ta.info & 0x40) != 0) {
250 // prefetchable
251 tai_p = i;
252 }
253 }
254 }
255 }
256 if (tai_p != -1) {
257 ta = translate_address_array[tai_p];
258 bios_device.vmem_addr = ta.address;
259 bios_device.vmem_size = ta.size;
260 DEBUG_PRINTF
261 ("%s: Found prefetchable Virtual Legacy Memory BAR: %llx, size: %llx\n",
262 __func__, bios_device.vmem_addr,
263 bios_device.vmem_size);
264 } else if (tai_np != -1) {
265 ta = translate_address_array[tai_np];
266 bios_device.vmem_addr = ta.address;
267 bios_device.vmem_size = ta.size;
268 DEBUG_PRINTF
269 ("%s: Found non-prefetchable Virtual Legacy Memory BAR: %llx, size: %llx",
270 __func__, bios_device.vmem_addr,
271 bios_device.vmem_size);
272 }
273 // disable vmem
274 //bios_device.vmem_size = 0;
275}
276
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000277void
278biosemu_dev_get_puid(void)
279{
280 // get puid
281 bios_device.puid = get_puid(bios_device.phandle);
282 DEBUG_PRINTF("puid: 0x%llx\n", bios_device.puid);
283}
284#endif
285
Uwe Hermann01ce6012010-03-05 10:03:50 +0000286static void
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000287biosemu_dev_get_device_vendor_id(void)
288{
289
290 u32 pci_config_0;
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000291#if CONFIG_PCI_OPTION_ROM_RUN_YABEL
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000292 pci_config_0 = pci_read_config32(bios_device.dev, 0x0);
293#else
294 pci_config_0 =
295 rtas_pci_config_read(bios_device.puid, 4, bios_device.bus,
296 bios_device.devfn, 0x0);
297#endif
298 bios_device.pci_device_id =
299 (u16) ((pci_config_0 & 0xFFFF0000) >> 16);
300 bios_device.pci_vendor_id = (u16) (pci_config_0 & 0x0000FFFF);
301 DEBUG_PRINTF("PCI Device ID: %04x, PCI Vendor ID: %x\n",
302 bios_device.pci_device_id, bios_device.pci_vendor_id);
303}
304
305/* Check whether the device has a valid Expansion ROM and search the PCI Data
306 * Structure and any Expansion ROM Header (using dev_scan_exp_header()) for
307 * needed information. If the rom_addr parameter is != 0, it is the address of
308 * the Expansion ROM image and will be used, if it is == 0, the Expansion ROM
309 * BAR address will be used.
310 */
311u8
312biosemu_dev_check_exprom(unsigned long rom_base_addr)
313{
314 int i = 0;
315 translate_address_t ta;
316 u16 pci_ds_offset;
317 pci_data_struct_t pci_ds;
318 if (rom_base_addr == 0) {
319 // check for ExpROM Address (Offset 30) in taa
320 for (i = 0; i <= taa_last_entry; i++) {
321 ta = translate_address_array[i];
322 if (ta.cfg_space_offset == 0x30) {
323 //translated address
324 rom_base_addr = ta.address + ta.address_offset;
325 break;
326 }
327 }
328 }
329 /* In the ROM there could be multiple Expansion ROM Images... start
330 * searching them for an x86 image.
331 */
332 do {
333 if (rom_base_addr == 0) {
334 printf("Error: no Expansion ROM address found!\n");
335 return -1;
336 }
337 set_ci();
338 u16 rom_signature = in16le((void *) rom_base_addr);
339 clr_ci();
340 if (rom_signature != 0xaa55) {
341 printf
342 ("Error: invalid Expansion ROM signature: %02x!\n",
343 *((u16 *) rom_base_addr));
344 return -1;
345 }
346 set_ci();
347 // at offset 0x18 is the (16bit little-endian) pointer to the PCI Data Structure
348 pci_ds_offset = in16le((void *) (rom_base_addr + 0x18));
349 //copy the PCI Data Structure
350 memcpy(&pci_ds, (void *) (rom_base_addr + pci_ds_offset),
351 sizeof(pci_ds));
352 clr_ci();
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000353#if CONFIG_X86EMU_DEBUG
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000354 DEBUG_PRINTF("PCI Data Structure @%lx:\n",
355 rom_base_addr + pci_ds_offset);
356 dump((void *) &pci_ds, sizeof(pci_ds));
357#endif
358 if (strncmp((const char *) pci_ds.signature, "PCIR", 4) != 0) {
359 printf("Invalid PCI Data Structure found!\n");
360 break;
361 }
362 //little-endian conversion
363 pci_ds.vendor_id = in16le(&pci_ds.vendor_id);
364 pci_ds.device_id = in16le(&pci_ds.device_id);
365 pci_ds.img_length = in16le(&pci_ds.img_length);
366 pci_ds.pci_ds_length = in16le(&pci_ds.pci_ds_length);
Stefan Reinauerc1efb902011-10-12 14:30:59 -0700367#ifdef DO_THIS_TEST_TWICE
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000368 if (pci_ds.vendor_id != bios_device.pci_vendor_id) {
369 printf
370 ("Image has invalid Vendor ID: %04x, expected: %04x\n",
371 pci_ds.vendor_id, bios_device.pci_vendor_id);
372 break;
373 }
374 if (pci_ds.device_id != bios_device.pci_device_id) {
375 printf
376 ("Image has invalid Device ID: %04x, expected: %04x\n",
377 pci_ds.device_id, bios_device.pci_device_id);
378 break;
379 }
Stefan Reinauerc1efb902011-10-12 14:30:59 -0700380#endif
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000381 DEBUG_PRINTF("Image Length: %d\n", pci_ds.img_length * 512);
382 DEBUG_PRINTF("Image Code Type: %d\n", pci_ds.code_type);
383 if (pci_ds.code_type == 0) {
384 //x86 image
385 //store image address and image length in bios_device struct
386 bios_device.img_addr = rom_base_addr;
387 bios_device.img_size = pci_ds.img_length * 512;
388 // we found the image, exit the loop
389 break;
390 } else {
391 // no x86 image, check next image (if any)
392 rom_base_addr += pci_ds.img_length * 512;
393 }
394 if ((pci_ds.indicator & 0x80) == 0x80) {
395 //last image found, exit the loop
396 DEBUG_PRINTF("Last PCI Expansion ROM Image found.\n");
397 break;
398 }
399 }
400 while (bios_device.img_addr == 0);
401 // in case we did not find a valid x86 Expansion ROM Image
402 if (bios_device.img_addr == 0) {
403 printf("Error: no valid x86 Expansion ROM Image found!\n");
404 return -1;
405 }
406 return 0;
407}
408
409u8
410biosemu_dev_init(struct device * device)
411{
412 u8 rval = 0;
413 //init bios_device struct
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000414 DEBUG_PRINTF("%s\n", __func__);
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000415 memset(&bios_device, 0, sizeof(bios_device));
416
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000417#if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000418 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();
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000428#if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000429 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
Patrick Georgi91443042011-01-13 11:38:46 +0000441biosemu_dev_translate_address(int type, unsigned long * addr)
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000442{
443 int i = 0;
444 translate_address_t ta;
Stefan Reinauer1d888a92011-04-21 20:24:43 +0000445#if !CONFIG_PCI_OPTION_ROM_RUN_YABEL
Martin Roth63373ed2013-07-08 16:24:19 -0600446 /* we don't need this hack for coreboot... we can access legacy areas */
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000447 //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];
Patrick Georgi91443042011-01-13 11:38:46 +0000465 if ((*addr >= ta.address) && (*addr <= (ta.address + ta.size)) && (ta.info & type)) {
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000466 *addr += ta.address_offset;
467 return 1;
468 }
469 }
470 return 0;
471}