xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/nouveau/nvkm/subdev/acr/nouveau_nvkm_subdev_acr_lsfw.c (revision 7649e88fcfe6a7c92de68bd5e592dec3e35224fb)
1 /*	$NetBSD: nouveau_nvkm_subdev_acr_lsfw.c,v 1.3 2021/12/19 10:51:58 riastradh Exp $	*/
2 
3 /*
4  * Copyright 2019 Red Hat Inc.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22  * OTHER DEALINGS IN THE SOFTWARE.
23  */
24 #include <sys/cdefs.h>
25 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_subdev_acr_lsfw.c,v 1.3 2021/12/19 10:51:58 riastradh Exp $");
26 
27 #include "priv.h"
28 #include <core/falcon.h>
29 #include <core/firmware.h>
30 #include <nvfw/fw.h>
31 #include <nvfw/ls.h>
32 
33 #include <linux/nbsd-namespace.h>
34 
35 void
nvkm_acr_lsfw_del(struct nvkm_acr_lsfw * lsfw)36 nvkm_acr_lsfw_del(struct nvkm_acr_lsfw *lsfw)
37 {
38 	nvkm_blob_dtor(&lsfw->img);
39 	nvkm_firmware_put(lsfw->sig);
40 	list_del(&lsfw->head);
41 	kfree(lsfw);
42 }
43 
44 void
nvkm_acr_lsfw_del_all(struct nvkm_acr * acr)45 nvkm_acr_lsfw_del_all(struct nvkm_acr *acr)
46 {
47 	struct nvkm_acr_lsfw *lsfw, *lsft;
48 	list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) {
49 		nvkm_acr_lsfw_del(lsfw);
50 	}
51 }
52 
53 static struct nvkm_acr_lsfw *
nvkm_acr_lsfw_get(struct nvkm_acr * acr,enum nvkm_acr_lsf_id id)54 nvkm_acr_lsfw_get(struct nvkm_acr *acr, enum nvkm_acr_lsf_id id)
55 {
56 	struct nvkm_acr_lsfw *lsfw;
57 	list_for_each_entry(lsfw, &acr->lsfw, head) {
58 		if (lsfw->id == id)
59 			return lsfw;
60 	}
61 	return NULL;
62 }
63 
64 struct nvkm_acr_lsfw *
nvkm_acr_lsfw_add(const struct nvkm_acr_lsf_func * func,struct nvkm_acr * acr,struct nvkm_falcon * falcon,enum nvkm_acr_lsf_id id)65 nvkm_acr_lsfw_add(const struct nvkm_acr_lsf_func *func, struct nvkm_acr *acr,
66 		 struct nvkm_falcon *falcon, enum nvkm_acr_lsf_id id)
67 {
68 	struct nvkm_acr_lsfw *lsfw;
69 
70 	if (!acr)
71 		return ERR_PTR(-ENOSYS);
72 
73 	lsfw = nvkm_acr_lsfw_get(acr, id);
74 	if (lsfw && lsfw->func) {
75 		nvkm_error(&acr->subdev, "LSFW %d redefined\n", id);
76 		return ERR_PTR(-EEXIST);
77 	}
78 
79 	if (!lsfw) {
80 		if (!(lsfw = kzalloc(sizeof(*lsfw), GFP_KERNEL)))
81 			return ERR_PTR(-ENOMEM);
82 
83 		lsfw->id = id;
84 		list_add_tail(&lsfw->head, &acr->lsfw);
85 	}
86 
87 	lsfw->func = func;
88 	lsfw->falcon = falcon;
89 	return lsfw;
90 }
91 
92 static struct nvkm_acr_lsfw *
nvkm_acr_lsfw_load_sig_image_desc_(struct nvkm_subdev * subdev,struct nvkm_falcon * falcon,enum nvkm_acr_lsf_id id,const char * path,int ver,const struct nvkm_acr_lsf_func * func,const struct firmware ** pdesc)93 nvkm_acr_lsfw_load_sig_image_desc_(struct nvkm_subdev *subdev,
94 				   struct nvkm_falcon *falcon,
95 				   enum nvkm_acr_lsf_id id,
96 				   const char *path, int ver,
97 				   const struct nvkm_acr_lsf_func *func,
98 				   const struct firmware **pdesc)
99 {
100 	struct nvkm_acr *acr = subdev->device->acr;
101 	struct nvkm_acr_lsfw *lsfw;
102 	int ret;
103 
104 	if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id))))
105 		return lsfw;
106 
107 	ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig);
108 	if (ret)
109 		goto done;
110 
111 	ret = nvkm_firmware_load_blob(subdev, path, "image", ver, &lsfw->img);
112 	if (ret)
113 		goto done;
114 
115 	ret = nvkm_firmware_load_name(subdev, path, "desc", ver, pdesc);
116 done:
117 	if (ret) {
118 		nvkm_acr_lsfw_del(lsfw);
119 		return ERR_PTR(ret);
120 	}
121 
122 	return lsfw;
123 }
124 
125 static void
nvkm_acr_lsfw_from_desc(const struct nvfw_ls_desc_head * desc,struct nvkm_acr_lsfw * lsfw)126 nvkm_acr_lsfw_from_desc(const struct nvfw_ls_desc_head *desc,
127 			struct nvkm_acr_lsfw *lsfw)
128 {
129 	lsfw->bootloader_size = ALIGN(desc->bootloader_size, 256);
130 	lsfw->bootloader_imem_offset = desc->bootloader_imem_offset;
131 
132 	lsfw->app_size = ALIGN(desc->app_size, 256);
133 	lsfw->app_start_offset = desc->app_start_offset;
134 	lsfw->app_imem_entry = desc->app_imem_entry;
135 	lsfw->app_resident_code_offset = desc->app_resident_code_offset;
136 	lsfw->app_resident_code_size = desc->app_resident_code_size;
137 	lsfw->app_resident_data_offset = desc->app_resident_data_offset;
138 	lsfw->app_resident_data_size = desc->app_resident_data_size;
139 
140 	lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) +
141 			   lsfw->bootloader_size;
142 	lsfw->data_size = lsfw->app_size + lsfw->bootloader_size -
143 			  lsfw->ucode_size;
144 }
145 
146 int
nvkm_acr_lsfw_load_sig_image_desc(struct nvkm_subdev * subdev,struct nvkm_falcon * falcon,enum nvkm_acr_lsf_id id,const char * path,int ver,const struct nvkm_acr_lsf_func * func)147 nvkm_acr_lsfw_load_sig_image_desc(struct nvkm_subdev *subdev,
148 				  struct nvkm_falcon *falcon,
149 				  enum nvkm_acr_lsf_id id,
150 				  const char *path, int ver,
151 				  const struct nvkm_acr_lsf_func *func)
152 {
153 	const struct firmware *fw;
154 	struct nvkm_acr_lsfw *lsfw;
155 
156 	lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver,
157 						  func, &fw);
158 	if (IS_ERR(lsfw))
159 		return PTR_ERR(lsfw);
160 
161 	nvkm_acr_lsfw_from_desc(&nvfw_ls_desc(subdev, fw->data)->head, lsfw);
162 	nvkm_firmware_put(fw);
163 	return 0;
164 }
165 
166 int
nvkm_acr_lsfw_load_sig_image_desc_v1(struct nvkm_subdev * subdev,struct nvkm_falcon * falcon,enum nvkm_acr_lsf_id id,const char * path,int ver,const struct nvkm_acr_lsf_func * func)167 nvkm_acr_lsfw_load_sig_image_desc_v1(struct nvkm_subdev *subdev,
168 				     struct nvkm_falcon *falcon,
169 				     enum nvkm_acr_lsf_id id,
170 				     const char *path, int ver,
171 				     const struct nvkm_acr_lsf_func *func)
172 {
173 	const struct firmware *fw;
174 	struct nvkm_acr_lsfw *lsfw;
175 
176 	lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver,
177 						  func, &fw);
178 	if (IS_ERR(lsfw))
179 		return PTR_ERR(lsfw);
180 
181 	nvkm_acr_lsfw_from_desc(&nvfw_ls_desc_v1(subdev, fw->data)->head, lsfw);
182 	nvkm_firmware_put(fw);
183 	return 0;
184 }
185 
186 int
nvkm_acr_lsfw_load_bl_inst_data_sig(struct nvkm_subdev * subdev,struct nvkm_falcon * falcon,enum nvkm_acr_lsf_id id,const char * path,int ver,const struct nvkm_acr_lsf_func * func)187 nvkm_acr_lsfw_load_bl_inst_data_sig(struct nvkm_subdev *subdev,
188 				    struct nvkm_falcon *falcon,
189 				    enum nvkm_acr_lsf_id id,
190 				    const char *path, int ver,
191 				    const struct nvkm_acr_lsf_func *func)
192 {
193 	struct nvkm_acr *acr = subdev->device->acr;
194 	struct nvkm_acr_lsfw *lsfw;
195 	const struct firmware *bl = NULL, *inst = NULL, *data = NULL;
196 	const struct nvfw_bin_hdr *hdr;
197 	const struct nvfw_bl_desc *desc;
198 	u32 *bldata;
199 	int ret;
200 
201 	if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id))))
202 		return PTR_ERR(lsfw);
203 
204 	ret = nvkm_firmware_load_name(subdev, path, "bl", ver, &bl);
205 	if (ret)
206 		goto done;
207 
208 	hdr = nvfw_bin_hdr(subdev, bl->data);
209 	desc = nvfw_bl_desc(subdev, bl->data + hdr->header_offset);
210 	bldata = (void *)(bl->data + hdr->data_offset);
211 
212 	ret = nvkm_firmware_load_name(subdev, path, "inst", ver, &inst);
213 	if (ret)
214 		goto done;
215 
216 	ret = nvkm_firmware_load_name(subdev, path, "data", ver, &data);
217 	if (ret)
218 		goto done;
219 
220 	ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig);
221 	if (ret)
222 		goto done;
223 
224 	lsfw->bootloader_size = ALIGN(desc->code_size, 256);
225 	lsfw->bootloader_imem_offset = desc->start_tag << 8;
226 
227 	lsfw->app_start_offset = lsfw->bootloader_size;
228 	lsfw->app_imem_entry = 0;
229 	lsfw->app_resident_code_offset = 0;
230 	lsfw->app_resident_code_size = ALIGN(inst->size, 256);
231 	lsfw->app_resident_data_offset = lsfw->app_resident_code_size;
232 	lsfw->app_resident_data_size = ALIGN(data->size, 256);
233 	lsfw->app_size = lsfw->app_resident_code_size +
234 			 lsfw->app_resident_data_size;
235 
236 	lsfw->img.size = lsfw->bootloader_size + lsfw->app_size;
237 	if (!(lsfw->img.data = kzalloc(lsfw->img.size, GFP_KERNEL))) {
238 		ret = -ENOMEM;
239 		goto done;
240 	}
241 
242 	memcpy(lsfw->img.data, bldata, lsfw->bootloader_size);
243 	memcpy(lsfw->img.data + lsfw->app_start_offset +
244 	       lsfw->app_resident_code_offset, inst->data, inst->size);
245 	memcpy(lsfw->img.data + lsfw->app_start_offset +
246 	       lsfw->app_resident_data_offset, data->data, data->size);
247 
248 	lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) +
249 			   lsfw->bootloader_size;
250 	lsfw->data_size = lsfw->app_size + lsfw->bootloader_size -
251 			  lsfw->ucode_size;
252 
253 done:
254 	if (ret)
255 		nvkm_acr_lsfw_del(lsfw);
256 	nvkm_firmware_put(data);
257 	nvkm_firmware_put(inst);
258 	nvkm_firmware_put(bl);
259 	return ret;
260 }
261