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