2019-06-04 06:00:37 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* DAWR infrastructure
*
* Copyright 2019 , Michael Neuling , IBM Corporation .
*/
# include <linux/types.h>
# include <linux/export.h>
# include <linux/fs.h>
# include <linux/debugfs.h>
# include <asm/debugfs.h>
# include <asm/machdep.h>
# include <asm/hvcall.h>
bool dawr_force_enable ;
EXPORT_SYMBOL_GPL ( dawr_force_enable ) ;
2020-05-14 14:17:30 +03:00
int set_dawr ( int nr , struct arch_hw_breakpoint * brk )
2019-06-04 06:00:37 +03:00
{
unsigned long dawr , dawrx , mrd ;
dawr = brk - > address ;
dawrx = ( brk - > type & ( HW_BRK_TYPE_READ | HW_BRK_TYPE_WRITE ) )
< < ( 63 - 58 ) ;
dawrx | = ( ( brk - > type & ( HW_BRK_TYPE_TRANSLATE ) ) > > 2 ) < < ( 63 - 59 ) ;
dawrx | = ( brk - > type & ( HW_BRK_TYPE_PRIV_ALL ) ) > > 3 ;
/*
* DAWR length is stored in field MDR bits 48 : 53. Matches range in
* doublewords ( 64 bits ) baised by - 1 eg . 0 b000000 = 1 DW and
* 0 b111111 = 64 DW .
2019-10-17 12:31:59 +03:00
* brk - > hw_len is in bytes .
2019-06-04 06:00:37 +03:00
* This aligns up to double word size , shifts and does the bias .
*/
2019-10-17 12:31:59 +03:00
mrd = ( ( brk - > hw_len + 7 ) > > 3 ) - 1 ;
2019-06-04 06:00:37 +03:00
dawrx | = ( mrd & 0x3f ) < < ( 63 - 53 ) ;
if ( ppc_md . set_dawr )
2020-07-23 12:08:11 +03:00
return ppc_md . set_dawr ( nr , dawr , dawrx ) ;
2019-06-04 06:00:37 +03:00
2020-05-14 14:17:30 +03:00
if ( nr = = 0 ) {
mtspr ( SPRN_DAWR0 , dawr ) ;
mtspr ( SPRN_DAWRX0 , dawrx ) ;
} else {
mtspr ( SPRN_DAWR1 , dawr ) ;
mtspr ( SPRN_DAWRX1 , dawrx ) ;
}
2019-06-04 06:00:37 +03:00
return 0 ;
}
2020-05-14 14:17:33 +03:00
static void disable_dawrs_cb ( void * info )
2019-06-04 06:00:37 +03:00
{
2020-05-14 14:17:33 +03:00
struct arch_hw_breakpoint null_brk = { 0 } ;
int i ;
for ( i = 0 ; i < nr_wp_slots ( ) ; i + + )
set_dawr ( i , & null_brk ) ;
2019-06-04 06:00:37 +03:00
}
static ssize_t dawr_write_file_bool ( struct file * file ,
const char __user * user_buf ,
size_t count , loff_t * ppos )
{
2019-10-17 12:31:59 +03:00
struct arch_hw_breakpoint null_brk = { 0 } ;
2019-06-04 06:00:37 +03:00
size_t rc ;
/* Send error to user if they hypervisor won't allow us to write DAWR */
if ( ! dawr_force_enable & &
firmware_has_feature ( FW_FEATURE_LPAR ) & &
2020-05-14 14:17:30 +03:00
set_dawr ( 0 , & null_brk ) ! = H_SUCCESS )
2019-06-04 06:00:37 +03:00
return - ENODEV ;
rc = debugfs_write_file_bool ( file , user_buf , count , ppos ) ;
if ( rc )
return rc ;
/* If we are clearing, make sure all CPUs have the DAWR cleared */
if ( ! dawr_force_enable )
2020-05-14 14:17:33 +03:00
smp_call_function ( disable_dawrs_cb , NULL , 0 ) ;
2019-06-04 06:00:37 +03:00
return rc ;
}
static const struct file_operations dawr_enable_fops = {
. read = debugfs_read_file_bool ,
. write = dawr_write_file_bool ,
. open = simple_open ,
. llseek = default_llseek ,
} ;
static int __init dawr_force_setup ( void )
{
if ( cpu_has_feature ( CPU_FTR_DAWR ) ) {
/* Don't setup sysfs file for user control on P8 */
dawr_force_enable = true ;
return 0 ;
}
if ( PVR_VER ( mfspr ( SPRN_PVR ) ) = = PVR_POWER9 ) {
/* Turn DAWR off by default, but allow admin to turn it on */
debugfs_create_file_unsafe ( " dawr_enable_dangerous " , 0600 ,
powerpc_debugfs_root ,
& dawr_force_enable ,
& dawr_enable_fops ) ;
}
return 0 ;
}
arch_initcall ( dawr_force_setup ) ;