1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2014-2019 Intel Corporation 4 */ 5 6 #include "gt/intel_gsc.h" 7 #include "gt/intel_gt.h" 8 #include "intel_gsc_binary_headers.h" 9 #include "intel_gsc_uc_heci_cmd_submit.h" 10 #include "intel_huc.h" 11 #include "intel_huc_fw.h" 12 #include "intel_huc_print.h" 13 #include "i915_drv.h" 14 #include "pxp/intel_pxp_huc.h" 15 #include "pxp/intel_pxp_cmd_interface_43.h" 16 17 struct mtl_huc_auth_msg_in { 18 struct intel_gsc_mtl_header header; 19 struct pxp43_new_huc_auth_in huc_in; 20 } __packed; 21 22 struct mtl_huc_auth_msg_out { 23 struct intel_gsc_mtl_header header; 24 struct pxp43_huc_auth_out huc_out; 25 } __packed; 26 27 int intel_huc_fw_auth_via_gsccs(struct intel_huc *huc) 28 { 29 struct intel_gt *gt = huc_to_gt(huc); 30 struct drm_i915_private *i915 = gt->i915; 31 struct drm_i915_gem_object *obj; 32 struct mtl_huc_auth_msg_in *msg_in; 33 struct mtl_huc_auth_msg_out *msg_out; 34 void *pkt_vaddr; 35 u64 pkt_offset; 36 int retry = 5; 37 int err = 0; 38 39 if (!huc->heci_pkt) 40 return -ENODEV; 41 42 obj = huc->heci_pkt->obj; 43 pkt_offset = i915_ggtt_offset(huc->heci_pkt); 44 45 pkt_vaddr = i915_gem_object_pin_map_unlocked(obj, 46 i915_coherent_map_type(i915, obj, true)); 47 if (IS_ERR(pkt_vaddr)) 48 return PTR_ERR(pkt_vaddr); 49 50 msg_in = pkt_vaddr; 51 msg_out = pkt_vaddr + PXP43_HUC_AUTH_INOUT_SIZE; 52 53 intel_gsc_uc_heci_cmd_emit_mtl_header(&msg_in->header, 54 HECI_MEADDRESS_PXP, 55 sizeof(*msg_in), 0); 56 57 msg_in->huc_in.header.api_version = PXP_APIVER(4, 3); 58 msg_in->huc_in.header.command_id = PXP43_CMDID_NEW_HUC_AUTH; 59 msg_in->huc_in.header.status = 0; 60 msg_in->huc_in.header.buffer_len = sizeof(msg_in->huc_in) - 61 sizeof(msg_in->huc_in.header); 62 msg_in->huc_in.huc_base_address = huc->fw.vma_res.start; 63 msg_in->huc_in.huc_size = huc->fw.obj->base.size; 64 65 do { 66 err = intel_gsc_uc_heci_cmd_submit_packet(>->uc.gsc, 67 pkt_offset, sizeof(*msg_in), 68 pkt_offset + PXP43_HUC_AUTH_INOUT_SIZE, 69 PXP43_HUC_AUTH_INOUT_SIZE); 70 if (err) { 71 huc_err(huc, "failed to submit GSC request to auth: %d\n", err); 72 goto out_unpin; 73 } 74 75 if (msg_out->header.flags & GSC_OUTFLAG_MSG_PENDING) { 76 msg_in->header.gsc_message_handle = msg_out->header.gsc_message_handle; 77 err = -EBUSY; 78 msleep(50); 79 } 80 } while (--retry && err == -EBUSY); 81 82 if (err) 83 goto out_unpin; 84 85 if (msg_out->header.message_size != sizeof(*msg_out)) { 86 huc_err(huc, "invalid GSC reply length %u [expected %zu]\n", 87 msg_out->header.message_size, sizeof(*msg_out)); 88 err = -EPROTO; 89 goto out_unpin; 90 } 91 92 /* 93 * The GSC will return PXP_STATUS_OP_NOT_PERMITTED if the HuC is already 94 * loaded. If the same error is ever returned with HuC not loaded we'll 95 * still catch it when we check the authentication bit later. 96 */ 97 if (msg_out->huc_out.header.status != PXP_STATUS_SUCCESS && 98 msg_out->huc_out.header.status != PXP_STATUS_OP_NOT_PERMITTED) { 99 huc_err(huc, "auth failed with GSC error = 0x%x\n", 100 msg_out->huc_out.header.status); 101 err = -EIO; 102 goto out_unpin; 103 } 104 105 out_unpin: 106 i915_gem_object_unpin_map(obj); 107 return err; 108 } 109 110 static void get_version_from_gsc_manifest(struct intel_uc_fw_ver *ver, const void *data) 111 { 112 const struct intel_gsc_manifest_header *manifest = data; 113 114 ver->major = manifest->fw_version.major; 115 ver->minor = manifest->fw_version.minor; 116 ver->patch = manifest->fw_version.hotfix; 117 } 118 119 static bool css_valid(const void *data, size_t size) 120 { 121 const struct uc_css_header *css = data; 122 123 if (unlikely(size < sizeof(struct uc_css_header))) 124 return false; 125 126 if (css->module_type != 0x6) 127 return false; 128 129 if (css->module_vendor != PCI_VENDOR_ID_INTEL) 130 return false; 131 132 return true; 133 } 134 135 static inline u32 entry_offset(const struct intel_gsc_cpd_entry *entry) 136 { 137 return entry->offset & INTEL_GSC_CPD_ENTRY_OFFSET_MASK; 138 } 139 140 int intel_huc_fw_get_binary_info(struct intel_uc_fw *huc_fw, const void *data, size_t size) 141 { 142 struct intel_huc *huc = container_of(huc_fw, struct intel_huc, fw); 143 const struct intel_gsc_cpd_header_v2 *header = data; 144 const struct intel_gsc_cpd_entry *entry; 145 size_t min_size = sizeof(*header); 146 int i; 147 148 if (!huc_fw->has_gsc_headers) { 149 huc_err(huc, "Invalid FW type for GSC header parsing!\n"); 150 return -EINVAL; 151 } 152 153 if (size < sizeof(*header)) { 154 huc_err(huc, "FW too small! %zu < %zu\n", size, min_size); 155 return -ENODATA; 156 } 157 158 /* 159 * The GSC-enabled HuC binary starts with a directory header, followed 160 * by a series of entries. Each entry is identified by a name and 161 * points to a specific section of the binary containing the relevant 162 * data. The entries we're interested in are: 163 * - "HUCP.man": points to the GSC manifest header for the HuC, which 164 * contains the version info. 165 * - "huc_fw": points to the legacy-style binary that can be used for 166 * load via the DMA. This entry only contains a valid CSS 167 * on binaries for platforms that support 2-step HuC load 168 * via dma and auth via GSC (like MTL). 169 * 170 * -------------------------------------------------- 171 * [ intel_gsc_cpd_header_v2 ] 172 * -------------------------------------------------- 173 * [ intel_gsc_cpd_entry[] ] 174 * [ entry1 ] 175 * [ ... ] 176 * [ entryX ] 177 * [ "HUCP.man" ] 178 * [ ... ] 179 * [ offset >----------------------------]------o 180 * [ ... ] | 181 * [ entryY ] | 182 * [ "huc_fw" ] | 183 * [ ... ] | 184 * [ offset >----------------------------]----------o 185 * -------------------------------------------------- | | 186 * | | 187 * -------------------------------------------------- | | 188 * [ intel_gsc_manifest_header ]<-----o | 189 * [ ... ] | 190 * [ intel_gsc_version fw_version ] | 191 * [ ... ] | 192 * -------------------------------------------------- | 193 * | 194 * -------------------------------------------------- | 195 * [ data[] ]<---------o 196 * [ ... ] 197 * [ ... ] 198 * -------------------------------------------------- 199 */ 200 201 if (header->header_marker != INTEL_GSC_CPD_HEADER_MARKER) { 202 huc_err(huc, "invalid marker for CPD header: 0x%08x!\n", 203 header->header_marker); 204 return -EINVAL; 205 } 206 207 /* we only have binaries with header v2 and entry v1 for now */ 208 if (header->header_version != 2 || header->entry_version != 1) { 209 huc_err(huc, "invalid CPD header/entry version %u:%u!\n", 210 header->header_version, header->entry_version); 211 return -EINVAL; 212 } 213 214 if (header->header_length < sizeof(struct intel_gsc_cpd_header_v2)) { 215 huc_err(huc, "invalid CPD header length %u!\n", 216 header->header_length); 217 return -EINVAL; 218 } 219 220 min_size = header->header_length + sizeof(*entry) * header->num_of_entries; 221 if (size < min_size) { 222 huc_err(huc, "FW too small! %zu < %zu\n", size, min_size); 223 return -ENODATA; 224 } 225 226 entry = data + header->header_length; 227 228 for (i = 0; i < header->num_of_entries; i++, entry++) { 229 if (strcmp(entry->name, "HUCP.man") == 0) 230 get_version_from_gsc_manifest(&huc_fw->file_selected.ver, 231 data + entry_offset(entry)); 232 233 if (strcmp(entry->name, "huc_fw") == 0) { 234 u32 offset = entry_offset(entry); 235 236 if (offset < size && css_valid(data + offset, size - offset)) 237 huc_fw->dma_start_offset = offset; 238 } 239 } 240 241 return 0; 242 } 243 244 int intel_huc_fw_load_and_auth_via_gsc(struct intel_huc *huc) 245 { 246 int ret; 247 248 if (!intel_huc_is_loaded_by_gsc(huc)) 249 return -ENODEV; 250 251 if (!intel_uc_fw_is_loadable(&huc->fw)) 252 return -ENOEXEC; 253 254 /* 255 * If we abort a suspend, HuC might still be loaded when the mei 256 * component gets re-bound and this function called again. If so, just 257 * mark the HuC as loaded. 258 */ 259 if (intel_huc_is_authenticated(huc, INTEL_HUC_AUTH_BY_GSC)) { 260 intel_uc_fw_change_status(&huc->fw, INTEL_UC_FIRMWARE_RUNNING); 261 return 0; 262 } 263 264 GEM_WARN_ON(intel_uc_fw_is_loaded(&huc->fw)); 265 266 ret = intel_pxp_huc_load_and_auth(huc_to_gt(huc)->i915->pxp); 267 if (ret) 268 return ret; 269 270 intel_uc_fw_change_status(&huc->fw, INTEL_UC_FIRMWARE_TRANSFERRED); 271 272 return intel_huc_wait_for_auth_complete(huc, INTEL_HUC_AUTH_BY_GSC); 273 } 274 275 /** 276 * intel_huc_fw_upload() - load HuC uCode to device via DMA transfer 277 * @huc: intel_huc structure 278 * 279 * Called from intel_uc_init_hw() during driver load, resume from sleep and 280 * after a GPU reset. Note that HuC must be loaded before GuC. 281 * 282 * The firmware image should have already been fetched into memory, so only 283 * check that fetch succeeded, and then transfer the image to the h/w. 284 * 285 * Return: non-zero code on error 286 */ 287 int intel_huc_fw_upload(struct intel_huc *huc) 288 { 289 if (intel_huc_is_loaded_by_gsc(huc)) 290 return -ENODEV; 291 292 /* HW doesn't look at destination address for HuC, so set it to 0 */ 293 return intel_uc_fw_upload(&huc->fw, 0, HUC_UKERNEL); 294 } 295