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