2019-08-12 09:29:35 +00:00
// SPDX-License-Identifier: MIT
2017-01-18 08:05:53 -08:00
/*
2019-08-12 09:29:35 +00:00
* Copyright © 2016 - 2019 Intel Corporation
2017-01-18 08:05:53 -08:00
*/
2017-10-06 09:02:09 +00:00
# include <linux/types.h>
2019-07-13 11:00:14 +01:00
# include "gt/intel_gt.h"
2017-10-06 09:02:09 +00:00
# include "intel_huc.h"
2017-01-18 08:05:53 -08:00
# include "i915_drv.h"
2019-10-14 11:36:02 -07:00
/**
* DOC : HuC
*
* The HuC is a dedicated microcontroller for usage in media HEVC ( High
* Efficiency Video Coding ) operations . Userspace can directly use the firmware
* capabilities by adding HuC specific commands to batch buffers .
*
* The kernel driver is only responsible for loading the HuC firmware and
* triggering its security authentication , which is performed by the GuC . For
* The GuC to correctly perform the authentication , the HuC binary must be
* loaded before the GuC one . Loading the HuC is optional ; however , not using
* the HuC might negatively impact power usage and / or performance of media
* workloads , depending on the use - cases .
*
* See https : //github.com/intel/media-driver for the latest details on HuC
* functionality .
*/
/**
* DOC : HuC Memory Management
*
* Similarly to the GuC , the HuC can ' t do any memory allocations on its own ,
* with the difference being that the allocations for HuC usage are handled by
* the userspace driver instead of the kernel one . The HuC accesses the memory
* via the PPGTT belonging to the context loaded on the VCS executing the
* HuC - specific commands .
*/
2017-12-06 13:53:10 +00:00
void intel_huc_init_early ( struct intel_huc * huc )
{
2019-07-13 11:00:16 +01:00
struct drm_i915_private * i915 = huc_to_gt ( huc ) - > i915 ;
2019-05-27 18:36:06 +00:00
2020-03-26 11:11:18 -07:00
intel_uc_fw_init_early ( & huc - > fw , INTEL_UC_FW_TYPE_HUC ) ;
2019-05-27 18:36:06 +00:00
if ( INTEL_GEN ( i915 ) > = 11 ) {
huc - > status . reg = GEN11_HUC_KERNEL_LOAD_INFO ;
huc - > status . mask = HUC_LOAD_SUCCESSFUL ;
huc - > status . value = HUC_LOAD_SUCCESSFUL ;
} else {
huc - > status . reg = HUC_STATUS2 ;
huc - > status . mask = HUC_FW_VERIFIED ;
huc - > status . value = HUC_FW_VERIFIED ;
}
2017-01-18 08:05:53 -08:00
}
2019-04-19 16:00:13 -07:00
static int intel_huc_rsa_data_create ( struct intel_huc * huc )
{
2019-07-13 11:00:14 +01:00
struct intel_gt * gt = huc_to_gt ( huc ) ;
struct intel_guc * guc = & gt - > uc . guc ;
2019-04-19 16:00:13 -07:00
struct i915_vma * vma ;
2019-07-24 17:18:11 -07:00
size_t copied ;
2019-04-19 16:00:13 -07:00
void * vaddr ;
2019-08-04 19:50:52 +00:00
int err ;
2019-10-29 11:20:35 +01:00
err = i915_inject_probe_error ( gt - > i915 , - ENXIO ) ;
2019-08-04 19:50:52 +00:00
if ( err )
return err ;
2019-04-19 16:00:13 -07:00
/*
* HuC firmware will sit above GUC_GGTT_TOP and will not map
* through GTT . Unfortunately , this means GuC cannot perform
* the HuC auth . as the rsa offset now falls within the GuC
* inaccessible range . We resort to perma - pinning an additional
* vma within the accessible range that only contains the rsa
* signature . The GuC can use this extra pinning to perform
* the authentication since its GGTT offset will be GuC
* accessible .
*/
2019-07-24 17:18:11 -07:00
GEM_BUG_ON ( huc - > fw . rsa_size > PAGE_SIZE ) ;
2019-04-19 16:00:13 -07:00
vma = intel_guc_allocate_vma ( guc , PAGE_SIZE ) ;
if ( IS_ERR ( vma ) )
return PTR_ERR ( vma ) ;
vaddr = i915_gem_object_pin_map ( vma - > obj , I915_MAP_WB ) ;
if ( IS_ERR ( vaddr ) ) {
i915_vma_unpin_and_release ( & vma , 0 ) ;
return PTR_ERR ( vaddr ) ;
}
2019-07-24 17:18:11 -07:00
copied = intel_uc_fw_copy_rsa ( & huc - > fw , vaddr , vma - > size ) ;
GEM_BUG_ON ( copied < huc - > fw . rsa_size ) ;
i915_gem_object_unpin_map ( vma - > obj ) ;
2019-04-19 16:00:13 -07:00
huc - > rsa_data = vma ;
return 0 ;
}
static void intel_huc_rsa_data_destroy ( struct intel_huc * huc )
{
2019-07-24 17:18:11 -07:00
i915_vma_unpin_and_release ( & huc - > rsa_data , 0 ) ;
2019-04-19 16:00:13 -07:00
}
int intel_huc_init ( struct intel_huc * huc )
{
2019-08-17 13:11:43 +00:00
struct drm_i915_private * i915 = huc_to_gt ( huc ) - > i915 ;
2019-04-19 16:00:13 -07:00
int err ;
2019-07-24 17:18:11 -07:00
err = intel_uc_fw_init ( & huc - > fw ) ;
2019-04-19 16:00:13 -07:00
if ( err )
2019-08-17 13:11:43 +00:00
goto out ;
2019-04-19 16:00:13 -07:00
2019-07-24 17:18:11 -07:00
/*
* HuC firmware image is outside GuC accessible range .
* Copy the RSA signature out of the image into
* a perma - pinned region set aside for it
*/
err = intel_huc_rsa_data_create ( huc ) ;
if ( err )
goto out_fini ;
2020-02-18 14:33:26 -08:00
intel_uc_fw_change_status ( & huc - > fw , INTEL_UC_FIRMWARE_LOADABLE ) ;
2019-07-24 17:18:11 -07:00
return 0 ;
out_fini :
intel_uc_fw_fini ( & huc - > fw ) ;
2019-08-17 13:11:43 +00:00
out :
2020-02-18 14:33:25 -08:00
i915_probe_error ( i915 , " failed with %d \n " , err ) ;
2019-07-24 17:18:11 -07:00
return err ;
2019-04-19 16:00:13 -07:00
}
void intel_huc_fini ( struct intel_huc * huc )
{
2020-02-18 14:33:26 -08:00
if ( ! intel_uc_fw_is_loadable ( & huc - > fw ) )
2019-08-17 13:11:44 +00:00
return ;
2019-04-19 16:00:13 -07:00
intel_huc_rsa_data_destroy ( huc ) ;
2019-08-04 19:50:52 +00:00
intel_uc_fw_fini ( & huc - > fw ) ;
2019-04-19 16:00:13 -07:00
}
2017-01-18 08:05:57 -08:00
/**
2017-09-26 12:47:16 +05:30
* intel_huc_auth ( ) - Authenticate HuC uCode
* @ huc : intel_huc structure
*
* Called after HuC and GuC firmware loading during intel_uc_init_hw ( ) .
2017-01-18 08:05:57 -08:00
*
2019-10-14 11:36:02 -07:00
* This function invokes the GuC action to authenticate the HuC firmware ,
* passing the offset of the RSA signature to intel_guc_auth_huc ( ) . It then
* waits for up to 50 ms for firmware verification ACK .
2017-01-18 08:05:57 -08:00
*/
2017-12-06 13:53:16 +00:00
int intel_huc_auth ( struct intel_huc * huc )
2017-01-18 08:05:57 -08:00
{
2019-07-13 11:00:14 +01:00
struct intel_gt * gt = huc_to_gt ( huc ) ;
struct intel_guc * guc = & gt - > uc . guc ;
2017-01-18 08:05:57 -08:00
int ret ;
2019-07-24 17:18:09 -07:00
GEM_BUG_ON ( intel_huc_is_authenticated ( huc ) ) ;
2017-01-20 20:23:46 +01:00
2019-08-18 09:52:04 +00:00
if ( ! intel_uc_fw_is_loaded ( & huc - > fw ) )
return - ENOEXEC ;
2019-10-29 11:20:35 +01:00
ret = i915_inject_probe_error ( gt - > i915 , - ENXIO ) ;
2019-08-02 18:40:54 +00:00
if ( ret )
goto fail ;
2017-09-26 12:47:16 +05:30
ret = intel_guc_auth_huc ( guc ,
2019-04-19 16:00:13 -07:00
intel_guc_ggtt_offset ( guc , huc - > rsa_data ) ) ;
2017-01-18 08:05:57 -08:00
if ( ret ) {
DRM_ERROR ( " HuC: GuC did not ack Auth request %d \n " , ret ) ;
2019-04-19 16:00:13 -07:00
goto fail ;
2017-01-18 08:05:57 -08:00
}
/* Check authentication status, it should be done by now */
2019-07-13 11:00:14 +01:00
ret = __intel_wait_for_register ( gt - > uncore ,
2019-05-27 18:36:06 +00:00
huc - > status . reg ,
huc - > status . mask ,
huc - > status . value ,
2 , 50 , NULL ) ;
2017-01-18 08:05:57 -08:00
if ( ret ) {
2019-05-27 18:36:06 +00:00
DRM_ERROR ( " HuC: Firmware not verified %d \n " , ret ) ;
2019-04-19 16:00:13 -07:00
goto fail ;
2017-01-18 08:05:57 -08:00
}
2019-08-13 08:15:59 +00:00
intel_uc_fw_change_status ( & huc - > fw , INTEL_UC_FIRMWARE_RUNNING ) ;
2018-03-02 13:37:17 +00:00
return 0 ;
fail :
2019-08-02 18:40:54 +00:00
i915_probe_error ( gt - > i915 , " HuC: Authentication failed %d \n " , ret ) ;
2019-08-13 08:15:59 +00:00
intel_uc_fw_change_status ( & huc - > fw , INTEL_UC_FIRMWARE_FAIL ) ;
2017-12-06 13:53:16 +00:00
return ret ;
2017-01-18 08:05:57 -08:00
}
2018-03-14 20:04:29 +00:00
/**
* intel_huc_check_status ( ) - check HuC status
* @ huc : intel_huc structure
*
* This function reads status register to verify if HuC
* firmware was successfully loaded .
*
2020-03-30 13:33:02 +02:00
* Returns :
* * - ENODEV if HuC is not present on this platform ,
* * - EOPNOTSUPP if HuC firmware is disabled ,
* * - ENOPKG if HuC firmware was not installed ,
* * - ENOEXEC if HuC firmware is invalid or mismatched ,
* * 0 if HuC firmware is not running ,
* * 1 if HuC firmware is authenticated and running .
2018-03-14 20:04:29 +00:00
*/
int intel_huc_check_status ( struct intel_huc * huc )
{
2019-07-13 11:00:14 +01:00
struct intel_gt * gt = huc_to_gt ( huc ) ;
2019-01-14 14:21:17 +00:00
intel_wakeref_t wakeref ;
2019-07-23 08:37:33 -07:00
u32 status = 0 ;
2018-03-14 20:04:29 +00:00
2020-03-30 13:33:02 +02:00
switch ( __intel_uc_fw_status ( & huc - > fw ) ) {
case INTEL_UC_FIRMWARE_NOT_SUPPORTED :
2018-03-14 20:04:29 +00:00
return - ENODEV ;
2020-03-30 13:33:02 +02:00
case INTEL_UC_FIRMWARE_DISABLED :
return - EOPNOTSUPP ;
case INTEL_UC_FIRMWARE_MISSING :
return - ENOPKG ;
case INTEL_UC_FIRMWARE_ERROR :
return - ENOEXEC ;
default :
break ;
}
2018-03-14 20:04:29 +00:00
2019-10-07 16:45:31 +01:00
with_intel_runtime_pm ( gt - > uncore - > rpm , wakeref )
2019-07-13 11:00:14 +01:00
status = intel_uncore_read ( gt - > uncore , huc - > status . reg ) ;
2018-03-14 20:04:29 +00:00
2019-07-13 11:00:14 +01:00
return ( status & huc - > status . mask ) = = huc - > status . value ;
2018-03-14 20:04:29 +00:00
}
2020-03-26 11:11:19 -07:00
/**
* intel_huc_load_status - dump information about HuC load status
* @ huc : the HuC
* @ p : the & drm_printer
*
* Pretty printer for HuC load status .
*/
void intel_huc_load_status ( struct intel_huc * huc , struct drm_printer * p )
{
struct intel_gt * gt = huc_to_gt ( huc ) ;
intel_wakeref_t wakeref ;
if ( ! intel_huc_is_supported ( huc ) ) {
drm_printf ( p , " HuC not supported \n " ) ;
return ;
}
if ( ! intel_huc_is_wanted ( huc ) ) {
drm_printf ( p , " HuC disabled \n " ) ;
return ;
}
intel_uc_fw_dump ( & huc - > fw , p ) ;
with_intel_runtime_pm ( gt - > uncore - > rpm , wakeref )
2020-03-30 13:33:38 +02:00
drm_printf ( p , " HuC status: 0x%08x \n " ,
intel_uncore_read ( gt - > uncore , huc - > status . reg ) ) ;
2020-03-26 11:11:19 -07:00
}