2018-12-06 23:40:01 +03:00
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2018 Intel Corporation. All rights reserved. */
# include <linux/module.h>
# include <linux/device.h>
# include <linux/ndctl.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/mm.h>
# include <linux/cred.h>
# include <linux/key.h>
# include <linux/key-type.h>
# include <keys/user-type.h>
# include <keys/encrypted-type.h>
# include "nd-core.h"
# include "nd.h"
2018-12-07 23:29:09 +03:00
# define NVDIMM_BASE_KEY 0
# define NVDIMM_NEW_KEY 1
2018-12-06 23:40:01 +03:00
static bool key_revalidate = true ;
module_param ( key_revalidate , bool , 0444 ) ;
MODULE_PARM_DESC ( key_revalidate , " Require key validation at init. " ) ;
2019-03-27 21:10:44 +03:00
static const char zero_key [ NVDIMM_PASSPHRASE_LEN ] ;
2018-12-06 23:40:01 +03:00
static void * key_data ( struct key * key )
{
struct encrypted_key_payload * epayload = dereference_key_locked ( key ) ;
lockdep_assert_held_read ( & key - > sem ) ;
return epayload - > decrypted_data ;
}
static void nvdimm_put_key ( struct key * key )
{
2018-12-08 00:02:12 +03:00
if ( ! key )
return ;
2018-12-06 23:40:01 +03:00
up_read ( & key - > sem ) ;
key_put ( key ) ;
}
/*
* Retrieve kernel key for DIMM and request from user space if
* necessary . Returns a key held for read and must be put by
* nvdimm_put_key ( ) before the usage goes out of scope .
*/
static struct key * nvdimm_request_key ( struct nvdimm * nvdimm )
{
struct key * key = NULL ;
static const char NVDIMM_PREFIX [ ] = " nvdimm: " ;
char desc [ NVDIMM_KEY_DESC_LEN + sizeof ( NVDIMM_PREFIX ) ] ;
struct device * dev = & nvdimm - > dev ;
sprintf ( desc , " %s%s " , NVDIMM_PREFIX , nvdimm - > dimm_id ) ;
2019-07-11 04:43:43 +03:00
key = request_key ( & key_type_encrypted , desc , " " ) ;
2018-12-06 23:40:01 +03:00
if ( IS_ERR ( key ) ) {
if ( PTR_ERR ( key ) = = - ENOKEY )
2018-12-22 22:35:41 +03:00
dev_dbg ( dev , " request_key() found no key \n " ) ;
2018-12-06 23:40:01 +03:00
else
2018-12-22 22:35:41 +03:00
dev_dbg ( dev , " request_key() upcall failed \n " ) ;
2018-12-06 23:40:01 +03:00
key = NULL ;
} else {
struct encrypted_key_payload * epayload ;
down_read ( & key - > sem ) ;
epayload = dereference_key_locked ( key ) ;
if ( epayload - > decrypted_datalen ! = NVDIMM_PASSPHRASE_LEN ) {
up_read ( & key - > sem ) ;
key_put ( key ) ;
key = NULL ;
}
}
return key ;
}
2019-03-27 21:12:45 +03:00
static const void * nvdimm_get_key_payload ( struct nvdimm * nvdimm ,
struct key * * key )
{
* key = nvdimm_request_key ( nvdimm ) ;
if ( ! * key )
return zero_key ;
return key_data ( * key ) ;
}
2018-12-07 20:33:30 +03:00
static struct key * nvdimm_lookup_user_key ( struct nvdimm * nvdimm ,
2018-12-07 23:29:09 +03:00
key_serial_t id , int subclass )
2018-12-07 20:33:30 +03:00
{
key_ref_t keyref ;
struct key * key ;
struct encrypted_key_payload * epayload ;
struct device * dev = & nvdimm - > dev ;
2020-06-24 07:35:26 +03:00
keyref = lookup_user_key ( id , 0 , KEY_NEED_SEARCH ) ;
2018-12-07 20:33:30 +03:00
if ( IS_ERR ( keyref ) )
return NULL ;
key = key_ref_to_ptr ( keyref ) ;
if ( key - > type ! = & key_type_encrypted ) {
key_put ( key ) ;
return NULL ;
}
2018-12-07 23:29:09 +03:00
dev_dbg ( dev , " %s: key found: %#x \n " , __func__ , key_serial ( key ) ) ;
2018-12-07 20:33:30 +03:00
2018-12-07 23:29:09 +03:00
down_read_nested ( & key - > sem , subclass ) ;
2018-12-07 20:33:30 +03:00
epayload = dereference_key_locked ( key ) ;
if ( epayload - > decrypted_datalen ! = NVDIMM_PASSPHRASE_LEN ) {
up_read ( & key - > sem ) ;
key_put ( key ) ;
key = NULL ;
}
return key ;
}
2019-03-27 21:12:45 +03:00
static const void * nvdimm_get_user_key_payload ( struct nvdimm * nvdimm ,
key_serial_t id , int subclass , struct key * * key )
{
* key = NULL ;
if ( id = = 0 ) {
if ( subclass = = NVDIMM_BASE_KEY )
return zero_key ;
else
return NULL ;
}
* key = nvdimm_lookup_user_key ( nvdimm , id , subclass ) ;
if ( ! * key )
return NULL ;
return key_data ( * key ) ;
}
static int nvdimm_key_revalidate ( struct nvdimm * nvdimm )
2018-12-06 23:40:01 +03:00
{
struct key * key ;
int rc ;
2019-03-27 21:12:45 +03:00
const void * data ;
2018-12-06 23:40:01 +03:00
if ( ! nvdimm - > sec . ops - > change_key )
2019-03-27 21:12:45 +03:00
return - EOPNOTSUPP ;
2018-12-06 23:40:01 +03:00
2019-03-27 21:12:45 +03:00
data = nvdimm_get_key_payload ( nvdimm , & key ) ;
2018-12-06 23:40:01 +03:00
/*
* Send the same key to the hardware as new and old key to
* verify that the key is good .
*/
2019-03-27 21:12:45 +03:00
rc = nvdimm - > sec . ops - > change_key ( nvdimm , data , data , NVDIMM_USER ) ;
2018-12-06 23:40:01 +03:00
if ( rc < 0 ) {
nvdimm_put_key ( key ) ;
2019-03-27 21:12:45 +03:00
return rc ;
2018-12-06 23:40:01 +03:00
}
2019-03-27 21:12:45 +03:00
nvdimm_put_key ( key ) ;
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_USER ) ;
2019-03-27 21:12:45 +03:00
return 0 ;
2018-12-06 23:40:01 +03:00
}
static int __nvdimm_security_unlock ( struct nvdimm * nvdimm )
{
struct device * dev = & nvdimm - > dev ;
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( dev ) ;
2019-03-27 21:12:45 +03:00
struct key * key ;
const void * data ;
2018-12-06 23:40:01 +03:00
int rc ;
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > unlock
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-06 23:40:01 +03:00
return - EIO ;
2019-09-24 20:34:49 +03:00
/* No need to go further if security is disabled */
if ( test_bit ( NVDIMM_SECURITY_DISABLED , & nvdimm - > sec . flags ) )
return 0 ;
2018-12-14 01:36:18 +03:00
if ( test_bit ( NDD_SECURITY_OVERWRITE , & nvdimm - > flags ) ) {
2018-12-22 22:35:41 +03:00
dev_dbg ( dev , " Security operation in progress. \n " ) ;
2018-12-14 01:36:18 +03:00
return - EBUSY ;
}
2018-12-06 23:40:01 +03:00
/*
* If the pre - OS has unlocked the DIMM , attempt to send the key
* from request_key ( ) to the hardware for verification . Failure
* to revalidate the key against the hardware results in a
* freeze of the security configuration . I . e . if the OS does not
* have the key , security is being managed pre - OS .
*/
2019-08-27 03:54:54 +03:00
if ( test_bit ( NVDIMM_SECURITY_UNLOCKED , & nvdimm - > sec . flags ) ) {
2018-12-06 23:40:01 +03:00
if ( ! key_revalidate )
return 0 ;
2019-03-27 21:12:45 +03:00
return nvdimm_key_revalidate ( nvdimm ) ;
2018-12-06 23:40:01 +03:00
} else
2019-03-27 21:12:45 +03:00
data = nvdimm_get_key_payload ( nvdimm , & key ) ;
2018-12-06 23:40:01 +03:00
2019-03-27 21:12:45 +03:00
rc = nvdimm - > sec . ops - > unlock ( nvdimm , data ) ;
2018-12-06 23:40:01 +03:00
dev_dbg ( dev , " key: %d unlock: %s \n " , key_serial ( key ) ,
rc = = 0 ? " success " : " fail " ) ;
nvdimm_put_key ( key ) ;
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_USER ) ;
2018-12-06 23:40:01 +03:00
return rc ;
}
int nvdimm_security_unlock ( struct device * dev )
{
struct nvdimm * nvdimm = to_nvdimm ( dev ) ;
int rc ;
nvdimm_bus_lock ( dev ) ;
rc = __nvdimm_security_unlock ( nvdimm ) ;
nvdimm_bus_unlock ( dev ) ;
return rc ;
}
2018-12-07 20:33:30 +03:00
2019-08-27 03:54:54 +03:00
static int check_security_state ( struct nvdimm * nvdimm )
{
struct device * dev = & nvdimm - > dev ;
if ( test_bit ( NVDIMM_SECURITY_FROZEN , & nvdimm - > sec . flags ) ) {
dev_dbg ( dev , " Incorrect security state: %#lx \n " ,
nvdimm - > sec . flags ) ;
return - EIO ;
}
if ( test_bit ( NDD_SECURITY_OVERWRITE , & nvdimm - > flags ) ) {
dev_dbg ( dev , " Security operation in progress. \n " ) ;
return - EBUSY ;
}
return 0 ;
}
2019-08-27 03:55:05 +03:00
static int security_disable ( struct nvdimm * nvdimm , unsigned int keyid )
2018-12-07 20:33:30 +03:00
{
struct device * dev = & nvdimm - > dev ;
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( dev ) ;
struct key * key ;
int rc ;
2019-03-27 21:12:45 +03:00
const void * data ;
2018-12-07 20:33:30 +03:00
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > disable
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-07 20:33:30 +03:00
return - EOPNOTSUPP ;
2019-08-27 03:54:54 +03:00
rc = check_security_state ( nvdimm ) ;
if ( rc )
return rc ;
2018-12-14 01:36:18 +03:00
2019-03-27 21:12:45 +03:00
data = nvdimm_get_user_key_payload ( nvdimm , keyid ,
NVDIMM_BASE_KEY , & key ) ;
if ( ! data )
2018-12-07 20:33:30 +03:00
return - ENOKEY ;
2019-03-27 21:12:45 +03:00
rc = nvdimm - > sec . ops - > disable ( nvdimm , data ) ;
2018-12-07 20:33:30 +03:00
dev_dbg ( dev , " key: %d disable: %s \n " , key_serial ( key ) ,
rc = = 0 ? " success " : " fail " ) ;
nvdimm_put_key ( key ) ;
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_USER ) ;
2018-12-07 20:33:30 +03:00
return rc ;
}
2018-12-07 23:29:09 +03:00
2019-08-27 03:55:05 +03:00
static int security_update ( struct nvdimm * nvdimm , unsigned int keyid ,
2018-12-10 20:53:22 +03:00
unsigned int new_keyid ,
enum nvdimm_passphrase_type pass_type )
2018-12-07 23:29:09 +03:00
{
struct device * dev = & nvdimm - > dev ;
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( dev ) ;
struct key * key , * newkey ;
int rc ;
2019-03-27 21:12:45 +03:00
const void * data , * newdata ;
2018-12-07 23:29:09 +03:00
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > change_key
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-07 23:29:09 +03:00
return - EOPNOTSUPP ;
2019-08-27 03:54:54 +03:00
rc = check_security_state ( nvdimm ) ;
if ( rc )
return rc ;
2018-12-07 23:29:09 +03:00
2019-03-27 21:12:45 +03:00
data = nvdimm_get_user_key_payload ( nvdimm , keyid ,
NVDIMM_BASE_KEY , & key ) ;
if ( ! data )
return - ENOKEY ;
2018-12-07 23:29:09 +03:00
2019-03-27 21:12:45 +03:00
newdata = nvdimm_get_user_key_payload ( nvdimm , new_keyid ,
NVDIMM_NEW_KEY , & newkey ) ;
if ( ! newdata ) {
2018-12-07 23:29:09 +03:00
nvdimm_put_key ( key ) ;
return - ENOKEY ;
}
2019-03-27 21:12:45 +03:00
rc = nvdimm - > sec . ops - > change_key ( nvdimm , data , newdata , pass_type ) ;
2018-12-10 20:53:22 +03:00
dev_dbg ( dev , " key: %d %d update%s: %s \n " ,
2018-12-07 23:29:09 +03:00
key_serial ( key ) , key_serial ( newkey ) ,
2018-12-10 20:53:22 +03:00
pass_type = = NVDIMM_MASTER ? " (master) " : " (user) " ,
2018-12-07 23:29:09 +03:00
rc = = 0 ? " success " : " fail " ) ;
nvdimm_put_key ( newkey ) ;
nvdimm_put_key ( key ) ;
2018-12-10 20:53:22 +03:00
if ( pass_type = = NVDIMM_MASTER )
2019-08-27 03:54:54 +03:00
nvdimm - > sec . ext_flags = nvdimm_security_flags ( nvdimm ,
2018-12-10 20:53:22 +03:00
NVDIMM_MASTER ) ;
else
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm ,
2018-12-10 20:53:22 +03:00
NVDIMM_USER ) ;
2018-12-07 23:29:09 +03:00
return rc ;
}
2018-12-08 00:02:12 +03:00
2019-08-27 03:55:05 +03:00
static int security_erase ( struct nvdimm * nvdimm , unsigned int keyid ,
2018-12-10 20:53:22 +03:00
enum nvdimm_passphrase_type pass_type )
2018-12-08 00:02:12 +03:00
{
struct device * dev = & nvdimm - > dev ;
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( dev ) ;
2019-03-27 21:10:44 +03:00
struct key * key = NULL ;
2018-12-08 00:02:12 +03:00
int rc ;
2019-03-27 21:10:44 +03:00
const void * data ;
2018-12-08 00:02:12 +03:00
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > erase
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-08 00:02:12 +03:00
return - EOPNOTSUPP ;
2019-08-27 03:54:54 +03:00
rc = check_security_state ( nvdimm ) ;
if ( rc )
return rc ;
2018-12-14 01:36:18 +03:00
2019-08-27 03:54:54 +03:00
if ( ! test_bit ( NVDIMM_SECURITY_UNLOCKED , & nvdimm - > sec . ext_flags )
2018-12-10 20:53:22 +03:00
& & pass_type = = NVDIMM_MASTER ) {
2018-12-22 22:35:41 +03:00
dev_dbg ( dev ,
2018-12-10 20:53:22 +03:00
" Attempt to secure erase in wrong master state. \n " ) ;
return - EOPNOTSUPP ;
}
2019-03-27 21:12:45 +03:00
data = nvdimm_get_user_key_payload ( nvdimm , keyid ,
NVDIMM_BASE_KEY , & key ) ;
if ( ! data )
return - ENOKEY ;
2018-12-08 00:02:12 +03:00
2019-03-27 21:10:44 +03:00
rc = nvdimm - > sec . ops - > erase ( nvdimm , data , pass_type ) ;
2018-12-10 20:53:22 +03:00
dev_dbg ( dev , " key: %d erase%s: %s \n " , key_serial ( key ) ,
pass_type = = NVDIMM_MASTER ? " (master) " : " (user) " ,
2018-12-08 00:02:12 +03:00
rc = = 0 ? " success " : " fail " ) ;
nvdimm_put_key ( key ) ;
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_USER ) ;
2018-12-08 00:02:12 +03:00
return rc ;
}
2018-12-14 01:36:18 +03:00
2019-08-27 03:55:05 +03:00
static int security_overwrite ( struct nvdimm * nvdimm , unsigned int keyid )
2018-12-14 01:36:18 +03:00
{
struct device * dev = & nvdimm - > dev ;
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( dev ) ;
2019-03-27 21:12:45 +03:00
struct key * key = NULL ;
2018-12-14 01:36:18 +03:00
int rc ;
2019-03-27 21:12:45 +03:00
const void * data ;
2018-12-14 01:36:18 +03:00
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > overwrite
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-14 01:36:18 +03:00
return - EOPNOTSUPP ;
2019-08-27 03:54:54 +03:00
rc = check_security_state ( nvdimm ) ;
if ( rc )
return rc ;
2018-12-14 01:36:18 +03:00
2019-03-27 21:12:45 +03:00
data = nvdimm_get_user_key_payload ( nvdimm , keyid ,
NVDIMM_BASE_KEY , & key ) ;
if ( ! data )
return - ENOKEY ;
2018-12-14 01:36:18 +03:00
2019-03-27 21:12:45 +03:00
rc = nvdimm - > sec . ops - > overwrite ( nvdimm , data ) ;
2018-12-14 01:36:18 +03:00
dev_dbg ( dev , " key: %d overwrite submission: %s \n " , key_serial ( key ) ,
rc = = 0 ? " success " : " fail " ) ;
nvdimm_put_key ( key ) ;
if ( rc = = 0 ) {
set_bit ( NDD_SECURITY_OVERWRITE , & nvdimm - > flags ) ;
set_bit ( NDD_WORK_PENDING , & nvdimm - > flags ) ;
2019-08-27 03:54:54 +03:00
set_bit ( NVDIMM_SECURITY_OVERWRITE , & nvdimm - > sec . flags ) ;
2018-12-14 01:36:18 +03:00
/*
* Make sure we don ' t lose device while doing overwrite
* query .
*/
get_device ( dev ) ;
queue_delayed_work ( system_wq , & nvdimm - > dwork , 0 ) ;
}
2018-12-10 20:53:22 +03:00
2018-12-14 01:36:18 +03:00
return rc ;
}
2022-09-14 09:12:51 +03:00
static void __nvdimm_security_overwrite_query ( struct nvdimm * nvdimm )
2018-12-14 01:36:18 +03:00
{
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( & nvdimm - > dev ) ;
int rc ;
unsigned int tmo ;
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
/*
* Abort and release device if we no longer have the overwrite
* flag set . It means the work has been canceled .
*/
if ( ! test_bit ( NDD_WORK_PENDING , & nvdimm - > flags ) )
return ;
tmo = nvdimm - > sec . overwrite_tmo ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > query_overwrite
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-14 01:36:18 +03:00
return ;
rc = nvdimm - > sec . ops - > query_overwrite ( nvdimm ) ;
if ( rc = = - EBUSY ) {
/* setup delayed work again */
tmo + = 10 ;
queue_delayed_work ( system_wq , & nvdimm - > dwork , tmo * HZ ) ;
nvdimm - > sec . overwrite_tmo = min ( 15U * 60U , tmo ) ;
return ;
}
if ( rc < 0 )
2018-12-22 22:35:41 +03:00
dev_dbg ( & nvdimm - > dev , " overwrite failed \n " ) ;
2018-12-14 01:36:18 +03:00
else
dev_dbg ( & nvdimm - > dev , " overwrite completed \n " ) ;
2020-08-04 01:41:39 +03:00
/*
* Mark the overwrite work done and update dimm security flags ,
* then send a sysfs event notification to wake up userspace
* poll threads to picked up the changed state .
*/
2018-12-14 01:36:18 +03:00
nvdimm - > sec . overwrite_tmo = 0 ;
clear_bit ( NDD_SECURITY_OVERWRITE , & nvdimm - > flags ) ;
clear_bit ( NDD_WORK_PENDING , & nvdimm - > flags ) ;
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_USER ) ;
2020-08-04 01:41:37 +03:00
nvdimm - > sec . ext_flags = nvdimm_security_flags ( nvdimm , NVDIMM_MASTER ) ;
2020-08-04 01:41:39 +03:00
if ( nvdimm - > sec . overwrite_state )
sysfs_notify_dirent ( nvdimm - > sec . overwrite_state ) ;
put_device ( & nvdimm - > dev ) ;
2018-12-14 01:36:18 +03:00
}
void nvdimm_security_overwrite_query ( struct work_struct * work )
{
struct nvdimm * nvdimm =
container_of ( work , typeof ( * nvdimm ) , dwork . work ) ;
nvdimm_bus_lock ( & nvdimm - > dev ) ;
__nvdimm_security_overwrite_query ( nvdimm ) ;
nvdimm_bus_unlock ( & nvdimm - > dev ) ;
}
2019-08-27 03:55:05 +03:00
# define OPS \
C ( OP_FREEZE , " freeze " , 1 ) , \
C ( OP_DISABLE , " disable " , 2 ) , \
C ( OP_UPDATE , " update " , 3 ) , \
C ( OP_ERASE , " erase " , 2 ) , \
C ( OP_OVERWRITE , " overwrite " , 2 ) , \
C ( OP_MASTER_UPDATE , " master_update " , 3 ) , \
C ( OP_MASTER_ERASE , " master_erase " , 2 )
# undef C
# define C(a, b, c) a
enum nvdimmsec_op_ids { OPS } ;
# undef C
# define C(a, b, c) { b, c }
static struct {
const char * name ;
int args ;
} ops [ ] = { OPS } ;
# undef C
# define SEC_CMD_SIZE 32
# define KEY_ID_SIZE 10
ssize_t nvdimm_security_store ( struct device * dev , const char * buf , size_t len )
{
struct nvdimm * nvdimm = to_nvdimm ( dev ) ;
ssize_t rc ;
char cmd [ SEC_CMD_SIZE + 1 ] , keystr [ KEY_ID_SIZE + 1 ] ,
nkeystr [ KEY_ID_SIZE + 1 ] ;
unsigned int key , newkey ;
int i ;
rc = sscanf ( buf , " % " __stringify ( SEC_CMD_SIZE ) " s "
" % " __stringify ( KEY_ID_SIZE ) " s "
" % " __stringify ( KEY_ID_SIZE ) " s " ,
cmd , keystr , nkeystr ) ;
if ( rc < 1 )
return - EINVAL ;
for ( i = 0 ; i < ARRAY_SIZE ( ops ) ; i + + )
if ( sysfs_streq ( cmd , ops [ i ] . name ) )
break ;
if ( i > = ARRAY_SIZE ( ops ) )
return - EINVAL ;
if ( ops [ i ] . args > 1 )
rc = kstrtouint ( keystr , 0 , & key ) ;
if ( rc > = 0 & & ops [ i ] . args > 2 )
rc = kstrtouint ( nkeystr , 0 , & newkey ) ;
if ( rc < 0 )
return rc ;
if ( i = = OP_FREEZE ) {
dev_dbg ( dev , " freeze \n " ) ;
rc = nvdimm_security_freeze ( nvdimm ) ;
} else if ( i = = OP_DISABLE ) {
dev_dbg ( dev , " disable %u \n " , key ) ;
rc = security_disable ( nvdimm , key ) ;
} else if ( i = = OP_UPDATE | | i = = OP_MASTER_UPDATE ) {
dev_dbg ( dev , " %s %u %u \n " , ops [ i ] . name , key , newkey ) ;
rc = security_update ( nvdimm , key , newkey , i = = OP_UPDATE
? NVDIMM_USER : NVDIMM_MASTER ) ;
} else if ( i = = OP_ERASE | | i = = OP_MASTER_ERASE ) {
dev_dbg ( dev , " %s %u \n " , ops [ i ] . name , key ) ;
if ( atomic_read ( & nvdimm - > busy ) ) {
dev_dbg ( dev , " Unable to secure erase while DIMM active. \n " ) ;
return - EBUSY ;
}
rc = security_erase ( nvdimm , key , i = = OP_ERASE
? NVDIMM_USER : NVDIMM_MASTER ) ;
} else if ( i = = OP_OVERWRITE ) {
dev_dbg ( dev , " overwrite %u \n " , key ) ;
if ( atomic_read ( & nvdimm - > busy ) ) {
dev_dbg ( dev , " Unable to overwrite while DIMM active. \n " ) ;
return - EBUSY ;
}
rc = security_overwrite ( nvdimm , key ) ;
} else
return - EINVAL ;
if ( rc = = 0 )
rc = len ;
return rc ;
}