1 /* $NetBSD: nouveau_nvkm_subdev_bios_shadow.c,v 1.4 2021/12/18 23:45:38 riastradh Exp $ */ 2 3 /* 4 * Copyright 2014 Red Hat Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Ben Skeggs <bskeggs (at) redhat.com> 25 */ 26 #include <sys/cdefs.h> 27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_bios_shadow.c,v 1.4 2021/12/18 23:45:38 riastradh Exp $"); 28 29 #include "priv.h" 30 31 #include <core/option.h> 32 #include <subdev/bios.h> 33 #include <subdev/bios/image.h> 34 35 struct shadow { 36 u32 skip; 37 const struct nvbios_source *func; 38 void *data; 39 u32 size; 40 int score; 41 }; 42 43 static bool 44 shadow_fetch(struct nvkm_bios *bios, struct shadow *mthd, u32 upto) 45 { 46 const u32 limit = (upto + 3) & ~3; 47 const u32 start = bios->size; 48 void *data = mthd->data; 49 if (nvbios_extend(bios, limit) > 0) { 50 u32 read = mthd->func->read(data, start, limit - start, bios); 51 bios->size = start + read; 52 } 53 return bios->size >= upto; 54 } 55 56 static int 57 shadow_image(struct nvkm_bios *bios, int idx, u32 offset, struct shadow *mthd) 58 { 59 struct nvkm_subdev *subdev = &bios->subdev; 60 struct nvbios_image image; 61 int score = 1; 62 63 if (mthd->func->no_pcir) { 64 image.base = 0; 65 image.type = 0; 66 image.size = mthd->func->size(mthd->data); 67 image.last = 1; 68 } else { 69 if (!shadow_fetch(bios, mthd, offset + 0x1000)) { 70 nvkm_debug(subdev, "%08x: header fetch failed\n", 71 offset); 72 return 0; 73 } 74 75 if (!nvbios_image(bios, idx, &image)) { 76 nvkm_debug(subdev, "image %d invalid\n", idx); 77 return 0; 78 } 79 } 80 nvkm_debug(subdev, "%08x: type %02x, %d bytes\n", 81 image.base, image.type, image.size); 82 83 if (!shadow_fetch(bios, mthd, image.size)) { 84 nvkm_debug(subdev, "%08x: fetch failed\n", image.base); 85 return 0; 86 } 87 88 switch (image.type) { 89 case 0x00: 90 if (!mthd->func->ignore_checksum && 91 nvbios_checksum(&bios->data[image.base], image.size)) { 92 nvkm_debug(subdev, "%08x: checksum failed\n", 93 image.base); 94 if (!mthd->func->require_checksum) { 95 if (mthd->func->rw) 96 score += 1; 97 score += 1; 98 } else 99 return 0; 100 } else { 101 score += 3; 102 } 103 break; 104 default: 105 score += 3; 106 break; 107 } 108 109 if (!image.last) 110 score += shadow_image(bios, idx + 1, offset + image.size, mthd); 111 return score; 112 } 113 114 static int 115 shadow_method(struct nvkm_bios *bios, struct shadow *mthd, const char *name) 116 { 117 const struct nvbios_source *func = mthd->func; 118 struct nvkm_subdev *subdev = &bios->subdev; 119 if (func->name) { 120 nvkm_debug(subdev, "trying %s...\n", name ? name : func->name); 121 if (func->init) { 122 mthd->data = func->init(bios, name); 123 if (IS_ERR(mthd->data)) { 124 mthd->data = NULL; 125 return 0; 126 } 127 } 128 mthd->score = shadow_image(bios, 0, 0, mthd); 129 if (func->fini) 130 func->fini(mthd->data); 131 nvkm_debug(subdev, "scored %d\n", mthd->score); 132 mthd->data = bios->data; 133 mthd->size = bios->size; 134 bios->data = NULL; 135 bios->size = 0; 136 } 137 return mthd->score; 138 } 139 140 static u32 141 shadow_fw_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios) 142 { 143 const struct firmware *fw = data; 144 if (offset + length <= fw->size) { 145 memcpy(bios->data + offset, fw->data + offset, length); 146 return length; 147 } 148 return 0; 149 } 150 151 static void * 152 shadow_fw_init(struct nvkm_bios *bios, const char *name) 153 { 154 struct device *dev = bios->subdev.device->dev; 155 const struct firmware *fw; 156 int ret = request_firmware(&fw, name, dev); 157 if (ret) 158 return ERR_PTR(-ENOENT); 159 return __UNCONST(fw); 160 } 161 162 static const struct nvbios_source 163 shadow_fw = { 164 .name = "firmware", 165 .init = shadow_fw_init, 166 .fini = (void(*)(void *))release_firmware, 167 .read = shadow_fw_read, 168 .rw = false, 169 }; 170 171 int 172 nvbios_shadow(struct nvkm_bios *bios) 173 { 174 struct nvkm_subdev *subdev = &bios->subdev; 175 struct nvkm_device *device = subdev->device; 176 struct shadow mthds[] = { 177 { 0, &nvbios_of }, 178 { 0, &nvbios_ramin }, 179 { 0, &nvbios_rom }, 180 { 0, &nvbios_acpi_fast }, 181 { 4, &nvbios_acpi_slow }, 182 { 1, &nvbios_pcirom }, 183 { 1, &nvbios_platform }, 184 {} 185 }, *mthd, *best = NULL; 186 const char *optarg; 187 char *source; 188 int optlen; 189 190 /* handle user-specified bios source */ 191 optarg = nvkm_stropt(device->cfgopt, "NvBios", &optlen); 192 source = optarg ? kstrndup(optarg, optlen, GFP_KERNEL) : NULL; 193 if (source) { 194 /* try to match one of the built-in methods */ 195 for (mthd = mthds; mthd->func; mthd++) { 196 if (mthd->func->name && 197 !strcasecmp(source, mthd->func->name)) { 198 best = mthd; 199 if (shadow_method(bios, mthd, NULL)) 200 break; 201 } 202 } 203 204 /* otherwise, attempt to load as firmware */ 205 if (!best && (best = mthd)) { 206 mthd->func = &shadow_fw; 207 shadow_method(bios, mthd, source); 208 mthd->func = NULL; 209 } 210 211 if (!best->score) { 212 nvkm_error(subdev, "%s invalid\n", source); 213 kfree(source); 214 source = NULL; 215 } 216 } 217 218 /* scan all potential bios sources, looking for best image */ 219 if (!best || !best->score) { 220 for (mthd = mthds, best = mthd; mthd->func; mthd++) { 221 if (!mthd->skip || best->score < mthd->skip) { 222 if (shadow_method(bios, mthd, NULL)) { 223 if (mthd->score > best->score) 224 best = mthd; 225 } 226 } 227 } 228 } 229 230 /* cleanup the ones we didn't use */ 231 for (mthd = mthds; mthd->func; mthd++) { 232 if (mthd != best) 233 kfree(mthd->data); 234 } 235 236 if (!best->score) { 237 nvkm_error(subdev, "unable to locate usable image\n"); 238 return -EINVAL; 239 } 240 241 nvkm_debug(subdev, "using image from %s\n", best->func ? 242 best->func->name : source); 243 bios->data = best->data; 244 bios->size = best->size; 245 kfree(source); 246 return 0; 247 } 248