2019-04-30 21:42:39 +03:00
// SPDX-License-Identifier: GPL-2.0
2017-02-03 22:50:31 +03:00
/*
* Copyright © 2016 Intel Corporation
*
* Authors :
* Scott Bauer < scott . bauer @ intel . com >
* Rafael Antognolli < rafael . antognolli @ intel . com >
*/
# define pr_fmt(fmt) KBUILD_MODNAME ":OPAL: " fmt
# include <linux/delay.h>
# include <linux/device.h>
# include <linux/kernel.h>
# include <linux/list.h>
2022-01-24 12:39:13 +03:00
# include <linux/blkdev.h>
2017-02-03 22:50:31 +03:00
# include <linux/slab.h>
# include <linux/uaccess.h>
# include <uapi/linux/sed-opal.h>
# include <linux/sed-opal.h>
# include <linux/string.h>
# include <linux/kdev_t.h>
# include "opal_proto.h"
2017-02-17 15:59:39 +03:00
# define IO_BUFFER_LENGTH 2048
# define MAX_TOKS 64
2019-05-21 23:46:45 +03:00
/* Number of bytes needed by cmd_finalize. */
# define CMD_FINALIZE_BYTES_NEEDED 7
2017-02-22 17:55:13 +03:00
struct opal_step {
int ( * fn ) ( struct opal_dev * dev , void * data ) ;
void * data ;
} ;
typedef int ( cont_fn ) ( struct opal_dev * dev ) ;
2017-02-17 15:59:39 +03:00
enum opal_atom_width {
OPAL_WIDTH_TINY ,
OPAL_WIDTH_SHORT ,
OPAL_WIDTH_MEDIUM ,
OPAL_WIDTH_LONG ,
OPAL_WIDTH_TOKEN
} ;
/*
* On the parsed response , we don ' t store again the toks that are already
* stored in the response buffer . Instead , for each token , we just store a
* pointer to the position in the buffer where the token starts , and the size
* of the token in bytes .
*/
struct opal_resp_tok {
const u8 * pos ;
size_t len ;
enum opal_response_token type ;
enum opal_atom_width width ;
union {
u64 u ;
s64 s ;
} stored ;
} ;
/*
* From the response header it ' s not possible to know how many tokens there are
* on the payload . So we hardcode that the maximum will be MAX_TOKS , and later
* if we start dealing with messages that have more than that , we can increase
* this number . This is done to avoid having to make two passes through the
* response , the first one counting how many tokens we have and the second one
* actually storing the positions .
*/
struct parsed_resp {
int num ;
struct opal_resp_tok toks [ MAX_TOKS ] ;
} ;
struct opal_dev {
2022-08-16 17:07:13 +03:00
u32 flags ;
2017-02-17 15:59:39 +03:00
void * data ;
sec_send_recv * send_recv ;
struct mutex dev_lock ;
u16 comid ;
u32 hsn ;
u32 tsn ;
u64 align ;
u64 lowest_lba ;
size_t pos ;
2022-11-07 23:39:44 +03:00
u8 * cmd ;
u8 * resp ;
2017-02-17 15:59:39 +03:00
struct parsed_resp parsed ;
size_t prev_d_len ;
void * prev_data ;
struct list_head unlk_lst ;
} ;
2017-02-03 22:50:31 +03:00
static const u8 opaluid [ ] [ OPAL_UID_LENGTH ] = {
/* users */
[ OPAL_SMUID_UID ] =
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xff } ,
[ OPAL_THISSP_UID ] =
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x01 } ,
[ OPAL_ADMINSP_UID ] =
{ 0x00 , 0x00 , 0x02 , 0x05 , 0x00 , 0x00 , 0x00 , 0x01 } ,
[ OPAL_LOCKINGSP_UID ] =
{ 0x00 , 0x00 , 0x02 , 0x05 , 0x00 , 0x00 , 0x00 , 0x02 } ,
[ OPAL_ENTERPRISE_LOCKINGSP_UID ] =
{ 0x00 , 0x00 , 0x02 , 0x05 , 0x00 , 0x01 , 0x00 , 0x01 } ,
[ OPAL_ANYBODY_UID ] =
{ 0x00 , 0x00 , 0x00 , 0x09 , 0x00 , 0x00 , 0x00 , 0x01 } ,
[ OPAL_SID_UID ] =
{ 0x00 , 0x00 , 0x00 , 0x09 , 0x00 , 0x00 , 0x00 , 0x06 } ,
[ OPAL_ADMIN1_UID ] =
{ 0x00 , 0x00 , 0x00 , 0x09 , 0x00 , 0x01 , 0x00 , 0x01 } ,
[ OPAL_USER1_UID ] =
{ 0x00 , 0x00 , 0x00 , 0x09 , 0x00 , 0x03 , 0x00 , 0x01 } ,
[ OPAL_USER2_UID ] =
{ 0x00 , 0x00 , 0x00 , 0x09 , 0x00 , 0x03 , 0x00 , 0x02 } ,
[ OPAL_PSID_UID ] =
{ 0x00 , 0x00 , 0x00 , 0x09 , 0x00 , 0x01 , 0xff , 0x01 } ,
[ OPAL_ENTERPRISE_BANDMASTER0_UID ] =
{ 0x00 , 0x00 , 0x00 , 0x09 , 0x00 , 0x00 , 0x80 , 0x01 } ,
[ OPAL_ENTERPRISE_ERASEMASTER_UID ] =
{ 0x00 , 0x00 , 0x00 , 0x09 , 0x00 , 0x00 , 0x84 , 0x01 } ,
/* tables */
2019-10-03 05:23:05 +03:00
[ OPAL_TABLE_TABLE ] =
2019-05-21 23:46:46 +03:00
{ 0x00 , 0x00 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x01 } ,
2017-02-03 22:50:31 +03:00
[ OPAL_LOCKINGRANGE_GLOBAL ] =
{ 0x00 , 0x00 , 0x08 , 0x02 , 0x00 , 0x00 , 0x00 , 0x01 } ,
2023-04-05 14:12:21 +03:00
[ OPAL_LOCKINGRANGE_ACE_START_TO_KEY ] =
{ 0x00 , 0x00 , 0x00 , 0x08 , 0x00 , 0x03 , 0xD0 , 0x01 } ,
2017-02-03 22:50:31 +03:00
[ OPAL_LOCKINGRANGE_ACE_RDLOCKED ] =
{ 0x00 , 0x00 , 0x00 , 0x08 , 0x00 , 0x03 , 0xE0 , 0x01 } ,
[ OPAL_LOCKINGRANGE_ACE_WRLOCKED ] =
{ 0x00 , 0x00 , 0x00 , 0x08 , 0x00 , 0x03 , 0xE8 , 0x01 } ,
[ OPAL_MBRCONTROL ] =
{ 0x00 , 0x00 , 0x08 , 0x03 , 0x00 , 0x00 , 0x00 , 0x01 } ,
[ OPAL_MBR ] =
{ 0x00 , 0x00 , 0x08 , 0x04 , 0x00 , 0x00 , 0x00 , 0x00 } ,
[ OPAL_AUTHORITY_TABLE ] =
{ 0x00 , 0x00 , 0x00 , 0x09 , 0x00 , 0x00 , 0x00 , 0x00 } ,
[ OPAL_C_PIN_TABLE ] =
{ 0x00 , 0x00 , 0x00 , 0x0B , 0x00 , 0x00 , 0x00 , 0x00 } ,
[ OPAL_LOCKING_INFO_TABLE ] =
{ 0x00 , 0x00 , 0x08 , 0x01 , 0x00 , 0x00 , 0x00 , 0x01 } ,
[ OPAL_ENTERPRISE_LOCKING_INFO_TABLE ] =
{ 0x00 , 0x00 , 0x08 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 } ,
2019-10-31 19:13:22 +03:00
[ OPAL_DATASTORE ] =
{ 0x00 , 0x00 , 0x10 , 0x01 , 0x00 , 0x00 , 0x00 , 0x00 } ,
2017-02-03 22:50:31 +03:00
/* C_PIN_TABLE object ID's */
2019-02-14 03:15:54 +03:00
[ OPAL_C_PIN_MSID ] =
2017-02-03 22:50:31 +03:00
{ 0x00 , 0x00 , 0x00 , 0x0B , 0x00 , 0x00 , 0x84 , 0x02 } ,
[ OPAL_C_PIN_SID ] =
{ 0x00 , 0x00 , 0x00 , 0x0B , 0x00 , 0x00 , 0x00 , 0x01 } ,
[ OPAL_C_PIN_ADMIN1 ] =
{ 0x00 , 0x00 , 0x00 , 0x0B , 0x00 , 0x01 , 0x00 , 0x01 } ,
/* half UID's (only first 4 bytes used) */
[ OPAL_HALF_UID_AUTHORITY_OBJ_REF ] =
{ 0x00 , 0x00 , 0x0C , 0x05 , 0xff , 0xff , 0xff , 0xff } ,
[ OPAL_HALF_UID_BOOLEAN_ACE ] =
{ 0x00 , 0x00 , 0x04 , 0x0E , 0xff , 0xff , 0xff , 0xff } ,
/* special value for omitted optional parameter */
[ OPAL_UID_HEXFF ] =
{ 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff } ,
} ;
/*
* TCG Storage SSC Methods .
* Derived from : TCG_Storage_Architecture_Core_Spec_v2 .01 _r1 .00
* Section : 6.3 Assigned UIDs
*/
2019-02-14 03:15:55 +03:00
static const u8 opalmethod [ ] [ OPAL_METHOD_LENGTH ] = {
2017-02-03 22:50:31 +03:00
[ OPAL_PROPERTIES ] =
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xff , 0x01 } ,
[ OPAL_STARTSESSION ] =
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xff , 0x02 } ,
[ OPAL_REVERT ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x02 , 0x02 } ,
[ OPAL_ACTIVATE ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x02 , 0x03 } ,
[ OPAL_EGET ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x06 } ,
[ OPAL_ESET ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x07 } ,
[ OPAL_NEXT ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x08 } ,
[ OPAL_EAUTHENTICATE ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x0c } ,
[ OPAL_GETACL ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x0d } ,
[ OPAL_GENKEY ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x10 } ,
[ OPAL_REVERTSP ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x11 } ,
[ OPAL_GET ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x16 } ,
[ OPAL_SET ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x17 } ,
[ OPAL_AUTHENTICATE ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x00 , 0x1c } ,
[ OPAL_RANDOM ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x06 , 0x01 } ,
[ OPAL_ERASE ] =
{ 0x00 , 0x00 , 0x00 , 0x06 , 0x00 , 0x00 , 0x08 , 0x03 } ,
} ;
static int end_opal_session_error ( struct opal_dev * dev ) ;
2019-02-14 03:16:07 +03:00
static int opal_discovery0_step ( struct opal_dev * dev ) ;
2017-02-03 22:50:31 +03:00
struct opal_suspend_data {
struct opal_lock_unlock unlk ;
u8 lr ;
struct list_head node ;
} ;
/*
* Derived from :
* TCG_Storage_Architecture_Core_Spec_v2 .01 _r1 .00
* Section : 5.1 .5 Method Status Codes
*/
static const char * const opal_errors [ ] = {
" Success " ,
" Not Authorized " ,
" Unknown Error " ,
" SP Busy " ,
" SP Failed " ,
" SP Disabled " ,
" SP Frozen " ,
" No Sessions Available " ,
" Uniqueness Conflict " ,
" Insufficient Space " ,
" Insufficient Rows " ,
" Invalid Function " ,
" Invalid Parameter " ,
" Invalid Reference " ,
" Unknown Error " ,
" TPER Malfunction " ,
" Transaction Failure " ,
" Response Overflow " ,
" Authority Locked Out " ,
} ;
static const char * opal_error_to_human ( int error )
{
if ( error = = 0x3f )
return " Failed " ;
if ( error > = ARRAY_SIZE ( opal_errors ) | | error < 0 )
return " Unknown Error " ;
return opal_errors [ error ] ;
}
static void print_buffer ( const u8 * ptr , u32 length )
{
# ifdef DEBUG
print_hex_dump_bytes ( " OPAL: " , DUMP_PREFIX_OFFSET , ptr , length ) ;
pr_debug ( " \n " ) ;
# endif
}
static bool check_tper ( const void * data )
{
const struct d0_tper_features * tper = data ;
u8 flags = tper - > supported_features ;
if ( ! ( flags & TPER_SYNC_SUPPORTED ) ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " TPer sync not supported. flags = %d \n " ,
tper - > supported_features ) ;
2017-02-03 22:50:31 +03:00
return false ;
}
return true ;
}
2022-08-16 17:07:13 +03:00
static bool check_lcksuppt ( const void * data )
{
const struct d0_locking_features * lfeat = data ;
u8 sup_feat = lfeat - > supported_features ;
return ! ! ( sup_feat & LOCKING_SUPPORTED_MASK ) ;
}
static bool check_lckenabled ( const void * data )
{
const struct d0_locking_features * lfeat = data ;
u8 sup_feat = lfeat - > supported_features ;
return ! ! ( sup_feat & LOCKING_ENABLED_MASK ) ;
}
static bool check_locked ( const void * data )
{
const struct d0_locking_features * lfeat = data ;
u8 sup_feat = lfeat - > supported_features ;
return ! ! ( sup_feat & LOCKED_MASK ) ;
}
2017-09-01 17:53:35 +03:00
static bool check_mbrenabled ( const void * data )
{
const struct d0_locking_features * lfeat = data ;
u8 sup_feat = lfeat - > supported_features ;
return ! ! ( sup_feat & MBR_ENABLED_MASK ) ;
}
2022-08-16 17:07:13 +03:00
static bool check_mbrdone ( const void * data )
{
const struct d0_locking_features * lfeat = data ;
u8 sup_feat = lfeat - > supported_features ;
return ! ! ( sup_feat & MBR_DONE_MASK ) ;
}
2017-02-03 22:50:31 +03:00
static bool check_sum ( const void * data )
{
const struct d0_single_user_mode * sum = data ;
u32 nlo = be32_to_cpu ( sum - > num_locking_objects ) ;
if ( nlo = = 0 ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Need at least one locking object. \n " ) ;
2017-02-03 22:50:31 +03:00
return false ;
}
pr_debug ( " Number of locking objects: %d \n " , nlo ) ;
return true ;
}
static u16 get_comid_v100 ( const void * data )
{
const struct d0_opal_v100 * v100 = data ;
return be16_to_cpu ( v100 - > baseComID ) ;
}
static u16 get_comid_v200 ( const void * data )
{
const struct d0_opal_v200 * v200 = data ;
return be16_to_cpu ( v200 - > baseComID ) ;
}
static int opal_send_cmd ( struct opal_dev * dev )
{
2017-02-17 15:59:39 +03:00
return dev - > send_recv ( dev - > data , dev - > comid , TCG_SECP_01 ,
2017-02-03 22:50:31 +03:00
dev - > cmd , IO_BUFFER_LENGTH ,
true ) ;
}
static int opal_recv_cmd ( struct opal_dev * dev )
{
2017-02-17 15:59:39 +03:00
return dev - > send_recv ( dev - > data , dev - > comid , TCG_SECP_01 ,
2017-02-03 22:50:31 +03:00
dev - > resp , IO_BUFFER_LENGTH ,
false ) ;
}
static int opal_recv_check ( struct opal_dev * dev )
{
size_t buflen = IO_BUFFER_LENGTH ;
void * buffer = dev - > resp ;
struct opal_header * hdr = buffer ;
int ret ;
do {
pr_debug ( " Sent OPAL command: outstanding=%d, minTransfer=%d \n " ,
hdr - > cp . outstandingData ,
hdr - > cp . minTransfer ) ;
if ( hdr - > cp . outstandingData = = 0 | |
hdr - > cp . minTransfer ! = 0 )
return 0 ;
memset ( buffer , 0 , buflen ) ;
ret = opal_recv_cmd ( dev ) ;
} while ( ! ret ) ;
return ret ;
}
static int opal_send_recv ( struct opal_dev * dev , cont_fn * cont )
{
int ret ;
ret = opal_send_cmd ( dev ) ;
if ( ret )
return ret ;
ret = opal_recv_cmd ( dev ) ;
if ( ret )
return ret ;
ret = opal_recv_check ( dev ) ;
if ( ret )
return ret ;
return cont ( dev ) ;
}
static void check_geometry ( struct opal_dev * dev , const void * data )
{
const struct d0_geometry_features * geo = data ;
2019-10-03 05:23:15 +03:00
dev - > align = be64_to_cpu ( geo - > alignment_granularity ) ;
dev - > lowest_lba = be64_to_cpu ( geo - > lowest_aligned_lba ) ;
2017-02-03 22:50:31 +03:00
}
2019-02-14 03:16:07 +03:00
static int execute_step ( struct opal_dev * dev ,
const struct opal_step * step , size_t stepIndex )
{
int error = step - > fn ( dev , step - > data ) ;
if ( error ) {
pr_debug ( " Step %zu (%pS) failed with error %d: %s \n " ,
stepIndex , step - > fn , error ,
opal_error_to_human ( error ) ) ;
}
return error ;
}
2019-02-14 03:16:08 +03:00
static int execute_steps ( struct opal_dev * dev ,
const struct opal_step * steps , size_t n_steps )
2017-02-03 22:50:31 +03:00
{
2019-02-14 03:16:07 +03:00
size_t state = 0 ;
int error ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:16:07 +03:00
/* first do a discovery0 */
error = opal_discovery0_step ( dev ) ;
if ( error )
return error ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:16:07 +03:00
for ( state = 0 ; state < n_steps ; state + + ) {
error = execute_step ( dev , & steps [ state ] , state ) ;
2019-02-14 03:16:06 +03:00
if ( error )
goto out_error ;
}
2017-02-22 20:15:08 +03:00
2019-02-14 03:16:06 +03:00
return 0 ;
out_error :
/*
2019-02-14 03:16:07 +03:00
* For each OPAL command the first step in steps starts some sort of
* session . If an error occurred in the initial discovery0 or if an
* error occurred in the first step ( and thus stopping the loop with
* state = = 0 ) then there was an error before or during the attempt to
* start a session . Therefore we shouldn ' t attempt to terminate a
* session , as one has not yet been created .
2019-02-14 03:16:06 +03:00
*/
2019-02-14 03:16:07 +03:00
if ( state > 0 )
2019-02-14 03:16:06 +03:00
end_opal_session_error ( dev ) ;
2017-02-03 22:50:31 +03:00
return error ;
}
static int opal_discovery0_end ( struct opal_dev * dev )
{
bool found_com_id = false , supported = true , single_user = false ;
const struct d0_header * hdr = ( struct d0_header * ) dev - > resp ;
const u8 * epos = dev - > resp , * cpos = dev - > resp ;
u16 comid = 0 ;
2017-02-21 21:59:15 +03:00
u32 hlen = be32_to_cpu ( hdr - > length ) ;
2017-02-03 22:50:31 +03:00
2017-02-21 21:59:15 +03:00
print_buffer ( dev - > resp , hlen ) ;
2022-08-16 17:07:13 +03:00
dev - > flags & = OPAL_FL_SUPPORTED ;
2017-02-03 22:50:31 +03:00
2017-02-21 21:59:15 +03:00
if ( hlen > IO_BUFFER_LENGTH - sizeof ( * hdr ) ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Discovery length overflows buffer (%zu+%u)/%u \n " ,
sizeof ( * hdr ) , hlen , IO_BUFFER_LENGTH ) ;
2017-02-21 21:59:15 +03:00
return - EFAULT ;
}
epos + = hlen ; /* end of buffer */
2017-02-03 22:50:31 +03:00
cpos + = sizeof ( * hdr ) ; /* current position on buffer */
while ( cpos < epos & & supported ) {
const struct d0_features * body =
( const struct d0_features * ) cpos ;
switch ( be16_to_cpu ( body - > code ) ) {
case FC_TPER :
supported = check_tper ( body - > features ) ;
break ;
case FC_SINGLEUSER :
single_user = check_sum ( body - > features ) ;
2023-02-10 04:06:12 +03:00
if ( single_user )
dev - > flags | = OPAL_FL_SUM_SUPPORTED ;
2017-02-03 22:50:31 +03:00
break ;
case FC_GEOMETRY :
check_geometry ( dev , body ) ;
break ;
case FC_LOCKING :
2022-08-16 17:07:13 +03:00
if ( check_lcksuppt ( body - > features ) )
dev - > flags | = OPAL_FL_LOCKING_SUPPORTED ;
if ( check_lckenabled ( body - > features ) )
dev - > flags | = OPAL_FL_LOCKING_ENABLED ;
if ( check_locked ( body - > features ) )
dev - > flags | = OPAL_FL_LOCKED ;
if ( check_mbrenabled ( body - > features ) )
dev - > flags | = OPAL_FL_MBR_ENABLED ;
if ( check_mbrdone ( body - > features ) )
dev - > flags | = OPAL_FL_MBR_DONE ;
2017-09-01 17:53:35 +03:00
break ;
2017-02-03 22:50:31 +03:00
case FC_ENTERPRISE :
case FC_DATASTORE :
/* some ignored properties */
pr_debug ( " Found OPAL feature description: %d \n " ,
be16_to_cpu ( body - > code ) ) ;
break ;
case FC_OPALV100 :
comid = get_comid_v100 ( body - > features ) ;
found_com_id = true ;
break ;
case FC_OPALV200 :
comid = get_comid_v200 ( body - > features ) ;
found_com_id = true ;
break ;
case 0xbfff . . . 0xffff :
/* vendor specific, just ignore */
break ;
default :
pr_debug ( " OPAL Unknown feature: %d \n " ,
be16_to_cpu ( body - > code ) ) ;
}
cpos + = body - > length + 4 ;
}
if ( ! supported ) {
2017-02-17 15:59:38 +03:00
pr_debug ( " This device is not Opal enabled. Not Supported! \n " ) ;
2017-02-03 22:50:31 +03:00
return - EOPNOTSUPP ;
}
if ( ! single_user )
2017-02-17 15:59:38 +03:00
pr_debug ( " Device doesn't support single user mode \n " ) ;
2017-02-03 22:50:31 +03:00
if ( ! found_com_id ) {
2017-02-17 15:59:38 +03:00
pr_debug ( " Could not find OPAL comid for device. Returning early \n " ) ;
2018-02-22 12:54:55 +03:00
return - EOPNOTSUPP ;
2017-02-03 22:50:31 +03:00
}
dev - > comid = comid ;
return 0 ;
}
2017-02-22 17:55:13 +03:00
static int opal_discovery0 ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
int ret ;
memset ( dev - > resp , 0 , IO_BUFFER_LENGTH ) ;
dev - > comid = OPAL_DISCOVERY_COMID ;
ret = opal_recv_cmd ( dev ) ;
if ( ret )
return ret ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return opal_discovery0_end ( dev ) ;
}
2019-02-14 03:16:07 +03:00
static int opal_discovery0_step ( struct opal_dev * dev )
{
const struct opal_step discovery0_step = {
opal_discovery0 ,
} ;
2019-08-20 18:30:49 +03:00
2019-02-14 03:16:07 +03:00
return execute_step ( dev , & discovery0_step , 0 ) ;
}
2019-05-21 23:46:45 +03:00
static size_t remaining_size ( struct opal_dev * cmd )
{
return IO_BUFFER_LENGTH - cmd - > pos ;
}
2019-02-14 03:15:56 +03:00
static bool can_add ( int * err , struct opal_dev * cmd , size_t len )
2017-02-03 22:50:31 +03:00
{
if ( * err )
2019-02-14 03:15:56 +03:00
return false ;
2019-05-21 23:46:45 +03:00
if ( remaining_size ( cmd ) < len ) {
2019-02-14 03:15:56 +03:00
pr_debug ( " Error adding %zu bytes: end of buffer. \n " , len ) ;
2017-02-03 22:50:31 +03:00
* err = - ERANGE ;
2019-02-14 03:15:56 +03:00
return false ;
2017-02-03 22:50:31 +03:00
}
2019-02-14 03:15:56 +03:00
return true ;
}
static void add_token_u8 ( int * err , struct opal_dev * cmd , u8 tok )
{
if ( ! can_add ( err , cmd , 1 ) )
return ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
cmd - > cmd [ cmd - > pos + + ] = tok ;
}
static void add_short_atom_header ( struct opal_dev * cmd , bool bytestring ,
bool has_sign , int len )
{
u8 atom ;
int err = 0 ;
atom = SHORT_ATOM_ID ;
atom | = bytestring ? SHORT_ATOM_BYTESTRING : 0 ;
atom | = has_sign ? SHORT_ATOM_SIGNED : 0 ;
atom | = len & SHORT_ATOM_LEN_MASK ;
add_token_u8 ( & err , cmd , atom ) ;
}
static void add_medium_atom_header ( struct opal_dev * cmd , bool bytestring ,
bool has_sign , int len )
{
u8 header0 ;
header0 = MEDIUM_ATOM_ID ;
header0 | = bytestring ? MEDIUM_ATOM_BYTESTRING : 0 ;
header0 | = has_sign ? MEDIUM_ATOM_SIGNED : 0 ;
header0 | = ( len > > 8 ) & MEDIUM_ATOM_LEN_MASK ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
cmd - > cmd [ cmd - > pos + + ] = header0 ;
cmd - > cmd [ cmd - > pos + + ] = len ;
}
static void add_token_u64 ( int * err , struct opal_dev * cmd , u64 number )
{
size_t len ;
int msb ;
if ( ! ( number & ~ TINY_ATOM_DATA_MASK ) ) {
add_token_u8 ( err , cmd , number ) ;
return ;
}
2018-03-07 19:55:56 +03:00
msb = fls64 ( number ) ;
len = DIV_ROUND_UP ( msb , 8 ) ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:15:56 +03:00
if ( ! can_add ( err , cmd , len + 1 ) ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error adding u64: end of buffer. \n " ) ;
2017-02-03 22:50:31 +03:00
return ;
}
add_short_atom_header ( cmd , false , false , len ) ;
2018-03-07 19:55:56 +03:00
while ( len - - )
add_token_u8 ( err , cmd , number > > ( len * 8 ) ) ;
2017-02-03 22:50:31 +03:00
}
2019-02-14 03:16:02 +03:00
static u8 * add_bytestring_header ( int * err , struct opal_dev * cmd , size_t len )
2017-02-03 22:50:31 +03:00
{
size_t header_len = 1 ;
bool is_short_atom = true ;
if ( len & ~ SHORT_ATOM_LEN_MASK ) {
header_len = 2 ;
is_short_atom = false ;
}
2019-02-14 03:15:56 +03:00
if ( ! can_add ( err , cmd , header_len + len ) ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error adding bytestring: end of buffer. \n " ) ;
2019-02-14 03:16:02 +03:00
return NULL ;
2017-02-03 22:50:31 +03:00
}
if ( is_short_atom )
add_short_atom_header ( cmd , true , false , len ) ;
else
add_medium_atom_header ( cmd , true , false , len ) ;
2019-02-14 03:16:02 +03:00
return & cmd - > cmd [ cmd - > pos ] ;
}
2017-02-03 22:50:31 +03:00
2019-02-14 03:16:02 +03:00
static void add_token_bytestring ( int * err , struct opal_dev * cmd ,
const u8 * bytestring , size_t len )
{
u8 * start ;
start = add_bytestring_header ( err , cmd , len ) ;
if ( ! start )
return ;
memcpy ( start , bytestring , len ) ;
cmd - > pos + = len ;
2017-02-03 22:50:31 +03:00
}
static int build_locking_range ( u8 * buffer , size_t length , u8 lr )
{
if ( length > OPAL_UID_LENGTH ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Can't build locking range. Length OOB \n " ) ;
2017-02-03 22:50:31 +03:00
return - ERANGE ;
}
memcpy ( buffer , opaluid [ OPAL_LOCKINGRANGE_GLOBAL ] , OPAL_UID_LENGTH ) ;
if ( lr = = 0 )
return 0 ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
buffer [ 5 ] = LOCKING_RANGE_NON_GLOBAL ;
buffer [ 7 ] = lr ;
return 0 ;
}
static int build_locking_user ( u8 * buffer , size_t length , u8 lr )
{
if ( length > OPAL_UID_LENGTH ) {
2019-02-14 03:15:54 +03:00
pr_debug ( " Can't build locking range user. Length OOB \n " ) ;
2017-02-03 22:50:31 +03:00
return - ERANGE ;
}
memcpy ( buffer , opaluid [ OPAL_USER1_UID ] , OPAL_UID_LENGTH ) ;
buffer [ 7 ] = lr + 1 ;
return 0 ;
}
static void set_comid ( struct opal_dev * cmd , u16 comid )
{
struct opal_header * hdr = ( struct opal_header * ) cmd - > cmd ;
hdr - > cp . extendedComID [ 0 ] = comid > > 8 ;
hdr - > cp . extendedComID [ 1 ] = comid ;
hdr - > cp . extendedComID [ 2 ] = 0 ;
hdr - > cp . extendedComID [ 3 ] = 0 ;
}
static int cmd_finalize ( struct opal_dev * cmd , u32 hsn , u32 tsn )
{
struct opal_header * hdr ;
int err = 0 ;
2019-05-21 23:46:45 +03:00
/*
* Close the parameter list opened from cmd_start .
* The number of bytes added must be equal to
* CMD_FINALIZE_BYTES_NEEDED .
*/
2019-02-14 03:15:57 +03:00
add_token_u8 ( & err , cmd , OPAL_ENDLIST ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , cmd , OPAL_ENDOFDATA ) ;
add_token_u8 ( & err , cmd , OPAL_STARTLIST ) ;
add_token_u8 ( & err , cmd , 0 ) ;
add_token_u8 ( & err , cmd , 0 ) ;
add_token_u8 ( & err , cmd , 0 ) ;
add_token_u8 ( & err , cmd , OPAL_ENDLIST ) ;
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error finalizing command. \n " ) ;
2017-02-03 22:50:31 +03:00
return - EFAULT ;
}
hdr = ( struct opal_header * ) cmd - > cmd ;
hdr - > pkt . tsn = cpu_to_be32 ( tsn ) ;
hdr - > pkt . hsn = cpu_to_be32 ( hsn ) ;
hdr - > subpkt . length = cpu_to_be32 ( cmd - > pos - sizeof ( * hdr ) ) ;
while ( cmd - > pos % 4 ) {
if ( cmd - > pos > = IO_BUFFER_LENGTH ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error: Buffer overrun \n " ) ;
2017-02-03 22:50:31 +03:00
return - ERANGE ;
}
cmd - > cmd [ cmd - > pos + + ] = 0 ;
}
hdr - > pkt . length = cpu_to_be32 ( cmd - > pos - sizeof ( hdr - > cp ) -
sizeof ( hdr - > pkt ) ) ;
hdr - > cp . length = cpu_to_be32 ( cmd - > pos - sizeof ( hdr - > cp ) ) ;
return 0 ;
}
2017-02-21 21:59:14 +03:00
static const struct opal_resp_tok * response_get_token (
const struct parsed_resp * resp ,
int n )
2017-02-03 22:50:31 +03:00
{
const struct opal_resp_tok * tok ;
2019-02-14 03:15:59 +03:00
if ( ! resp ) {
pr_debug ( " Response is NULL \n " ) ;
return ERR_PTR ( - EINVAL ) ;
}
2017-02-03 22:50:31 +03:00
if ( n > = resp - > num ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Token number doesn't exist: %d, resp: %d \n " ,
n , resp - > num ) ;
2017-02-21 21:59:14 +03:00
return ERR_PTR ( - EINVAL ) ;
2017-02-03 22:50:31 +03:00
}
tok = & resp - > toks [ n ] ;
if ( tok - > len = = 0 ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Token length must be non-zero \n " ) ;
2017-02-21 21:59:14 +03:00
return ERR_PTR ( - EINVAL ) ;
2017-02-03 22:50:31 +03:00
}
2017-02-21 21:59:14 +03:00
return tok ;
2017-02-03 22:50:31 +03:00
}
2017-02-21 21:59:13 +03:00
static ssize_t response_parse_tiny ( struct opal_resp_tok * tok ,
const u8 * pos )
2017-02-03 22:50:31 +03:00
{
tok - > pos = pos ;
tok - > len = 1 ;
tok - > width = OPAL_WIDTH_TINY ;
if ( pos [ 0 ] & TINY_ATOM_SIGNED ) {
tok - > type = OPAL_DTA_TOKENID_SINT ;
} else {
tok - > type = OPAL_DTA_TOKENID_UINT ;
tok - > stored . u = pos [ 0 ] & 0x3f ;
}
return tok - > len ;
}
2017-02-21 21:59:13 +03:00
static ssize_t response_parse_short ( struct opal_resp_tok * tok ,
const u8 * pos )
2017-02-03 22:50:31 +03:00
{
tok - > pos = pos ;
tok - > len = ( pos [ 0 ] & SHORT_ATOM_LEN_MASK ) + 1 ;
tok - > width = OPAL_WIDTH_SHORT ;
if ( pos [ 0 ] & SHORT_ATOM_BYTESTRING ) {
tok - > type = OPAL_DTA_TOKENID_BYTESTRING ;
} else if ( pos [ 0 ] & SHORT_ATOM_SIGNED ) {
tok - > type = OPAL_DTA_TOKENID_SINT ;
} else {
u64 u_integer = 0 ;
2017-02-21 21:59:13 +03:00
ssize_t i , b = 0 ;
2017-02-03 22:50:31 +03:00
tok - > type = OPAL_DTA_TOKENID_UINT ;
if ( tok - > len > 9 ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " uint64 with more than 8 bytes \n " ) ;
2017-02-03 22:50:31 +03:00
return - EINVAL ;
}
for ( i = tok - > len - 1 ; i > 0 ; i - - ) {
u_integer | = ( ( u64 ) pos [ i ] < < ( 8 * b ) ) ;
b + + ;
}
tok - > stored . u = u_integer ;
}
return tok - > len ;
}
2017-02-21 21:59:13 +03:00
static ssize_t response_parse_medium ( struct opal_resp_tok * tok ,
const u8 * pos )
2017-02-03 22:50:31 +03:00
{
tok - > pos = pos ;
tok - > len = ( ( ( pos [ 0 ] & MEDIUM_ATOM_LEN_MASK ) < < 8 ) | pos [ 1 ] ) + 2 ;
tok - > width = OPAL_WIDTH_MEDIUM ;
if ( pos [ 0 ] & MEDIUM_ATOM_BYTESTRING )
tok - > type = OPAL_DTA_TOKENID_BYTESTRING ;
else if ( pos [ 0 ] & MEDIUM_ATOM_SIGNED )
tok - > type = OPAL_DTA_TOKENID_SINT ;
else
tok - > type = OPAL_DTA_TOKENID_UINT ;
return tok - > len ;
}
2017-02-21 21:59:13 +03:00
static ssize_t response_parse_long ( struct opal_resp_tok * tok ,
const u8 * pos )
2017-02-03 22:50:31 +03:00
{
tok - > pos = pos ;
tok - > len = ( ( pos [ 1 ] < < 16 ) | ( pos [ 2 ] < < 8 ) | pos [ 3 ] ) + 4 ;
tok - > width = OPAL_WIDTH_LONG ;
if ( pos [ 0 ] & LONG_ATOM_BYTESTRING )
tok - > type = OPAL_DTA_TOKENID_BYTESTRING ;
else if ( pos [ 0 ] & LONG_ATOM_SIGNED )
tok - > type = OPAL_DTA_TOKENID_SINT ;
else
tok - > type = OPAL_DTA_TOKENID_UINT ;
return tok - > len ;
}
2017-02-21 21:59:13 +03:00
static ssize_t response_parse_token ( struct opal_resp_tok * tok ,
const u8 * pos )
2017-02-03 22:50:31 +03:00
{
tok - > pos = pos ;
tok - > len = 1 ;
tok - > type = OPAL_DTA_TOKENID_TOKEN ;
tok - > width = OPAL_WIDTH_TOKEN ;
return tok - > len ;
}
static int response_parse ( const u8 * buf , size_t length ,
struct parsed_resp * resp )
{
const struct opal_header * hdr ;
struct opal_resp_tok * iter ;
int num_entries = 0 ;
int total ;
2017-02-21 21:59:13 +03:00
ssize_t token_length ;
2017-02-03 22:50:31 +03:00
const u8 * pos ;
2017-02-21 21:59:15 +03:00
u32 clen , plen , slen ;
2017-02-03 22:50:31 +03:00
if ( ! buf )
return - EFAULT ;
if ( ! resp )
return - EFAULT ;
hdr = ( struct opal_header * ) buf ;
pos = buf ;
pos + = sizeof ( * hdr ) ;
2017-02-21 21:59:15 +03:00
clen = be32_to_cpu ( hdr - > cp . length ) ;
plen = be32_to_cpu ( hdr - > pkt . length ) ;
slen = be32_to_cpu ( hdr - > subpkt . length ) ;
pr_debug ( " Response size: cp: %u, pkt: %u, subpkt: %u \n " ,
clen , plen , slen ) ;
if ( clen = = 0 | | plen = = 0 | | slen = = 0 | |
slen > IO_BUFFER_LENGTH - sizeof ( * hdr ) ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Bad header length. cp: %u, pkt: %u, subpkt: %u \n " ,
clen , plen , slen ) ;
2017-02-03 22:50:31 +03:00
print_buffer ( pos , sizeof ( * hdr ) ) ;
return - EINVAL ;
}
if ( pos > buf + length )
return - EFAULT ;
iter = resp - > toks ;
2017-02-21 21:59:15 +03:00
total = slen ;
2017-02-03 22:50:31 +03:00
print_buffer ( pos , total ) ;
while ( total > 0 ) {
if ( pos [ 0 ] < = TINY_ATOM_BYTE ) /* tiny atom */
token_length = response_parse_tiny ( iter , pos ) ;
else if ( pos [ 0 ] < = SHORT_ATOM_BYTE ) /* short atom */
token_length = response_parse_short ( iter , pos ) ;
else if ( pos [ 0 ] < = MEDIUM_ATOM_BYTE ) /* medium atom */
token_length = response_parse_medium ( iter , pos ) ;
else if ( pos [ 0 ] < = LONG_ATOM_BYTE ) /* long atom */
token_length = response_parse_long ( iter , pos ) ;
else /* TOKEN */
token_length = response_parse_token ( iter , pos ) ;
2017-02-21 21:59:13 +03:00
if ( token_length < 0 )
return token_length ;
2017-02-03 22:50:31 +03:00
pos + = token_length ;
total - = token_length ;
iter + + ;
num_entries + + ;
}
resp - > num = num_entries ;
return 0 ;
}
static size_t response_get_string ( const struct parsed_resp * resp , int n ,
const char * * store )
{
2018-03-01 16:26:37 +03:00
u8 skip ;
2019-02-14 03:16:00 +03:00
const struct opal_resp_tok * tok ;
2018-03-01 16:26:37 +03:00
2017-02-03 22:50:31 +03:00
* store = NULL ;
2019-02-14 03:16:00 +03:00
tok = response_get_token ( resp , n ) ;
if ( IS_ERR ( tok ) )
2017-02-03 22:50:31 +03:00
return 0 ;
2019-02-14 03:16:00 +03:00
if ( tok - > type ! = OPAL_DTA_TOKENID_BYTESTRING ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Token is not a byte string! \n " ) ;
2017-02-03 22:50:31 +03:00
return 0 ;
}
2019-02-14 03:16:00 +03:00
switch ( tok - > width ) {
2018-03-01 16:26:37 +03:00
case OPAL_WIDTH_TINY :
case OPAL_WIDTH_SHORT :
skip = 1 ;
break ;
case OPAL_WIDTH_MEDIUM :
skip = 2 ;
break ;
case OPAL_WIDTH_LONG :
skip = 4 ;
break ;
default :
pr_debug ( " Token has invalid width! \n " ) ;
return 0 ;
}
2019-02-14 03:16:00 +03:00
* store = tok - > pos + skip ;
2019-08-20 18:30:49 +03:00
2019-02-14 03:16:00 +03:00
return tok - > len - skip ;
2017-02-03 22:50:31 +03:00
}
static u64 response_get_u64 ( const struct parsed_resp * resp , int n )
{
2019-02-14 03:16:00 +03:00
const struct opal_resp_tok * tok ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:16:00 +03:00
tok = response_get_token ( resp , n ) ;
if ( IS_ERR ( tok ) )
2017-02-03 22:50:31 +03:00
return 0 ;
2019-02-14 03:16:00 +03:00
if ( tok - > type ! = OPAL_DTA_TOKENID_UINT ) {
pr_debug ( " Token is not unsigned int: %d \n " , tok - > type ) ;
2017-02-03 22:50:31 +03:00
return 0 ;
}
2019-02-14 03:16:00 +03:00
if ( tok - > width ! = OPAL_WIDTH_TINY & & tok - > width ! = OPAL_WIDTH_SHORT ) {
pr_debug ( " Atom is not short or tiny: %d \n " , tok - > width ) ;
2017-02-03 22:50:31 +03:00
return 0 ;
}
2019-02-14 03:16:00 +03:00
return tok - > stored . u ;
2017-02-03 22:50:31 +03:00
}
2017-02-21 21:59:14 +03:00
static bool response_token_matches ( const struct opal_resp_tok * token , u8 match )
{
if ( IS_ERR ( token ) | |
token - > type ! = OPAL_DTA_TOKENID_TOKEN | |
token - > pos [ 0 ] ! = match )
return false ;
return true ;
}
2017-02-03 22:50:31 +03:00
static u8 response_status ( const struct parsed_resp * resp )
{
2017-02-21 21:59:14 +03:00
const struct opal_resp_tok * tok ;
tok = response_get_token ( resp , 0 ) ;
if ( response_token_matches ( tok , OPAL_ENDOFSESSION ) )
2017-02-03 22:50:31 +03:00
return 0 ;
if ( resp - > num < 5 )
return DTAERROR_NO_METHOD_STATUS ;
2017-02-21 21:59:14 +03:00
tok = response_get_token ( resp , resp - > num - 5 ) ;
if ( ! response_token_matches ( tok , OPAL_STARTLIST ) )
return DTAERROR_NO_METHOD_STATUS ;
tok = response_get_token ( resp , resp - > num - 1 ) ;
if ( ! response_token_matches ( tok , OPAL_ENDLIST ) )
2017-02-03 22:50:31 +03:00
return DTAERROR_NO_METHOD_STATUS ;
return response_get_u64 ( resp , resp - > num - 4 ) ;
}
/* Parses and checks for errors */
static int parse_and_check_status ( struct opal_dev * dev )
{
int error ;
print_buffer ( dev - > cmd , dev - > pos ) ;
error = response_parse ( dev - > resp , IO_BUFFER_LENGTH , & dev - > parsed ) ;
if ( error ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Couldn't parse response. \n " ) ;
2017-02-03 22:50:31 +03:00
return error ;
}
return response_status ( & dev - > parsed ) ;
}
static void clear_opal_cmd ( struct opal_dev * dev )
{
dev - > pos = sizeof ( struct opal_header ) ;
memset ( dev - > cmd , 0 , IO_BUFFER_LENGTH ) ;
}
2019-02-14 03:15:58 +03:00
static int cmd_start ( struct opal_dev * dev , const u8 * uid , const u8 * method )
{
int err = 0 ;
clear_opal_cmd ( dev ) ;
set_comid ( dev , dev - > comid ) ;
add_token_u8 ( & err , dev , OPAL_CALL ) ;
add_token_bytestring ( & err , dev , uid , OPAL_UID_LENGTH ) ;
add_token_bytestring ( & err , dev , method , OPAL_METHOD_LENGTH ) ;
/*
* Every method call is followed by its parameters enclosed within
* OPAL_STARTLIST and OPAL_ENDLIST tokens . We automatically open the
* parameter list here and close it later in cmd_finalize .
*/
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
return err ;
}
2017-02-03 22:50:31 +03:00
static int start_opal_session_cont ( struct opal_dev * dev )
{
u32 hsn , tsn ;
int error = 0 ;
error = parse_and_check_status ( dev ) ;
if ( error )
return error ;
hsn = response_get_u64 ( & dev - > parsed , 4 ) ;
tsn = response_get_u64 ( & dev - > parsed , 5 ) ;
2020-03-03 22:17:00 +03:00
if ( hsn ! = GENERIC_HOST_SESSION_NUM | | tsn < FIRST_TPER_SESSION_NUM ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Couldn't authenticate session \n " ) ;
2017-02-03 22:50:31 +03:00
return - EPERM ;
}
dev - > hsn = hsn ;
dev - > tsn = tsn ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return 0 ;
}
static void add_suspend_info ( struct opal_dev * dev ,
struct opal_suspend_data * sus )
{
struct opal_suspend_data * iter ;
list_for_each_entry ( iter , & dev - > unlk_lst , node ) {
if ( iter - > lr = = sus - > lr ) {
list_del ( & iter - > node ) ;
kfree ( iter ) ;
break ;
}
}
list_add_tail ( & sus - > node , & dev - > unlk_lst ) ;
}
static int end_session_cont ( struct opal_dev * dev )
{
dev - > hsn = 0 ;
dev - > tsn = 0 ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return parse_and_check_status ( dev ) ;
}
static int finalize_and_send ( struct opal_dev * dev , cont_fn cont )
{
int ret ;
ret = cmd_finalize ( dev , dev - > hsn , dev - > tsn ) ;
if ( ret ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error finalizing command buffer: %d \n " , ret ) ;
2017-02-03 22:50:31 +03:00
return ret ;
}
print_buffer ( dev - > cmd , dev - > pos ) ;
return opal_send_recv ( dev , cont ) ;
}
2023-04-05 14:12:22 +03:00
static int generic_get_columns ( struct opal_dev * dev , const u8 * table ,
u64 start_column , u64 end_column )
2019-02-14 03:16:04 +03:00
{
int err ;
err = cmd_start ( dev , table , opalmethod [ OPAL_GET ] ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTCOLUMN ) ;
2023-04-05 14:12:22 +03:00
add_token_u64 ( & err , dev , start_column ) ;
2019-02-14 03:16:04 +03:00
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDCOLUMN ) ;
2023-04-05 14:12:22 +03:00
add_token_u64 ( & err , dev , end_column ) ;
2019-02-14 03:16:04 +03:00
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
if ( err )
return err ;
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2023-04-05 14:12:22 +03:00
/*
* request @ column from table @ table on device @ dev . On success , the column
* data will be available in dev - > resp - > tok [ 4 ]
*/
static int generic_get_column ( struct opal_dev * dev , const u8 * table ,
u64 column )
{
return generic_get_columns ( dev , table , column , column ) ;
}
2019-05-21 23:46:46 +03:00
/*
* see TCG SAS 5.3 .2 .3 for a description of the available columns
*
* the result is provided in dev - > resp - > tok [ 4 ]
*/
2019-10-31 19:13:20 +03:00
static int generic_get_table_info ( struct opal_dev * dev , const u8 * table_uid ,
2019-05-21 23:46:46 +03:00
u64 column )
{
u8 uid [ OPAL_UID_LENGTH ] ;
2019-10-31 19:13:20 +03:00
const unsigned int half = OPAL_UID_LENGTH_HALF ;
2019-05-21 23:46:46 +03:00
/* sed-opal UIDs can be split in two halves:
* first : actual table index
* second : relative index in the table
* so we have to get the first half of the OPAL_TABLE_TABLE and use the
* first part of the target table as relative index into that table
*/
memcpy ( uid , opaluid [ OPAL_TABLE_TABLE ] , half ) ;
2019-10-31 19:13:20 +03:00
memcpy ( uid + half , table_uid , half ) ;
2019-05-21 23:46:46 +03:00
return generic_get_column ( dev , uid , column ) ;
}
2017-02-22 17:55:13 +03:00
static int gen_key ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
u8 uid [ OPAL_UID_LENGTH ] ;
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
memcpy ( uid , dev - > prev_data , min ( sizeof ( uid ) , dev - > prev_d_len ) ) ;
kfree ( dev - > prev_data ) ;
dev - > prev_data = NULL ;
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , uid , opalmethod [ OPAL_GENKEY ] ) ;
2017-02-03 22:50:31 +03:00
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building gen key command \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return finalize_and_send ( dev , parse_and_check_status ) ;
}
static int get_active_key_cont ( struct opal_dev * dev )
{
const char * activekey ;
size_t keylen ;
int error = 0 ;
error = parse_and_check_status ( dev ) ;
if ( error )
return error ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
keylen = response_get_string ( & dev - > parsed , 4 , & activekey ) ;
if ( ! activekey ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " %s: Couldn't extract the Activekey from the response \n " ,
__func__ ) ;
2017-02-03 22:50:31 +03:00
return OPAL_INVAL_PARAM ;
}
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
dev - > prev_data = kmemdup ( activekey , keylen , GFP_KERNEL ) ;
if ( ! dev - > prev_data )
return - ENOMEM ;
dev - > prev_d_len = keylen ;
return 0 ;
}
2017-02-22 17:55:13 +03:00
static int get_active_key ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
u8 uid [ OPAL_UID_LENGTH ] ;
2019-02-14 03:15:58 +03:00
int err ;
2017-02-22 17:55:13 +03:00
u8 * lr = data ;
2017-02-03 22:50:31 +03:00
err = build_locking_range ( uid , sizeof ( uid ) , * lr ) ;
if ( err )
return err ;
2019-02-14 03:16:04 +03:00
err = generic_get_column ( dev , uid , OPAL_ACTIVEKEY ) ;
if ( err )
2017-02-03 22:50:31 +03:00
return err ;
2019-02-14 03:16:04 +03:00
return get_active_key_cont ( dev ) ;
2017-02-03 22:50:31 +03:00
}
2019-10-31 19:13:20 +03:00
static int generic_table_write_data ( struct opal_dev * dev , const u64 data ,
u64 offset , u64 size , const u8 * uid )
{
const u8 __user * src = ( u8 __user * ) ( uintptr_t ) data ;
u8 * dst ;
u64 len ;
size_t off = 0 ;
int err ;
/* do we fit in the available space? */
err = generic_get_table_info ( dev , uid , OPAL_TABLE_ROWS ) ;
if ( err ) {
pr_debug ( " Couldn't get the table size \n " ) ;
return err ;
}
len = response_get_u64 ( & dev - > parsed , 4 ) ;
if ( size > len | | offset > len - size ) {
pr_debug ( " Does not fit in the table (%llu vs. %llu) \n " ,
offset + size , len ) ;
return - ENOSPC ;
}
/* do the actual transmission(s) */
while ( off < size ) {
err = cmd_start ( dev , uid , opalmethod [ OPAL_SET ] ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_WHERE ) ;
add_token_u64 ( & err , dev , offset + off ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_VALUES ) ;
/*
* The bytestring header is either 1 or 2 bytes , so assume 2.
* There also needs to be enough space to accommodate the
* trailing OPAL_ENDNAME ( 1 byte ) and tokens added by
* cmd_finalize .
*/
len = min ( remaining_size ( dev ) - ( 2 + 1 + CMD_FINALIZE_BYTES_NEEDED ) ,
( size_t ) ( size - off ) ) ;
pr_debug ( " Write bytes %zu+%llu/%llu \n " , off , len , size ) ;
dst = add_bytestring_header ( & err , dev , len ) ;
if ( ! dst )
break ;
if ( copy_from_user ( dst , src + off , len ) ) {
err = - EFAULT ;
break ;
}
dev - > pos + = len ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
if ( err )
break ;
err = finalize_and_send ( dev , parse_and_check_status ) ;
if ( err )
break ;
off + = len ;
}
return err ;
}
2017-02-03 22:50:31 +03:00
static int generic_lr_enable_disable ( struct opal_dev * dev ,
u8 * uid , bool rle , bool wle ,
bool rl , bool wl )
{
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , uid , opalmethod [ OPAL_SET ] ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_VALUES ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
2019-02-14 03:16:05 +03:00
add_token_u8 ( & err , dev , OPAL_READLOCKENABLED ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , rle ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
2019-02-14 03:16:05 +03:00
add_token_u8 ( & err , dev , OPAL_WRITELOCKENABLED ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , wle ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_READLOCKED ) ;
add_token_u8 ( & err , dev , rl ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_WRITELOCKED ) ;
add_token_u8 ( & err , dev , wl ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return err ;
}
static inline int enable_global_lr ( struct opal_dev * dev , u8 * uid ,
struct opal_user_lr_setup * setup )
{
int err ;
err = generic_lr_enable_disable ( dev , uid , ! ! setup - > RLE , ! ! setup - > WLE ,
0 , 0 ) ;
if ( err )
2017-04-07 22:58:50 +03:00
pr_debug ( " Failed to create enable global lr command \n " ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return err ;
}
2017-02-22 17:55:13 +03:00
static int setup_locking_range ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
u8 uid [ OPAL_UID_LENGTH ] ;
2017-02-22 17:55:13 +03:00
struct opal_user_lr_setup * setup = data ;
2017-02-03 22:50:31 +03:00
u8 lr ;
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
lr = setup - > session . opal_key . lr ;
err = build_locking_range ( uid , sizeof ( uid ) , lr ) ;
if ( err )
return err ;
if ( lr = = 0 )
err = enable_global_lr ( dev , uid , setup ) ;
else {
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , uid , opalmethod [ OPAL_SET ] ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_VALUES ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
2019-02-14 03:16:05 +03:00
add_token_u8 ( & err , dev , OPAL_RANGESTART ) ;
2017-02-03 22:50:31 +03:00
add_token_u64 ( & err , dev , setup - > range_start ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
2019-02-14 03:16:05 +03:00
add_token_u8 ( & err , dev , OPAL_RANGELENGTH ) ;
2017-02-03 22:50:31 +03:00
add_token_u64 ( & err , dev , setup - > range_length ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
2019-02-14 03:16:05 +03:00
add_token_u8 ( & err , dev , OPAL_READLOCKENABLED ) ;
2017-02-03 22:50:31 +03:00
add_token_u64 ( & err , dev , ! ! setup - > RLE ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
2019-02-14 03:16:05 +03:00
add_token_u8 ( & err , dev , OPAL_WRITELOCKENABLED ) ;
2017-02-03 22:50:31 +03:00
add_token_u64 ( & err , dev , ! ! setup - > WLE ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
}
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building Setup Locking range command. \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2023-04-05 14:12:23 +03:00
static int response_get_column ( const struct parsed_resp * resp ,
int * iter ,
u8 column ,
u64 * value )
{
const struct opal_resp_tok * tok ;
int n = * iter ;
u64 val ;
tok = response_get_token ( resp , n ) ;
if ( IS_ERR ( tok ) )
return PTR_ERR ( tok ) ;
if ( ! response_token_matches ( tok , OPAL_STARTNAME ) ) {
pr_debug ( " Unexpected response token type %d. \n " , n ) ;
return OPAL_INVAL_PARAM ;
}
n + + ;
if ( response_get_u64 ( resp , n ) ! = column ) {
pr_debug ( " Token %d does not match expected column %u. \n " ,
n , column ) ;
return OPAL_INVAL_PARAM ;
}
n + + ;
val = response_get_u64 ( resp , n ) ;
n + + ;
tok = response_get_token ( resp , n ) ;
if ( IS_ERR ( tok ) )
return PTR_ERR ( tok ) ;
if ( ! response_token_matches ( tok , OPAL_ENDNAME ) ) {
pr_debug ( " Unexpected response token type %d. \n " , n ) ;
return OPAL_INVAL_PARAM ;
}
n + + ;
* value = val ;
* iter = n ;
return 0 ;
}
static int locking_range_status ( struct opal_dev * dev , void * data )
{
u8 lr_buffer [ OPAL_UID_LENGTH ] ;
u64 resp ;
bool rlocked , wlocked ;
int err , tok_n = 2 ;
struct opal_lr_status * lrst = data ;
err = build_locking_range ( lr_buffer , sizeof ( lr_buffer ) ,
lrst - > session . opal_key . lr ) ;
if ( err )
return err ;
err = generic_get_columns ( dev , lr_buffer , OPAL_RANGESTART ,
OPAL_WRITELOCKED ) ;
if ( err ) {
pr_debug ( " Couldn't get lr %u table columns %d to %d. \n " ,
lrst - > session . opal_key . lr , OPAL_RANGESTART ,
OPAL_WRITELOCKED ) ;
return err ;
}
/* range start */
err = response_get_column ( & dev - > parsed , & tok_n , OPAL_RANGESTART ,
& lrst - > range_start ) ;
if ( err )
return err ;
/* range length */
err = response_get_column ( & dev - > parsed , & tok_n , OPAL_RANGELENGTH ,
& lrst - > range_length ) ;
if ( err )
return err ;
/* RLE */
err = response_get_column ( & dev - > parsed , & tok_n , OPAL_READLOCKENABLED ,
& resp ) ;
if ( err )
return err ;
lrst - > RLE = ! ! resp ;
/* WLE */
err = response_get_column ( & dev - > parsed , & tok_n , OPAL_WRITELOCKENABLED ,
& resp ) ;
if ( err )
return err ;
lrst - > WLE = ! ! resp ;
/* read locked */
err = response_get_column ( & dev - > parsed , & tok_n , OPAL_READLOCKED , & resp ) ;
if ( err )
return err ;
rlocked = ! ! resp ;
/* write locked */
err = response_get_column ( & dev - > parsed , & tok_n , OPAL_WRITELOCKED , & resp ) ;
if ( err )
return err ;
wlocked = ! ! resp ;
/* opal_lock_state can not map 'read locked' only state. */
lrst - > l_state = OPAL_RW ;
if ( rlocked & & wlocked )
lrst - > l_state = OPAL_LK ;
else if ( wlocked )
lrst - > l_state = OPAL_RO ;
else if ( rlocked ) {
pr_debug ( " Can not report read locked only state. \n " ) ;
return - EINVAL ;
}
return 0 ;
}
2017-02-03 22:50:31 +03:00
static int start_generic_opal_session ( struct opal_dev * dev ,
enum opal_uid auth ,
enum opal_uid sp_type ,
const char * key ,
u8 key_len )
{
u32 hsn ;
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
2017-04-07 22:58:50 +03:00
if ( key = = NULL & & auth ! = OPAL_ANYBODY_UID )
2017-02-03 22:50:31 +03:00
return OPAL_INVAL_PARAM ;
hsn = GENERIC_HOST_SESSION_NUM ;
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , opaluid [ OPAL_SMUID_UID ] ,
opalmethod [ OPAL_STARTSESSION ] ) ;
2017-02-03 22:50:31 +03:00
add_token_u64 ( & err , dev , hsn ) ;
add_token_bytestring ( & err , dev , opaluid [ sp_type ] , OPAL_UID_LENGTH ) ;
add_token_u8 ( & err , dev , 1 ) ;
switch ( auth ) {
case OPAL_ANYBODY_UID :
break ;
case OPAL_ADMIN1_UID :
case OPAL_SID_UID :
2019-06-28 01:30:02 +03:00
case OPAL_PSID_UID :
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , 0 ) ; /* HostChallenge */
add_token_bytestring ( & err , dev , key , key_len ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , 3 ) ; /* HostSignAuth */
add_token_bytestring ( & err , dev , opaluid [ auth ] ,
OPAL_UID_LENGTH ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
break ;
default :
2017-04-07 22:58:50 +03:00
pr_debug ( " Cannot start Admin SP session with auth %d \n " , auth ) ;
2017-02-03 22:50:31 +03:00
return OPAL_INVAL_PARAM ;
}
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building start adminsp session command. \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
return finalize_and_send ( dev , start_opal_session_cont ) ;
}
2017-02-22 17:55:13 +03:00
static int start_anybodyASP_opal_session ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
return start_generic_opal_session ( dev , OPAL_ANYBODY_UID ,
OPAL_ADMINSP_UID , NULL , 0 ) ;
}
2017-02-22 17:55:13 +03:00
static int start_SIDASP_opal_session ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
int ret ;
const u8 * key = dev - > prev_data ;
if ( ! key ) {
2017-02-22 17:55:13 +03:00
const struct opal_key * okey = data ;
2019-02-14 03:15:54 +03:00
2017-02-03 22:50:31 +03:00
ret = start_generic_opal_session ( dev , OPAL_SID_UID ,
OPAL_ADMINSP_UID ,
okey - > key ,
okey - > key_len ) ;
} else {
ret = start_generic_opal_session ( dev , OPAL_SID_UID ,
OPAL_ADMINSP_UID ,
key , dev - > prev_d_len ) ;
kfree ( key ) ;
dev - > prev_data = NULL ;
}
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
2017-02-22 17:55:13 +03:00
static int start_admin1LSP_opal_session ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
2017-02-22 17:55:13 +03:00
struct opal_key * key = data ;
2019-02-14 03:15:54 +03:00
2017-02-03 22:50:31 +03:00
return start_generic_opal_session ( dev , OPAL_ADMIN1_UID ,
OPAL_LOCKINGSP_UID ,
key - > key , key - > key_len ) ;
}
2019-06-28 01:30:02 +03:00
static int start_PSID_opal_session ( struct opal_dev * dev , void * data )
{
const struct opal_key * okey = data ;
return start_generic_opal_session ( dev , OPAL_PSID_UID ,
OPAL_ADMINSP_UID ,
okey - > key ,
okey - > key_len ) ;
}
2017-02-22 17:55:13 +03:00
static int start_auth_opal_session ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
2017-02-22 17:55:13 +03:00
struct opal_session_info * session = data ;
2017-02-03 22:50:31 +03:00
u8 lk_ul_user [ OPAL_UID_LENGTH ] ;
2017-02-22 17:55:13 +03:00
size_t keylen = session - > opal_key . key_len ;
2017-02-03 22:50:31 +03:00
int err = 0 ;
u8 * key = session - > opal_key . key ;
u32 hsn = GENERIC_HOST_SESSION_NUM ;
2019-02-14 03:15:58 +03:00
if ( session - > sum )
2017-02-03 22:50:31 +03:00
err = build_locking_user ( lk_ul_user , sizeof ( lk_ul_user ) ,
session - > opal_key . lr ) ;
2019-02-14 03:15:58 +03:00
else if ( session - > who ! = OPAL_ADMIN1 & & ! session - > sum )
2017-02-03 22:50:31 +03:00
err = build_locking_user ( lk_ul_user , sizeof ( lk_ul_user ) ,
session - > who - 1 ) ;
2019-02-14 03:15:58 +03:00
else
2017-02-03 22:50:31 +03:00
memcpy ( lk_ul_user , opaluid [ OPAL_ADMIN1_UID ] , OPAL_UID_LENGTH ) ;
2019-02-14 03:15:58 +03:00
if ( err )
return err ;
err = cmd_start ( dev , opaluid [ OPAL_SMUID_UID ] ,
opalmethod [ OPAL_STARTSESSION ] ) ;
2017-02-03 22:50:31 +03:00
add_token_u64 ( & err , dev , hsn ) ;
add_token_bytestring ( & err , dev , opaluid [ OPAL_LOCKINGSP_UID ] ,
OPAL_UID_LENGTH ) ;
add_token_u8 ( & err , dev , 1 ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , 0 ) ;
add_token_bytestring ( & err , dev , key , keylen ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , 3 ) ;
add_token_bytestring ( & err , dev , lk_ul_user , OPAL_UID_LENGTH ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building STARTSESSION command. \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
return finalize_and_send ( dev , start_opal_session_cont ) ;
}
2017-02-22 17:55:13 +03:00
static int revert_tper ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , opaluid [ OPAL_ADMINSP_UID ] ,
opalmethod [ OPAL_REVERT ] ) ;
2017-02-03 22:50:31 +03:00
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building REVERT TPER command. \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2017-02-22 17:55:13 +03:00
static int internal_activate_user ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
2017-02-22 17:55:13 +03:00
struct opal_session_info * session = data ;
2017-02-03 22:50:31 +03:00
u8 uid [ OPAL_UID_LENGTH ] ;
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
memcpy ( uid , opaluid [ OPAL_USER1_UID ] , OPAL_UID_LENGTH ) ;
uid [ 7 ] = session - > who ;
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , uid , opalmethod [ OPAL_SET ] ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_VALUES ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , 5 ) ; /* Enabled */
add_token_u8 ( & err , dev , OPAL_TRUE ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building Activate UserN command. \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2017-02-22 17:55:13 +03:00
static int erase_locking_range ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
2017-02-22 17:55:13 +03:00
struct opal_session_info * session = data ;
2017-02-03 22:50:31 +03:00
u8 uid [ OPAL_UID_LENGTH ] ;
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
if ( build_locking_range ( uid , sizeof ( uid ) , session - > opal_key . lr ) < 0 )
return - ERANGE ;
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , uid , opalmethod [ OPAL_ERASE ] ) ;
2017-02-03 22:50:31 +03:00
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building Erase Locking Range Command. \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2017-02-22 17:55:13 +03:00
static int set_mbr_done ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
2017-02-22 17:55:13 +03:00
u8 * mbr_done_tf = data ;
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , opaluid [ OPAL_MBRCONTROL ] ,
opalmethod [ OPAL_SET ] ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_VALUES ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
2019-02-14 03:16:05 +03:00
add_token_u8 ( & err , dev , OPAL_MBRDONE ) ;
2017-02-22 17:55:13 +03:00
add_token_u8 ( & err , dev , * mbr_done_tf ) ; /* Done T or F */
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error Building set MBR Done command \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2017-02-22 17:55:13 +03:00
static int set_mbr_enable_disable ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
2017-02-22 17:55:13 +03:00
u8 * mbr_en_dis = data ;
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , opaluid [ OPAL_MBRCONTROL ] ,
opalmethod [ OPAL_SET ] ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_VALUES ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
2019-02-14 03:16:05 +03:00
add_token_u8 ( & err , dev , OPAL_MBRENABLE ) ;
2017-02-22 17:55:13 +03:00
add_token_u8 ( & err , dev , * mbr_en_dis ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error Building set MBR done command \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2019-05-21 23:46:45 +03:00
static int write_shadow_mbr ( struct opal_dev * dev , void * data )
{
struct opal_shadow_mbr * shadow = data ;
2019-10-31 19:13:20 +03:00
return generic_table_write_data ( dev , shadow - > data , shadow - > offset ,
shadow - > size , opaluid [ OPAL_MBR ] ) ;
2019-05-21 23:46:45 +03:00
}
2017-02-03 22:50:31 +03:00
static int generic_pw_cmd ( u8 * key , size_t key_len , u8 * cpin_uid ,
struct opal_dev * dev )
{
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , cpin_uid , opalmethod [ OPAL_SET ] ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_VALUES ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
2019-02-14 03:16:05 +03:00
add_token_u8 ( & err , dev , OPAL_PIN ) ;
2017-02-03 22:50:31 +03:00
add_token_bytestring ( & err , dev , key , key_len ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
return err ;
}
2017-02-22 17:55:13 +03:00
static int set_new_pw ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
u8 cpin_uid [ OPAL_UID_LENGTH ] ;
2017-02-22 17:55:13 +03:00
struct opal_session_info * usr = data ;
2017-02-03 22:50:31 +03:00
memcpy ( cpin_uid , opaluid [ OPAL_C_PIN_ADMIN1 ] , OPAL_UID_LENGTH ) ;
if ( usr - > who ! = OPAL_ADMIN1 ) {
cpin_uid [ 5 ] = 0x03 ;
if ( usr - > sum )
cpin_uid [ 7 ] = usr - > opal_key . lr + 1 ;
else
cpin_uid [ 7 ] = usr - > who ;
}
if ( generic_pw_cmd ( usr - > opal_key . key , usr - > opal_key . key_len ,
cpin_uid , dev ) ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building set password command. \n " ) ;
2017-02-03 22:50:31 +03:00
return - ERANGE ;
}
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2017-02-22 17:55:13 +03:00
static int set_sid_cpin_pin ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
u8 cpin_uid [ OPAL_UID_LENGTH ] ;
2017-02-22 17:55:13 +03:00
struct opal_key * key = data ;
2017-02-03 22:50:31 +03:00
memcpy ( cpin_uid , opaluid [ OPAL_C_PIN_SID ] , OPAL_UID_LENGTH ) ;
if ( generic_pw_cmd ( key - > key , key - > key_len , cpin_uid , dev ) ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building Set SID cpin \n " ) ;
2017-02-03 22:50:31 +03:00
return - ERANGE ;
}
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2023-04-05 14:12:20 +03:00
static void add_authority_object_ref ( int * err ,
struct opal_dev * dev ,
const u8 * uid ,
size_t uid_len )
{
add_token_u8 ( err , dev , OPAL_STARTNAME ) ;
add_token_bytestring ( err , dev ,
opaluid [ OPAL_HALF_UID_AUTHORITY_OBJ_REF ] ,
OPAL_UID_LENGTH / 2 ) ;
add_token_bytestring ( err , dev , uid , uid_len ) ;
add_token_u8 ( err , dev , OPAL_ENDNAME ) ;
}
static void add_boolean_object_ref ( int * err ,
struct opal_dev * dev ,
u8 boolean_op )
{
add_token_u8 ( err , dev , OPAL_STARTNAME ) ;
add_token_bytestring ( err , dev , opaluid [ OPAL_HALF_UID_BOOLEAN_ACE ] ,
OPAL_UID_LENGTH / 2 ) ;
add_token_u8 ( err , dev , boolean_op ) ;
add_token_u8 ( err , dev , OPAL_ENDNAME ) ;
}
static int set_lr_boolean_ace ( struct opal_dev * dev ,
unsigned int opal_uid ,
u8 lr ,
const u8 * users ,
size_t users_len )
2017-02-03 22:50:31 +03:00
{
u8 lr_buffer [ OPAL_UID_LENGTH ] ;
u8 user_uid [ OPAL_UID_LENGTH ] ;
2023-04-05 14:12:20 +03:00
u8 u ;
2019-02-14 03:15:58 +03:00
int err ;
2017-02-03 22:50:31 +03:00
2023-04-05 14:12:20 +03:00
memcpy ( lr_buffer , opaluid [ opal_uid ] , OPAL_UID_LENGTH ) ;
lr_buffer [ 7 ] = lr ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , lr_buffer , opalmethod [ OPAL_SET ] ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_VALUES ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , 3 ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
2023-04-05 14:12:20 +03:00
for ( u = 0 ; u < users_len ; u + + ) {
if ( users [ u ] = = OPAL_ADMIN1 )
memcpy ( user_uid , opaluid [ OPAL_ADMIN1_UID ] ,
OPAL_UID_LENGTH ) ;
else {
memcpy ( user_uid , opaluid [ OPAL_USER1_UID ] ,
OPAL_UID_LENGTH ) ;
user_uid [ 7 ] = users [ u ] ;
}
add_authority_object_ref ( & err , dev , user_uid , sizeof ( user_uid ) ) ;
2017-02-03 22:50:31 +03:00
2023-04-05 14:12:20 +03:00
/*
* Add boolean operator in postfix only with
* two or more authorities being added in ACE
* expresion .
* */
if ( u > 0 )
add_boolean_object_ref ( & err , dev , OPAL_BOOLEAN_OR ) ;
}
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
2023-04-05 14:12:20 +03:00
return err ;
}
static int add_user_to_lr ( struct opal_dev * dev , void * data )
{
int err ;
struct opal_lock_unlock * lkul = data ;
const u8 users [ ] = {
lkul - > session . who
} ;
err = set_lr_boolean_ace ( dev ,
lkul - > l_state = = OPAL_RW ?
OPAL_LOCKINGRANGE_ACE_WRLOCKED :
OPAL_LOCKINGRANGE_ACE_RDLOCKED ,
lkul - > session . opal_key . lr , users ,
ARRAY_SIZE ( users ) ) ;
2017-02-03 22:50:31 +03:00
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building add user to locking range command. \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2023-04-05 14:12:21 +03:00
static int add_user_to_lr_ace ( struct opal_dev * dev , void * data )
{
int err ;
struct opal_lock_unlock * lkul = data ;
const u8 users [ ] = {
OPAL_ADMIN1 ,
lkul - > session . who
} ;
err = set_lr_boolean_ace ( dev , OPAL_LOCKINGRANGE_ACE_START_TO_KEY ,
lkul - > session . opal_key . lr , users ,
ARRAY_SIZE ( users ) ) ;
if ( err ) {
pr_debug ( " Error building add user to locking ranges ACEs. \n " ) ;
return err ;
}
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2017-02-22 17:55:13 +03:00
static int lock_unlock_locking_range ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
u8 lr_buffer [ OPAL_UID_LENGTH ] ;
2017-02-22 17:55:13 +03:00
struct opal_lock_unlock * lkul = data ;
2017-02-03 22:50:31 +03:00
u8 read_locked = 1 , write_locked = 1 ;
int err = 0 ;
if ( build_locking_range ( lr_buffer , sizeof ( lr_buffer ) ,
lkul - > session . opal_key . lr ) < 0 )
return - ERANGE ;
switch ( lkul - > l_state ) {
case OPAL_RO :
read_locked = 0 ;
write_locked = 1 ;
break ;
case OPAL_RW :
read_locked = 0 ;
write_locked = 0 ;
break ;
case OPAL_LK :
2019-02-14 03:15:54 +03:00
/* vars are initialized to locked */
2017-02-03 22:50:31 +03:00
break ;
default :
2017-04-07 22:58:50 +03:00
pr_debug ( " Tried to set an invalid locking state... returning to uland \n " ) ;
2017-02-03 22:50:31 +03:00
return OPAL_INVAL_PARAM ;
}
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , lr_buffer , opalmethod [ OPAL_SET ] ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_VALUES ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_READLOCKED ) ;
add_token_u8 ( & err , dev , read_locked ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_WRITELOCKED ) ;
add_token_u8 ( & err , dev , write_locked ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building SET command. \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2017-02-22 17:55:13 +03:00
static int lock_unlock_locking_range_sum ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
u8 lr_buffer [ OPAL_UID_LENGTH ] ;
u8 read_locked = 1 , write_locked = 1 ;
2017-02-22 17:55:13 +03:00
struct opal_lock_unlock * lkul = data ;
2017-02-03 22:50:31 +03:00
int ret ;
clear_opal_cmd ( dev ) ;
set_comid ( dev , dev - > comid ) ;
if ( build_locking_range ( lr_buffer , sizeof ( lr_buffer ) ,
lkul - > session . opal_key . lr ) < 0 )
return - ERANGE ;
switch ( lkul - > l_state ) {
case OPAL_RO :
read_locked = 0 ;
write_locked = 1 ;
break ;
case OPAL_RW :
read_locked = 0 ;
write_locked = 0 ;
break ;
case OPAL_LK :
2019-02-14 03:15:54 +03:00
/* vars are initialized to locked */
2017-02-03 22:50:31 +03:00
break ;
default :
2017-04-07 22:58:50 +03:00
pr_debug ( " Tried to set an invalid locking state. \n " ) ;
2017-02-03 22:50:31 +03:00
return OPAL_INVAL_PARAM ;
}
ret = generic_lr_enable_disable ( dev , lr_buffer , 1 , 1 ,
read_locked , write_locked ) ;
if ( ret < 0 ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building SET command. \n " ) ;
2017-02-03 22:50:31 +03:00
return ret ;
}
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2017-02-22 17:55:13 +03:00
static int activate_lsp ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
2017-02-22 17:55:13 +03:00
struct opal_lr_act * opal_act = data ;
2017-02-03 22:50:31 +03:00
u8 user_lr [ OPAL_UID_LENGTH ] ;
2019-02-14 03:15:58 +03:00
int err , i ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:15:58 +03:00
err = cmd_start ( dev , opaluid [ OPAL_LOCKINGSP_UID ] ,
opalmethod [ OPAL_ACTIVATE ] ) ;
2017-02-03 22:50:31 +03:00
if ( opal_act - > sum ) {
err = build_locking_range ( user_lr , sizeof ( user_lr ) ,
opal_act - > lr [ 0 ] ) ;
if ( err )
return err ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
2019-11-09 02:09:04 +03:00
add_token_u64 ( & err , dev , OPAL_SUM_SET_LIST ) ;
2017-02-03 22:50:31 +03:00
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_bytestring ( & err , dev , user_lr , OPAL_UID_LENGTH ) ;
for ( i = 1 ; i < opal_act - > num_lrs ; i + + ) {
user_lr [ 7 ] = opal_act - > lr [ i ] ;
add_token_bytestring ( & err , dev , user_lr , OPAL_UID_LENGTH ) ;
}
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
}
if ( err ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Error building Activate LockingSP command. \n " ) ;
2017-02-03 22:50:31 +03:00
return err ;
}
return finalize_and_send ( dev , parse_and_check_status ) ;
}
2019-02-14 03:16:04 +03:00
/* Determine if we're in the Manufactured Inactive or Active state */
static int get_lsp_lifecycle ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
u8 lc_status ;
2019-02-14 03:16:04 +03:00
int err ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:16:04 +03:00
err = generic_get_column ( dev , opaluid [ OPAL_LOCKINGSP_UID ] ,
OPAL_LIFECYCLE ) ;
if ( err )
return err ;
2017-02-03 22:50:31 +03:00
lc_status = response_get_u64 ( & dev - > parsed , 4 ) ;
2019-02-14 03:15:54 +03:00
/* 0x08 is Manufactured Inactive */
2017-02-03 22:50:31 +03:00
/* 0x09 is Manufactured */
if ( lc_status ! = OPAL_MANUFACTURED_INACTIVE ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Couldn't determine the status of the Lifecycle state \n " ) ;
2017-02-03 22:50:31 +03:00
return - ENODEV ;
}
return 0 ;
}
2019-02-14 03:16:04 +03:00
static int get_msid_cpin_pin ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
const char * msid_pin ;
size_t strlen ;
2019-02-14 03:16:04 +03:00
int err ;
2017-02-03 22:50:31 +03:00
2019-02-14 03:16:04 +03:00
err = generic_get_column ( dev , opaluid [ OPAL_C_PIN_MSID ] , OPAL_PIN ) ;
if ( err )
return err ;
2017-02-03 22:50:31 +03:00
strlen = response_get_string ( & dev - > parsed , 4 , & msid_pin ) ;
if ( ! msid_pin ) {
2019-02-14 03:16:04 +03:00
pr_debug ( " Couldn't extract MSID_CPIN from response \n " ) ;
2017-02-03 22:50:31 +03:00
return OPAL_INVAL_PARAM ;
}
dev - > prev_data = kmemdup ( msid_pin , strlen , GFP_KERNEL ) ;
if ( ! dev - > prev_data )
return - ENOMEM ;
dev - > prev_d_len = strlen ;
return 0 ;
}
2019-10-31 19:13:21 +03:00
static int write_table_data ( struct opal_dev * dev , void * data )
{
struct opal_read_write_table * write_tbl = data ;
return generic_table_write_data ( dev , write_tbl - > data , write_tbl - > offset ,
write_tbl - > size , write_tbl - > table_uid ) ;
}
static int read_table_data_cont ( struct opal_dev * dev )
{
int err ;
const char * data_read ;
err = parse_and_check_status ( dev ) ;
if ( err )
return err ;
dev - > prev_d_len = response_get_string ( & dev - > parsed , 1 , & data_read ) ;
dev - > prev_data = ( void * ) data_read ;
if ( ! dev - > prev_data ) {
pr_debug ( " %s: Couldn't read data from the table. \n " , __func__ ) ;
return OPAL_INVAL_PARAM ;
}
return 0 ;
}
/*
* IO_BUFFER_LENGTH = 2048
* sizeof ( header ) = 56
* No . of Token Bytes in the Response = 11
* MAX size of data that can be carried in response buffer
* at a time is : 2048 - ( 56 + 11 ) = 1981 = 0x7BD .
*/
# define OPAL_MAX_READ_TABLE (0x7BD)
static int read_table_data ( struct opal_dev * dev , void * data )
{
struct opal_read_write_table * read_tbl = data ;
int err ;
size_t off = 0 , max_read_size = OPAL_MAX_READ_TABLE ;
u64 table_len , len ;
u64 offset = read_tbl - > offset , read_size = read_tbl - > size - 1 ;
u8 __user * dst ;
err = generic_get_table_info ( dev , read_tbl - > table_uid , OPAL_TABLE_ROWS ) ;
if ( err ) {
pr_debug ( " Couldn't get the table size \n " ) ;
return err ;
}
table_len = response_get_u64 ( & dev - > parsed , 4 ) ;
/* Check if the user is trying to read from the table limits */
if ( read_size > table_len | | offset > table_len - read_size ) {
pr_debug ( " Read size exceeds the Table size limits (%llu vs. %llu) \n " ,
offset + read_size , table_len ) ;
return - EINVAL ;
}
while ( off < read_size ) {
err = cmd_start ( dev , read_tbl - > table_uid , opalmethod [ OPAL_GET ] ) ;
add_token_u8 ( & err , dev , OPAL_STARTLIST ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTROW ) ;
add_token_u64 ( & err , dev , offset + off ) ; /* start row value */
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_STARTNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDROW ) ;
len = min ( max_read_size , ( size_t ) ( read_size - off ) ) ;
add_token_u64 ( & err , dev , offset + off + len ) ; /* end row value
*/
add_token_u8 ( & err , dev , OPAL_ENDNAME ) ;
add_token_u8 ( & err , dev , OPAL_ENDLIST ) ;
if ( err ) {
pr_debug ( " Error building read table data command. \n " ) ;
break ;
}
err = finalize_and_send ( dev , read_table_data_cont ) ;
if ( err )
break ;
/* len+1: This includes the NULL terminator at the end*/
if ( dev - > prev_d_len > len + 1 ) {
err = - EOVERFLOW ;
break ;
}
dst = ( u8 __user * ) ( uintptr_t ) read_tbl - > data ;
if ( copy_to_user ( dst + off , dev - > prev_data , dev - > prev_d_len ) ) {
pr_debug ( " Error copying data to userspace \n " ) ;
err = - EFAULT ;
break ;
}
dev - > prev_data = NULL ;
off + = len ;
}
return err ;
}
2017-02-22 17:55:13 +03:00
static int end_opal_session ( struct opal_dev * dev , void * data )
2017-02-03 22:50:31 +03:00
{
int err = 0 ;
clear_opal_cmd ( dev ) ;
set_comid ( dev , dev - > comid ) ;
add_token_u8 ( & err , dev , OPAL_ENDOFSESSION ) ;
2017-02-22 17:55:13 +03:00
if ( err < 0 )
return err ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return finalize_and_send ( dev , end_session_cont ) ;
}
static int end_opal_session_error ( struct opal_dev * dev )
{
2019-02-14 03:16:07 +03:00
const struct opal_step error_end_session = {
end_opal_session ,
2017-02-03 22:50:31 +03:00
} ;
2019-08-20 18:30:49 +03:00
2019-02-14 03:16:07 +03:00
return execute_step ( dev , & error_end_session , 0 ) ;
2017-02-03 22:50:31 +03:00
}
2019-02-14 03:16:06 +03:00
static inline void setup_opal_dev ( struct opal_dev * dev )
2017-02-03 22:50:31 +03:00
{
dev - > tsn = 0 ;
dev - > hsn = 0 ;
dev - > prev_data = NULL ;
}
static int check_opal_support ( struct opal_dev * dev )
{
int ret ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-02-14 03:16:07 +03:00
ret = opal_discovery0_step ( dev ) ;
2022-08-16 17:07:13 +03:00
if ( ! ret )
dev - > flags | = OPAL_FL_SUPPORTED ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
2017-02-22 20:15:06 +03:00
static void clean_opal_dev ( struct opal_dev * dev )
{
struct opal_suspend_data * suspend , * next ;
mutex_lock ( & dev - > dev_lock ) ;
list_for_each_entry_safe ( suspend , next , & dev - > unlk_lst , node ) {
list_del ( & suspend - > node ) ;
kfree ( suspend ) ;
}
mutex_unlock ( & dev - > dev_lock ) ;
}
void free_opal_dev ( struct opal_dev * dev )
{
if ( ! dev )
return ;
2019-08-20 18:30:49 +03:00
2017-02-22 20:15:06 +03:00
clean_opal_dev ( dev ) ;
2022-11-07 23:39:44 +03:00
kfree ( dev - > resp ) ;
kfree ( dev - > cmd ) ;
2017-02-22 20:15:06 +03:00
kfree ( dev ) ;
}
EXPORT_SYMBOL ( free_opal_dev ) ;
2017-02-17 15:59:39 +03:00
struct opal_dev * init_opal_dev ( void * data , sec_send_recv * send_recv )
2017-02-03 22:50:31 +03:00
{
2017-02-17 15:59:39 +03:00
struct opal_dev * dev ;
dev = kmalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev )
return NULL ;
2022-11-07 23:39:44 +03:00
/*
* Presumably DMA - able buffers must be cache - aligned . Kmalloc makes
* sure the allocated buffer is DMA - safe in that regard .
*/
dev - > cmd = kmalloc ( IO_BUFFER_LENGTH , GFP_KERNEL ) ;
if ( ! dev - > cmd )
goto err_free_dev ;
dev - > resp = kmalloc ( IO_BUFFER_LENGTH , GFP_KERNEL ) ;
if ( ! dev - > resp )
goto err_free_cmd ;
2017-02-17 15:59:39 +03:00
INIT_LIST_HEAD ( & dev - > unlk_lst ) ;
mutex_init ( & dev - > dev_lock ) ;
2022-08-16 17:07:13 +03:00
dev - > flags = 0 ;
2017-02-17 15:59:39 +03:00
dev - > data = data ;
dev - > send_recv = send_recv ;
if ( check_opal_support ( dev ) ! = 0 ) {
2017-02-17 15:59:38 +03:00
pr_debug ( " Opal is not supported on this device \n " ) ;
2022-11-07 23:39:44 +03:00
goto err_free_resp ;
2017-02-17 15:59:39 +03:00
}
2019-08-20 18:30:49 +03:00
2017-02-17 15:59:39 +03:00
return dev ;
2022-11-07 23:39:44 +03:00
err_free_resp :
kfree ( dev - > resp ) ;
err_free_cmd :
kfree ( dev - > cmd ) ;
err_free_dev :
kfree ( dev ) ;
return NULL ;
2017-02-03 22:50:31 +03:00
}
EXPORT_SYMBOL ( init_opal_dev ) ;
static int opal_secure_erase_locking_range ( struct opal_dev * dev ,
struct opal_session_info * opal_session )
{
2017-02-22 17:55:13 +03:00
const struct opal_step erase_steps [ ] = {
{ start_auth_opal_session , opal_session } ,
{ get_active_key , & opal_session - > opal_key . lr } ,
{ gen_key , } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
int ret ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-02-14 03:16:08 +03:00
ret = execute_steps ( dev , erase_steps , ARRAY_SIZE ( erase_steps ) ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
static int opal_erase_locking_range ( struct opal_dev * dev ,
struct opal_session_info * opal_session )
{
2017-02-22 17:55:13 +03:00
const struct opal_step erase_steps [ ] = {
{ start_auth_opal_session , opal_session } ,
{ erase_locking_range , opal_session } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
int ret ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-02-14 03:16:08 +03:00
ret = execute_steps ( dev , erase_steps , ARRAY_SIZE ( erase_steps ) ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
static int opal_enable_disable_shadow_mbr ( struct opal_dev * dev ,
struct opal_mbr_data * opal_mbr )
{
2019-02-14 03:15:53 +03:00
u8 enable_disable = opal_mbr - > enable_disable = = OPAL_MBR_ENABLE ?
OPAL_TRUE : OPAL_FALSE ;
2017-02-22 17:55:13 +03:00
const struct opal_step mbr_steps [ ] = {
{ start_admin1LSP_opal_session , & opal_mbr - > key } ,
2019-02-14 03:15:53 +03:00
{ set_mbr_done , & enable_disable } ,
2017-02-22 17:55:13 +03:00
{ end_opal_session , } ,
{ start_admin1LSP_opal_session , & opal_mbr - > key } ,
2019-02-14 03:15:53 +03:00
{ set_mbr_enable_disable , & enable_disable } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
int ret ;
if ( opal_mbr - > enable_disable ! = OPAL_MBR_ENABLE & &
opal_mbr - > enable_disable ! = OPAL_MBR_DISABLE )
return - EINVAL ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-02-14 03:16:08 +03:00
ret = execute_steps ( dev , mbr_steps , ARRAY_SIZE ( mbr_steps ) ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
2019-05-21 23:46:44 +03:00
static int opal_set_mbr_done ( struct opal_dev * dev ,
struct opal_mbr_done * mbr_done )
{
u8 mbr_done_tf = mbr_done - > done_flag = = OPAL_MBR_DONE ?
OPAL_TRUE : OPAL_FALSE ;
const struct opal_step mbr_steps [ ] = {
{ start_admin1LSP_opal_session , & mbr_done - > key } ,
{ set_mbr_done , & mbr_done_tf } ,
{ end_opal_session , }
} ;
int ret ;
if ( mbr_done - > done_flag ! = OPAL_MBR_DONE & &
mbr_done - > done_flag ! = OPAL_MBR_NOT_DONE )
return - EINVAL ;
mutex_lock ( & dev - > dev_lock ) ;
setup_opal_dev ( dev ) ;
ret = execute_steps ( dev , mbr_steps , ARRAY_SIZE ( mbr_steps ) ) ;
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2019-05-21 23:46:44 +03:00
return ret ;
}
2019-05-21 23:46:45 +03:00
static int opal_write_shadow_mbr ( struct opal_dev * dev ,
struct opal_shadow_mbr * info )
{
const struct opal_step mbr_steps [ ] = {
{ start_admin1LSP_opal_session , & info - > key } ,
{ write_shadow_mbr , info } ,
{ end_opal_session , }
} ;
int ret ;
if ( info - > size = = 0 )
return 0 ;
mutex_lock ( & dev - > dev_lock ) ;
setup_opal_dev ( dev ) ;
ret = execute_steps ( dev , mbr_steps , ARRAY_SIZE ( mbr_steps ) ) ;
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2019-05-21 23:46:45 +03:00
return ret ;
}
2017-02-03 22:50:31 +03:00
static int opal_save ( struct opal_dev * dev , struct opal_lock_unlock * lk_unlk )
{
struct opal_suspend_data * suspend ;
suspend = kzalloc ( sizeof ( * suspend ) , GFP_KERNEL ) ;
if ( ! suspend )
return - ENOMEM ;
suspend - > unlk = * lk_unlk ;
suspend - > lr = lk_unlk - > session . opal_key . lr ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2017-02-03 22:50:31 +03:00
add_suspend_info ( dev , suspend ) ;
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return 0 ;
}
static int opal_add_user_to_lr ( struct opal_dev * dev ,
struct opal_lock_unlock * lk_unlk )
{
2017-02-22 17:55:13 +03:00
const struct opal_step steps [ ] = {
{ start_admin1LSP_opal_session , & lk_unlk - > session . opal_key } ,
{ add_user_to_lr , lk_unlk } ,
2023-04-05 14:12:21 +03:00
{ add_user_to_lr_ace , lk_unlk } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
int ret ;
if ( lk_unlk - > l_state ! = OPAL_RO & &
lk_unlk - > l_state ! = OPAL_RW ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Locking state was not RO or RW \n " ) ;
2017-02-03 22:50:31 +03:00
return - EINVAL ;
}
2019-08-20 18:30:49 +03:00
2017-03-06 18:41:04 +03:00
if ( lk_unlk - > session . who < OPAL_USER1 | |
2017-02-03 22:50:31 +03:00
lk_unlk - > session . who > OPAL_USER9 ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Authority was not within the range of users: %d \n " ,
lk_unlk - > session . who ) ;
2017-02-03 22:50:31 +03:00
return - EINVAL ;
}
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
if ( lk_unlk - > session . sum ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " %s not supported in sum. Use setup locking range \n " ,
__func__ ) ;
2017-02-03 22:50:31 +03:00
return - EINVAL ;
}
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-02-14 03:16:08 +03:00
ret = execute_steps ( dev , steps , ARRAY_SIZE ( steps ) ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
2019-06-28 01:30:02 +03:00
static int opal_reverttper ( struct opal_dev * dev , struct opal_key * opal , bool psid )
2017-02-03 22:50:31 +03:00
{
2019-06-28 01:30:02 +03:00
/* controller will terminate session */
2017-02-22 17:55:13 +03:00
const struct opal_step revert_steps [ ] = {
{ start_SIDASP_opal_session , opal } ,
2019-06-28 01:30:02 +03:00
{ revert_tper , }
2017-02-03 22:50:31 +03:00
} ;
2019-06-28 01:30:02 +03:00
const struct opal_step psid_revert_steps [ ] = {
{ start_PSID_opal_session , opal } ,
{ revert_tper , }
} ;
2017-02-03 22:50:31 +03:00
int ret ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-06-28 01:30:02 +03:00
if ( psid )
ret = execute_steps ( dev , psid_revert_steps ,
ARRAY_SIZE ( psid_revert_steps ) ) ;
else
ret = execute_steps ( dev , revert_steps ,
ARRAY_SIZE ( revert_steps ) ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2017-02-22 20:15:06 +03:00
/*
* If we successfully reverted lets clean
* any saved locking ranges .
*/
if ( ! ret )
clean_opal_dev ( dev ) ;
2017-02-03 22:50:31 +03:00
return ret ;
}
2017-02-22 17:55:13 +03:00
static int __opal_lock_unlock ( struct opal_dev * dev ,
struct opal_lock_unlock * lk_unlk )
2017-02-03 22:50:31 +03:00
{
2017-02-22 17:55:13 +03:00
const struct opal_step unlock_steps [ ] = {
{ start_auth_opal_session , & lk_unlk - > session } ,
{ lock_unlock_locking_range , lk_unlk } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
2017-02-22 17:55:13 +03:00
const struct opal_step unlock_sum_steps [ ] = {
{ start_auth_opal_session , & lk_unlk - > session } ,
{ lock_unlock_locking_range_sum , lk_unlk } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
2019-02-14 03:16:06 +03:00
if ( lk_unlk - > session . sum )
2019-02-14 03:16:08 +03:00
return execute_steps ( dev , unlock_sum_steps ,
ARRAY_SIZE ( unlock_sum_steps ) ) ;
2019-02-14 03:16:06 +03:00
else
2019-02-14 03:16:08 +03:00
return execute_steps ( dev , unlock_steps ,
ARRAY_SIZE ( unlock_steps ) ) ;
2017-02-03 22:50:31 +03:00
}
2017-09-01 17:53:35 +03:00
static int __opal_set_mbr_done ( struct opal_dev * dev , struct opal_key * key )
{
2019-02-14 03:15:53 +03:00
u8 mbr_done_tf = OPAL_TRUE ;
2019-02-14 03:15:54 +03:00
const struct opal_step mbrdone_step [ ] = {
2017-09-01 17:53:35 +03:00
{ start_admin1LSP_opal_session , key } ,
{ set_mbr_done , & mbr_done_tf } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-09-01 17:53:35 +03:00
} ;
2019-02-14 03:16:08 +03:00
return execute_steps ( dev , mbrdone_step , ARRAY_SIZE ( mbrdone_step ) ) ;
2017-09-01 17:53:35 +03:00
}
2022-12-06 12:29:13 +03:00
static void opal_lock_check_for_saved_key ( struct opal_dev * dev ,
struct opal_lock_unlock * lk_unlk )
{
struct opal_suspend_data * iter ;
if ( lk_unlk - > l_state ! = OPAL_LK | |
lk_unlk - > session . opal_key . key_len > 0 )
return ;
/*
* Usually when closing a crypto device ( eg : dm - crypt with LUKS ) the
* volume key is not required , as it requires root privileges anyway ,
* and root can deny access to a disk in many ways regardless .
* Requiring the volume key to lock the device is a peculiarity of the
* OPAL specification . Given we might already have saved the key if
* the user requested it via the ' IOC_OPAL_SAVE ' ioctl , we can use
* that key to lock the device if no key was provided here , the
* locking range matches and the appropriate flag was passed with
* ' IOC_OPAL_SAVE ' .
* This allows integrating OPAL with tools and libraries that are used
* to the common behaviour and do not ask for the volume key when
* closing a device .
*/
setup_opal_dev ( dev ) ;
list_for_each_entry ( iter , & dev - > unlk_lst , node ) {
if ( ( iter - > unlk . flags & OPAL_SAVE_FOR_LOCK ) & &
iter - > lr = = lk_unlk - > session . opal_key . lr & &
iter - > unlk . session . opal_key . key_len > 0 ) {
lk_unlk - > session . opal_key . key_len =
iter - > unlk . session . opal_key . key_len ;
memcpy ( lk_unlk - > session . opal_key . key ,
iter - > unlk . session . opal_key . key ,
iter - > unlk . session . opal_key . key_len ) ;
break ;
}
}
}
2017-02-22 17:55:13 +03:00
static int opal_lock_unlock ( struct opal_dev * dev ,
struct opal_lock_unlock * lk_unlk )
2017-02-03 22:50:31 +03:00
{
int ret ;
2019-06-28 01:31:09 +03:00
if ( lk_unlk - > session . who > OPAL_USER9 )
2017-02-03 22:50:31 +03:00
return - EINVAL ;
mutex_lock ( & dev - > dev_lock ) ;
2022-12-06 12:29:13 +03:00
opal_lock_check_for_saved_key ( dev , lk_unlk ) ;
2017-02-22 17:55:13 +03:00
ret = __opal_lock_unlock ( dev , lk_unlk ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
static int opal_take_ownership ( struct opal_dev * dev , struct opal_key * opal )
{
2017-02-22 17:55:13 +03:00
const struct opal_step owner_steps [ ] = {
{ start_anybodyASP_opal_session , } ,
{ get_msid_cpin_pin , } ,
{ end_opal_session , } ,
{ start_SIDASP_opal_session , opal } ,
{ set_sid_cpin_pin , opal } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
int ret ;
if ( ! dev )
return - ENODEV ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-02-14 03:16:08 +03:00
ret = execute_steps ( dev , owner_steps , ARRAY_SIZE ( owner_steps ) ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
2019-02-14 03:15:54 +03:00
static int opal_activate_lsp ( struct opal_dev * dev ,
struct opal_lr_act * opal_lr_act )
2017-02-03 22:50:31 +03:00
{
2017-02-22 17:55:13 +03:00
const struct opal_step active_steps [ ] = {
{ start_SIDASP_opal_session , & opal_lr_act - > key } ,
{ get_lsp_lifecycle , } ,
{ activate_lsp , opal_lr_act } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
int ret ;
if ( ! opal_lr_act - > num_lrs | | opal_lr_act - > num_lrs > OPAL_MAX_LRS )
return - EINVAL ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-02-14 03:16:08 +03:00
ret = execute_steps ( dev , active_steps , ARRAY_SIZE ( active_steps ) ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
static int opal_setup_locking_range ( struct opal_dev * dev ,
struct opal_user_lr_setup * opal_lrs )
{
2017-02-22 17:55:13 +03:00
const struct opal_step lr_steps [ ] = {
{ start_auth_opal_session , & opal_lrs - > session } ,
{ setup_locking_range , opal_lrs } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
int ret ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-02-14 03:16:08 +03:00
ret = execute_steps ( dev , lr_steps , ARRAY_SIZE ( lr_steps ) ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
2023-04-05 14:12:23 +03:00
static int opal_locking_range_status ( struct opal_dev * dev ,
struct opal_lr_status * opal_lrst ,
void __user * data )
{
const struct opal_step lr_steps [ ] = {
{ start_auth_opal_session , & opal_lrst - > session } ,
{ locking_range_status , opal_lrst } ,
{ end_opal_session , }
} ;
int ret ;
mutex_lock ( & dev - > dev_lock ) ;
setup_opal_dev ( dev ) ;
ret = execute_steps ( dev , lr_steps , ARRAY_SIZE ( lr_steps ) ) ;
mutex_unlock ( & dev - > dev_lock ) ;
/* skip session info when copying back to uspace */
if ( ! ret & & copy_to_user ( data + offsetof ( struct opal_lr_status , range_start ) ,
( void * ) opal_lrst + offsetof ( struct opal_lr_status , range_start ) ,
sizeof ( * opal_lrst ) - offsetof ( struct opal_lr_status , range_start ) ) ) {
pr_debug ( " Error copying status to userspace \n " ) ;
return - EFAULT ;
}
return ret ;
}
2017-02-03 22:50:31 +03:00
static int opal_set_new_pw ( struct opal_dev * dev , struct opal_new_pw * opal_pw )
{
2017-02-22 17:55:13 +03:00
const struct opal_step pw_steps [ ] = {
{ start_auth_opal_session , & opal_pw - > session } ,
{ set_new_pw , & opal_pw - > new_user_pw } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
int ret ;
2019-06-28 01:31:09 +03:00
if ( opal_pw - > session . who > OPAL_USER9 | |
2017-02-03 22:50:31 +03:00
opal_pw - > new_user_pw . who > OPAL_USER9 )
return - EINVAL ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-02-14 03:16:08 +03:00
ret = execute_steps ( dev , pw_steps , ARRAY_SIZE ( pw_steps ) ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
static int opal_activate_user ( struct opal_dev * dev ,
struct opal_session_info * opal_session )
{
2017-02-22 17:55:13 +03:00
const struct opal_step act_steps [ ] = {
{ start_admin1LSP_opal_session , & opal_session - > opal_key } ,
{ internal_activate_user , opal_session } ,
2019-02-14 03:16:06 +03:00
{ end_opal_session , }
2017-02-03 22:50:31 +03:00
} ;
int ret ;
/* We can't activate Admin1 it's active as manufactured */
2017-03-06 18:41:04 +03:00
if ( opal_session - > who < OPAL_USER1 | |
2017-02-03 22:50:31 +03:00
opal_session - > who > OPAL_USER9 ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Who was not a valid user: %d \n " , opal_session - > who ) ;
2017-02-03 22:50:31 +03:00
return - EINVAL ;
}
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2019-02-14 03:16:08 +03:00
ret = execute_steps ( dev , act_steps , ARRAY_SIZE ( act_steps ) ) ;
2017-02-03 22:50:31 +03:00
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return ret ;
}
bool opal_unlock_from_suspend ( struct opal_dev * dev )
{
struct opal_suspend_data * suspend ;
bool was_failure = false ;
int ret = 0 ;
if ( ! dev )
return false ;
2019-08-20 18:30:49 +03:00
2022-08-16 17:07:13 +03:00
if ( ! ( dev - > flags & OPAL_FL_SUPPORTED ) )
2017-02-03 22:50:31 +03:00
return false ;
mutex_lock ( & dev - > dev_lock ) ;
2019-02-14 03:16:06 +03:00
setup_opal_dev ( dev ) ;
2017-02-03 22:50:31 +03:00
list_for_each_entry ( suspend , & dev - > unlk_lst , node ) {
dev - > tsn = 0 ;
dev - > hsn = 0 ;
2017-02-22 17:55:13 +03:00
ret = __opal_lock_unlock ( dev , & suspend - > unlk ) ;
2017-02-03 22:50:31 +03:00
if ( ret ) {
2017-04-07 22:58:50 +03:00
pr_debug ( " Failed to unlock LR %hhu with sum %d \n " ,
suspend - > unlk . session . opal_key . lr ,
suspend - > unlk . session . sum ) ;
2017-02-03 22:50:31 +03:00
was_failure = true ;
}
2019-08-20 18:30:49 +03:00
2022-08-16 17:07:13 +03:00
if ( dev - > flags & OPAL_FL_MBR_ENABLED ) {
2017-09-01 17:53:35 +03:00
ret = __opal_set_mbr_done ( dev , & suspend - > unlk . session . opal_key ) ;
if ( ret )
pr_debug ( " Failed to set MBR Done in S3 resume \n " ) ;
}
2017-02-03 22:50:31 +03:00
}
mutex_unlock ( & dev - > dev_lock ) ;
2019-08-20 18:30:49 +03:00
2017-02-03 22:50:31 +03:00
return was_failure ;
}
EXPORT_SYMBOL ( opal_unlock_from_suspend ) ;
2019-10-31 19:13:21 +03:00
static int opal_read_table ( struct opal_dev * dev ,
struct opal_read_write_table * rw_tbl )
{
const struct opal_step read_table_steps [ ] = {
{ start_admin1LSP_opal_session , & rw_tbl - > key } ,
{ read_table_data , rw_tbl } ,
{ end_opal_session , }
} ;
int ret = 0 ;
if ( ! rw_tbl - > size )
return ret ;
return execute_steps ( dev , read_table_steps ,
ARRAY_SIZE ( read_table_steps ) ) ;
}
static int opal_write_table ( struct opal_dev * dev ,
struct opal_read_write_table * rw_tbl )
{
const struct opal_step write_table_steps [ ] = {
{ start_admin1LSP_opal_session , & rw_tbl - > key } ,
{ write_table_data , rw_tbl } ,
{ end_opal_session , }
} ;
int ret = 0 ;
if ( ! rw_tbl - > size )
return ret ;
return execute_steps ( dev , write_table_steps ,
ARRAY_SIZE ( write_table_steps ) ) ;
}
static int opal_generic_read_write_table ( struct opal_dev * dev ,
struct opal_read_write_table * rw_tbl )
{
int ret , bit_set ;
mutex_lock ( & dev - > dev_lock ) ;
setup_opal_dev ( dev ) ;
bit_set = fls64 ( rw_tbl - > flags ) - 1 ;
switch ( bit_set ) {
case OPAL_READ_TABLE :
ret = opal_read_table ( dev , rw_tbl ) ;
break ;
case OPAL_WRITE_TABLE :
ret = opal_write_table ( dev , rw_tbl ) ;
break ;
default :
pr_debug ( " Invalid bit set in the flag (%016llx). \n " ,
rw_tbl - > flags ) ;
ret = - EINVAL ;
break ;
}
mutex_unlock ( & dev - > dev_lock ) ;
return ret ;
}
2022-08-16 17:07:13 +03:00
static int opal_get_status ( struct opal_dev * dev , void __user * data )
{
struct opal_status sts = { 0 } ;
/*
* check_opal_support ( ) error is not fatal ,
* ! dev - > supported is a valid condition
*/
if ( ! check_opal_support ( dev ) )
sts . flags = dev - > flags ;
if ( copy_to_user ( data , & sts , sizeof ( sts ) ) ) {
pr_debug ( " Error copying status to userspace \n " ) ;
return - EFAULT ;
}
return 0 ;
}
2017-02-15 03:29:36 +03:00
int sed_ioctl ( struct opal_dev * dev , unsigned int cmd , void __user * arg )
2017-02-03 22:50:31 +03:00
{
2017-02-15 03:29:36 +03:00
void * p ;
int ret = - ENOTTY ;
2017-02-03 22:50:31 +03:00
if ( ! capable ( CAP_SYS_ADMIN ) )
return - EACCES ;
2017-02-17 15:59:39 +03:00
if ( ! dev )
return - ENOTSUPP ;
2022-08-16 17:07:13 +03:00
if ( ! ( dev - > flags & OPAL_FL_SUPPORTED ) )
2017-02-03 22:50:31 +03:00
return - ENOTSUPP ;
2022-08-16 17:07:13 +03:00
if ( cmd & IOC_IN ) {
p = memdup_user ( arg , _IOC_SIZE ( cmd ) ) ;
if ( IS_ERR ( p ) )
return PTR_ERR ( p ) ;
}
2017-02-03 22:50:31 +03:00
2017-02-15 03:29:36 +03:00
switch ( cmd ) {
case IOC_OPAL_SAVE :
ret = opal_save ( dev , p ) ;
break ;
case IOC_OPAL_LOCK_UNLOCK :
ret = opal_lock_unlock ( dev , p ) ;
break ;
case IOC_OPAL_TAKE_OWNERSHIP :
ret = opal_take_ownership ( dev , p ) ;
break ;
case IOC_OPAL_ACTIVATE_LSP :
ret = opal_activate_lsp ( dev , p ) ;
break ;
case IOC_OPAL_SET_PW :
ret = opal_set_new_pw ( dev , p ) ;
break ;
case IOC_OPAL_ACTIVATE_USR :
ret = opal_activate_user ( dev , p ) ;
break ;
case IOC_OPAL_REVERT_TPR :
2019-06-28 01:30:02 +03:00
ret = opal_reverttper ( dev , p , false ) ;
2017-02-15 03:29:36 +03:00
break ;
case IOC_OPAL_LR_SETUP :
ret = opal_setup_locking_range ( dev , p ) ;
break ;
case IOC_OPAL_ADD_USR_TO_LR :
ret = opal_add_user_to_lr ( dev , p ) ;
break ;
case IOC_OPAL_ENABLE_DISABLE_MBR :
ret = opal_enable_disable_shadow_mbr ( dev , p ) ;
break ;
2019-05-21 23:46:44 +03:00
case IOC_OPAL_MBR_DONE :
ret = opal_set_mbr_done ( dev , p ) ;
break ;
2019-05-21 23:46:45 +03:00
case IOC_OPAL_WRITE_SHADOW_MBR :
ret = opal_write_shadow_mbr ( dev , p ) ;
break ;
2017-02-15 03:29:36 +03:00
case IOC_OPAL_ERASE_LR :
ret = opal_erase_locking_range ( dev , p ) ;
break ;
case IOC_OPAL_SECURE_ERASE_LR :
ret = opal_secure_erase_locking_range ( dev , p ) ;
break ;
2019-06-28 01:30:02 +03:00
case IOC_OPAL_PSID_REVERT_TPR :
ret = opal_reverttper ( dev , p , true ) ;
break ;
2019-10-31 19:13:21 +03:00
case IOC_OPAL_GENERIC_TABLE_RW :
ret = opal_generic_read_write_table ( dev , p ) ;
break ;
2022-08-16 17:07:13 +03:00
case IOC_OPAL_GET_STATUS :
ret = opal_get_status ( dev , arg ) ;
break ;
2023-04-05 14:12:23 +03:00
case IOC_OPAL_GET_LR_STATUS :
ret = opal_locking_range_status ( dev , p , arg ) ;
break ;
2017-02-03 22:50:31 +03:00
default :
2017-04-07 22:58:50 +03:00
break ;
2017-02-03 22:50:31 +03:00
}
2017-02-15 03:29:36 +03:00
2022-08-16 17:07:13 +03:00
if ( cmd & IOC_IN )
kfree ( p ) ;
2017-02-15 03:29:36 +03:00
return ret ;
2017-02-03 22:50:31 +03:00
}
EXPORT_SYMBOL_GPL ( sed_ioctl ) ;