linux/drivers/gpu/drm/nouveau/core/subdev/bios/base.c
<<
>>
Prefs
   1/*
   2 * Copyright 2012 Red Hat Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: Ben Skeggs
  23 */
  24
  25#include <core/object.h>
  26#include <core/device.h>
  27#include <core/subdev.h>
  28#include <core/option.h>
  29
  30#include <subdev/bios.h>
  31#include <subdev/bios/bmp.h>
  32#include <subdev/bios/bit.h>
  33
  34u8
  35nvbios_checksum(const u8 *data, int size)
  36{
  37        u8 sum = 0;
  38        while (size--)
  39                sum += *data++;
  40        return sum;
  41}
  42
  43u16
  44nvbios_findstr(const u8 *data, int size, const char *str, int len)
  45{
  46        int i, j;
  47
  48        for (i = 0; i <= (size - len); i++) {
  49                for (j = 0; j < len; j++)
  50                        if ((char)data[i + j] != str[j])
  51                                break;
  52                if (j == len)
  53                        return i;
  54        }
  55
  56        return 0;
  57}
  58
  59#if defined(__powerpc__)
  60static void
  61nouveau_bios_shadow_of(struct nouveau_bios *bios)
  62{
  63        struct pci_dev *pdev = nv_device(bios)->pdev;
  64        struct device_node *dn;
  65        const u32 *data;
  66        int size;
  67
  68        dn = pci_device_to_OF_node(pdev);
  69        if (!dn) {
  70                nv_info(bios, "Unable to get the OF node\n");
  71                return;
  72        }
  73
  74        data = of_get_property(dn, "NVDA,BMP", &size);
  75        if (data && size) {
  76                bios->size = size;
  77                bios->data = kmalloc(bios->size, GFP_KERNEL);
  78                if (bios->data)
  79                        memcpy(bios->data, data, size);
  80        }
  81}
  82#endif
  83
  84static void
  85nouveau_bios_shadow_pramin(struct nouveau_bios *bios)
  86{
  87        struct nouveau_device *device = nv_device(bios);
  88        u32 bar0 = 0;
  89        int i;
  90
  91        if (device->card_type >= NV_50) {
  92                u64 addr = (u64)(nv_rd32(bios, 0x619f04) & 0xffffff00) << 8;
  93                if (!addr) {
  94                        addr  = (u64)nv_rd32(bios, 0x001700) << 16;
  95                        addr += 0xf0000;
  96                }
  97
  98                bar0 = nv_mask(bios, 0x001700, 0xffffffff, addr >> 16);
  99        }
 100
 101        /* bail if no rom signature */
 102        if (nv_rd08(bios, 0x700000) != 0x55 ||
 103            nv_rd08(bios, 0x700001) != 0xaa)
 104                goto out;
 105
 106        bios->size = nv_rd08(bios, 0x700002) * 512;
 107        if (!bios->size)
 108                goto out;
 109
 110        bios->data = kmalloc(bios->size, GFP_KERNEL);
 111        if (bios->data) {
 112                for (i = 0; i < bios->size; i++)
 113                        nv_wo08(bios, i, nv_rd08(bios, 0x700000 + i));
 114        }
 115
 116out:
 117        if (device->card_type >= NV_50)
 118                nv_wr32(bios, 0x001700, bar0);
 119}
 120
 121static void
 122nouveau_bios_shadow_prom(struct nouveau_bios *bios)
 123{
 124        struct nouveau_device *device = nv_device(bios);
 125        u32 pcireg, access;
 126        u16 pcir;
 127        int i;
 128
 129        /* enable access to rom */
 130        if (device->card_type >= NV_50)
 131                pcireg = 0x088050;
 132        else
 133                pcireg = 0x001850;
 134        access = nv_mask(bios, pcireg, 0x00000001, 0x00000000);
 135
 136        /* bail if no rom signature, with a workaround for a PROM reading
 137         * issue on some chipsets.  the first read after a period of
 138         * inactivity returns the wrong result, so retry the first header
 139         * byte a few times before giving up as a workaround
 140         */
 141        i = 16;
 142        do {
 143                if (nv_rd08(bios, 0x300000) == 0x55)
 144                        break;
 145        } while (i--);
 146
 147        if (!i || nv_rd08(bios, 0x300001) != 0xaa)
 148                goto out;
 149
 150        /* additional check (see note below) - read PCI record header */
 151        pcir = nv_rd08(bios, 0x300018) |
 152               nv_rd08(bios, 0x300019) << 8;
 153        if (nv_rd08(bios, 0x300000 + pcir) != 'P' ||
 154            nv_rd08(bios, 0x300001 + pcir) != 'C' ||
 155            nv_rd08(bios, 0x300002 + pcir) != 'I' ||
 156            nv_rd08(bios, 0x300003 + pcir) != 'R')
 157                goto out;
 158
 159        /* read entire bios image to system memory */
 160        bios->size = nv_rd08(bios, 0x300002) * 512;
 161        if (!bios->size)
 162                goto out;
 163
 164        bios->data = kmalloc(bios->size, GFP_KERNEL);
 165        if (bios->data) {
 166                for (i = 0; i < bios->size; i++)
 167                        nv_wo08(bios, i, nv_rd08(bios, 0x300000 + i));
 168        }
 169
 170out:
 171        /* disable access to rom */
 172        nv_wr32(bios, pcireg, access);
 173}
 174
 175#if defined(CONFIG_ACPI) && defined(CONFIG_X86)
 176int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len);
 177bool nouveau_acpi_rom_supported(struct pci_dev *pdev);
 178#else
 179static inline bool
 180nouveau_acpi_rom_supported(struct pci_dev *pdev) {
 181        return false;
 182}
 183
 184static inline int
 185nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) {
 186        return -EINVAL;
 187}
 188#endif
 189
 190static void
 191nouveau_bios_shadow_acpi(struct nouveau_bios *bios)
 192{
 193        struct pci_dev *pdev = nv_device(bios)->pdev;
 194        int ret, cnt, i;
 195
 196        if (!nouveau_acpi_rom_supported(pdev)) {
 197                bios->data = NULL;
 198                return;
 199        }
 200
 201        bios->size = 0;
 202        bios->data = kmalloc(4096, GFP_KERNEL);
 203        if (bios->data) {
 204                if (nouveau_acpi_get_bios_chunk(bios->data, 0, 4096) == 4096)
 205                        bios->size = bios->data[2] * 512;
 206                kfree(bios->data);
 207        }
 208
 209        if (!bios->size)
 210                return;
 211
 212        bios->data = kmalloc(bios->size, GFP_KERNEL);
 213        if (bios->data) {
 214                /* disobey the acpi spec - much faster on at least w530 ... */
 215                ret = nouveau_acpi_get_bios_chunk(bios->data, 0, bios->size);
 216                if (ret != bios->size ||
 217                    nvbios_checksum(bios->data, bios->size)) {
 218                        /* ... that didn't work, ok, i'll be good now */
 219                        for (i = 0; i < bios->size; i += cnt) {
 220                                cnt = min((bios->size - i), (u32)4096);
 221                                ret = nouveau_acpi_get_bios_chunk(bios->data, i, cnt);
 222                                if (ret != cnt)
 223                                        break;
 224                        }
 225                }
 226        }
 227}
 228
 229static void
 230nouveau_bios_shadow_pci(struct nouveau_bios *bios)
 231{
 232        struct pci_dev *pdev = nv_device(bios)->pdev;
 233        size_t size;
 234
 235        if (!pci_enable_rom(pdev)) {
 236                void __iomem *rom = pci_map_rom(pdev, &size);
 237                if (rom && size) {
 238                        bios->data = kmalloc(size, GFP_KERNEL);
 239                        if (bios->data) {
 240                                memcpy_fromio(bios->data, rom, size);
 241                                bios->size = size;
 242                        }
 243                }
 244                if (rom)
 245                        pci_unmap_rom(pdev, rom);
 246
 247                pci_disable_rom(pdev);
 248        }
 249}
 250
 251static void
 252nouveau_bios_shadow_platform(struct nouveau_bios *bios)
 253{
 254        struct pci_dev *pdev = nv_device(bios)->pdev;
 255        size_t size;
 256
 257        void __iomem *rom = pci_platform_rom(pdev, &size);
 258        if (rom && size) {
 259                bios->data = kmalloc(size, GFP_KERNEL);
 260                if (bios->data) {
 261                        memcpy_fromio(bios->data, rom, size);
 262                        bios->size = size;
 263                }
 264        }
 265}
 266
 267static int
 268nouveau_bios_score(struct nouveau_bios *bios, const bool writeable)
 269{
 270        if (bios->size < 3 || !bios->data || bios->data[0] != 0x55 ||
 271                        bios->data[1] != 0xAA) {
 272                nv_info(bios, "... signature not found\n");
 273                return 0;
 274        }
 275
 276        if (nvbios_checksum(bios->data,
 277                        min_t(u32, bios->data[2] * 512, bios->size))) {
 278                nv_info(bios, "... checksum invalid\n");
 279                /* if a ro image is somewhat bad, it's probably all rubbish */
 280                return writeable ? 2 : 1;
 281        }
 282
 283        nv_info(bios, "... appears to be valid\n");
 284        return 3;
 285}
 286
 287struct methods {
 288        const char desc[16];
 289        void (*shadow)(struct nouveau_bios *);
 290        const bool rw;
 291        int score;
 292        u32 size;
 293        u8 *data;
 294};
 295
 296static int
 297nouveau_bios_shadow(struct nouveau_bios *bios)
 298{
 299        struct methods shadow_methods[] = {
 300#if defined(__powerpc__)
 301                { "OpenFirmware", nouveau_bios_shadow_of, true, 0, 0, NULL },
 302#endif
 303                { "PRAMIN", nouveau_bios_shadow_pramin, true, 0, 0, NULL },
 304                { "PROM", nouveau_bios_shadow_prom, false, 0, 0, NULL },
 305                { "ACPI", nouveau_bios_shadow_acpi, true, 0, 0, NULL },
 306                { "PCIROM", nouveau_bios_shadow_pci, true, 0, 0, NULL },
 307                { "PLATFORM", nouveau_bios_shadow_platform, true, 0, 0, NULL },
 308                {}
 309        };
 310        struct methods *mthd, *best;
 311        const struct firmware *fw;
 312        const char *optarg;
 313        int optlen, ret;
 314        char *source;
 315
 316        optarg = nouveau_stropt(nv_device(bios)->cfgopt, "NvBios", &optlen);
 317        source = optarg ? kstrndup(optarg, optlen, GFP_KERNEL) : NULL;
 318        if (source) {
 319                /* try to match one of the built-in methods */
 320                mthd = shadow_methods;
 321                do {
 322                        if (strcasecmp(source, mthd->desc))
 323                                continue;
 324                        nv_info(bios, "source: %s\n", mthd->desc);
 325
 326                        mthd->shadow(bios);
 327                        mthd->score = nouveau_bios_score(bios, mthd->rw);
 328                        if (mthd->score) {
 329                                kfree(source);
 330                                return 0;
 331                        }
 332                } while ((++mthd)->shadow);
 333
 334                /* attempt to load firmware image */
 335                ret = request_firmware(&fw, source, &nv_device(bios)->pdev->dev);
 336                if (ret == 0) {
 337                        bios->size = fw->size;
 338                        bios->data = kmemdup(fw->data, fw->size, GFP_KERNEL);
 339                        release_firmware(fw);
 340
 341                        nv_info(bios, "image: %s\n", source);
 342                        if (nouveau_bios_score(bios, 1)) {
 343                                kfree(source);
 344                                return 0;
 345                        }
 346
 347                        kfree(bios->data);
 348                        bios->data = NULL;
 349                }
 350
 351                nv_error(bios, "source \'%s\' invalid\n", source);
 352                kfree(source);
 353        }
 354
 355        mthd = shadow_methods;
 356        do {
 357                nv_info(bios, "checking %s for image...\n", mthd->desc);
 358                mthd->shadow(bios);
 359                mthd->score = nouveau_bios_score(bios, mthd->rw);
 360                mthd->size = bios->size;
 361                mthd->data = bios->data;
 362                bios->data = NULL;
 363        } while (mthd->score != 3 && (++mthd)->shadow);
 364
 365        mthd = shadow_methods;
 366        best = mthd;
 367        do {
 368                if (mthd->score > best->score) {
 369                        kfree(best->data);
 370                        best = mthd;
 371                }
 372        } while ((++mthd)->shadow);
 373
 374        if (best->score) {
 375                nv_info(bios, "using image from %s\n", best->desc);
 376                bios->size = best->size;
 377                bios->data = best->data;
 378                return 0;
 379        }
 380
 381        nv_error(bios, "unable to locate usable image\n");
 382        return -EINVAL;
 383}
 384
 385static u8
 386nouveau_bios_rd08(struct nouveau_object *object, u64 addr)
 387{
 388        struct nouveau_bios *bios = (void *)object;
 389        return bios->data[addr];
 390}
 391
 392static u16
 393nouveau_bios_rd16(struct nouveau_object *object, u64 addr)
 394{
 395        struct nouveau_bios *bios = (void *)object;
 396        return get_unaligned_le16(&bios->data[addr]);
 397}
 398
 399static u32
 400nouveau_bios_rd32(struct nouveau_object *object, u64 addr)
 401{
 402        struct nouveau_bios *bios = (void *)object;
 403        return get_unaligned_le32(&bios->data[addr]);
 404}
 405
 406static void
 407nouveau_bios_wr08(struct nouveau_object *object, u64 addr, u8 data)
 408{
 409        struct nouveau_bios *bios = (void *)object;
 410        bios->data[addr] = data;
 411}
 412
 413static void
 414nouveau_bios_wr16(struct nouveau_object *object, u64 addr, u16 data)
 415{
 416        struct nouveau_bios *bios = (void *)object;
 417        put_unaligned_le16(data, &bios->data[addr]);
 418}
 419
 420static void
 421nouveau_bios_wr32(struct nouveau_object *object, u64 addr, u32 data)
 422{
 423        struct nouveau_bios *bios = (void *)object;
 424        put_unaligned_le32(data, &bios->data[addr]);
 425}
 426
 427static int
 428nouveau_bios_ctor(struct nouveau_object *parent,
 429                  struct nouveau_object *engine,
 430                  struct nouveau_oclass *oclass, void *data, u32 size,
 431                  struct nouveau_object **pobject)
 432{
 433        struct nouveau_bios *bios;
 434        struct bit_entry bit_i;
 435        int ret;
 436
 437        ret = nouveau_subdev_create(parent, engine, oclass, 0,
 438                                    "VBIOS", "bios", &bios);
 439        *pobject = nv_object(bios);
 440        if (ret)
 441                return ret;
 442
 443        ret = nouveau_bios_shadow(bios);
 444        if (ret)
 445                return ret;
 446
 447        /* detect type of vbios we're dealing with */
 448        bios->bmp_offset = nvbios_findstr(bios->data, bios->size,
 449                                          "\xff\x7f""NV\0", 5);
 450        if (bios->bmp_offset) {
 451                nv_info(bios, "BMP version %x.%x\n",
 452                        bmp_version(bios) >> 8,
 453                        bmp_version(bios) & 0xff);
 454        }
 455
 456        bios->bit_offset = nvbios_findstr(bios->data, bios->size,
 457                                          "\xff\xb8""BIT", 5);
 458        if (bios->bit_offset)
 459                nv_info(bios, "BIT signature found\n");
 460
 461        /* determine the vbios version number */
 462        if (!bit_entry(bios, 'i', &bit_i) && bit_i.length >= 4) {
 463                bios->version.major = nv_ro08(bios, bit_i.offset + 3);
 464                bios->version.chip  = nv_ro08(bios, bit_i.offset + 2);
 465                bios->version.minor = nv_ro08(bios, bit_i.offset + 1);
 466                bios->version.micro = nv_ro08(bios, bit_i.offset + 0);
 467                bios->version.patch = nv_ro08(bios, bit_i.offset + 4);
 468        } else
 469        if (bmp_version(bios)) {
 470                bios->version.major = nv_ro08(bios, bios->bmp_offset + 13);
 471                bios->version.chip  = nv_ro08(bios, bios->bmp_offset + 12);
 472                bios->version.minor = nv_ro08(bios, bios->bmp_offset + 11);
 473                bios->version.micro = nv_ro08(bios, bios->bmp_offset + 10);
 474        }
 475
 476        nv_info(bios, "version %02x.%02x.%02x.%02x.%02x\n",
 477                bios->version.major, bios->version.chip,
 478                bios->version.minor, bios->version.micro, bios->version.patch);
 479
 480        return 0;
 481}
 482
 483static void
 484nouveau_bios_dtor(struct nouveau_object *object)
 485{
 486        struct nouveau_bios *bios = (void *)object;
 487        kfree(bios->data);
 488        nouveau_subdev_destroy(&bios->base);
 489}
 490
 491static int
 492nouveau_bios_init(struct nouveau_object *object)
 493{
 494        struct nouveau_bios *bios = (void *)object;
 495        return nouveau_subdev_init(&bios->base);
 496}
 497
 498static int
 499nouveau_bios_fini(struct nouveau_object *object, bool suspend)
 500{
 501        struct nouveau_bios *bios = (void *)object;
 502        return nouveau_subdev_fini(&bios->base, suspend);
 503}
 504
 505struct nouveau_oclass
 506nouveau_bios_oclass = {
 507        .handle = NV_SUBDEV(VBIOS, 0x00),
 508        .ofuncs = &(struct nouveau_ofuncs) {
 509                .ctor = nouveau_bios_ctor,
 510                .dtor = nouveau_bios_dtor,
 511                .init = nouveau_bios_init,
 512                .fini = nouveau_bios_fini,
 513                .rd08 = nouveau_bios_rd08,
 514                .rd16 = nouveau_bios_rd16,
 515                .rd32 = nouveau_bios_rd32,
 516                .wr08 = nouveau_bios_wr08,
 517                .wr16 = nouveau_bios_wr16,
 518                .wr32 = nouveau_bios_wr32,
 519        },
 520};
 521