1 /* 2 * Copyright 2019 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 #include "priv.h" 23 24 #include <core/firmware.h> 25 #include <core/memory.h> 26 #include <subdev/mmu.h> 27 28 static struct nvkm_acr_hsf * 29 nvkm_acr_hsf_find(struct nvkm_acr *acr, const char *name) 30 { 31 struct nvkm_acr_hsf *hsf; 32 list_for_each_entry(hsf, &acr->hsf, head) { 33 if (!strcmp(hsf->name, name)) 34 return hsf; 35 } 36 return NULL; 37 } 38 39 int 40 nvkm_acr_hsf_boot(struct nvkm_acr *acr, const char *name) 41 { 42 struct nvkm_subdev *subdev = &acr->subdev; 43 struct nvkm_acr_hsf *hsf; 44 int ret; 45 46 hsf = nvkm_acr_hsf_find(acr, name); 47 if (!hsf) 48 return -EINVAL; 49 50 nvkm_debug(subdev, "executing %s binary\n", hsf->name); 51 ret = nvkm_falcon_get(hsf->falcon, subdev); 52 if (ret) 53 return ret; 54 55 ret = hsf->func->boot(acr, hsf); 56 nvkm_falcon_put(hsf->falcon, subdev); 57 if (ret) { 58 nvkm_error(subdev, "%s binary failed\n", hsf->name); 59 return ret; 60 } 61 62 nvkm_debug(subdev, "%s binary completed successfully\n", hsf->name); 63 return 0; 64 } 65 66 static void 67 nvkm_acr_unload(struct nvkm_acr *acr) 68 { 69 if (acr->done) { 70 nvkm_acr_hsf_boot(acr, "unload"); 71 acr->done = false; 72 } 73 } 74 75 static int 76 nvkm_acr_load(struct nvkm_acr *acr) 77 { 78 struct nvkm_subdev *subdev = &acr->subdev; 79 struct nvkm_acr_lsf *lsf; 80 u64 start, limit; 81 int ret; 82 83 if (list_empty(&acr->lsf)) { 84 nvkm_debug(subdev, "No LSF(s) present.\n"); 85 return 0; 86 } 87 88 ret = acr->func->init(acr); 89 if (ret) 90 return ret; 91 92 acr->func->wpr_check(acr, &start, &limit); 93 94 if (start != acr->wpr_start || limit != acr->wpr_end) { 95 nvkm_error(subdev, "WPR not configured as expected: " 96 "%016llx-%016llx vs %016llx-%016llx\n", 97 acr->wpr_start, acr->wpr_end, start, limit); 98 return -EIO; 99 } 100 101 acr->done = true; 102 103 list_for_each_entry(lsf, &acr->lsf, head) { 104 if (lsf->func->boot) { 105 ret = lsf->func->boot(lsf->falcon); 106 if (ret) 107 break; 108 } 109 } 110 111 return ret; 112 } 113 114 static int 115 nvkm_acr_reload(struct nvkm_acr *acr) 116 { 117 nvkm_acr_unload(acr); 118 return nvkm_acr_load(acr); 119 } 120 121 static struct nvkm_acr_lsf * 122 nvkm_acr_falcon(struct nvkm_device *device) 123 { 124 struct nvkm_acr *acr = device->acr; 125 struct nvkm_acr_lsf *lsf; 126 127 if (acr) { 128 list_for_each_entry(lsf, &acr->lsf, head) { 129 if (lsf->func->bootstrap_falcon) 130 return lsf; 131 } 132 } 133 134 return NULL; 135 } 136 137 int 138 nvkm_acr_bootstrap_falcons(struct nvkm_device *device, unsigned long mask) 139 { 140 struct nvkm_acr_lsf *acrflcn = nvkm_acr_falcon(device); 141 struct nvkm_acr *acr = device->acr; 142 unsigned long id; 143 144 if (!acrflcn) { 145 int ret = nvkm_acr_reload(acr); 146 if (ret) 147 return ret; 148 149 return acr->done ? 0 : -EINVAL; 150 } 151 152 if (acrflcn->func->bootstrap_multiple_falcons) { 153 return acrflcn->func-> 154 bootstrap_multiple_falcons(acrflcn->falcon, mask); 155 } 156 157 for_each_set_bit(id, &mask, NVKM_ACR_LSF_NUM) { 158 int ret = acrflcn->func->bootstrap_falcon(acrflcn->falcon, id); 159 if (ret) 160 return ret; 161 } 162 163 return 0; 164 } 165 166 bool 167 nvkm_acr_managed_falcon(struct nvkm_device *device, enum nvkm_acr_lsf_id id) 168 { 169 struct nvkm_acr *acr = device->acr; 170 struct nvkm_acr_lsf *lsf; 171 172 if (acr) { 173 list_for_each_entry(lsf, &acr->lsf, head) { 174 if (lsf->id == id) 175 return true; 176 } 177 } 178 179 return false; 180 } 181 182 static int 183 nvkm_acr_fini(struct nvkm_subdev *subdev, bool suspend) 184 { 185 nvkm_acr_unload(nvkm_acr(subdev)); 186 return 0; 187 } 188 189 static int 190 nvkm_acr_init(struct nvkm_subdev *subdev) 191 { 192 if (!nvkm_acr_falcon(subdev->device)) 193 return 0; 194 195 return nvkm_acr_load(nvkm_acr(subdev)); 196 } 197 198 static void 199 nvkm_acr_cleanup(struct nvkm_acr *acr) 200 { 201 nvkm_acr_lsfw_del_all(acr); 202 nvkm_acr_hsfw_del_all(acr); 203 nvkm_firmware_put(acr->wpr_fw); 204 acr->wpr_fw = NULL; 205 } 206 207 static int 208 nvkm_acr_oneinit(struct nvkm_subdev *subdev) 209 { 210 struct nvkm_device *device = subdev->device; 211 struct nvkm_acr *acr = nvkm_acr(subdev); 212 struct nvkm_acr_hsfw *hsfw; 213 struct nvkm_acr_lsfw *lsfw, *lsft; 214 struct nvkm_acr_lsf *lsf; 215 u32 wpr_size = 0; 216 int ret, i; 217 218 if (list_empty(&acr->hsfw)) { 219 nvkm_debug(subdev, "No HSFW(s)\n"); 220 nvkm_acr_cleanup(acr); 221 return 0; 222 } 223 224 /* Determine layout/size of WPR image up-front, as we need to know 225 * it to allocate memory before we begin constructing it. 226 */ 227 list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) { 228 /* Cull unknown falcons that are present in WPR image. */ 229 if (acr->wpr_fw) { 230 if (!lsfw->func) { 231 nvkm_acr_lsfw_del(lsfw); 232 continue; 233 } 234 235 wpr_size = acr->wpr_fw->size; 236 } 237 238 /* Ensure we've fetched falcon configuration. */ 239 ret = nvkm_falcon_get(lsfw->falcon, subdev); 240 if (ret) 241 return ret; 242 243 nvkm_falcon_put(lsfw->falcon, subdev); 244 245 if (!(lsf = kmalloc(sizeof(*lsf), GFP_KERNEL))) 246 return -ENOMEM; 247 lsf->func = lsfw->func; 248 lsf->falcon = lsfw->falcon; 249 lsf->id = lsfw->id; 250 list_add_tail(&lsf->head, &acr->lsf); 251 } 252 253 /* Ensure the falcon that'll provide ACR functions is booted first. */ 254 lsf = nvkm_acr_falcon(device); 255 if (lsf) 256 list_move(&lsf->head, &acr->lsf); 257 258 if (!acr->wpr_fw || acr->wpr_comp) 259 wpr_size = acr->func->wpr_layout(acr); 260 261 /* Allocate/Locate WPR + fill ucode blob pointer. 262 * 263 * dGPU: allocate WPR + shadow blob 264 * Tegra: locate WPR with regs, ensure size is sufficient, 265 * allocate ucode blob. 266 */ 267 ret = acr->func->wpr_alloc(acr, wpr_size); 268 if (ret) 269 return ret; 270 271 nvkm_debug(subdev, "WPR region is from 0x%llx-0x%llx (shadow 0x%llx)\n", 272 acr->wpr_start, acr->wpr_end, acr->shadow_start); 273 274 /* Write WPR to ucode blob. */ 275 nvkm_kmap(acr->wpr); 276 if (acr->wpr_fw && !acr->wpr_comp) 277 nvkm_wobj(acr->wpr, 0, acr->wpr_fw->data, acr->wpr_fw->size); 278 279 if (!acr->wpr_fw || acr->wpr_comp) 280 acr->func->wpr_build(acr, nvkm_acr_falcon(device)); 281 acr->func->wpr_patch(acr, (s64)acr->wpr_start - acr->wpr_prev); 282 283 if (acr->wpr_fw && acr->wpr_comp) { 284 nvkm_kmap(acr->wpr); 285 for (i = 0; i < acr->wpr_fw->size; i += 4) { 286 u32 us = nvkm_ro32(acr->wpr, i); 287 u32 fw = ((u32 *)acr->wpr_fw->data)[i/4]; 288 if (fw != us) { 289 nvkm_warn(subdev, "%08x: %08x %08x\n", 290 i, us, fw); 291 } 292 } 293 return -EINVAL; 294 } 295 nvkm_done(acr->wpr); 296 297 /* Allocate instance block for ACR-related stuff. */ 298 ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0, true, 299 &acr->inst); 300 if (ret) 301 return ret; 302 303 ret = nvkm_vmm_new(device, 0, 0, NULL, 0, NULL, "acr", &acr->vmm); 304 if (ret) 305 return ret; 306 307 acr->vmm->debug = acr->subdev.debug; 308 309 ret = nvkm_vmm_join(acr->vmm, acr->inst); 310 if (ret) 311 return ret; 312 313 /* Load HS firmware blobs into ACR VMM. */ 314 list_for_each_entry(hsfw, &acr->hsfw, head) { 315 nvkm_debug(subdev, "loading %s fw\n", hsfw->name); 316 ret = hsfw->func->load(acr, hsfw); 317 if (ret) 318 return ret; 319 } 320 321 /* Kill temporary data. */ 322 nvkm_acr_cleanup(acr); 323 return 0; 324 } 325 326 static void * 327 nvkm_acr_dtor(struct nvkm_subdev *subdev) 328 { 329 struct nvkm_acr *acr = nvkm_acr(subdev); 330 struct nvkm_acr_hsf *hsf, *hst; 331 struct nvkm_acr_lsf *lsf, *lst; 332 333 list_for_each_entry_safe(hsf, hst, &acr->hsf, head) { 334 nvkm_vmm_put(acr->vmm, &hsf->vma); 335 nvkm_memory_unref(&hsf->ucode); 336 kfree(hsf->imem); 337 list_del(&hsf->head); 338 kfree(hsf); 339 } 340 341 nvkm_vmm_part(acr->vmm, acr->inst); 342 nvkm_vmm_unref(&acr->vmm); 343 nvkm_memory_unref(&acr->inst); 344 345 nvkm_memory_unref(&acr->wpr); 346 347 list_for_each_entry_safe(lsf, lst, &acr->lsf, head) { 348 list_del(&lsf->head); 349 kfree(lsf); 350 } 351 352 nvkm_acr_cleanup(acr); 353 return acr; 354 } 355 356 static const struct nvkm_subdev_func 357 nvkm_acr = { 358 .dtor = nvkm_acr_dtor, 359 .oneinit = nvkm_acr_oneinit, 360 .init = nvkm_acr_init, 361 .fini = nvkm_acr_fini, 362 }; 363 364 static int 365 nvkm_acr_ctor_wpr(struct nvkm_acr *acr, int ver) 366 { 367 struct nvkm_subdev *subdev = &acr->subdev; 368 struct nvkm_device *device = subdev->device; 369 int ret; 370 371 ret = nvkm_firmware_get(subdev, "acr/wpr", ver, &acr->wpr_fw); 372 if (ret < 0) 373 return ret; 374 375 /* Pre-add LSFs in the order they appear in the FW WPR image so that 376 * we're able to do a binary comparison with our own generator. 377 */ 378 ret = acr->func->wpr_parse(acr); 379 if (ret) 380 return ret; 381 382 acr->wpr_comp = nvkm_boolopt(device->cfgopt, "NvAcrWprCompare", false); 383 acr->wpr_prev = nvkm_longopt(device->cfgopt, "NvAcrWprPrevAddr", 0); 384 return 0; 385 } 386 387 int 388 nvkm_acr_new_(const struct nvkm_acr_fwif *fwif, struct nvkm_device *device, 389 int index, struct nvkm_acr **pacr) 390 { 391 struct nvkm_acr *acr; 392 long wprfw; 393 394 if (!(acr = *pacr = kzalloc(sizeof(*acr), GFP_KERNEL))) 395 return -ENOMEM; 396 nvkm_subdev_ctor(&nvkm_acr, device, index, &acr->subdev); 397 INIT_LIST_HEAD(&acr->hsfw); 398 INIT_LIST_HEAD(&acr->lsfw); 399 INIT_LIST_HEAD(&acr->hsf); 400 INIT_LIST_HEAD(&acr->lsf); 401 402 fwif = nvkm_firmware_load(&acr->subdev, fwif, "Acr", acr); 403 if (IS_ERR(fwif)) 404 return PTR_ERR(fwif); 405 406 acr->func = fwif->func; 407 408 wprfw = nvkm_longopt(device->cfgopt, "NvAcrWpr", -1); 409 if (wprfw >= 0) { 410 int ret = nvkm_acr_ctor_wpr(acr, wprfw); 411 if (ret) 412 return ret; 413 } 414 415 return 0; 416 } 417