2019-12-04 09:18:59 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* AMD Platform Security Processor ( PSP ) interface
*
* Copyright ( C ) 2016 , 2019 Advanced Micro Devices , Inc .
*
* Author : Brijesh Singh < brijesh . singh @ amd . com >
*/
# include <linux/kernel.h>
# include <linux/irqreturn.h>
# include "sp-dev.h"
# include "psp-dev.h"
# include "sev-dev.h"
2019-12-04 09:19:02 +03:00
# include "tee-dev.h"
2019-12-04 09:18:59 +03:00
struct psp_device * psp_master ;
static struct psp_device * psp_alloc_struct ( struct sp_device * sp )
{
struct device * dev = sp - > dev ;
struct psp_device * psp ;
psp = devm_kzalloc ( dev , sizeof ( * psp ) , GFP_KERNEL ) ;
if ( ! psp )
return NULL ;
psp - > dev = dev ;
psp - > sp = sp ;
snprintf ( psp - > name , sizeof ( psp - > name ) , " psp-%u " , sp - > ord ) ;
return psp ;
}
static irqreturn_t psp_irq_handler ( int irq , void * data )
{
struct psp_device * psp = data ;
unsigned int status ;
/* Read the interrupt status: */
status = ioread32 ( psp - > io_regs + psp - > vdata - > intsts_reg ) ;
/* invoke subdevice interrupt handlers */
if ( status ) {
if ( psp - > sev_irq_handler )
psp - > sev_irq_handler ( irq , psp - > sev_irq_data , status ) ;
2019-12-04 09:19:02 +03:00
if ( psp - > tee_irq_handler )
psp - > tee_irq_handler ( irq , psp - > tee_irq_data , status ) ;
2019-12-04 09:18:59 +03:00
}
/* Clear the interrupt status by writing the same value we read. */
iowrite32 ( status , psp - > io_regs + psp - > vdata - > intsts_reg ) ;
return IRQ_HANDLED ;
}
2019-12-04 09:19:01 +03:00
static unsigned int psp_get_capability ( struct psp_device * psp )
2019-12-04 09:18:59 +03:00
{
unsigned int val = ioread32 ( psp - > io_regs + psp - > vdata - > feature_reg ) ;
/*
* Check for a access to the registers . If this read returns
* 0xffffffff , it ' s likely that the system is running a broken
* BIOS which disallows access to the device . Stop here and
* fail the PSP initialization ( but not the load , as the CCP
* could get properly initialized ) .
*/
if ( val = = 0xffffffff ) {
dev_notice ( psp - > dev , " psp: unable to access the device: you might be running a broken BIOS. \n " ) ;
2019-12-04 09:19:01 +03:00
return 0 ;
2019-12-04 09:18:59 +03:00
}
2019-12-04 09:19:01 +03:00
return val ;
}
static int psp_check_sev_support ( struct psp_device * psp ,
unsigned int capability )
{
/* Check if device supports SEV feature */
if ( ! ( capability & 1 ) ) {
2019-12-04 09:18:59 +03:00
dev_dbg ( psp - > dev , " psp does not support SEV \n " ) ;
return - ENODEV ;
}
return 0 ;
}
2019-12-04 09:19:01 +03:00
static int psp_check_tee_support ( struct psp_device * psp ,
unsigned int capability )
{
/* Check if device supports TEE feature */
if ( ! ( capability & 2 ) ) {
dev_dbg ( psp - > dev , " psp does not support TEE \n " ) ;
return - ENODEV ;
}
return 0 ;
}
static int psp_check_support ( struct psp_device * psp ,
unsigned int capability )
{
int sev_support = psp_check_sev_support ( psp , capability ) ;
int tee_support = psp_check_tee_support ( psp , capability ) ;
/* Return error if device neither supports SEV nor TEE */
if ( sev_support & & tee_support )
return - ENODEV ;
return 0 ;
}
2019-12-04 09:19:02 +03:00
static int psp_init ( struct psp_device * psp , unsigned int capability )
{
int ret ;
if ( ! psp_check_sev_support ( psp , capability ) ) {
ret = sev_dev_init ( psp ) ;
if ( ret )
return ret ;
}
if ( ! psp_check_tee_support ( psp , capability ) ) {
ret = tee_dev_init ( psp ) ;
if ( ret )
return ret ;
}
return 0 ;
}
2019-12-04 09:18:59 +03:00
int psp_dev_init ( struct sp_device * sp )
{
struct device * dev = sp - > dev ;
struct psp_device * psp ;
2019-12-04 09:19:01 +03:00
unsigned int capability ;
2019-12-04 09:18:59 +03:00
int ret ;
ret = - ENOMEM ;
psp = psp_alloc_struct ( sp ) ;
if ( ! psp )
goto e_err ;
sp - > psp_data = psp ;
psp - > vdata = ( struct psp_vdata * ) sp - > dev_vdata - > psp_vdata ;
if ( ! psp - > vdata ) {
ret = - ENODEV ;
dev_err ( dev , " missing driver data \n " ) ;
goto e_err ;
}
psp - > io_regs = sp - > io_map ;
2019-12-04 09:19:01 +03:00
capability = psp_get_capability ( psp ) ;
if ( ! capability )
goto e_disable ;
ret = psp_check_support ( psp , capability ) ;
2019-12-04 09:18:59 +03:00
if ( ret )
goto e_disable ;
/* Disable and clear interrupts until ready */
iowrite32 ( 0 , psp - > io_regs + psp - > vdata - > inten_reg ) ;
iowrite32 ( - 1 , psp - > io_regs + psp - > vdata - > intsts_reg ) ;
/* Request an irq */
ret = sp_request_psp_irq ( psp - > sp , psp_irq_handler , psp - > name , psp ) ;
if ( ret ) {
dev_err ( dev , " psp: unable to allocate an IRQ \n " ) ;
goto e_err ;
}
2019-12-04 09:19:02 +03:00
ret = psp_init ( psp , capability ) ;
2019-12-04 09:18:59 +03:00
if ( ret )
goto e_irq ;
if ( sp - > set_psp_master_device )
sp - > set_psp_master_device ( sp ) ;
/* Enable interrupt */
iowrite32 ( - 1 , psp - > io_regs + psp - > vdata - > inten_reg ) ;
dev_notice ( dev , " psp enabled \n " ) ;
return 0 ;
e_irq :
sp_free_psp_irq ( psp - > sp , psp ) ;
e_err :
sp - > psp_data = NULL ;
dev_notice ( dev , " psp initialization failed \n " ) ;
return ret ;
e_disable :
sp - > psp_data = NULL ;
return ret ;
}
void psp_dev_destroy ( struct sp_device * sp )
{
struct psp_device * psp = sp - > psp_data ;
if ( ! psp )
return ;
sev_dev_destroy ( psp ) ;
2019-12-04 09:19:02 +03:00
tee_dev_destroy ( psp ) ;
2019-12-04 09:18:59 +03:00
sp_free_psp_irq ( sp , psp ) ;
}
void psp_set_sev_irq_handler ( struct psp_device * psp , psp_irq_handler_t handler ,
void * data )
{
psp - > sev_irq_data = data ;
psp - > sev_irq_handler = handler ;
}
void psp_clear_sev_irq_handler ( struct psp_device * psp )
{
psp_set_sev_irq_handler ( psp , NULL , NULL ) ;
}
2019-12-04 09:19:02 +03:00
void psp_set_tee_irq_handler ( struct psp_device * psp , psp_irq_handler_t handler ,
void * data )
{
psp - > tee_irq_data = data ;
psp - > tee_irq_handler = handler ;
}
void psp_clear_tee_irq_handler ( struct psp_device * psp )
{
psp_set_tee_irq_handler ( psp , NULL , NULL ) ;
}
2019-12-04 09:18:59 +03:00
struct psp_device * psp_get_master_device ( void )
{
struct sp_device * sp = sp_get_psp_master_device ( ) ;
return sp ? sp - > psp_data : NULL ;
}
void psp_pci_init ( void )
{
psp_master = psp_get_master_device ( ) ;
if ( ! psp_master )
return ;
sev_pci_init ( ) ;
}
void psp_pci_exit ( void )
{
if ( ! psp_master )
return ;
sev_pci_exit ( ) ;
}