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