Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright 2014 Red Hat Inc.
0003  *
0004  * Permission is hereby granted, free of charge, to any person obtaining a
0005  * copy of this software and associated documentation files (the "Software"),
0006  * to deal in the Software without restriction, including without limitation
0007  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0008  * and/or sell copies of the Software, and to permit persons to whom the
0009  * Software is furnished to do so, subject to the following conditions:
0010  *
0011  * The above copyright notice and this permission notice shall be included in
0012  * all copies or substantial portions of the Software.
0013  *
0014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0017  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
0018  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0019  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0020  * OTHER DEALINGS IN THE SOFTWARE.
0021  *
0022  * Authors: Ben Skeggs <bskeggs@redhat.com>
0023  */
0024 #include "priv.h"
0025 
0026 #include <core/option.h>
0027 #include <subdev/bios.h>
0028 #include <subdev/bios/image.h>
0029 
0030 struct shadow {
0031     u32 skip;
0032     const struct nvbios_source *func;
0033     void *data;
0034     u32 size;
0035     int score;
0036 };
0037 
0038 static bool
0039 shadow_fetch(struct nvkm_bios *bios, struct shadow *mthd, u32 upto)
0040 {
0041     const u32 limit = (upto + 3) & ~3;
0042     const u32 start = bios->size;
0043     void *data = mthd->data;
0044     if (nvbios_extend(bios, limit) > 0) {
0045         u32 read = mthd->func->read(data, start, limit - start, bios);
0046         bios->size = start + read;
0047     }
0048     return bios->size >= upto;
0049 }
0050 
0051 static int
0052 shadow_image(struct nvkm_bios *bios, int idx, u32 offset, struct shadow *mthd)
0053 {
0054     struct nvkm_subdev *subdev = &bios->subdev;
0055     struct nvbios_image image;
0056     int score = 1;
0057 
0058     if (mthd->func->no_pcir) {
0059         image.base = 0;
0060         image.type = 0;
0061         image.size = mthd->func->size(mthd->data);
0062         image.last = 1;
0063     } else {
0064         if (!shadow_fetch(bios, mthd, offset + 0x1000)) {
0065             nvkm_debug(subdev, "%08x: header fetch failed\n",
0066                    offset);
0067             return 0;
0068         }
0069 
0070         if (!nvbios_image(bios, idx, &image)) {
0071             nvkm_debug(subdev, "image %d invalid\n", idx);
0072             return 0;
0073         }
0074     }
0075     nvkm_debug(subdev, "%08x: type %02x, %d bytes\n",
0076            image.base, image.type, image.size);
0077 
0078     if (!shadow_fetch(bios, mthd, image.base + image.size)) {
0079         nvkm_debug(subdev, "%08x: fetch failed\n", image.base);
0080         return 0;
0081     }
0082 
0083     switch (image.type) {
0084     case 0x00:
0085         if (!mthd->func->ignore_checksum &&
0086             nvbios_checksum(&bios->data[image.base], image.size)) {
0087             nvkm_debug(subdev, "%08x: checksum failed\n",
0088                    image.base);
0089             if (!mthd->func->require_checksum) {
0090                 if (mthd->func->rw)
0091                     score += 1;
0092                 score += 1;
0093             } else
0094                 return 0;
0095         } else {
0096             score += 3;
0097         }
0098         break;
0099     default:
0100         score += 3;
0101         break;
0102     }
0103 
0104     if (!image.last)
0105         score += shadow_image(bios, idx + 1, offset + image.size, mthd);
0106     return score;
0107 }
0108 
0109 static int
0110 shadow_method(struct nvkm_bios *bios, struct shadow *mthd, const char *name)
0111 {
0112     const struct nvbios_source *func = mthd->func;
0113     struct nvkm_subdev *subdev = &bios->subdev;
0114     if (func->name) {
0115         nvkm_debug(subdev, "trying %s...\n", name ? name : func->name);
0116         if (func->init) {
0117             mthd->data = func->init(bios, name);
0118             if (IS_ERR(mthd->data)) {
0119                 mthd->data = NULL;
0120                 return 0;
0121             }
0122         }
0123         mthd->score = shadow_image(bios, 0, 0, mthd);
0124         if (func->fini)
0125             func->fini(mthd->data);
0126         nvkm_debug(subdev, "scored %d\n", mthd->score);
0127         mthd->data = bios->data;
0128         mthd->size = bios->size;
0129         bios->data  = NULL;
0130         bios->size  = 0;
0131     }
0132     return mthd->score;
0133 }
0134 
0135 static u32
0136 shadow_fw_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
0137 {
0138     const struct firmware *fw = data;
0139     if (offset + length <= fw->size) {
0140         memcpy(bios->data + offset, fw->data + offset, length);
0141         return length;
0142     }
0143     return 0;
0144 }
0145 
0146 static void *
0147 shadow_fw_init(struct nvkm_bios *bios, const char *name)
0148 {
0149     struct device *dev = bios->subdev.device->dev;
0150     const struct firmware *fw;
0151     int ret = request_firmware(&fw, name, dev);
0152     if (ret)
0153         return ERR_PTR(-ENOENT);
0154     return (void *)fw;
0155 }
0156 
0157 static const struct nvbios_source
0158 shadow_fw = {
0159     .name = "firmware",
0160     .init = shadow_fw_init,
0161     .fini = (void(*)(void *))release_firmware,
0162     .read = shadow_fw_read,
0163     .rw = false,
0164 };
0165 
0166 int
0167 nvbios_shadow(struct nvkm_bios *bios)
0168 {
0169     struct nvkm_subdev *subdev = &bios->subdev;
0170     struct nvkm_device *device = subdev->device;
0171     struct shadow mthds[] = {
0172         { 0, &nvbios_of },
0173         { 0, &nvbios_ramin },
0174         { 0, &nvbios_prom },
0175         { 0, &nvbios_acpi_fast },
0176         { 4, &nvbios_acpi_slow },
0177         { 1, &nvbios_pcirom },
0178         { 1, &nvbios_platform },
0179         {}
0180     }, *mthd, *best = NULL;
0181     const char *optarg;
0182     char *source;
0183     int optlen;
0184 
0185     /* handle user-specified bios source */
0186     optarg = nvkm_stropt(device->cfgopt, "NvBios", &optlen);
0187     source = optarg ? kstrndup(optarg, optlen, GFP_KERNEL) : NULL;
0188     if (source) {
0189         /* try to match one of the built-in methods */
0190         for (mthd = mthds; mthd->func; mthd++) {
0191             if (mthd->func->name &&
0192                 !strcasecmp(source, mthd->func->name)) {
0193                 best = mthd;
0194                 if (shadow_method(bios, mthd, NULL))
0195                     break;
0196             }
0197         }
0198 
0199         /* otherwise, attempt to load as firmware */
0200         if (!best && (best = mthd)) {
0201             mthd->func = &shadow_fw;
0202             shadow_method(bios, mthd, source);
0203             mthd->func = NULL;
0204         }
0205 
0206         if (!best->score) {
0207             nvkm_error(subdev, "%s invalid\n", source);
0208             kfree(source);
0209             source = NULL;
0210         }
0211     }
0212 
0213     /* scan all potential bios sources, looking for best image */
0214     if (!best || !best->score) {
0215         for (mthd = mthds, best = mthd; mthd->func; mthd++) {
0216             if (!mthd->skip || best->score < mthd->skip) {
0217                 if (shadow_method(bios, mthd, NULL)) {
0218                     if (mthd->score > best->score)
0219                         best = mthd;
0220                 }
0221             }
0222         }
0223     }
0224 
0225     /* cleanup the ones we didn't use */
0226     for (mthd = mthds; mthd->func; mthd++) {
0227         if (mthd != best)
0228             kfree(mthd->data);
0229     }
0230 
0231     if (!best->score) {
0232         nvkm_error(subdev, "unable to locate usable image\n");
0233         return -EINVAL;
0234     }
0235 
0236     nvkm_debug(subdev, "using image from %s\n", best->func ?
0237            best->func->name : source);
0238     bios->data = best->data;
0239     bios->size = best->size;
0240     kfree(source);
0241     return 0;
0242 }