diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_guc_loader.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_guc_loader.c | 105 |
1 files changed, 67 insertions, 38 deletions
diff --git a/drivers/gpu/drm/i915/intel_guc_loader.c b/drivers/gpu/drm/i915/intel_guc_loader.c index 3541f76c65a7..550921f2ef7d 100644 --- a/drivers/gpu/drm/i915/intel_guc_loader.c +++ b/drivers/gpu/drm/i915/intel_guc_loader.c | |||
| @@ -31,7 +31,7 @@ | |||
| 31 | #include "intel_guc.h" | 31 | #include "intel_guc.h" |
| 32 | 32 | ||
| 33 | /** | 33 | /** |
| 34 | * DOC: GuC | 34 | * DOC: GuC-specific firmware loader |
| 35 | * | 35 | * |
| 36 | * intel_guc: | 36 | * intel_guc: |
| 37 | * Top level structure of guc. It handles firmware loading and manages client | 37 | * Top level structure of guc. It handles firmware loading and manages client |
| @@ -208,16 +208,6 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv, | |||
| 208 | /* | 208 | /* |
| 209 | * Transfer the firmware image to RAM for execution by the microcontroller. | 209 | * Transfer the firmware image to RAM for execution by the microcontroller. |
| 210 | * | 210 | * |
| 211 | * GuC Firmware layout: | ||
| 212 | * +-------------------------------+ ---- | ||
| 213 | * | CSS header | 128B | ||
| 214 | * | contains major/minor version | | ||
| 215 | * +-------------------------------+ ---- | ||
| 216 | * | uCode | | ||
| 217 | * +-------------------------------+ ---- | ||
| 218 | * | RSA signature | 256B | ||
| 219 | * +-------------------------------+ ---- | ||
| 220 | * | ||
| 221 | * Architecturally, the DMA engine is bidirectional, and can potentially even | 211 | * Architecturally, the DMA engine is bidirectional, and can potentially even |
| 222 | * transfer between GTT locations. This functionality is left out of the API | 212 | * transfer between GTT locations. This functionality is left out of the API |
| 223 | * for now as there is no need for it. | 213 | * for now as there is no need for it. |
| @@ -225,33 +215,29 @@ static inline bool guc_ucode_response(struct drm_i915_private *dev_priv, | |||
| 225 | * Note that GuC needs the CSS header plus uKernel code to be copied by the | 215 | * Note that GuC needs the CSS header plus uKernel code to be copied by the |
| 226 | * DMA engine in one operation, whereas the RSA signature is loaded via MMIO. | 216 | * DMA engine in one operation, whereas the RSA signature is loaded via MMIO. |
| 227 | */ | 217 | */ |
| 228 | |||
| 229 | #define UOS_CSS_HEADER_OFFSET 0 | ||
| 230 | #define UOS_VER_MINOR_OFFSET 0x44 | ||
| 231 | #define UOS_VER_MAJOR_OFFSET 0x46 | ||
| 232 | #define UOS_CSS_HEADER_SIZE 0x80 | ||
| 233 | #define UOS_RSA_SIG_SIZE 0x100 | ||
| 234 | |||
| 235 | static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv) | 218 | static int guc_ucode_xfer_dma(struct drm_i915_private *dev_priv) |
| 236 | { | 219 | { |
| 237 | struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; | 220 | struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; |
| 238 | struct drm_i915_gem_object *fw_obj = guc_fw->guc_fw_obj; | 221 | struct drm_i915_gem_object *fw_obj = guc_fw->guc_fw_obj; |
| 239 | unsigned long offset; | 222 | unsigned long offset; |
| 240 | struct sg_table *sg = fw_obj->pages; | 223 | struct sg_table *sg = fw_obj->pages; |
| 241 | u32 status, ucode_size, rsa[UOS_RSA_SIG_SIZE / sizeof(u32)]; | 224 | u32 status, rsa[UOS_RSA_SCRATCH_MAX_COUNT]; |
| 242 | int i, ret = 0; | 225 | int i, ret = 0; |
| 243 | 226 | ||
| 244 | /* uCode size, also is where RSA signature starts */ | 227 | /* where RSA signature starts */ |
| 245 | offset = ucode_size = guc_fw->guc_fw_size - UOS_RSA_SIG_SIZE; | 228 | offset = guc_fw->rsa_offset; |
| 246 | I915_WRITE(DMA_COPY_SIZE, ucode_size); | ||
| 247 | 229 | ||
| 248 | /* Copy RSA signature from the fw image to HW for verification */ | 230 | /* Copy RSA signature from the fw image to HW for verification */ |
| 249 | sg_pcopy_to_buffer(sg->sgl, sg->nents, rsa, UOS_RSA_SIG_SIZE, offset); | 231 | sg_pcopy_to_buffer(sg->sgl, sg->nents, rsa, sizeof(rsa), offset); |
| 250 | for (i = 0; i < UOS_RSA_SIG_SIZE / sizeof(u32); i++) | 232 | for (i = 0; i < UOS_RSA_SCRATCH_MAX_COUNT; i++) |
| 251 | I915_WRITE(UOS_RSA_SCRATCH(i), rsa[i]); | 233 | I915_WRITE(UOS_RSA_SCRATCH(i), rsa[i]); |
| 252 | 234 | ||
| 235 | /* The header plus uCode will be copied to WOPCM via DMA, excluding any | ||
| 236 | * other components */ | ||
| 237 | I915_WRITE(DMA_COPY_SIZE, guc_fw->header_size + guc_fw->ucode_size); | ||
| 238 | |||
| 253 | /* Set the source address for the new blob */ | 239 | /* Set the source address for the new blob */ |
| 254 | offset = i915_gem_obj_ggtt_offset(fw_obj); | 240 | offset = i915_gem_obj_ggtt_offset(fw_obj) + guc_fw->header_offset; |
| 255 | I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset)); | 241 | I915_WRITE(DMA_ADDR_0_LOW, lower_32_bits(offset)); |
| 256 | I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF); | 242 | I915_WRITE(DMA_ADDR_0_HIGH, upper_32_bits(offset) & 0xFFFF); |
| 257 | 243 | ||
| @@ -322,8 +308,8 @@ static int guc_ucode_xfer(struct drm_i915_private *dev_priv) | |||
| 322 | I915_WRITE(GUC_SHIM_CONTROL, GUC_SHIM_CONTROL_VALUE); | 308 | I915_WRITE(GUC_SHIM_CONTROL, GUC_SHIM_CONTROL_VALUE); |
| 323 | 309 | ||
| 324 | /* WaDisableMinuteIaClockGating:skl,bxt */ | 310 | /* WaDisableMinuteIaClockGating:skl,bxt */ |
| 325 | if ((IS_SKYLAKE(dev) && INTEL_REVID(dev) <= SKL_REVID_B0) || | 311 | if (IS_SKL_REVID(dev, 0, SKL_REVID_B0) || |
| 326 | (IS_BROXTON(dev) && INTEL_REVID(dev) == BXT_REVID_A0)) { | 312 | IS_BXT_REVID(dev, 0, BXT_REVID_A1)) { |
| 327 | I915_WRITE(GUC_SHIM_CONTROL, (I915_READ(GUC_SHIM_CONTROL) & | 313 | I915_WRITE(GUC_SHIM_CONTROL, (I915_READ(GUC_SHIM_CONTROL) & |
| 328 | ~GUC_ENABLE_MIA_CLOCK_GATING)); | 314 | ~GUC_ENABLE_MIA_CLOCK_GATING)); |
| 329 | } | 315 | } |
| @@ -378,6 +364,9 @@ int intel_guc_ucode_load(struct drm_device *dev) | |||
| 378 | struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; | 364 | struct intel_guc_fw *guc_fw = &dev_priv->guc.guc_fw; |
| 379 | int err = 0; | 365 | int err = 0; |
| 380 | 366 | ||
| 367 | if (!i915.enable_guc_submission) | ||
| 368 | return 0; | ||
| 369 | |||
| 381 | DRM_DEBUG_DRIVER("GuC fw status: fetch %s, load %s\n", | 370 | DRM_DEBUG_DRIVER("GuC fw status: fetch %s, load %s\n", |
| 382 | intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), | 371 | intel_guc_fw_status_repr(guc_fw->guc_fw_fetch_status), |
| 383 | intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); | 372 | intel_guc_fw_status_repr(guc_fw->guc_fw_load_status)); |
| @@ -457,10 +446,8 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) | |||
| 457 | { | 446 | { |
| 458 | struct drm_i915_gem_object *obj; | 447 | struct drm_i915_gem_object *obj; |
| 459 | const struct firmware *fw; | 448 | const struct firmware *fw; |
| 460 | const u8 *css_header; | 449 | struct guc_css_header *css; |
| 461 | const size_t minsize = UOS_CSS_HEADER_SIZE + UOS_RSA_SIG_SIZE; | 450 | size_t size; |
| 462 | const size_t maxsize = GUC_WOPCM_SIZE_VALUE + UOS_RSA_SIG_SIZE | ||
| 463 | - 0x8000; /* 32k reserved (8K stack + 24k context) */ | ||
| 464 | int err; | 451 | int err; |
| 465 | 452 | ||
| 466 | DRM_DEBUG_DRIVER("before requesting firmware: GuC fw fetch status %s\n", | 453 | DRM_DEBUG_DRIVER("before requesting firmware: GuC fw fetch status %s\n", |
| @@ -474,12 +461,52 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) | |||
| 474 | 461 | ||
| 475 | DRM_DEBUG_DRIVER("fetch GuC fw from %s succeeded, fw %p\n", | 462 | DRM_DEBUG_DRIVER("fetch GuC fw from %s succeeded, fw %p\n", |
| 476 | guc_fw->guc_fw_path, fw); | 463 | guc_fw->guc_fw_path, fw); |
| 477 | DRM_DEBUG_DRIVER("firmware file size %zu (minimum %zu, maximum %zu)\n", | ||
| 478 | fw->size, minsize, maxsize); | ||
| 479 | 464 | ||
| 480 | /* Check the size of the blob befoe examining buffer contents */ | 465 | /* Check the size of the blob before examining buffer contents */ |
| 481 | if (fw->size < minsize || fw->size > maxsize) | 466 | if (fw->size < sizeof(struct guc_css_header)) { |
| 467 | DRM_ERROR("Firmware header is missing\n"); | ||
| 482 | goto fail; | 468 | goto fail; |
| 469 | } | ||
| 470 | |||
| 471 | css = (struct guc_css_header *)fw->data; | ||
| 472 | |||
| 473 | /* Firmware bits always start from header */ | ||
| 474 | guc_fw->header_offset = 0; | ||
| 475 | guc_fw->header_size = (css->header_size_dw - css->modulus_size_dw - | ||
| 476 | css->key_size_dw - css->exponent_size_dw) * sizeof(u32); | ||
| 477 | |||
| 478 | if (guc_fw->header_size != sizeof(struct guc_css_header)) { | ||
| 479 | DRM_ERROR("CSS header definition mismatch\n"); | ||
| 480 | goto fail; | ||
| 481 | } | ||
| 482 | |||
| 483 | /* then, uCode */ | ||
| 484 | guc_fw->ucode_offset = guc_fw->header_offset + guc_fw->header_size; | ||
| 485 | guc_fw->ucode_size = (css->size_dw - css->header_size_dw) * sizeof(u32); | ||
| 486 | |||
| 487 | /* now RSA */ | ||
| 488 | if (css->key_size_dw != UOS_RSA_SCRATCH_MAX_COUNT) { | ||
| 489 | DRM_ERROR("RSA key size is bad\n"); | ||
| 490 | goto fail; | ||
| 491 | } | ||
| 492 | guc_fw->rsa_offset = guc_fw->ucode_offset + guc_fw->ucode_size; | ||
| 493 | guc_fw->rsa_size = css->key_size_dw * sizeof(u32); | ||
| 494 | |||
| 495 | /* At least, it should have header, uCode and RSA. Size of all three. */ | ||
| 496 | size = guc_fw->header_size + guc_fw->ucode_size + guc_fw->rsa_size; | ||
| 497 | if (fw->size < size) { | ||
| 498 | DRM_ERROR("Missing firmware components\n"); | ||
| 499 | goto fail; | ||
| 500 | } | ||
| 501 | |||
| 502 | /* Header and uCode will be loaded to WOPCM. Size of the two. */ | ||
| 503 | size = guc_fw->header_size + guc_fw->ucode_size; | ||
| 504 | |||
| 505 | /* Top 32k of WOPCM is reserved (8K stack + 24k RC6 context). */ | ||
| 506 | if (size > GUC_WOPCM_SIZE_VALUE - 0x8000) { | ||
| 507 | DRM_ERROR("Firmware is too large to fit in WOPCM\n"); | ||
| 508 | goto fail; | ||
| 509 | } | ||
| 483 | 510 | ||
| 484 | /* | 511 | /* |
| 485 | * The GuC firmware image has the version number embedded at a well-known | 512 | * The GuC firmware image has the version number embedded at a well-known |
| @@ -487,9 +514,8 @@ static void guc_fw_fetch(struct drm_device *dev, struct intel_guc_fw *guc_fw) | |||
| 487 | * TWO bytes each (i.e. u16), although all pointers and offsets are defined | 514 | * TWO bytes each (i.e. u16), although all pointers and offsets are defined |
| 488 | * in terms of bytes (u8). | 515 | * in terms of bytes (u8). |
| 489 | */ | 516 | */ |
| 490 | css_header = fw->data + UOS_CSS_HEADER_OFFSET; | 517 | guc_fw->guc_fw_major_found = css->guc_sw_version >> 16; |
| 491 | guc_fw->guc_fw_major_found = *(u16 *)(css_header + UOS_VER_MAJOR_OFFSET); | 518 | guc_fw->guc_fw_minor_found = css->guc_sw_version & 0xFFFF; |
| 492 | guc_fw->guc_fw_minor_found = *(u16 *)(css_header + UOS_VER_MINOR_OFFSET); | ||
| 493 | 519 | ||
| 494 | if (guc_fw->guc_fw_major_found != guc_fw->guc_fw_major_wanted || | 520 | if (guc_fw->guc_fw_major_found != guc_fw->guc_fw_major_wanted || |
| 495 | guc_fw->guc_fw_minor_found < guc_fw->guc_fw_minor_wanted) { | 521 | guc_fw->guc_fw_minor_found < guc_fw->guc_fw_minor_wanted) { |
| @@ -566,6 +592,9 @@ void intel_guc_ucode_init(struct drm_device *dev) | |||
| 566 | fw_path = ""; /* unknown device */ | 592 | fw_path = ""; /* unknown device */ |
| 567 | } | 593 | } |
| 568 | 594 | ||
| 595 | if (!i915.enable_guc_submission) | ||
| 596 | return; | ||
| 597 | |||
| 569 | guc_fw->guc_dev = dev; | 598 | guc_fw->guc_dev = dev; |
| 570 | guc_fw->guc_fw_path = fw_path; | 599 | guc_fw->guc_fw_path = fw_path; |
| 571 | guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_NONE; | 600 | guc_fw->guc_fw_fetch_status = GUC_FIRMWARE_NONE; |
