lsfw.c (7347B)
1/* 2 * Copyright 2019 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#include <core/falcon.h> 24#include <core/firmware.h> 25#include <nvfw/fw.h> 26#include <nvfw/ls.h> 27 28void 29nvkm_acr_lsfw_del(struct nvkm_acr_lsfw *lsfw) 30{ 31 nvkm_blob_dtor(&lsfw->img); 32 nvkm_firmware_put(lsfw->sig); 33 list_del(&lsfw->head); 34 kfree(lsfw); 35} 36 37void 38nvkm_acr_lsfw_del_all(struct nvkm_acr *acr) 39{ 40 struct nvkm_acr_lsfw *lsfw, *lsft; 41 list_for_each_entry_safe(lsfw, lsft, &acr->lsfw, head) { 42 nvkm_acr_lsfw_del(lsfw); 43 } 44} 45 46static struct nvkm_acr_lsfw * 47nvkm_acr_lsfw_get(struct nvkm_acr *acr, enum nvkm_acr_lsf_id id) 48{ 49 struct nvkm_acr_lsfw *lsfw; 50 list_for_each_entry(lsfw, &acr->lsfw, head) { 51 if (lsfw->id == id) 52 return lsfw; 53 } 54 return NULL; 55} 56 57struct nvkm_acr_lsfw * 58nvkm_acr_lsfw_add(const struct nvkm_acr_lsf_func *func, struct nvkm_acr *acr, 59 struct nvkm_falcon *falcon, enum nvkm_acr_lsf_id id) 60{ 61 struct nvkm_acr_lsfw *lsfw; 62 63 if (!acr || list_empty(&acr->hsfw)) 64 return ERR_PTR(-ENOSYS); 65 66 lsfw = nvkm_acr_lsfw_get(acr, id); 67 if (lsfw && lsfw->func) { 68 nvkm_error(&acr->subdev, "LSFW %d redefined\n", id); 69 return ERR_PTR(-EEXIST); 70 } 71 72 if (!lsfw) { 73 if (!(lsfw = kzalloc(sizeof(*lsfw), GFP_KERNEL))) 74 return ERR_PTR(-ENOMEM); 75 76 lsfw->id = id; 77 list_add_tail(&lsfw->head, &acr->lsfw); 78 } 79 80 lsfw->func = func; 81 lsfw->falcon = falcon; 82 return lsfw; 83} 84 85static struct nvkm_acr_lsfw * 86nvkm_acr_lsfw_load_sig_image_desc_(struct nvkm_subdev *subdev, 87 struct nvkm_falcon *falcon, 88 enum nvkm_acr_lsf_id id, 89 const char *path, int ver, 90 const struct nvkm_acr_lsf_func *func, 91 const struct firmware **pdesc) 92{ 93 struct nvkm_acr *acr = subdev->device->acr; 94 struct nvkm_acr_lsfw *lsfw; 95 int ret; 96 97 if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id)))) 98 return lsfw; 99 100 ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig); 101 if (ret) 102 goto done; 103 104 ret = nvkm_firmware_load_blob(subdev, path, "image", ver, &lsfw->img); 105 if (ret) 106 goto done; 107 108 ret = nvkm_firmware_load_name(subdev, path, "desc", ver, pdesc); 109done: 110 if (ret) { 111 nvkm_acr_lsfw_del(lsfw); 112 return ERR_PTR(ret); 113 } 114 115 return lsfw; 116} 117 118static void 119nvkm_acr_lsfw_from_desc(const struct nvfw_ls_desc_head *desc, 120 struct nvkm_acr_lsfw *lsfw) 121{ 122 lsfw->bootloader_size = ALIGN(desc->bootloader_size, 256); 123 lsfw->bootloader_imem_offset = desc->bootloader_imem_offset; 124 125 lsfw->app_size = ALIGN(desc->app_size, 256); 126 lsfw->app_start_offset = desc->app_start_offset; 127 lsfw->app_imem_entry = desc->app_imem_entry; 128 lsfw->app_resident_code_offset = desc->app_resident_code_offset; 129 lsfw->app_resident_code_size = desc->app_resident_code_size; 130 lsfw->app_resident_data_offset = desc->app_resident_data_offset; 131 lsfw->app_resident_data_size = desc->app_resident_data_size; 132 133 lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) + 134 lsfw->bootloader_size; 135 lsfw->data_size = lsfw->app_size + lsfw->bootloader_size - 136 lsfw->ucode_size; 137} 138 139int 140nvkm_acr_lsfw_load_sig_image_desc(struct nvkm_subdev *subdev, 141 struct nvkm_falcon *falcon, 142 enum nvkm_acr_lsf_id id, 143 const char *path, int ver, 144 const struct nvkm_acr_lsf_func *func) 145{ 146 const struct firmware *fw; 147 struct nvkm_acr_lsfw *lsfw; 148 149 lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver, 150 func, &fw); 151 if (IS_ERR(lsfw)) 152 return PTR_ERR(lsfw); 153 154 nvkm_acr_lsfw_from_desc(&nvfw_ls_desc(subdev, fw->data)->head, lsfw); 155 nvkm_firmware_put(fw); 156 return 0; 157} 158 159int 160nvkm_acr_lsfw_load_sig_image_desc_v1(struct nvkm_subdev *subdev, 161 struct nvkm_falcon *falcon, 162 enum nvkm_acr_lsf_id id, 163 const char *path, int ver, 164 const struct nvkm_acr_lsf_func *func) 165{ 166 const struct firmware *fw; 167 struct nvkm_acr_lsfw *lsfw; 168 169 lsfw = nvkm_acr_lsfw_load_sig_image_desc_(subdev, falcon, id, path, ver, 170 func, &fw); 171 if (IS_ERR(lsfw)) 172 return PTR_ERR(lsfw); 173 174 nvkm_acr_lsfw_from_desc(&nvfw_ls_desc_v1(subdev, fw->data)->head, lsfw); 175 nvkm_firmware_put(fw); 176 return 0; 177} 178 179int 180nvkm_acr_lsfw_load_bl_inst_data_sig(struct nvkm_subdev *subdev, 181 struct nvkm_falcon *falcon, 182 enum nvkm_acr_lsf_id id, 183 const char *path, int ver, 184 const struct nvkm_acr_lsf_func *func) 185{ 186 struct nvkm_acr *acr = subdev->device->acr; 187 struct nvkm_acr_lsfw *lsfw; 188 const struct firmware *bl = NULL, *inst = NULL, *data = NULL; 189 const struct nvfw_bin_hdr *hdr; 190 const struct nvfw_bl_desc *desc; 191 u32 *bldata; 192 int ret; 193 194 if (IS_ERR((lsfw = nvkm_acr_lsfw_add(func, acr, falcon, id)))) 195 return PTR_ERR(lsfw); 196 197 ret = nvkm_firmware_load_name(subdev, path, "bl", ver, &bl); 198 if (ret) 199 goto done; 200 201 hdr = nvfw_bin_hdr(subdev, bl->data); 202 desc = nvfw_bl_desc(subdev, bl->data + hdr->header_offset); 203 bldata = (void *)(bl->data + hdr->data_offset); 204 205 ret = nvkm_firmware_load_name(subdev, path, "inst", ver, &inst); 206 if (ret) 207 goto done; 208 209 ret = nvkm_firmware_load_name(subdev, path, "data", ver, &data); 210 if (ret) 211 goto done; 212 213 ret = nvkm_firmware_load_name(subdev, path, "sig", ver, &lsfw->sig); 214 if (ret) 215 goto done; 216 217 lsfw->bootloader_size = ALIGN(desc->code_size, 256); 218 lsfw->bootloader_imem_offset = desc->start_tag << 8; 219 220 lsfw->app_start_offset = lsfw->bootloader_size; 221 lsfw->app_imem_entry = 0; 222 lsfw->app_resident_code_offset = 0; 223 lsfw->app_resident_code_size = ALIGN(inst->size, 256); 224 lsfw->app_resident_data_offset = lsfw->app_resident_code_size; 225 lsfw->app_resident_data_size = ALIGN(data->size, 256); 226 lsfw->app_size = lsfw->app_resident_code_size + 227 lsfw->app_resident_data_size; 228 229 lsfw->img.size = lsfw->bootloader_size + lsfw->app_size; 230 if (!(lsfw->img.data = kzalloc(lsfw->img.size, GFP_KERNEL))) { 231 ret = -ENOMEM; 232 goto done; 233 } 234 235 memcpy(lsfw->img.data, bldata, lsfw->bootloader_size); 236 memcpy(lsfw->img.data + lsfw->app_start_offset + 237 lsfw->app_resident_code_offset, inst->data, inst->size); 238 memcpy(lsfw->img.data + lsfw->app_start_offset + 239 lsfw->app_resident_data_offset, data->data, data->size); 240 241 lsfw->ucode_size = ALIGN(lsfw->app_resident_data_offset, 256) + 242 lsfw->bootloader_size; 243 lsfw->data_size = lsfw->app_size + lsfw->bootloader_size - 244 lsfw->ucode_size; 245 246done: 247 if (ret) 248 nvkm_acr_lsfw_del(lsfw); 249 nvkm_firmware_put(data); 250 nvkm_firmware_put(inst); 251 nvkm_firmware_put(bl); 252 return ret; 253}