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
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
120 done:
121 if (ret || release)
122 nvkm_falcon_put(falcon, user);
123 return ret;
124 }
125
126 int
nvkm_falcon_fw_oneinit(struct nvkm_falcon_fw * fw,struct nvkm_falcon * falcon,struct nvkm_vmm * vmm,struct nvkm_memory * inst)127 nvkm_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
154 void
nvkm_falcon_fw_dtor(struct nvkm_falcon_fw * fw)155 nvkm_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 }
163
164 static const struct nvkm_firmware_func
165 nvkm_falcon_fw_dma = {
166 .type = NVKM_FIRMWARE_IMG_DMA,
167 };
168
169 static const struct nvkm_firmware_func
170 nvkm_falcon_fw = {
171 .type = NVKM_FIRMWARE_IMG_RAM,
172 };
173
174 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)175 nvkm_falcon_fw_sign(struct nvkm_falcon_fw *fw, u32 sig_base_img, u32 sig_size, const u8 *sigs,
176 int sig_nr_prd, u32 sig_base_prd, int sig_nr_dbg, u32 sig_base_dbg)
177 {
178 fw->sig_base_prd = sig_base_prd;
179 fw->sig_base_dbg = sig_base_dbg;
180 fw->sig_base_img = sig_base_img;
181 fw->sig_size = sig_size;
182 fw->sig_nr = sig_nr_prd + sig_nr_dbg;
183
184 fw->sigs = kmalloc_array(fw->sig_nr, fw->sig_size, GFP_KERNEL);
185 if (!fw->sigs)
186 return -ENOMEM;
187
188 memcpy(fw->sigs, sigs + sig_base_prd, sig_nr_prd * fw->sig_size);
189 if (sig_nr_dbg)
190 memcpy(fw->sigs + sig_size, sigs + sig_base_dbg, sig_nr_dbg * fw->sig_size);
191
192 return 0;
193 }
194
195 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)196 nvkm_falcon_fw_ctor(const struct nvkm_falcon_fw_func *func, const char *name,
197 struct nvkm_device *device, bool dma, const void *src, u32 len,
198 struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)
199 {
200 const struct nvkm_firmware_func *type = dma ? &nvkm_falcon_fw_dma : &nvkm_falcon_fw;
201 int ret;
202
203 fw->func = func;
204
205 ret = nvkm_firmware_ctor(type, name, device, src, len, &fw->fw);
206 if (ret)
207 return ret;
208
209 return falcon ? nvkm_falcon_fw_oneinit(fw, falcon, NULL, NULL) : 0;
210 }
211
212 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)213 nvkm_falcon_fw_ctor_hs(const struct nvkm_falcon_fw_func *func, const char *name,
214 struct nvkm_subdev *subdev, const char *bl, const char *img, int ver,
215 struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)
216 {
217 const struct firmware *blob;
218 const struct nvfw_bin_hdr *hdr;
219 const struct nvfw_hs_header *hshdr;
220 const struct nvfw_hs_load_header *lhdr;
221 const struct nvfw_bl_desc *desc;
222 u32 loc, sig;
223 int ret;
224
225 ret = nvkm_firmware_load_name(subdev, img, "", ver, &blob);
226 if (ret)
227 return ret;
228
229 hdr = nvfw_bin_hdr(subdev, blob->data);
230 hshdr = nvfw_hs_header(subdev, blob->data + hdr->header_offset);
231
232 ret = nvkm_falcon_fw_ctor(func, name, subdev->device, bl != NULL,
233 blob->data + hdr->data_offset, hdr->data_size, falcon, fw);
234 if (ret)
235 goto done;
236
237 /* Earlier FW releases by NVIDIA for Nouveau's use aren't in NVIDIA's
238 * standard format, and don't have the indirection seen in the 0x10de
239 * case.
240 */
241 switch (hdr->bin_magic) {
242 case 0x000010de:
243 loc = *(u32 *)(blob->data + hshdr->patch_loc);
244 sig = *(u32 *)(blob->data + hshdr->patch_sig);
245 break;
246 case 0x3b1d14f0:
247 loc = hshdr->patch_loc;
248 sig = hshdr->patch_sig;
249 break;
250 default:
251 WARN_ON(1);
252 ret = -EINVAL;
253 goto done;
254 }
255
256 ret = nvkm_falcon_fw_sign(fw, loc, hshdr->sig_prod_size, blob->data,
257 1, hshdr->sig_prod_offset + sig,
258 1, hshdr->sig_dbg_offset + sig);
259 if (ret)
260 goto done;
261
262 lhdr = nvfw_hs_load_header(subdev, blob->data + hshdr->hdr_offset);
263
264 fw->nmem_base_img = 0;
265 fw->nmem_base = lhdr->non_sec_code_off;
266 fw->nmem_size = lhdr->non_sec_code_size;
267
268 fw->imem_base_img = lhdr->apps[0];
269 fw->imem_base = ALIGN(lhdr->apps[0], 0x100);
270 fw->imem_size = lhdr->apps[lhdr->num_apps + 0];
271
272 fw->dmem_base_img = lhdr->data_dma_base;
273 fw->dmem_base = 0;
274 fw->dmem_size = lhdr->data_size;
275 fw->dmem_sign = loc - lhdr->data_dma_base;
276
277 if (bl) {
278 nvkm_firmware_put(blob);
279
280 ret = nvkm_firmware_load_name(subdev, bl, "", ver, &blob);
281 if (ret)
282 return ret;
283
284 hdr = nvfw_bin_hdr(subdev, blob->data);
285 desc = nvfw_bl_desc(subdev, blob->data + hdr->header_offset);
286
287 fw->boot_addr = desc->start_tag << 8;
288 fw->boot_size = desc->code_size;
289 fw->boot = kmemdup(blob->data + hdr->data_offset + desc->code_off,
290 fw->boot_size, GFP_KERNEL);
291 if (!fw->boot)
292 ret = -ENOMEM;
293 } else {
294 fw->boot_addr = fw->nmem_base;
295 }
296
297 done:
298 if (ret)
299 nvkm_falcon_fw_dtor(fw);
300
301 nvkm_firmware_put(blob);
302 return ret;
303 }
304
305 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)306 nvkm_falcon_fw_ctor_hs_v2(const struct nvkm_falcon_fw_func *func, const char *name,
307 struct nvkm_subdev *subdev, const char *img, int ver,
308 struct nvkm_falcon *falcon, struct nvkm_falcon_fw *fw)
309 {
310 const struct nvfw_bin_hdr *hdr;
311 const struct nvfw_hs_header_v2 *hshdr;
312 const struct nvfw_hs_load_header_v2 *lhdr;
313 const struct firmware *blob;
314 u32 loc, sig, cnt, *meta;
315 int ret;
316
317 ret = nvkm_firmware_load_name(subdev, img, "", ver, &blob);
318 if (ret)
319 return ret;
320
321 hdr = nvfw_bin_hdr(subdev, blob->data);
322 hshdr = nvfw_hs_header_v2(subdev, blob->data + hdr->header_offset);
323 meta = (u32 *)(blob->data + hshdr->meta_data_offset);
324 loc = *(u32 *)(blob->data + hshdr->patch_loc);
325 sig = *(u32 *)(blob->data + hshdr->patch_sig);
326 cnt = *(u32 *)(blob->data + hshdr->num_sig);
327
328 ret = nvkm_falcon_fw_ctor(func, name, subdev->device, true,
329 blob->data + hdr->data_offset, hdr->data_size, falcon, fw);
330 if (ret)
331 goto done;
332
333 ret = nvkm_falcon_fw_sign(fw, loc, hshdr->sig_prod_size / cnt, blob->data,
334 cnt, hshdr->sig_prod_offset + sig, 0, 0);
335 if (ret)
336 goto done;
337
338 lhdr = nvfw_hs_load_header_v2(subdev, blob->data + hshdr->header_offset);
339
340 fw->imem_base_img = lhdr->app[0].offset;
341 fw->imem_base = 0;
342 fw->imem_size = lhdr->app[0].size;
343
344 fw->dmem_base_img = lhdr->os_data_offset;
345 fw->dmem_base = 0;
346 fw->dmem_size = lhdr->os_data_size;
347 fw->dmem_sign = loc - lhdr->os_data_offset;
348
349 fw->boot_addr = lhdr->app[0].offset;
350
351 fw->fuse_ver = meta[0];
352 fw->engine_id = meta[1];
353 fw->ucode_id = meta[2];
354
355 done:
356 if (ret)
357 nvkm_falcon_fw_dtor(fw);
358
359 nvkm_firmware_put(blob);
360 return ret;
361 }
362