drm/nouveau/secboot: support standard NVIDIA HS binaries
authorAlexandre Courbot <acourbot@nvidia.com>
Thu, 26 Jan 2017 07:12:10 +0000 (16:12 +0900)
committerBen Skeggs <bskeggs@redhat.com>
Tue, 7 Mar 2017 07:05:15 +0000 (17:05 +1000)
I had the brilliant idea to "improve" the binary format by removing
a useless indirection in the HS binary files. In the end it just
makes things more complicated than they ought to be as NVIDIA-provided
files need to be adapted. Since the format used can be identified by the
header, support both.

Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
drivers/gpu/drm/nouveau/nvkm/subdev/secboot/acr_r352.c

index 77ed8f0..b4e0add 100644 (file)
@@ -599,19 +599,35 @@ cleanup:
 
 
 /**
- * acr_r352_hsf_patch_signature() - patch HS blob with correct signature
+ * acr_r352_hsf_patch_signature() - patch HS blob with correct signature for
+ * specified falcon.
  */
 static void
-acr_r352_hsf_patch_signature(struct nvkm_secboot *sb, void *acr_image)
+acr_r352_hsf_patch_signature(const struct nvkm_falcon *falcon, void *acr_image,
+                            bool new_format)
 {
        struct fw_bin_header *hsbin_hdr = acr_image;
        struct hsf_fw_header *fw_hdr = acr_image + hsbin_hdr->header_offset;
        void *hs_data = acr_image + hsbin_hdr->data_offset;
        void *sig;
        u32 sig_size;
+       u32 patch_loc, patch_sig;
+
+       /*
+        * I had the brilliant idea to "improve" the binary format by
+        * removing this useless indirection. However to make NVIDIA files
+        * directly compatible, let's support both format.
+        */
+       if (new_format) {
+               patch_loc = fw_hdr->patch_loc;
+               patch_sig = fw_hdr->patch_sig;
+       } else {
+               patch_loc = *(u32 *)(acr_image + fw_hdr->patch_loc);
+               patch_sig = *(u32 *)(acr_image + fw_hdr->patch_sig);
+       }
 
        /* Falcon in debug or production mode? */
-       if (sb->boot_falcon->debug) {
+       if (falcon->debug) {
                sig = acr_image + fw_hdr->sig_dbg_offset;
                sig_size = fw_hdr->sig_dbg_size;
        } else {
@@ -620,7 +636,7 @@ acr_r352_hsf_patch_signature(struct nvkm_secboot *sb, void *acr_image)
        }
 
        /* Patch signature */
-       memcpy(hs_data + fw_hdr->patch_loc, sig + fw_hdr->patch_sig, sig_size);
+       memcpy(hs_data + patch_loc, sig + patch_sig, sig_size);
 }
 
 void
@@ -670,6 +686,37 @@ acr_r352_generate_hs_bl_desc(const struct hsf_load_header *hdr, void *_bl_desc,
        bl_desc->data_size = hdr->data_size;
 }
 
+void *
+acr_r352_load_hs_blob(struct nvkm_secboot *sb, const struct nvkm_falcon *falcon,
+                     const char *fw)
+{
+       struct nvkm_subdev *subdev = &sb->subdev;
+       void *acr_image;
+       bool new_format;
+
+       acr_image = nvkm_acr_load_firmware(subdev, fw, 0);
+       if (IS_ERR(acr_image))
+               return acr_image;
+
+       /* detect the format to define how signature should be patched */
+       switch (((u32 *)acr_image)[0]) {
+       case 0x3b1d14f0:
+               new_format = true;
+               break;
+       case 0x000010de:
+               new_format = false;
+               break;
+       default:
+               nvkm_error(subdev, "unknown header for HS blob %s\n", fw);
+               return ERR_PTR(-EINVAL);
+       }
+
+       /* Patch signature */
+       acr_r352_hsf_patch_signature(falcon, acr_image, new_format);
+
+       return acr_image;
+}
+
 /**
  * acr_r352_prepare_hs_blob - load and prepare a HS blob and BL descriptor
  *
@@ -692,7 +739,7 @@ acr_r352_prepare_hs_blob(struct acr_r352 *acr, struct nvkm_secboot *sb,
        void *acr_data;
        int ret;
 
-       acr_image = nvkm_acr_load_firmware(subdev, fw, 0);
+       acr_image = acr_r352_load_hs_blob(sb, sb->boot_falcon, fw);
        if (IS_ERR(acr_image))
                return PTR_ERR(acr_image);
 
@@ -701,9 +748,6 @@ acr_r352_prepare_hs_blob(struct acr_r352 *acr, struct nvkm_secboot *sb,
        load_hdr = acr_image + fw_hdr->hdr_offset;
        acr_data = acr_image + hsbin_hdr->data_offset;
 
-       /* Patch signature */
-       acr_r352_hsf_patch_signature(sb, acr_image);
-
        /* Patch descriptor with WPR information? */
        if (patch) {
                struct hsflcn_acr_desc *desc;