File Index
Files and directories
Sign in
FILE

fw.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/nouveau/nvkm/falcon/fw.c C
  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
 93	FLCNFW_DBG(fw, "resetting");
 94	fw->func->reset(fw);
 95
 96	FLCNFW_DBG(fw, "loading");
 97	if (fw->func->setup) {
 98		ret = fw->func->setup(fw);
 99		if (ret)
100			goto done;
101	}
102
103	/* after last write to the img, sync dma mappings */
104	dma_sync_single_for_device(fw->fw.device->dev,
105				   fw->fw.phys,
106				   sg_dma_len(&fw->fw.mem.sgl),
107				   DMA_TO_DEVICE);
108
109	ret = fw->func->load(fw);
110	if (ret)
111		goto done;
112
113	FLCNFW_DBG(fw, "booting");
114	ret = fw->func->boot(fw, pmbox0, pmbox1, mbox0_ok, irqsclr);
115	if (ret)
116		FLCNFW_ERR(fw, "boot failed: %d", ret);
117	else
118		FLCNFW_DBG(fw, "booted");
119
120done:
121	if (ret || release)
122		nvkm_falcon_put(falcon, user);
123	return ret;
124}
125
126int
127nvkm_falcon_fw_oneinit(struct nvkm_falcon_fw *fw, struct nvkm_falcon *falcon,
128		       struct nvkm_vmm *vmm, struct nvkm_memory *inst)
129{
130	int ret;
131
132	fw->falcon = falcon;
133	fw->vmm = nvkm_vmm_ref(vmm);
134	fw->inst = nvkm_memory_ref(inst);
135
136	if (fw->boot) {
137		FLCN_DBG(falcon, "mapping %s fw", fw->fw.name);
138		ret = nvkm_vmm_get(fw->vmm, 12, nvkm_memory_size(&fw->fw.mem.memory), &fw->vma);
139		if (ret) {
140			FLCN_ERR(falcon, "get %d", ret);
141			return ret;
142		}
143
144		ret = nvkm_memory_map(&fw->fw.mem.memory, 0, fw->vmm, fw->vma, NULL, 0);
145		if (ret) {
146			FLCN_ERR(falcon, "map %d", ret);
147			return ret;
148		}
149	}
150
151	return 0;
152}
153
154void
155nvkm_falcon_fw_dtor(struct nvkm_falcon_fw *fw)
156{
157	nvkm_vmm_put(fw->vmm, &fw->vma);
158	nvkm_vmm_unref(&fw->vmm);
159	nvkm_memory_unref(&fw->inst);
160	nvkm_falcon_fw_dtor_sigs(fw);
161	nvkm_firmware_dtor(&fw->fw);
162	kfree(fw->boot);
163	fw->boot = NULL;
164}
165
166static const struct nvkm_firmware_func
167nvkm_falcon_fw_dma = {
168	.type = NVKM_FIRMWARE_IMG_DMA,
169};
170
171static const struct nvkm_firmware_func
172nvkm_falcon_fw = {
173	.type = NVKM_FIRMWARE_IMG_RAM,
174};
175
176int
177nvkm_falcon_fw_sign(struct nvkm_falcon_fw *fw, u32 sig_base_img, u32 sig_size, const u8 *sigs,
178		    int sig_nr_prd, u32 sig_base_prd, int sig_nr_dbg, u32 sig_base_dbg)
179{
180	fw->sig_base_prd = sig_base_prd;
181	fw->sig_base_dbg = sig_base_dbg;
182	fw->sig_base_img = sig_base_img;
183	fw->sig_size = sig_size;
184	fw->sig_nr = sig_nr_prd + sig_nr_dbg;
185
186	fw->sigs = kmalloc_array(fw->sig_nr, fw->sig_size, GFP_KERNEL);
187	if (!fw->sigs)
188		return -ENOMEM;
189
190	memcpy(fw->sigs, sigs + sig_base_prd, sig_nr_prd * fw->sig_size);
191	if (sig_nr_dbg)
192		memcpy(fw->sigs + sig_size, sigs + sig_base_dbg, sig_nr_dbg * fw->sig_size);
193
194	return 0;
195}
196
197int
198nvkm_falcon_fw_ctor(const struct nvkm_falcon_fw_func *func, const char *name,
199		    struct nvkm_device *device, bool dma, const void *src, u32 len,
200		    struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)
201{
202	const struct nvkm_firmware_func *type = dma ? &nvkm_falcon_fw_dma : &nvkm_falcon_fw;
203	int ret;
204
205	fw->func = func;
206
207	ret = nvkm_firmware_ctor(type, name, device, src, len, &fw->fw);
208	if (ret)
209		return ret;
210
211	return falcon ? nvkm_falcon_fw_oneinit(fw, falcon, NULL, NULL) : 0;
212}
213
214int
215nvkm_falcon_fw_ctor_hs(const struct nvkm_falcon_fw_func *func, const char *name,
216		       struct nvkm_subdev *subdev, const char *bl, const char *img, int ver,
217		       struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)
218{
219	const struct firmware *blob;
220	const struct nvfw_bin_hdr *hdr;
221	const struct nvfw_hs_header *hshdr;
222	const struct nvfw_hs_load_header *lhdr;
223	const struct nvfw_bl_desc *desc;
224	u32 loc, sig;
225	int ret;
226
227	ret = nvkm_firmware_load_name(subdev, img, "", ver, &blob);
228	if (ret)
229		return ret;
230
231	hdr = nvfw_bin_hdr(subdev, blob->data);
232	hshdr = nvfw_hs_header(subdev, blob->data + hdr->header_offset);
233
234	ret = nvkm_falcon_fw_ctor(func, name, subdev->device, bl != NULL,
235				  blob->data + hdr->data_offset, hdr->data_size, falcon, fw);
236	if (ret)
237		goto done;
238
239	/* Earlier FW releases by NVIDIA for Nouveau's use aren't in NVIDIA's
240	 * standard format, and don't have the indirection seen in the 0x10de
241	 * case.
242	 */
243	switch (hdr->bin_magic) {
244	case 0x000010de:
245		loc = *(u32 *)(blob->data + hshdr->patch_loc);
246		sig = *(u32 *)(blob->data + hshdr->patch_sig);
247		break;
248	case 0x3b1d14f0:
249		loc = hshdr->patch_loc;
250		sig = hshdr->patch_sig;
251		break;
252	default:
253		WARN_ON(1);
254		ret = -EINVAL;
255		goto done;
256	}
257
258	ret = nvkm_falcon_fw_sign(fw, loc, hshdr->sig_prod_size, blob->data,
259				  1, hshdr->sig_prod_offset + sig,
260				  1, hshdr->sig_dbg_offset + sig);
261	if (ret)
262		goto done;
263
264	lhdr = nvfw_hs_load_header(subdev, blob->data + hshdr->hdr_offset);
265
266	fw->nmem_base_img = 0;
267	fw->nmem_base = lhdr->non_sec_code_off;
268	fw->nmem_size = lhdr->non_sec_code_size;
269
270	fw->imem_base_img = lhdr->apps[0];
271	fw->imem_base = ALIGN(lhdr->apps[0], 0x100);
272	fw->imem_size = lhdr->apps[lhdr->num_apps + 0];
273
274	fw->dmem_base_img = lhdr->data_dma_base;
275	fw->dmem_base = 0;
276	fw->dmem_size = lhdr->data_size;
277	fw->dmem_sign = loc - lhdr->data_dma_base;
278
279	if (bl) {
280		nvkm_firmware_put(blob);
281
282		ret = nvkm_firmware_load_name(subdev, bl, "", ver, &blob);
283		if (ret)
284			return ret;
285
286		hdr = nvfw_bin_hdr(subdev, blob->data);
287		desc = nvfw_bl_desc(subdev, blob->data + hdr->header_offset);
288
289		fw->boot_addr = desc->start_tag << 8;
290		fw->boot_size = desc->code_size;
291		fw->boot = kmemdup(blob->data + hdr->data_offset + desc->code_off,
292				   fw->boot_size, GFP_KERNEL);
293		if (!fw->boot)
294			ret = -ENOMEM;
295	} else {
296		fw->boot_addr = fw->nmem_base;
297	}
298
299done:
300	if (ret)
301		nvkm_falcon_fw_dtor(fw);
302
303	nvkm_firmware_put(blob);
304	return ret;
305}
306
307int
308nvkm_falcon_fw_ctor_hs_v2(const struct nvkm_falcon_fw_func *func, const char *name,
309			  struct nvkm_subdev *subdev, const char *img, int ver,
310			  struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)
311{
312	const struct nvfw_bin_hdr *hdr;
313	const struct nvfw_hs_header_v2 *hshdr;
314	const struct nvfw_hs_load_header_v2 *lhdr;
315	const struct firmware *blob;
316	u32 loc, sig, cnt, *meta;
317	int ret;
318
319	ret = nvkm_firmware_load_name(subdev, img, "", ver, &blob);
320	if (ret)
321		return ret;
322
323	hdr = nvfw_bin_hdr(subdev, blob->data);
324	hshdr = nvfw_hs_header_v2(subdev, blob->data + hdr->header_offset);
325	meta = (u32 *)(blob->data + hshdr->meta_data_offset);
326	loc = *(u32 *)(blob->data + hshdr->patch_loc);
327	sig = *(u32 *)(blob->data + hshdr->patch_sig);
328	cnt = *(u32 *)(blob->data + hshdr->num_sig);
329
330	ret = nvkm_falcon_fw_ctor(func, name, subdev->device, true,
331				  blob->data + hdr->data_offset, hdr->data_size, falcon, fw);
332	if (ret)
333		goto done;
334
335	ret = nvkm_falcon_fw_sign(fw, loc, hshdr->sig_prod_size / cnt, blob->data,
336				  cnt, hshdr->sig_prod_offset + sig, 0, 0);
337	if (ret)
338		goto done;
339
340	lhdr = nvfw_hs_load_header_v2(subdev, blob->data + hshdr->header_offset);
341
342	fw->imem_base_img = lhdr->app[0].offset;
343	fw->imem_base = 0;
344	fw->imem_size = lhdr->app[0].size;
345
346	fw->dmem_base_img = lhdr->os_data_offset;
347	fw->dmem_base = 0;
348	fw->dmem_size = lhdr->os_data_size;
349	fw->dmem_sign = loc - lhdr->os_data_offset;
350
351	fw->boot_addr = lhdr->app[0].offset;
352
353	fw->fuse_ver = meta[0];
354	fw->engine_id = meta[1];
355	fw->ucode_id = meta[2];
356
357done:
358	if (ret)
359		nvkm_falcon_fw_dtor(fw);
360
361	nvkm_firmware_put(blob);
362	return ret;
363}
~/ Read-only file preview.