Home | History | Annotate | Line # | Download | only in acr
      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