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 if (!acr->wpr_fw || acr->wpr_comp) 254 wpr_size = acr->func->wpr_layout(acr); 255 256 /* Allocate/Locate WPR + fill ucode blob pointer. 257 * 258 * dGPU: allocate WPR + shadow blob 259 * Tegra: locate WPR with regs, ensure size is sufficient, 260 * allocate ucode blob. 261 */ 262 ret = acr->func->wpr_alloc(acr, wpr_size); 263 if (ret) 264 return ret; 265 266 nvkm_debug(subdev, "WPR region is from 0x%llx-0x%llx (shadow 0x%llx)\n", 267 acr->wpr_start, acr->wpr_end, acr->shadow_start); 268 269 /* Write WPR to ucode blob. */ 270 nvkm_kmap(acr->wpr); 271 if (acr->wpr_fw && !acr->wpr_comp) 272 nvkm_wobj(acr->wpr, 0, acr->wpr_fw->data, acr->wpr_fw->size); 273 274 if (!acr->wpr_fw || acr->wpr_comp) 275 acr->func->wpr_build(acr, nvkm_acr_falcon(device)); 276 acr->func->wpr_patch(acr, (s64)acr->wpr_start - acr->wpr_prev); 277 278 if (acr->wpr_fw && acr->wpr_comp) { 279 nvkm_kmap(acr->wpr); 280 for (i = 0; i < acr->wpr_fw->size; i += 4) { 281 u32 us = nvkm_ro32(acr->wpr, i); 282 u32 fw = ((u32 *)acr->wpr_fw->data)[i/4]; 283 if (fw != us) { 284 nvkm_warn(subdev, "%08x: %08x %08x\n", 285 i, us, fw); 286 } 287 } 288 return -EINVAL; 289 } 290 nvkm_done(acr->wpr); 291 292 /* Allocate instance block for ACR-related stuff. */ 293 ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, 0x1000, 0, true, 294 &acr->inst); 295 if (ret) 296 return ret; 297 298 ret = nvkm_vmm_new(device, 0, 0, NULL, 0, NULL, "acr", &acr->vmm); 299 if (ret) 300 return ret; 301 302 acr->vmm->debug = acr->subdev.debug; 303 304 ret = nvkm_vmm_join(acr->vmm, acr->inst); 305 if (ret) 306 return ret; 307 308 /* Load HS firmware blobs into ACR VMM. */ 309 list_for_each_entry(hsfw, &acr->hsfw, head) { 310 nvkm_debug(subdev, "loading %s fw\n", hsfw->name); 311 ret = hsfw->func->load(acr, hsfw); 312 if (ret) 313 return ret; 314 } 315 316 /* Kill temporary data. */ 317 nvkm_acr_cleanup(acr); 318 return 0; 319 } 320 321 static void * 322 nvkm_acr_dtor(struct nvkm_subdev *subdev) 323 { 324 struct nvkm_acr *acr = nvkm_acr(subdev); 325 struct nvkm_acr_hsf *hsf, *hst; 326 struct nvkm_acr_lsf *lsf, *lst; 327 328 list_for_each_entry_safe(hsf, hst, &acr->hsf, head) { 329 nvkm_vmm_put(acr->vmm, &hsf->vma); 330 nvkm_memory_unref(&hsf->ucode); 331 kfree(hsf->imem); 332 list_del(&hsf->head); 333 kfree(hsf); 334 } 335 336 nvkm_vmm_part(acr->vmm, acr->inst); 337 nvkm_vmm_unref(&acr->vmm); 338 nvkm_memory_unref(&acr->inst); 339 340 nvkm_memory_unref(&acr->wpr); 341 342 list_for_each_entry_safe(lsf, lst, &acr->lsf, head) { 343 list_del(&lsf->head); 344 kfree(lsf); 345 } 346 347 nvkm_acr_cleanup(acr); 348 return acr; 349 } 350 351 static const struct nvkm_subdev_func 352 nvkm_acr = { 353 .dtor = nvkm_acr_dtor, 354 .oneinit = nvkm_acr_oneinit, 355 .init = nvkm_acr_init, 356 .fini = nvkm_acr_fini, 357 }; 358 359 static int 360 nvkm_acr_ctor_wpr(struct nvkm_acr *acr, int ver) 361 { 362 struct nvkm_subdev *subdev = &acr->subdev; 363 struct nvkm_device *device = subdev->device; 364 int ret; 365 366 ret = nvkm_firmware_get(subdev, "acr/wpr", ver, &acr->wpr_fw); 367 if (ret < 0) 368 return ret; 369 370 /* Pre-add LSFs in the order they appear in the FW WPR image so that 371 * we're able to do a binary comparison with our own generator. 372 */ 373 ret = acr->func->wpr_parse(acr); 374 if (ret) 375 return ret; 376 377 acr->wpr_comp = nvkm_boolopt(device->cfgopt, "NvAcrWprCompare", false); 378 acr->wpr_prev = nvkm_longopt(device->cfgopt, "NvAcrWprPrevAddr", 0); 379 return 0; 380 } 381 382 int 383 nvkm_acr_new_(const struct nvkm_acr_fwif *fwif, struct nvkm_device *device, 384 int index, struct nvkm_acr **pacr) 385 { 386 struct nvkm_acr *acr; 387 long wprfw; 388 389 if (!(acr = *pacr = kzalloc(sizeof(*acr), GFP_KERNEL))) 390 return -ENOMEM; 391 nvkm_subdev_ctor(&nvkm_acr, device, index, &acr->subdev); 392 INIT_LIST_HEAD(&acr->hsfw); 393 INIT_LIST_HEAD(&acr->lsfw); 394 INIT_LIST_HEAD(&acr->hsf); 395 INIT_LIST_HEAD(&acr->lsf); 396 397 fwif = nvkm_firmware_load(&acr->subdev, fwif, "Acr", acr); 398 if (IS_ERR(fwif)) 399 return PTR_ERR(fwif); 400 401 acr->func = fwif->func; 402 403 wprfw = nvkm_longopt(device->cfgopt, "NvAcrWpr", -1); 404 if (wprfw >= 0) { 405 int ret = nvkm_acr_ctor_wpr(acr, wprfw); 406 if (ret) 407 return ret; 408 } 409 410 return 0; 411 } 412