2009-06-12 10:26:42 +02:00
/*
* Access kernel memory without faulting - - s390 specific implementation .
*
2015-03-13 13:13:36 +01:00
* Copyright IBM Corp . 2009 , 2015
2009-06-12 10:26:42 +02:00
*
* Author ( s ) : Heiko Carstens < heiko . carstens @ de . ibm . com > ,
*
*/
# include <linux/uaccess.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/errno.h>
2011-10-30 15:16:39 +01:00
# include <linux/gfp.h>
s390: allow absolute memory access for /dev/mem
Currently dev/mem for s390 provides only real memory access. This means
that the CPU prefix pages are swapped. The prefix swap for real memory
works as follows:
Each CPU owns a prefix register that points to a page aligned memory
location "P". If this CPU accesses the address range [0,0x1fff], it is
translated by the hardware to [P,P+0x1fff]. Accordingly if this CPU
accesses the address range [P,P+0x1fff], it is translated by the hardware
to [0,0x1fff]. Therefore, if [P,P+0x1fff] or [0,0x1fff] is read from
the current /dev/mem device, the incorrectly swapped memory content is
returned.
With this patch the /dev/mem architecture code is modified to provide
absolute memory access. This is done via the arch specific functions
xlate_dev_mem_ptr() and unxlate_dev_mem_ptr(). For swapped pages on
s390 the function xlate_dev_mem_ptr() now returns a new buffer with a
copy of the requested absolute memory. In case the buffer was allocated,
the unxlate_dev_mem_ptr() function frees it after /dev/mem code has
called copy_to_user().
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2012-05-09 16:27:36 +02:00
# include <linux/cpu.h>
2012-03-28 18:30:02 +01:00
# include <asm/ctl_reg.h>
2013-09-06 19:10:48 +02:00
# include <asm/io.h>
2009-06-12 10:26:42 +02:00
2015-03-13 12:55:56 +01:00
static notrace long s390_kernel_write_odd ( void * dst , const void * src , size_t size )
2009-06-12 10:26:42 +02:00
{
2015-03-13 13:13:36 +01:00
unsigned long aligned , offset , count ;
char tmp [ 8 ] ;
2009-06-12 10:26:42 +02:00
2015-03-13 13:13:36 +01:00
aligned = ( unsigned long ) dst & ~ 7UL ;
offset = ( unsigned long ) dst & 7UL ;
size = min ( 8UL - offset , size ) ;
count = size - 1 ;
2009-06-12 10:26:42 +02:00
asm volatile (
" bras 1,0f \n "
2015-03-13 13:13:36 +01:00
" mvc 0(1,%4),0(%5) \n "
" 0: mvc 0(8,%3),0(%0) \n "
" ex %1,0(1) \n "
" lg %1,0(%3) \n "
" lra %0,0(%0) \n "
" sturg %1,%0 \n "
: " +&a " ( aligned ) , " +&a " ( count ) , " =m " ( tmp )
: " a " ( & tmp ) , " a " ( & tmp [ offset ] ) , " a " ( src )
: " cc " , " memory " , " 1 " ) ;
return size ;
2009-06-12 10:26:42 +02:00
}
2015-03-13 12:55:56 +01:00
/*
* s390_kernel_write - write to kernel memory bypassing DAT
* @ dst : destination address
* @ src : source address
* @ size : number of bytes to copy
*
* This function writes to kernel memory bypassing DAT and possible page table
* write protection . It writes to the destination using the sturg instruction .
2015-03-13 13:13:36 +01:00
* Therefore we have a read - modify - write sequence : the function reads eight
* bytes from destination at an eight byte boundary , modifies the bytes
2015-03-13 12:55:56 +01:00
* requested and writes the result back in a loop .
*
* Note : this means that this function may not be called concurrently on
* several cpus with overlapping words , since this may potentially
* cause data corruption .
*/
void notrace s390_kernel_write ( void * dst , const void * src , size_t size )
2009-06-12 10:26:42 +02:00
{
2015-03-13 13:13:36 +01:00
long copied ;
2009-06-12 10:26:42 +02:00
while ( size ) {
2015-03-13 12:55:56 +01:00
copied = s390_kernel_write_odd ( dst , src , size ) ;
2009-06-12 10:26:42 +02:00
dst + = copied ;
src + = copied ;
size - = copied ;
}
}
2010-03-24 11:49:50 +01:00
2012-04-11 14:28:06 +02:00
static int __memcpy_real ( void * dest , void * src , size_t count )
2010-03-24 11:49:50 +01:00
{
register unsigned long _dest asm ( " 2 " ) = ( unsigned long ) dest ;
register unsigned long _len1 asm ( " 3 " ) = ( unsigned long ) count ;
register unsigned long _src asm ( " 4 " ) = ( unsigned long ) src ;
register unsigned long _len2 asm ( " 5 " ) = ( unsigned long ) count ;
int rc = - EFAULT ;
asm volatile (
" 0: mvcle %1,%2,0x0 \n "
" 1: jo 0b \n "
" lhi %0,0x0 \n "
" 2: \n "
EX_TABLE ( 1 b , 2 b )
: " +d " ( rc ) , " +d " ( _dest ) , " +d " ( _src ) , " +d " ( _len1 ) ,
" +d " ( _len2 ) , " =m " ( * ( ( long * ) dest ) )
: " m " ( * ( ( long * ) src ) )
: " cc " , " memory " ) ;
2012-04-11 14:28:06 +02:00
return rc ;
}
/*
* Copy memory in real mode ( kernel to kernel )
*/
int memcpy_real ( void * dest , void * src , size_t count )
{
2016-02-12 12:40:31 +01:00
int irqs_disabled , rc ;
2012-04-11 14:28:06 +02:00
unsigned long flags ;
if ( ! count )
return 0 ;
2016-02-12 12:40:31 +01:00
flags = __arch_local_irq_stnsm ( 0xf8UL ) ;
irqs_disabled = arch_irqs_disabled_flags ( flags ) ;
if ( ! irqs_disabled )
trace_hardirqs_off ( ) ;
2012-04-11 14:28:06 +02:00
rc = __memcpy_real ( dest , src , count ) ;
2016-02-12 12:40:31 +01:00
if ( ! irqs_disabled )
2016-02-09 16:23:39 +01:00
trace_hardirqs_on ( ) ;
__arch_local_irq_ssm ( flags ) ;
2010-03-24 11:49:50 +01:00
return rc ;
}
2011-08-03 16:44:19 +02:00
/*
2012-05-24 14:35:16 +02:00
* Copy memory in absolute mode ( kernel to kernel )
2011-08-03 16:44:19 +02:00
*/
2012-05-24 14:35:16 +02:00
void memcpy_absolute ( void * dest , void * src , size_t count )
2011-08-03 16:44:19 +02:00
{
2012-05-24 14:35:16 +02:00
unsigned long cr0 , flags , prefix ;
2011-08-03 16:44:19 +02:00
2012-05-24 14:35:16 +02:00
flags = arch_local_irq_save ( ) ;
2011-08-03 16:44:19 +02:00
__ctl_store ( cr0 , 0 , 0 ) ;
__ctl_clear_bit ( 0 , 28 ) ; /* disable lowcore protection */
2012-05-24 14:35:16 +02:00
prefix = store_prefix ( ) ;
if ( prefix ) {
local_mcck_disable ( ) ;
set_prefix ( 0 ) ;
memcpy ( dest , src , count ) ;
set_prefix ( prefix ) ;
local_mcck_enable ( ) ;
} else {
memcpy ( dest , src , count ) ;
}
2011-08-03 16:44:19 +02:00
__ctl_load ( cr0 , 0 , 0 ) ;
2012-05-24 14:35:16 +02:00
arch_local_irq_restore ( flags ) ;
2011-08-03 16:44:19 +02:00
}
2011-10-30 15:16:39 +01:00
/*
* Copy memory from kernel ( real ) to user ( virtual )
*/
2014-01-24 12:51:27 +01:00
int copy_to_user_real ( void __user * dest , void * src , unsigned long count )
2011-10-30 15:16:39 +01:00
{
int offs = 0 , size , rc ;
char * buf ;
buf = ( char * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
rc = - EFAULT ;
while ( offs < count ) {
size = min ( PAGE_SIZE , count - offs ) ;
if ( memcpy_real ( buf , src + offs , size ) )
goto out ;
if ( copy_to_user ( dest + offs , buf , size ) )
goto out ;
offs + = size ;
}
rc = 0 ;
out :
free_page ( ( unsigned long ) buf ) ;
return rc ;
}
s390: allow absolute memory access for /dev/mem
Currently dev/mem for s390 provides only real memory access. This means
that the CPU prefix pages are swapped. The prefix swap for real memory
works as follows:
Each CPU owns a prefix register that points to a page aligned memory
location "P". If this CPU accesses the address range [0,0x1fff], it is
translated by the hardware to [P,P+0x1fff]. Accordingly if this CPU
accesses the address range [P,P+0x1fff], it is translated by the hardware
to [0,0x1fff]. Therefore, if [P,P+0x1fff] or [0,0x1fff] is read from
the current /dev/mem device, the incorrectly swapped memory content is
returned.
With this patch the /dev/mem architecture code is modified to provide
absolute memory access. This is done via the arch specific functions
xlate_dev_mem_ptr() and unxlate_dev_mem_ptr(). For swapped pages on
s390 the function xlate_dev_mem_ptr() now returns a new buffer with a
copy of the requested absolute memory. In case the buffer was allocated,
the unxlate_dev_mem_ptr() function frees it after /dev/mem code has
called copy_to_user().
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2012-05-09 16:27:36 +02:00
/*
* Check if physical address is within prefix or zero page
*/
static int is_swapped ( unsigned long addr )
{
unsigned long lc ;
int cpu ;
2015-12-31 10:29:00 +01:00
if ( addr < sizeof ( struct lowcore ) )
s390: allow absolute memory access for /dev/mem
Currently dev/mem for s390 provides only real memory access. This means
that the CPU prefix pages are swapped. The prefix swap for real memory
works as follows:
Each CPU owns a prefix register that points to a page aligned memory
location "P". If this CPU accesses the address range [0,0x1fff], it is
translated by the hardware to [P,P+0x1fff]. Accordingly if this CPU
accesses the address range [P,P+0x1fff], it is translated by the hardware
to [0,0x1fff]. Therefore, if [P,P+0x1fff] or [0,0x1fff] is read from
the current /dev/mem device, the incorrectly swapped memory content is
returned.
With this patch the /dev/mem architecture code is modified to provide
absolute memory access. This is done via the arch specific functions
xlate_dev_mem_ptr() and unxlate_dev_mem_ptr(). For swapped pages on
s390 the function xlate_dev_mem_ptr() now returns a new buffer with a
copy of the requested absolute memory. In case the buffer was allocated,
the unxlate_dev_mem_ptr() function frees it after /dev/mem code has
called copy_to_user().
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2012-05-09 16:27:36 +02:00
return 1 ;
for_each_online_cpu ( cpu ) {
lc = ( unsigned long ) lowcore_ptr [ cpu ] ;
2015-12-31 10:29:00 +01:00
if ( addr > lc + sizeof ( struct lowcore ) - 1 | | addr < lc )
s390: allow absolute memory access for /dev/mem
Currently dev/mem for s390 provides only real memory access. This means
that the CPU prefix pages are swapped. The prefix swap for real memory
works as follows:
Each CPU owns a prefix register that points to a page aligned memory
location "P". If this CPU accesses the address range [0,0x1fff], it is
translated by the hardware to [P,P+0x1fff]. Accordingly if this CPU
accesses the address range [P,P+0x1fff], it is translated by the hardware
to [0,0x1fff]. Therefore, if [P,P+0x1fff] or [0,0x1fff] is read from
the current /dev/mem device, the incorrectly swapped memory content is
returned.
With this patch the /dev/mem architecture code is modified to provide
absolute memory access. This is done via the arch specific functions
xlate_dev_mem_ptr() and unxlate_dev_mem_ptr(). For swapped pages on
s390 the function xlate_dev_mem_ptr() now returns a new buffer with a
copy of the requested absolute memory. In case the buffer was allocated,
the unxlate_dev_mem_ptr() function frees it after /dev/mem code has
called copy_to_user().
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2012-05-09 16:27:36 +02:00
continue ;
return 1 ;
}
return 0 ;
}
/*
* Convert a physical pointer for / dev / mem access
*
* For swapped prefix pages a new buffer is returned that contains a copy of
* the absolute memory . The buffer size is maximum one page large .
*/
2014-07-28 17:20:33 +02:00
void * xlate_dev_mem_ptr ( phys_addr_t addr )
s390: allow absolute memory access for /dev/mem
Currently dev/mem for s390 provides only real memory access. This means
that the CPU prefix pages are swapped. The prefix swap for real memory
works as follows:
Each CPU owns a prefix register that points to a page aligned memory
location "P". If this CPU accesses the address range [0,0x1fff], it is
translated by the hardware to [P,P+0x1fff]. Accordingly if this CPU
accesses the address range [P,P+0x1fff], it is translated by the hardware
to [0,0x1fff]. Therefore, if [P,P+0x1fff] or [0,0x1fff] is read from
the current /dev/mem device, the incorrectly swapped memory content is
returned.
With this patch the /dev/mem architecture code is modified to provide
absolute memory access. This is done via the arch specific functions
xlate_dev_mem_ptr() and unxlate_dev_mem_ptr(). For swapped pages on
s390 the function xlate_dev_mem_ptr() now returns a new buffer with a
copy of the requested absolute memory. In case the buffer was allocated,
the unxlate_dev_mem_ptr() function frees it after /dev/mem code has
called copy_to_user().
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2012-05-09 16:27:36 +02:00
{
void * bounce = ( void * ) addr ;
unsigned long size ;
get_online_cpus ( ) ;
preempt_disable ( ) ;
if ( is_swapped ( addr ) ) {
size = PAGE_SIZE - ( addr & ~ PAGE_MASK ) ;
bounce = ( void * ) __get_free_page ( GFP_ATOMIC ) ;
if ( bounce )
2012-05-24 14:35:16 +02:00
memcpy_absolute ( bounce , ( void * ) addr , size ) ;
s390: allow absolute memory access for /dev/mem
Currently dev/mem for s390 provides only real memory access. This means
that the CPU prefix pages are swapped. The prefix swap for real memory
works as follows:
Each CPU owns a prefix register that points to a page aligned memory
location "P". If this CPU accesses the address range [0,0x1fff], it is
translated by the hardware to [P,P+0x1fff]. Accordingly if this CPU
accesses the address range [P,P+0x1fff], it is translated by the hardware
to [0,0x1fff]. Therefore, if [P,P+0x1fff] or [0,0x1fff] is read from
the current /dev/mem device, the incorrectly swapped memory content is
returned.
With this patch the /dev/mem architecture code is modified to provide
absolute memory access. This is done via the arch specific functions
xlate_dev_mem_ptr() and unxlate_dev_mem_ptr(). For swapped pages on
s390 the function xlate_dev_mem_ptr() now returns a new buffer with a
copy of the requested absolute memory. In case the buffer was allocated,
the unxlate_dev_mem_ptr() function frees it after /dev/mem code has
called copy_to_user().
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2012-05-09 16:27:36 +02:00
}
preempt_enable ( ) ;
put_online_cpus ( ) ;
return bounce ;
}
/*
* Free converted buffer for / dev / mem access ( if necessary )
*/
2014-07-28 17:20:33 +02:00
void unxlate_dev_mem_ptr ( phys_addr_t addr , void * buf )
s390: allow absolute memory access for /dev/mem
Currently dev/mem for s390 provides only real memory access. This means
that the CPU prefix pages are swapped. The prefix swap for real memory
works as follows:
Each CPU owns a prefix register that points to a page aligned memory
location "P". If this CPU accesses the address range [0,0x1fff], it is
translated by the hardware to [P,P+0x1fff]. Accordingly if this CPU
accesses the address range [P,P+0x1fff], it is translated by the hardware
to [0,0x1fff]. Therefore, if [P,P+0x1fff] or [0,0x1fff] is read from
the current /dev/mem device, the incorrectly swapped memory content is
returned.
With this patch the /dev/mem architecture code is modified to provide
absolute memory access. This is done via the arch specific functions
xlate_dev_mem_ptr() and unxlate_dev_mem_ptr(). For swapped pages on
s390 the function xlate_dev_mem_ptr() now returns a new buffer with a
copy of the requested absolute memory. In case the buffer was allocated,
the unxlate_dev_mem_ptr() function frees it after /dev/mem code has
called copy_to_user().
Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
2012-05-09 16:27:36 +02:00
{
if ( ( void * ) addr ! = buf )
free_page ( ( unsigned long ) buf ) ;
}