1/*
2 * Copyright 2022 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/memory.h>
25#include <subdev/mmu.h>
26
27#include <nvfw/fw.h>
28#include <nvfw/hs.h>
29
30int
31nvkm_falcon_fw_patch(struct nvkm_falcon_fw *fw)
32{
33	struct nvkm_falcon *falcon = fw->falcon;
34	u32 sig_base_src = fw->sig_base_prd;
35	u32 src, dst, len, i;
36	int idx = 0;
37
38	FLCNFW_DBG(fw, "patching sigs:%d size:%d", fw->sig_nr, fw->sig_size);
39	if (fw->func->signature) {
40		idx = fw->func->signature(fw, &sig_base_src);
41		if (idx < 0)
42			return idx;
43	}
44
45	src = idx * fw->sig_size;
46	dst = fw->sig_base_img;
47	len = fw->sig_size / 4;
48	FLCNFW_DBG(fw, "patch idx:%d src:%08x dst:%08x", idx, sig_base_src + src, dst);
49	for (i = 0; i < len; i++) {
50		u32 sig = *(u32 *)(fw->sigs + src);
51
52		if (nvkm_printk_ok(falcon->owner, falcon->user, NV_DBG_TRACE)) {
53			if (i % 8 == 0)
54				printk(KERN_INFO "sig -> %08x:", dst);
55			printk(KERN_CONT " %08x", sig);
56		}
57
58		*(u32 *)(fw->fw.img + dst) = sig;
59		src += 4;
60		dst += 4;
61	}
62
63	return 0;
64}
65
66static void
67nvkm_falcon_fw_dtor_sigs(struct nvkm_falcon_fw *fw)
68{
69	kfree(fw->sigs);
70	fw->sigs = NULL;
71}
72
73int
74nvkm_falcon_fw_boot(struct nvkm_falcon_fw *fw, struct nvkm_subdev *user,
75		    bool release, u32 *pmbox0, u32 *pmbox1, u32 mbox0_ok, u32 irqsclr)
76{
77	struct nvkm_falcon *falcon = fw->falcon;
78	int ret;
79
80	ret = nvkm_falcon_get(falcon, user);
81	if (ret)
82		return ret;
83
84	if (fw->sigs) {
85		ret = nvkm_falcon_fw_patch(fw);
86		if (ret)
87			goto done;
88
89		nvkm_falcon_fw_dtor_sigs(fw);
90	}
91
92	FLCNFW_DBG(fw, "resetting");
93	fw->func->reset(fw);
94
95	FLCNFW_DBG(fw, "loading");
96	if (fw->func->setup) {
97		ret = fw->func->setup(fw);
98		if (ret)
99			goto done;
100	}
101
102	ret = fw->func->load(fw);
103	if (ret)
104		goto done;
105
106	FLCNFW_DBG(fw, "booting");
107	ret = fw->func->boot(fw, pmbox0, pmbox1, mbox0_ok, irqsclr);
108	if (ret)
109		FLCNFW_ERR(fw, "boot failed: %d", ret);
110	else
111		FLCNFW_DBG(fw, "booted");
112
113done:
114	if (ret || release)
115		nvkm_falcon_put(falcon, user);
116	return ret;
117}
118
119int
120nvkm_falcon_fw_oneinit(struct nvkm_falcon_fw *fw, struct nvkm_falcon *falcon,
121		       struct nvkm_vmm *vmm, struct nvkm_memory *inst)
122{
123	int ret;
124
125	fw->falcon = falcon;
126	fw->vmm = nvkm_vmm_ref(vmm);
127	fw->inst = nvkm_memory_ref(inst);
128
129	if (fw->boot) {
130		FLCN_DBG(falcon, "mapping %s fw", fw->fw.name);
131		ret = nvkm_vmm_get(fw->vmm, 12, nvkm_memory_size(&fw->fw.mem.memory), &fw->vma);
132		if (ret) {
133			FLCN_ERR(falcon, "get %d", ret);
134			return ret;
135		}
136
137		ret = nvkm_memory_map(&fw->fw.mem.memory, 0, fw->vmm, fw->vma, NULL, 0);
138		if (ret) {
139			FLCN_ERR(falcon, "map %d", ret);
140			return ret;
141		}
142	}
143
144	return 0;
145}
146
147void
148nvkm_falcon_fw_dtor(struct nvkm_falcon_fw *fw)
149{
150	nvkm_vmm_put(fw->vmm, &fw->vma);
151	nvkm_vmm_unref(&fw->vmm);
152	nvkm_memory_unref(&fw->inst);
153	nvkm_falcon_fw_dtor_sigs(fw);
154	nvkm_firmware_dtor(&fw->fw);
155}
156
157static const struct nvkm_firmware_func
158nvkm_falcon_fw_dma = {
159	.type = NVKM_FIRMWARE_IMG_DMA,
160};
161
162static const struct nvkm_firmware_func
163nvkm_falcon_fw = {
164	.type = NVKM_FIRMWARE_IMG_RAM,
165};
166
167int
168nvkm_falcon_fw_sign(struct nvkm_falcon_fw *fw, u32 sig_base_img, u32 sig_size, const u8 *sigs,
169		    int sig_nr_prd, u32 sig_base_prd, int sig_nr_dbg, u32 sig_base_dbg)
170{
171	fw->sig_base_prd = sig_base_prd;
172	fw->sig_base_dbg = sig_base_dbg;
173	fw->sig_base_img = sig_base_img;
174	fw->sig_size = sig_size;
175	fw->sig_nr = sig_nr_prd + sig_nr_dbg;
176
177	fw->sigs = kmalloc_array(fw->sig_nr, fw->sig_size, GFP_KERNEL);
178	if (!fw->sigs)
179		return -ENOMEM;
180
181	memcpy(fw->sigs, sigs + sig_base_prd, sig_nr_prd * fw->sig_size);
182	if (sig_nr_dbg)
183		memcpy(fw->sigs + sig_size, sigs + sig_base_dbg, sig_nr_dbg * fw->sig_size);
184
185	return 0;
186}
187
188int
189nvkm_falcon_fw_ctor(const struct nvkm_falcon_fw_func *func, const char *name,
190		    struct nvkm_device *device, bool dma, const void *src, u32 len,
191		    struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)
192{
193	const struct nvkm_firmware_func *type = dma ? &nvkm_falcon_fw_dma : &nvkm_falcon_fw;
194	int ret;
195
196	fw->func = func;
197
198	ret = nvkm_firmware_ctor(type, name, device, src, len, &fw->fw);
199	if (ret)
200		return ret;
201
202	return falcon ? nvkm_falcon_fw_oneinit(fw, falcon, NULL, NULL) : 0;
203}
204
205int
206nvkm_falcon_fw_ctor_hs(const struct nvkm_falcon_fw_func *func, const char *name,
207		       struct nvkm_subdev *subdev, const char *bl, const char *img, int ver,
208		       struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)
209{
210	const struct firmware *blob;
211	const struct nvfw_bin_hdr *hdr;
212	const struct nvfw_hs_header *hshdr;
213	const struct nvfw_hs_load_header *lhdr;
214	const struct nvfw_bl_desc *desc;
215	u32 loc, sig;
216	int ret;
217
218	ret = nvkm_firmware_load_name(subdev, img, "", ver, &blob);
219	if (ret)
220		return ret;
221
222	hdr = nvfw_bin_hdr(subdev, blob->data);
223	hshdr = nvfw_hs_header(subdev, blob->data + hdr->header_offset);
224
225	ret = nvkm_falcon_fw_ctor(func, name, subdev->device, bl != NULL,
226				  blob->data + hdr->data_offset, hdr->data_size, falcon, fw);
227	if (ret)
228		goto done;
229
230	/* Earlier FW releases by NVIDIA for Nouveau's use aren't in NVIDIA's
231	 * standard format, and don't have the indirection seen in the 0x10de
232	 * case.
233	 */
234	switch (hdr->bin_magic) {
235	case 0x000010de:
236		loc = *(u32 *)(blob->data + hshdr->patch_loc);
237		sig = *(u32 *)(blob->data + hshdr->patch_sig);
238		break;
239	case 0x3b1d14f0:
240		loc = hshdr->patch_loc;
241		sig = hshdr->patch_sig;
242		break;
243	default:
244		WARN_ON(1);
245		ret = -EINVAL;
246		goto done;
247	}
248
249	ret = nvkm_falcon_fw_sign(fw, loc, hshdr->sig_prod_size, blob->data,
250				  1, hshdr->sig_prod_offset + sig,
251				  1, hshdr->sig_dbg_offset + sig);
252	if (ret)
253		goto done;
254
255	lhdr = nvfw_hs_load_header(subdev, blob->data + hshdr->hdr_offset);
256
257	fw->nmem_base_img = 0;
258	fw->nmem_base = lhdr->non_sec_code_off;
259	fw->nmem_size = lhdr->non_sec_code_size;
260
261	fw->imem_base_img = lhdr->apps[0];
262	fw->imem_base = ALIGN(lhdr->apps[0], 0x100);
263	fw->imem_size = lhdr->apps[lhdr->num_apps + 0];
264
265	fw->dmem_base_img = lhdr->data_dma_base;
266	fw->dmem_base = 0;
267	fw->dmem_size = lhdr->data_size;
268	fw->dmem_sign = loc - lhdr->data_dma_base;
269
270	if (bl) {
271		nvkm_firmware_put(blob);
272
273		ret = nvkm_firmware_load_name(subdev, bl, "", ver, &blob);
274		if (ret)
275			return ret;
276
277		hdr = nvfw_bin_hdr(subdev, blob->data);
278		desc = nvfw_bl_desc(subdev, blob->data + hdr->header_offset);
279
280		fw->boot_addr = desc->start_tag << 8;
281		fw->boot_size = desc->code_size;
282		fw->boot = kmemdup(blob->data + hdr->data_offset + desc->code_off,
283				   fw->boot_size, GFP_KERNEL);
284		if (!fw->boot)
285			ret = -ENOMEM;
286	} else {
287		fw->boot_addr = fw->nmem_base;
288	}
289
290done:
291	if (ret)
292		nvkm_falcon_fw_dtor(fw);
293
294	nvkm_firmware_put(blob);
295	return ret;
296}
297
298int
299nvkm_falcon_fw_ctor_hs_v2(const struct nvkm_falcon_fw_func *func, const char *name,
300			  struct nvkm_subdev *subdev, const char *img, int ver,
301			  struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)
302{
303	const struct nvfw_bin_hdr *hdr;
304	const struct nvfw_hs_header_v2 *hshdr;
305	const struct nvfw_hs_load_header_v2 *lhdr;
306	const struct firmware *blob;
307	u32 loc, sig, cnt, *meta;
308	int ret;
309
310	ret = nvkm_firmware_load_name(subdev, img, "", ver, &blob);
311	if (ret)
312		return ret;
313
314	hdr = nvfw_bin_hdr(subdev, blob->data);
315	hshdr = nvfw_hs_header_v2(subdev, blob->data + hdr->header_offset);
316	meta = (u32 *)(blob->data + hshdr->meta_data_offset);
317	loc = *(u32 *)(blob->data + hshdr->patch_loc);
318	sig = *(u32 *)(blob->data + hshdr->patch_sig);
319	cnt = *(u32 *)(blob->data + hshdr->num_sig);
320
321	ret = nvkm_falcon_fw_ctor(func, name, subdev->device, true,
322				  blob->data + hdr->data_offset, hdr->data_size, falcon, fw);
323	if (ret)
324		goto done;
325
326	ret = nvkm_falcon_fw_sign(fw, loc, hshdr->sig_prod_size / cnt, blob->data,
327				  cnt, hshdr->sig_prod_offset + sig, 0, 0);
328	if (ret)
329		goto done;
330
331	lhdr = nvfw_hs_load_header_v2(subdev, blob->data + hshdr->header_offset);
332
333	fw->imem_base_img = lhdr->app[0].offset;
334	fw->imem_base = 0;
335	fw->imem_size = lhdr->app[0].size;
336
337	fw->dmem_base_img = lhdr->os_data_offset;
338	fw->dmem_base = 0;
339	fw->dmem_size = lhdr->os_data_size;
340	fw->dmem_sign = loc - lhdr->os_data_offset;
341
342	fw->boot_addr = lhdr->app[0].offset;
343
344	fw->fuse_ver = meta[0];
345	fw->engine_id = meta[1];
346	fw->ucode_id = meta[2];
347
348done:
349	if (ret)
350		nvkm_falcon_fw_dtor(fw);
351
352	nvkm_firmware_put(blob);
353	return ret;
354}
355