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 213 nvkm_firmware_put(bl); 214 215 if (!fw->boot) 216 return -ENOMEM; 217 218 /* Patch in interface data. */ 219 return nvkm_gsp_fwsec_patch(gsp, fw, desc->InterfaceOffset, init_cmd); 220 } 221 222 static int 223 nvkm_gsp_fwsec_v3(struct nvkm_gsp *gsp, const char *name, 224 const struct nvkm_falcon_ucode_desc_v3 *desc, u32 size, u32 init_cmd, 225 struct nvkm_falcon_fw *fw) 226 { 227 struct nvkm_device *device = gsp->subdev.device; 228 struct nvkm_bios *bios = device->bios; 229 int ret; 230 231 /* Build ucode. */ 232 ret = nvkm_falcon_fw_ctor(gsp->func->fwsec, name, device, true, 233 (u8 *)desc + size, desc->IMEMLoadSize + desc->DMEMLoadSize, 234 &gsp->falcon, fw); 235 if (WARN_ON(ret)) 236 return ret; 237 238 fw->imem_base_img = 0; 239 fw->imem_base = desc->IMEMPhysBase; 240 fw->imem_size = desc->IMEMLoadSize; 241 fw->dmem_base_img = desc->IMEMLoadSize; 242 fw->dmem_base = desc->DMEMPhysBase; 243 fw->dmem_size = ALIGN(desc->DMEMLoadSize, 256); 244 fw->dmem_sign = desc->PKCDataOffset; 245 fw->boot_addr = 0; 246 fw->fuse_ver = desc->SignatureVersions; 247 fw->ucode_id = desc->UcodeId; 248 fw->engine_id = desc->EngineIdMask; 249 250 /* Patch in signature. */ 251 ret = nvkm_falcon_fw_sign(fw, fw->dmem_base_img + desc->PKCDataOffset, 96 * 4, 252 nvbios_pointer(bios, 0), desc->SignatureCount, 253 (u8 *)desc + 0x2c - (u8 *)nvbios_pointer(bios, 0), 0, 0); 254 if (WARN_ON(ret)) 255 return ret; 256 257 /* Patch in interface data. */ 258 return nvkm_gsp_fwsec_patch(gsp, fw, desc->InterfaceOffset, init_cmd); 259 } 260 261 static int 262 nvkm_gsp_fwsec(struct nvkm_gsp *gsp, const char *name, u32 init_cmd) 263 { 264 struct nvkm_subdev *subdev = &gsp->subdev; 265 struct nvkm_device *device = subdev->device; 266 struct nvkm_bios *bios = device->bios; 267 const union nvfw_falcon_ucode_desc *desc; 268 struct nvbios_pmuE flcn_ucode; 269 u8 idx, ver, hdr; 270 u32 data; 271 u16 size, vers; 272 struct nvkm_falcon_fw fw = {}; 273 u32 mbox0 = 0; 274 int ret; 275 276 /* Lookup in VBIOS. */ 277 for (idx = 0; (data = nvbios_pmuEp(bios, idx, &ver, &hdr, &flcn_ucode)); idx++) { 278 if (flcn_ucode.type == 0x85) 279 break; 280 } 281 282 if (WARN_ON(!data)) 283 return -EINVAL; 284 285 /* Deteremine version. */ 286 desc = nvbios_pointer(bios, flcn_ucode.data); 287 if (WARN_ON(!(desc->v2.Hdr & 0x00000001))) 288 return -EINVAL; 289 290 size = (desc->v2.Hdr & 0xffff0000) >> 16; 291 vers = (desc->v2.Hdr & 0x0000ff00) >> 8; 292 293 switch (vers) { 294 case 2: ret = nvkm_gsp_fwsec_v2(gsp, name, &desc->v2, size, init_cmd, &fw); break; 295 case 3: ret = nvkm_gsp_fwsec_v3(gsp, name, &desc->v3, size, init_cmd, &fw); break; 296 default: 297 nvkm_error(subdev, "%s(v%d): version unknown\n", name, vers); 298 return -EINVAL; 299 } 300 301 if (ret) { 302 nvkm_error(subdev, "%s(v%d): %d\n", name, vers, ret); 303 return ret; 304 } 305 306 /* Boot. */ 307 ret = nvkm_falcon_fw_boot(&fw, subdev, true, &mbox0, NULL, 0, 0); 308 nvkm_falcon_fw_dtor(&fw); 309 if (ret) 310 return ret; 311 312 return 0; 313 } 314 315 int 316 nvkm_gsp_fwsec_sb(struct nvkm_gsp *gsp) 317 { 318 struct nvkm_subdev *subdev = &gsp->subdev; 319 struct nvkm_device *device = subdev->device; 320 int ret; 321 u32 err; 322 323 ret = nvkm_gsp_fwsec(gsp, "fwsec-sb", NVFW_FALCON_APPIF_DMEMMAPPER_CMD_SB); 324 if (ret) 325 return ret; 326 327 /* Verify. */ 328 err = nvkm_rd32(device, 0x001400 + (0x15 * 4)) & 0x0000ffff; 329 if (err) { 330 nvkm_error(subdev, "fwsec-sb: 0x%04x\n", err); 331 return -EIO; 332 } 333 334 return 0; 335 } 336 337 int 338 nvkm_gsp_fwsec_frts(struct nvkm_gsp *gsp) 339 { 340 struct nvkm_subdev *subdev = &gsp->subdev; 341 struct nvkm_device *device = subdev->device; 342 int ret; 343 u32 err, wpr2_lo, wpr2_hi; 344 345 ret = nvkm_gsp_fwsec(gsp, "fwsec-frts", NVFW_FALCON_APPIF_DMEMMAPPER_CMD_FRTS); 346 if (ret) 347 return ret; 348 349 /* Verify. */ 350 err = nvkm_rd32(device, 0x001400 + (0xe * 4)) >> 16; 351 if (err) { 352 nvkm_error(subdev, "fwsec-frts: 0x%04x\n", err); 353 return -EIO; 354 } 355 356 wpr2_lo = nvkm_rd32(device, 0x1fa824); 357 wpr2_hi = nvkm_rd32(device, 0x1fa828); 358 nvkm_debug(subdev, "fwsec-frts: WPR2 @ %08x - %08x\n", wpr2_lo, wpr2_hi); 359 return 0; 360 } 361