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