Home | History | Annotate | Line # | Download | only in acr
      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
     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
     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 *
     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 *
     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 *
     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
    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
    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
    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
    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