2019-11-14 19:02:54 +01:00
/* SPDX-License-Identifier: GPL-2.0 */
# ifndef _KERNEL_KCSAN_ENCODING_H
# define _KERNEL_KCSAN_ENCODING_H
# include <linux/bits.h>
# include <linux/log2.h>
# include <linux/mm.h>
# include "kcsan.h"
# define SLOT_RANGE PAGE_SIZE
2019-11-20 10:41:43 +01:00
# define INVALID_WATCHPOINT 0
2019-11-14 19:02:54 +01:00
# define CONSUMED_WATCHPOINT 1
/*
* The maximum useful size of accesses for which we set up watchpoints is the
* max range of slots we check on an access .
*/
# define MAX_ENCODABLE_SIZE (SLOT_RANGE * (1 + KCSAN_CHECK_ADJACENT))
/*
* Number of bits we use to store size info .
*/
# define WATCHPOINT_SIZE_BITS bits_per(MAX_ENCODABLE_SIZE)
/*
* This encoding for addresses discards the upper ( 1 for is - write + SIZE_BITS ) ;
* however , most 64 - bit architectures do not use the full 64 - bit address space .
* Also , in order for a false positive to be observable 2 things need to happen :
*
* 1. different addresses but with the same encoded address race ;
* 2. and both map onto the same watchpoint slots ;
*
* Both these are assumed to be very unlikely . However , in case it still happens
* happens , the report logic will filter out the false positive ( see report . c ) .
*/
2019-11-20 10:41:43 +01:00
# define WATCHPOINT_ADDR_BITS (BITS_PER_LONG-1 - WATCHPOINT_SIZE_BITS)
2019-11-14 19:02:54 +01:00
/*
* Masks to set / retrieve the encoded data .
*/
2019-11-20 10:41:43 +01:00
# define WATCHPOINT_WRITE_MASK BIT(BITS_PER_LONG-1)
2019-11-14 19:02:54 +01:00
# define WATCHPOINT_SIZE_MASK \
2019-11-20 10:41:43 +01:00
GENMASK ( BITS_PER_LONG - 2 , BITS_PER_LONG - 2 - WATCHPOINT_SIZE_BITS )
2019-11-14 19:02:54 +01:00
# define WATCHPOINT_ADDR_MASK \
2019-11-20 10:41:43 +01:00
GENMASK ( BITS_PER_LONG - 3 - WATCHPOINT_SIZE_BITS , 0 )
2019-11-14 19:02:54 +01:00
static inline bool check_encodable ( unsigned long addr , size_t size )
{
return size < = MAX_ENCODABLE_SIZE ;
}
2019-11-20 10:41:43 +01:00
static inline long
encode_watchpoint ( unsigned long addr , size_t size , bool is_write )
2019-11-14 19:02:54 +01:00
{
return ( long ) ( ( is_write ? WATCHPOINT_WRITE_MASK : 0 ) |
( size < < WATCHPOINT_ADDR_BITS ) |
( addr & WATCHPOINT_ADDR_MASK ) ) ;
}
2020-01-07 17:31:04 +01:00
static __always_inline bool decode_watchpoint ( long watchpoint ,
unsigned long * addr_masked ,
size_t * size ,
bool * is_write )
2019-11-14 19:02:54 +01:00
{
if ( watchpoint = = INVALID_WATCHPOINT | |
watchpoint = = CONSUMED_WATCHPOINT )
return false ;
2019-11-20 10:41:43 +01:00
* addr_masked = ( unsigned long ) watchpoint & WATCHPOINT_ADDR_MASK ;
* size = ( ( unsigned long ) watchpoint & WATCHPOINT_SIZE_MASK ) > > WATCHPOINT_ADDR_BITS ;
* is_write = ! ! ( ( unsigned long ) watchpoint & WATCHPOINT_WRITE_MASK ) ;
2019-11-14 19:02:54 +01:00
return true ;
}
/*
* Return watchpoint slot for an address .
*/
2020-01-07 17:31:04 +01:00
static __always_inline int watchpoint_slot ( unsigned long addr )
2019-11-14 19:02:54 +01:00
{
return ( addr / PAGE_SIZE ) % CONFIG_KCSAN_NUM_WATCHPOINTS ;
}
2020-01-07 17:31:04 +01:00
static __always_inline bool matching_access ( unsigned long addr1 , size_t size1 ,
unsigned long addr2 , size_t size2 )
2019-11-14 19:02:54 +01:00
{
unsigned long end_range1 = addr1 + size1 - 1 ;
unsigned long end_range2 = addr2 + size2 - 1 ;
return addr1 < = end_range2 & & addr2 < = end_range1 ;
}
# endif /* _KERNEL_KCSAN_ENCODING_H */