1 /* 2 * Copyright 2023 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 <subdev/bios.h> 25 #include <subdev/bios/pmu.h> 26 27 #include <nvfw/fw.h> 28 29 union nvfw_falcon_appif_hdr { 30 struct nvfw_falcon_appif_hdr_v1 { 31 u8 ver; 32 u8 hdr; 33 u8 len; 34 u8 cnt; 35 } v1; 36 }; 37 38 union nvfw_falcon_appif { 39 struct nvfw_falcon_appif_v1 { 40 #define NVFW_FALCON_APPIF_ID_DMEMMAPPER 0x00000004 41 u32 id; 42 u32 dmem_base; 43 } v1; 44 }; 45 46 union nvfw_falcon_appif_dmemmapper { 47 struct { 48 u32 signature; 49 u16 version; 50 u16 size; 51 u32 cmd_in_buffer_offset; 52 u32 cmd_in_buffer_size; 53 u32 cmd_out_buffer_offset; 54 u32 cmd_out_buffer_size; 55 u32 nvf_img_data_buffer_offset; 56 u32 nvf_img_data_buffer_size; 57 u32 printf_buffer_hdr; 58 u32 ucode_build_time_stamp; 59 u32 ucode_signature; 60 #define NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS 0x00000015 61 #define NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB 0x00000019 62 u32 init_cmd; 63 u32 ucode_feature; 64 u32 ucode_cmd_mask0; 65 u32 ucode_cmd_mask1; 66 u32 multi_tgt_tbl; 67 } v3; 68 }; 69 70 struct nvfw_fwsec_frts_cmd { 71 struct { 72 u32 ver; 73 u32 hdr; 74 u64 addr; 75 u32 size; 76 u32 flags; 77 } read_vbios; 78 struct { 79 u32 ver; 80 u32 hdr; 81 u32 addr; 82 u32 size; 83 #define NVFW_FRTS_CMD_REGION_TYPE_FB 0x00000002 84 u32 type; 85 } frts_region; 86 }; 87 88 static int 89 nvkm_gsp_fwsec_patch(struct nvkm_gsp *gsp, struct nvkm_falcon_fw *fw, u32 if_offset, u32 init_cmd) 90 { 91 union nvfw_falcon_appif_hdr *hdr = (void *)(fw->fw.img + fw->dmem_base_img + if_offset); 92 const u8 *dmem = fw->fw.img + fw->dmem_base_img; 93 int i; 94 95 if (WARN_ON(hdr->v1.ver != 1)) 96 return -EINVAL; 97 98 for (i = 0; i < hdr->v1.cnt; i++) { 99 union nvfw_falcon_appif *app = (void *)((u8 *)hdr + hdr->v1.hdr + i * hdr->v1.len); 100 union nvfw_falcon_appif_dmemmapper *dmemmap; 101 struct nvfw_fwsec_frts_cmd *frtscmd; 102 103 if (app->v1.id != NVFW_FALCON_APPIF_ID_DMEMMAPPER) 104 continue; 105 106 dmemmap = (void *)(dmem + app->v1.dmem_base); 107 dmemmap->v3.init_cmd = init_cmd; 108 109 frtscmd = (void *)(dmem + dmemmap->v3.cmd_in_buffer_offset); 110 111 frtscmd->read_vbios.ver = 1; 112 frtscmd->read_vbios.hdr = sizeof(frtscmd->read_vbios); 113 frtscmd->read_vbios.addr = 0; 114 frtscmd->read_vbios.size = 0; 115 frtscmd->read_vbios.flags = 2; 116 117 if (init_cmd == NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS) { 118 frtscmd->frts_region.ver = 1; 119 frtscmd->frts_region.hdr = sizeof(frtscmd->frts_region); 120 frtscmd->frts_region.addr = gsp->fb.wpr2.frts.addr >> 12; 121 frtscmd->frts_region.size = gsp->fb.wpr2.frts.size >> 12; 122 frtscmd->frts_region.type = NVFW_FRTS_CMD_REGION_TYPE_FB; 123 } 124 125 break; 126 } 127 128 if (WARN_ON(i == hdr->v1.cnt)) 129 return -EINVAL; 130 131 return 0; 132 } 133 134 union nvfw_falcon_ucode_desc { 135 struct nvkm_falcon_ucode_desc_v2 { 136 u32 Hdr; 137 u32 StoredSize; 138 u32 UncompressedSize; 139 u32 VirtualEntry; 140 u32 InterfaceOffset; 141 u32 IMEMPhysBase; 142 u32 IMEMLoadSize; 143 u32 IMEMVirtBase; 144 u32 IMEMSecBase; 145 u32 IMEMSecSize; 146 u32 DMEMOffset; 147 u32 DMEMPhysBase; 148 u32 DMEMLoadSize; 149 u32 altIMEMLoadSize; 150 u32 altDMEMLoadSize; 151 } v2; 152 153 struct nvkm_falcon_ucode_desc_v3 { 154 u32 Hdr; 155 u32 StoredSize; 156 u32 PKCDataOffset; 157 u32 InterfaceOffset; 158 u32 IMEMPhysBase; 159 u32 IMEMLoadSize; 160 u32 IMEMVirtBase; 161 u32 DMEMPhysBase; 162 u32 DMEMLoadSize; 163 u16 EngineIdMask; 164 u8 UcodeId; 165 u8 SignatureCount; 166 u16 SignatureVersions; 167 u16 Reserved; 168 } v3; 169 }; 170 171 static int 172 nvkm_gsp_fwsec_v2(struct nvkm_gsp *gsp, const char *name, 173 const struct nvkm_falcon_ucode_desc_v2 *desc, u32 size, u32 init_cmd, 174 struct nvkm_falcon_fw *fw) 175 { 176 struct nvkm_subdev *subdev = &gsp->subdev; 177 const struct firmware *bl; 178 const struct nvfw_bin_hdr *hdr; 179 const struct nvfw_bl_desc *bld; 180 int ret; 181 182 /* Build ucode. */ 183 ret = nvkm_falcon_fw_ctor(gsp->func->fwsec, name, subdev->device, true, 184 (u8 *)desc + size, desc->IMEMLoadSize + desc->DMEMLoadSize, 185 &gsp->falcon, fw); 186 if (WARN_ON(ret)) 187 return ret; 188 189 fw->nmem_base_img = 0; 190 fw->nmem_base = desc->IMEMPhysBase; 191 fw->nmem_size = desc->IMEMLoadSize - desc->IMEMSecSize; 192 193 fw->imem_base_img = 0; 194 fw->imem_base = desc->IMEMSecBase; 195 fw->imem_size = desc->IMEMSecSize; 196 197 fw->dmem_base_img = desc->DMEMOffset; 198 fw->dmem_base = desc->DMEMPhysBase; 199 fw->dmem_size = desc->DMEMLoadSize; 200 201 /* Bootloader. */ 202 ret = nvkm_firmware_get(subdev, "acr/bl", 0, &bl); 203 if (ret) 204 return ret; 205 206 hdr = nvfw_bin_hdr(subdev, bl->data); 207 bld = nvfw_bl_desc(subdev, bl->data + hdr->header_offset); 208 209 fw->boot_addr = bld->start_tag << 8; 210 fw->boot_size = bld->code_size; 211 fw->boot = kmemdup(bl->data + hdr->data_offset + bld->code_off, fw->boot_size, GFP_KERNEL); 212 if (!fw->boot) 213 ret = -ENOMEM; 214 215 nvkm_firmware_put(bl); 216 217 /* Patch in interface data. */ 218 return nvkm_gsp_fwsec_patch(gsp, fw, desc->InterfaceOffset, init_cmd); 219 } 220 221 static int 222 nvkm_gsp_fwsec_v3(struct nvkm_gsp *gsp, const char *name, 223 const struct nvkm_falcon_ucode_desc_v3 *desc, u32 size, u32 init_cmd, 224 struct nvkm_falcon_fw *fw) 225 { 226 struct nvkm_device *device = gsp->subdev.device; 227 struct nvkm_bios *bios = device->bios; 228 int ret; 229 230 /* Build ucode. */ 231 ret = nvkm_falcon_fw_ctor(gsp->func->fwsec, name, device, true, 232 (u8 *)desc + size, desc->IMEMLoadSize + desc->DMEMLoadSize, 233 &gsp->falcon, fw); 234 if (WARN_ON(ret)) 235 return ret; 236 237 fw->imem_base_img = 0; 238 fw->imem_base = desc->IMEMPhysBase; 239 fw->imem_size = desc->IMEMLoadSize; 240 fw->dmem_base_img = desc->IMEMLoadSize; 241 fw->dmem_base = desc->DMEMPhysBase; 242 fw->dmem_size = ALIGN(desc->DMEMLoadSize, 256); 243 fw->dmem_sign = desc->PKCDataOffset; 244 fw->boot_addr = 0; 245 fw->fuse_ver = desc->SignatureVersions; 246 fw->ucode_id = desc->UcodeId; 247 fw->engine_id = desc->EngineIdMask; 248 249 /* Patch in signature. */ 250 ret = nvkm_falcon_fw_sign(fw, fw->dmem_base_img + desc->PKCDataOffset, 96 * 4, 251 nvbios_pointer(bios, 0), desc->SignatureCount, 252 (u8 *)desc + 0x2c - (u8 *)nvbios_pointer(bios, 0), 0, 0); 253 if (WARN_ON(ret)) 254 return ret; 255 256 /* Patch in interface data. */ 257 return nvkm_gsp_fwsec_patch(gsp, fw, desc->InterfaceOffset, init_cmd); 258 } 259 260 static int 261 nvkm_gsp_fwsec(struct nvkm_gsp *gsp, const char *name, u32 init_cmd) 262 { 263 struct nvkm_subdev *subdev = &gsp->subdev; 264 struct nvkm_device *device = subdev->device; 265 struct nvkm_bios *bios = device->bios; 266 const union nvfw_falcon_ucode_desc *desc; 267 struct nvbios_pmuE flcn_ucode; 268 u8 idx, ver, hdr; 269 u32 data; 270 u16 size, vers; 271 struct nvkm_falcon_fw fw = {}; 272 u32 mbox0 = 0; 273 int ret; 274 275 /* Lookup in VBIOS. */ 276 for (idx = 0; (data = nvbios_pmuEp(bios, idx, &ver, &hdr, &flcn_ucode)); idx++) { 277 if (flcn_ucode.type == 0x85) 278 break; 279 } 280 281 if (WARN_ON(!data)) 282 return -EINVAL; 283 284 /* Deteremine version. */ 285 desc = nvbios_pointer(bios, flcn_ucode.data); 286 if (WARN_ON(!(desc->v2.Hdr & 0x00000001))) 287 return -EINVAL; 288 289 size = (desc->v2.Hdr & 0xffff0000) >> 16; 290 vers = (desc->v2.Hdr & 0x0000ff00) >> 8; 291 292 switch (vers) { 293 case 2: ret = nvkm_gsp_fwsec_v2(gsp, name, &desc->v2, size, init_cmd, &fw); break; 294 case 3: ret = nvkm_gsp_fwsec_v3(gsp, name, &desc->v3, size, init_cmd, &fw); break; 295 default: 296 nvkm_error(subdev, "%s(v%d): version unknown\n", name, vers); 297 return -EINVAL; 298 } 299 300 if (ret) { 301 nvkm_error(subdev, "%s(v%d): %d\n", name, vers, ret); 302 return ret; 303 } 304 305 /* Boot. */ 306 ret = nvkm_falcon_fw_boot(&fw, subdev, true, &mbox0, NULL, 0, 0); 307 nvkm_falcon_fw_dtor(&fw); 308 if (ret) 309 return ret; 310 311 return 0; 312 } 313 314 int 315 nvkm_gsp_fwsec_sb(struct nvkm_gsp *gsp) 316 { 317 struct nvkm_subdev *subdev = &gsp->subdev; 318 struct nvkm_device *device = subdev->device; 319 int ret; 320 u32 err; 321 322 ret = nvkm_gsp_fwsec(gsp, "fwsec-sb", NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB); 323 if (ret) 324 return ret; 325 326 /* Verify. */ 327 err = nvkm_rd32(device, 0x001400 + (0x15 * 4)) & 0x0000ffff; 328 if (err) { 329 nvkm_error(subdev, "fwsec-sb: 0x%04x\n", err); 330 return -EIO; 331 } 332 333 return 0; 334 } 335 336 int 337 nvkm_gsp_fwsec_frts(struct nvkm_gsp *gsp) 338 { 339 struct nvkm_subdev *subdev = &gsp->subdev; 340 struct nvkm_device *device = subdev->device; 341 int ret; 342 u32 err, wpr2_lo, wpr2_hi; 343 344 ret = nvkm_gsp_fwsec(gsp, "fwsec-frts", NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS); 345 if (ret) 346 return ret; 347 348 /* Verify. */ 349 err = nvkm_rd32(device, 0x001400 + (0xe * 4)) >> 16; 350 if (err) { 351 nvkm_error(subdev, "fwsec-frts: 0x%04x\n", err); 352 return -EIO; 353 } 354 355 wpr2_lo = nvkm_rd32(device, 0x1fa824); 356 wpr2_hi = nvkm_rd32(device, 0x1fa828); 357 nvkm_debug(subdev, "fwsec-frts: WPR2 @ %08x - %08x\n", wpr2_lo, wpr2_hi); 358 return 0; 359 } 360