2019-11-10 21:10:33 -06:00
// SPDX-License-Identifier: GPL-2.0
/*
* PowerNV code for secure variables
*
* Copyright ( C ) 2019 IBM Corporation
* Author : Claudio Carvalho
* Nayna Jain
*
* APIs to access secure variables managed by OPAL .
*/
# define pr_fmt(fmt) "secvar: "fmt
# include <linux/types.h>
2023-07-24 15:02:42 -06:00
# include <linux/of.h>
2019-11-10 21:10:33 -06:00
# include <linux/platform_device.h>
# include <asm/opal.h>
# include <asm/secvar.h>
# include <asm/secure_boot.h>
static int opal_status_to_err ( int rc )
{
int err ;
switch ( rc ) {
case OPAL_SUCCESS :
err = 0 ;
break ;
case OPAL_UNSUPPORTED :
err = - ENXIO ;
break ;
case OPAL_PARAMETER :
err = - EINVAL ;
break ;
case OPAL_RESOURCE :
err = - ENOSPC ;
break ;
case OPAL_HARDWARE :
err = - EIO ;
break ;
case OPAL_NO_MEM :
err = - ENOMEM ;
break ;
case OPAL_EMPTY :
err = - ENOENT ;
break ;
case OPAL_PARTIAL :
err = - EFBIG ;
break ;
default :
err = - EINVAL ;
}
return err ;
}
2023-02-10 19:03:39 +11:00
static int opal_get_variable ( const char * key , u64 ksize , u8 * data , u64 * dsize )
2019-11-10 21:10:33 -06:00
{
int rc ;
if ( ! key | | ! dsize )
return - EINVAL ;
* dsize = cpu_to_be64 ( * dsize ) ;
rc = opal_secvar_get ( key , ksize , data , dsize ) ;
* dsize = be64_to_cpu ( * dsize ) ;
return opal_status_to_err ( rc ) ;
}
2023-02-10 19:03:39 +11:00
static int opal_get_next_variable ( const char * key , u64 * keylen , u64 keybufsize )
2019-11-10 21:10:33 -06:00
{
int rc ;
if ( ! key | | ! keylen )
return - EINVAL ;
* keylen = cpu_to_be64 ( * keylen ) ;
rc = opal_secvar_get_next ( key , keylen , keybufsize ) ;
* keylen = be64_to_cpu ( * keylen ) ;
return opal_status_to_err ( rc ) ;
}
2023-02-10 19:03:39 +11:00
static int opal_set_variable ( const char * key , u64 ksize , u8 * data , u64 dsize )
2019-11-10 21:10:33 -06:00
{
int rc ;
if ( ! key | | ! data )
return - EINVAL ;
rc = opal_secvar_enqueue_update ( key , ksize , data , dsize ) ;
return opal_status_to_err ( rc ) ;
}
2023-02-10 19:03:42 +11:00
static ssize_t opal_secvar_format ( char * buf , size_t bufsize )
{
ssize_t rc = 0 ;
struct device_node * node ;
const char * format ;
node = of_find_compatible_node ( NULL , NULL , " ibm,secvar-backend " ) ;
if ( ! of_device_is_available ( node ) ) {
rc = - ENODEV ;
goto out ;
}
rc = of_property_read_string ( node , " format " , & format ) ;
if ( rc )
goto out ;
rc = snprintf ( buf , bufsize , " %s " , format ) ;
out :
of_node_put ( node ) ;
return rc ;
}
2023-02-10 19:03:43 +11:00
static int opal_secvar_max_size ( u64 * max_size )
{
int rc ;
struct device_node * node ;
node = of_find_compatible_node ( NULL , NULL , " ibm,secvar-backend " ) ;
if ( ! node )
return - ENODEV ;
if ( ! of_device_is_available ( node ) ) {
rc = - ENODEV ;
goto out ;
}
rc = of_property_read_u64 ( node , " max-var-size " , max_size ) ;
out :
of_node_put ( node ) ;
return rc ;
}
2019-11-10 21:10:33 -06:00
static const struct secvar_operations opal_secvar_ops = {
. get = opal_get_variable ,
. get_next = opal_get_next_variable ,
. set = opal_set_variable ,
2023-02-10 19:03:42 +11:00
. format = opal_secvar_format ,
2023-02-10 19:03:43 +11:00
. max_size = opal_secvar_max_size ,
2019-11-10 21:10:33 -06:00
} ;
static int opal_secvar_probe ( struct platform_device * pdev )
{
if ( ! opal_check_token ( OPAL_SECVAR_GET )
| | ! opal_check_token ( OPAL_SECVAR_GET_NEXT )
| | ! opal_check_token ( OPAL_SECVAR_ENQUEUE_UPDATE ) ) {
pr_err ( " OPAL doesn't support secure variables \n " ) ;
return - ENODEV ;
}
2023-02-10 19:03:40 +11:00
return set_secvar_ops ( & opal_secvar_ops ) ;
2019-11-10 21:10:33 -06:00
}
static const struct of_device_id opal_secvar_match [ ] = {
{ . compatible = " ibm,secvar-backend " , } ,
{ } ,
} ;
static struct platform_driver opal_secvar_driver = {
. driver = {
. name = " secvar " ,
. of_match_table = opal_secvar_match ,
} ,
} ;
static int __init opal_secvar_init ( void )
{
return platform_driver_probe ( & opal_secvar_driver , opal_secvar_probe ) ;
}
device_initcall ( opal_secvar_init ) ;