1 /* $NetBSD: nouveau_nvkm_subdev_acr_base.c,v 1.3 2021/12/19 10:51:58 riastradh Exp $ */ 2 3 /* 4 * Copyright 2019 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 #include <sys/cdefs.h> 25 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_acr_base.c,v 1.3 2021/12/19 10:51:58 riastradh Exp $"); 26 27 #include "priv.h" 28 29 #include <core/firmware.h> 30 #include <core/memory.h> 31 #include <subdev/mmu.h> 32 33 static struct nvkm_acr_hsf * 34 nvkm_acr_hsf_find(struct nvkm_acr *acr, const char *name) 35 { 36 struct nvkm_acr_hsf *hsf; 37 list_for_each_entry(hsf, &acr->hsf, head) { 38 if (!strcmp(hsf->name, name)) 39 return hsf; 40 } 41 return NULL; 42 } 43 44 int 45 nvkm_acr_hsf_boot(struct nvkm_acr *acr, const char *name) 46 { 47 struct nvkm_subdev *subdev = &acr->subdev; 48 struct nvkm_acr_hsf *hsf; 49 int ret; 50 51 hsf = nvkm_acr_hsf_find(acr, name); 52 if (!hsf) 53 return -EINVAL; 54 55 nvkm_debug(subdev, "executing %s binary\n", hsf->name); 56 ret = nvkm_falcon_get(hsf->falcon, subdev); 57 if (ret) 58 return ret; 59 60 ret = hsf->func->boot(acr, hsf); 61 nvkm_falcon_put(hsf->falcon, subdev); 62 if (ret) { 63 nvkm_error(subdev, "%s binary failed\n", hsf->name); 64 return ret; 65 } 66 67 nvkm_debug(subdev, "%s binary completed successfully\n", hsf->name); 68 return 0; 69 } 70 71 static void 72 nvkm_acr_unload(struct nvkm_acr *acr) 73 { 74 if (acr->done) { 75 nvkm_acr_hsf_boot(acr, "unload"); 76 acr->done = false; 77 } 78 } 79 80 static int 81 nvkm_acr_load(struct nvkm_acr *acr) 82 { 83 struct nvkm_subdev *subdev = &acr->subdev; 84 struct nvkm_acr_lsf *lsf; 85 u64 start, limit; 86 int ret; 87 88 if (list_empty(&acr->lsf)) { 89 nvkm_debug(subdev, "No LSF(s) present.\n"); 90 return 0; 91 } 92 93 ret = acr->func->init(acr); 94 if (ret) 95 return ret; 96 97 acr->func->wpr_check(acr, &start, &limit); 98 99 if (start != acr->wpr_start || limit != acr->wpr_end) { 100 nvkm_error(subdev, "WPR not configured as expected: " 101 "%016"PRIx64"-%016"PRIx64" vs %016"PRIx64"-%016"PRIx64"\n", 102 acr->wpr_start, acr->wpr_end, start, limit); 103 return -EIO; 104 } 105 106 acr->done = true; 107 108 list_for_each_entry(lsf, &acr->lsf, head) { 109 if (lsf->func->boot) { 110 ret = lsf->func->boot(lsf->falcon); 111 if (ret) 112 break; 113 } 114 } 115 116 return ret; 117 } 118 119 static int 120 nvkm_acr_reload(struct nvkm_acr *acr) 121 { 122 nvkm_acr_unload(acr); 123 return nvkm_acr_load(acr); 124 } 125 126 static struct nvkm_acr_lsf * 127 nvkm_acr_falcon(struct nvkm_device *device) 128 { 129 struct nvkm_acr *acr = device->acr; 130 struct nvkm_acr_lsf *lsf; 131 132 if (acr) { 133 list_for_each_entry(lsf, &acr->lsf, head) { 134 if (lsf->func->bootstrap_falcon) 135 return lsf; 136 } 137 } 138 139 return NULL; 140 } 141 142 int 143 nvkm_acr_bootstrap_falcons(struct nvkm_device *device, unsigned long mask) 144 { 145 struct nvkm_acr_lsf *acrflcn = nvkm_acr_falcon(device); 146 struct nvkm_acr *acr = device->acr; 147 unsigned long id; 148 149 if (!acrflcn) { 150 int ret = nvkm_acr_reload(acr); 151 if (ret) 152 return ret; 153 154 return acr->done ? 0 : -EINVAL; 155 } 156 157 if (acrflcn->func->bootstrap_multiple_falcons) { 158 return acrflcn->func-> 159 bootstrap_multiple_falcons(acrflcn->falcon, mask); 160 } 161 162 for_each_set_bit(id, &mask, NVKM_ACR_LSF_NUM) { 163 int ret = acrflcn->func->bootstrap_falcon(acrflcn->falcon, id); 164 if (ret) 165 return ret; 166 } 167 168 return 0; 169 } 170 171 bool 172 nvkm_acr_managed_falcon(struct nvkm_device *device, enum nvkm_acr_lsf_id id) 173 { 174 struct nvkm_acr *acr = device->acr; 175 struct nvkm_acr_lsf *lsf; 176 177 if (acr) { 178 list_for_each_entry(lsf, &acr->lsf, head) { 179 if (lsf->id == id) 180 return true; 181 } 182 } 183 184 return false; 185 } 186 187 static int 188 nvkm_acr_fini(struct nvkm_subdev *subdev, bool suspend) 189 { 190 nvkm_acr_unload(nvkm_acr(subdev)); 191 return 0; 192 } 193 194 static int 195 nvkm_acr_init(struct nvkm_subdev *subdev) 196 { 197 if (!nvkm_acr_falcon(subdev->device)) 198 return 0; 199 200 return nvkm_acr_load(nvkm_acr(subdev)); 201 } 202 203 static void 204 nvkm_acr_cleanup(struct nvkm_acr *acr) 205 { 206 nvkm_acr_lsfw_del_all(acr); 207 nvkm_acr_hsfw_del_all(acr); 208 nvkm_firmware_put(acr->wpr_fw); 209 acr->wpr_fw = NULL; 210 } 211 212 static int 213 nvkm_acr_oneinit(struct nvkm_subdev *subdev) 214 { 215 struct nvkm_device *device = subdev->device; 216 struct nvkm_acr *acr = nvkm_acr(subdev); 217 struct nvkm_acr_hsfw *hsfw; 218 struct nvkm_acr_lsfw *lsfw, *lsft; 219 struct nvkm_acr_lsf *lsf; 220 u32 wpr_size = 0; 221 int ret, i; 222 223 if (list_empty(&acr->hsfw)) { 224 nvkm_debug(subdev, "No HSFW(s)\n"); 225 nvkm_acr_cleanup(acr); 226 return 0; 227 } 228 229 /* Determine layout/size of WPR image up-front, as we need to know 230 * it to allocate memory before we begin constructing it. 231 */ 232 list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) { 233 /* Cull unknown falcons that are present in WPR image. */ 234 if (acr->wpr_fw) { 235 if (!lsfw->func) { 236 nvkm_acr_lsfw_del(lsfw); 237 continue; 238 } 239 240 wpr_size = acr->wpr_fw->size; 241 } 242 243 /* Ensure we've fetched falcon configuration. */ 244 ret = nvkm_falcon_get(lsfw->falcon, subdev); 245 if (ret) 246 return ret; 247 248 nvkm_falcon_put(lsfw->falcon, subdev); 249 250 if (!(lsf = kmalloc(sizeof(*lsf), GFP_KERNEL))) 251 return -ENOMEM; 252 lsf->func = lsfw->func; 253 lsf->falcon = lsfw->falcon; 254 lsf->id = lsfw->id; 255 list_add_tail(&lsf->head, &acr->lsf); 256 } 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%"PRIx64"-0x%"PRIx64" (shadow 0x%"PRIx64")\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