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