2019-08-25 10:49:17 +01:00
// SPDX-License-Identifier: GPL-2.0
2005-04-16 15:20:36 -07:00
/*
2007-10-16 01:27:08 -07:00
* Copyright ( C ) 2002 - 2007 Jeff Dike ( jdike @ { addtoit , linux . intel } . com )
2005-04-16 15:20:36 -07:00
*/
uml: header untangling
Untangle UML headers somewhat and add some includes where they were
needed explicitly, but gotten accidentally via some other header.
arch/um/include/um_uaccess.h loses asm/fixmap.h because it uses no
fixmap stuff and gains elf.h, because it needs FIXADDR_USER_*, and
archsetjmp.h, because it needs jmp_buf.
pmd_alloc_one is uninlined because it needs mm_struct, and that's
inconvenient to provide in asm-um/pgtable-3level.h.
elf_core_copy_fpregs is also uninlined from elf-i386.h and
elf-x86_64.h, which duplicated the code anyway, to
arch/um/kernel/process.c, so that the reference to current_thread
doesn't pull sched.h or anything related into asm/elf.h.
arch/um/sys-i386/ldt.c, arch/um/kernel/tlb.c and
arch/um/kernel/skas/uaccess.c got sched.h because they dereference
task_structs. Its includes of linux and asm headers got turned from
"" to <>.
arch/um/sys-i386/bug.c gets asm/errno.h because it needs errno
constants.
asm/elf-i386 gets asm/user.h because it needs user_regs_struct.
asm/fixmap.h gets page.h because it needs PAGE_SIZE and PAGE_MASK and
system.h for BUG_ON.
asm/pgtable doesn't need sched.h.
asm/processor-generic.h defined mm_segment_t, but didn't use it. So,
that definition is moved to uaccess.h, which defines a bunch of
mm_segment_t-related stuff. thread_info.h uses mm_segment_t, and
includes uaccess.h, which causes a recursion. So, the definition is
placed above the include of thread_info. in uaccess.h. thread_info.h
also gets page.h because it needs PAGE_SIZE.
ObCheckpatchViolationJustification - I'm not adding a typedef; I'm
moving mm_segment_t from one place to another.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-04 22:30:53 -08:00
# include <linux/err.h>
# include <linux/highmem.h>
# include <linux/mm.h>
2011-08-18 20:14:10 +01:00
# include <linux/module.h>
uml: header untangling
Untangle UML headers somewhat and add some includes where they were
needed explicitly, but gotten accidentally via some other header.
arch/um/include/um_uaccess.h loses asm/fixmap.h because it uses no
fixmap stuff and gains elf.h, because it needs FIXADDR_USER_*, and
archsetjmp.h, because it needs jmp_buf.
pmd_alloc_one is uninlined because it needs mm_struct, and that's
inconvenient to provide in asm-um/pgtable-3level.h.
elf_core_copy_fpregs is also uninlined from elf-i386.h and
elf-x86_64.h, which duplicated the code anyway, to
arch/um/kernel/process.c, so that the reference to current_thread
doesn't pull sched.h or anything related into asm/elf.h.
arch/um/sys-i386/ldt.c, arch/um/kernel/tlb.c and
arch/um/kernel/skas/uaccess.c got sched.h because they dereference
task_structs. Its includes of linux and asm headers got turned from
"" to <>.
arch/um/sys-i386/bug.c gets asm/errno.h because it needs errno
constants.
asm/elf-i386 gets asm/user.h because it needs user_regs_struct.
asm/fixmap.h gets page.h because it needs PAGE_SIZE and PAGE_MASK and
system.h for BUG_ON.
asm/pgtable doesn't need sched.h.
asm/processor-generic.h defined mm_segment_t, but didn't use it. So,
that definition is moved to uaccess.h, which defines a bunch of
mm_segment_t-related stuff. thread_info.h uses mm_segment_t, and
includes uaccess.h, which causes a recursion. So, the definition is
placed above the include of thread_info. in uaccess.h. thread_info.h
also gets page.h because it needs PAGE_SIZE.
ObCheckpatchViolationJustification - I'm not adding a typedef; I'm
moving mm_segment_t from one place to another.
Signed-off-by: Jeff Dike <jdike@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-02-04 22:30:53 -08:00
# include <linux/sched.h>
# include <asm/current.h>
# include <asm/page.h>
# include <asm/pgtable.h>
2012-10-08 03:27:32 +01:00
# include <kern_util.h>
# include <os.h>
2005-04-16 15:20:36 -07:00
2008-02-04 22:30:55 -08:00
pte_t * virt_to_pte ( struct mm_struct * mm , unsigned long addr )
2008-02-04 22:30:52 -08:00
{
pgd_t * pgd ;
2019-12-04 16:54:28 -08:00
p4d_t * p4d ;
2008-02-04 22:30:52 -08:00
pud_t * pud ;
pmd_t * pmd ;
2008-02-04 22:30:55 -08:00
if ( mm = = NULL )
return NULL ;
pgd = pgd_offset ( mm , addr ) ;
2008-02-04 22:30:52 -08:00
if ( ! pgd_present ( * pgd ) )
2008-02-04 22:30:55 -08:00
return NULL ;
2008-02-04 22:30:52 -08:00
2019-12-04 16:54:28 -08:00
p4d = p4d_offset ( pgd , addr ) ;
if ( ! p4d_present ( * p4d ) )
return NULL ;
pud = pud_offset ( p4d , addr ) ;
2008-02-04 22:30:52 -08:00
if ( ! pud_present ( * pud ) )
2008-02-04 22:30:55 -08:00
return NULL ;
2008-02-04 22:30:52 -08:00
pmd = pmd_offset ( pud , addr ) ;
if ( ! pmd_present ( * pmd ) )
2008-02-04 22:30:55 -08:00
return NULL ;
2008-02-04 22:30:52 -08:00
2008-02-04 22:30:55 -08:00
return pte_offset_kernel ( pmd , addr ) ;
2008-02-04 22:30:52 -08:00
}
2005-04-16 15:20:36 -07:00
2008-02-04 22:30:55 -08:00
static pte_t * maybe_map ( unsigned long virt , int is_write )
2005-04-16 15:20:36 -07:00
{
2008-02-04 22:30:55 -08:00
pte_t * pte = virt_to_pte ( current - > mm , virt ) ;
int err , dummy_code ;
2005-04-16 15:20:36 -07:00
2008-02-04 22:30:55 -08:00
if ( ( pte = = NULL ) | | ! pte_present ( * pte ) | |
( is_write & & ! pte_write ( * pte ) ) ) {
2005-04-16 15:20:36 -07:00
err = handle_page_fault ( virt , 0 , is_write , 1 , & dummy_code ) ;
2007-10-16 01:27:08 -07:00
if ( err )
2008-02-04 22:30:55 -08:00
return NULL ;
pte = virt_to_pte ( current - > mm , virt ) ;
2005-04-16 15:20:36 -07:00
}
2008-02-04 22:30:55 -08:00
if ( ! pte_present ( * pte ) )
pte = NULL ;
2005-05-06 21:30:55 -07:00
2008-02-04 22:30:55 -08:00
return pte ;
2005-04-16 15:20:36 -07:00
}
2006-07-01 04:36:19 -07:00
static int do_op_one_page ( unsigned long addr , int len , int is_write ,
2005-04-16 15:20:36 -07:00
int ( * op ) ( unsigned long addr , int len , void * arg ) , void * arg )
{
struct page * page ;
2008-02-04 22:30:55 -08:00
pte_t * pte ;
2018-11-22 14:45:13 +00:00
int n ;
2005-04-16 15:20:36 -07:00
2008-02-04 22:30:55 -08:00
pte = maybe_map ( addr , is_write ) ;
if ( pte = = NULL )
2007-10-16 01:27:08 -07:00
return - 1 ;
2005-04-16 15:20:36 -07:00
2008-02-04 22:30:55 -08:00
page = pte_page ( * pte ) ;
2018-11-22 14:45:13 +00:00
# ifdef CONFIG_64BIT
pagefault_disable ( ) ;
addr = ( unsigned long ) page_address ( page ) +
( addr & ~ PAGE_MASK ) ;
# else
2011-11-25 23:14:17 +08:00
addr = ( unsigned long ) kmap_atomic ( page ) +
2007-10-16 01:27:08 -07:00
( addr & ~ PAGE_MASK ) ;
2018-11-22 14:45:13 +00:00
# endif
n = ( * op ) ( addr , len , arg ) ;
2006-07-01 04:36:19 -07:00
2018-11-22 14:45:13 +00:00
# ifdef CONFIG_64BIT
pagefault_enable ( ) ;
# else
2011-11-25 23:14:17 +08:00
kunmap_atomic ( ( void * ) addr ) ;
2018-11-22 14:45:13 +00:00
# endif
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:08 -07:00
return n ;
2005-04-16 15:20:36 -07:00
}
2015-05-12 00:17:28 +02:00
static long buffer_op ( unsigned long addr , int len , int is_write ,
int ( * op ) ( unsigned long , int , void * ) , void * arg )
2005-04-16 15:20:36 -07:00
{
2015-05-12 00:17:28 +02:00
long size , remain , n ;
2008-02-04 22:31:07 -08:00
2005-04-16 15:20:36 -07:00
size = min ( PAGE_ALIGN ( addr ) - addr , ( unsigned long ) len ) ;
remain = len ;
2006-07-01 04:36:19 -07:00
n = do_op_one_page ( addr , size , is_write , op , arg ) ;
2007-10-16 01:27:08 -07:00
if ( n ! = 0 ) {
2008-02-04 22:31:07 -08:00
remain = ( n < 0 ? remain : 0 ) ;
2005-04-16 15:20:36 -07:00
goto out ;
}
addr + = size ;
remain - = size ;
2008-02-04 22:31:07 -08:00
if ( remain = = 0 )
2005-04-16 15:20:36 -07:00
goto out ;
2008-02-04 22:31:07 -08:00
while ( addr < ( ( addr + remain ) & PAGE_MASK ) ) {
2006-07-01 04:36:19 -07:00
n = do_op_one_page ( addr , PAGE_SIZE , is_write , op , arg ) ;
2007-10-16 01:27:08 -07:00
if ( n ! = 0 ) {
2008-02-04 22:31:07 -08:00
remain = ( n < 0 ? remain : 0 ) ;
2005-04-16 15:20:36 -07:00
goto out ;
}
addr + = PAGE_SIZE ;
remain - = PAGE_SIZE ;
}
2008-02-04 22:31:07 -08:00
if ( remain = = 0 )
2005-04-16 15:20:36 -07:00
goto out ;
2006-07-01 04:36:19 -07:00
n = do_op_one_page ( addr , remain , is_write , op , arg ) ;
2008-02-04 22:31:07 -08:00
if ( n ! = 0 ) {
remain = ( n < 0 ? remain : 0 ) ;
goto out ;
}
2005-04-16 15:20:36 -07:00
2008-02-04 22:31:07 -08:00
return 0 ;
out :
return remain ;
2005-04-16 15:20:36 -07:00
}
static int copy_chunk_from_user ( unsigned long from , int len , void * arg )
{
unsigned long * to_ptr = arg , to = * to_ptr ;
memcpy ( ( void * ) to , ( void * ) from , len ) ;
* to_ptr + = len ;
2007-10-16 01:27:08 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2017-03-28 01:33:09 -04:00
unsigned long raw_copy_from_user ( void * to , const void __user * from , unsigned long n )
2005-04-16 15:20:36 -07:00
{
2017-03-20 21:08:07 -04:00
if ( uaccess_kernel ( ) ) {
2005-04-16 15:20:36 -07:00
memcpy ( to , ( __force void * ) from , n ) ;
2007-10-16 01:27:08 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2015-05-12 00:17:28 +02:00
return buffer_op ( ( unsigned long ) from , n , 0 , copy_chunk_from_user , & to ) ;
2005-04-16 15:20:36 -07:00
}
2017-03-28 01:33:09 -04:00
EXPORT_SYMBOL ( raw_copy_from_user ) ;
2005-04-16 15:20:36 -07:00
static int copy_chunk_to_user ( unsigned long to , int len , void * arg )
{
unsigned long * from_ptr = arg , from = * from_ptr ;
memcpy ( ( void * ) to , ( void * ) from , len ) ;
* from_ptr + = len ;
2007-10-16 01:27:08 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2017-03-28 01:33:09 -04:00
unsigned long raw_copy_to_user ( void __user * to , const void * from , unsigned long n )
2005-04-16 15:20:36 -07:00
{
2017-03-20 21:08:07 -04:00
if ( uaccess_kernel ( ) ) {
2007-10-16 01:27:08 -07:00
memcpy ( ( __force void * ) to , from , n ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2015-05-12 00:17:28 +02:00
return buffer_op ( ( unsigned long ) to , n , 1 , copy_chunk_to_user , & from ) ;
2005-04-16 15:20:36 -07:00
}
2017-03-28 01:33:09 -04:00
EXPORT_SYMBOL ( raw_copy_to_user ) ;
2005-04-16 15:20:36 -07:00
static int strncpy_chunk_from_user ( unsigned long from , int len , void * arg )
{
char * * to_ptr = arg , * to = * to_ptr ;
int n ;
strncpy ( to , ( void * ) from , len ) ;
n = strnlen ( to , len ) ;
* to_ptr + = n ;
2007-10-16 01:27:08 -07:00
if ( n < len )
return 1 ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2015-05-12 00:17:28 +02:00
long __strncpy_from_user ( char * dst , const char __user * src , long count )
2005-04-16 15:20:36 -07:00
{
2015-05-12 00:17:28 +02:00
long n ;
2005-04-16 15:20:36 -07:00
char * ptr = dst ;
2017-03-20 21:08:07 -04:00
if ( uaccess_kernel ( ) ) {
2007-10-16 01:27:08 -07:00
strncpy ( dst , ( __force void * ) src , count ) ;
return strnlen ( dst , count ) ;
2005-04-16 15:20:36 -07:00
}
n = buffer_op ( ( unsigned long ) src , count , 0 , strncpy_chunk_from_user ,
& ptr ) ;
2007-10-16 01:27:08 -07:00
if ( n ! = 0 )
return - EFAULT ;
return strnlen ( dst , count ) ;
2005-04-16 15:20:36 -07:00
}
2015-05-12 00:17:28 +02:00
EXPORT_SYMBOL ( __strncpy_from_user ) ;
2005-04-16 15:20:36 -07:00
static int clear_chunk ( unsigned long addr , int len , void * unused )
{
memset ( ( void * ) addr , 0 , len ) ;
2007-10-16 01:27:08 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2015-05-12 00:17:28 +02:00
unsigned long __clear_user ( void __user * mem , unsigned long len )
2005-04-16 15:20:36 -07:00
{
2017-03-20 21:08:07 -04:00
if ( uaccess_kernel ( ) ) {
2005-04-16 15:20:36 -07:00
memset ( ( __force void * ) mem , 0 , len ) ;
2007-10-16 01:27:08 -07:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2015-05-12 00:17:28 +02:00
return buffer_op ( ( unsigned long ) mem , len , 1 , clear_chunk , NULL ) ;
2005-04-16 15:20:36 -07:00
}
2015-05-12 00:17:28 +02:00
EXPORT_SYMBOL ( __clear_user ) ;
2005-04-16 15:20:36 -07:00
static int strnlen_chunk ( unsigned long str , int len , void * arg )
{
int * len_ptr = arg , n ;
n = strnlen ( ( void * ) str , len ) ;
* len_ptr + = n ;
2007-10-16 01:27:08 -07:00
if ( n < len )
return 1 ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2015-05-12 00:17:28 +02:00
long __strnlen_user ( const void __user * str , long len )
2005-04-16 15:20:36 -07:00
{
int count = 0 , n ;
2017-03-20 21:08:07 -04:00
if ( uaccess_kernel ( ) )
2007-10-16 01:27:08 -07:00
return strnlen ( ( __force char * ) str , len ) + 1 ;
2005-04-16 15:20:36 -07:00
n = buffer_op ( ( unsigned long ) str , len , 0 , strnlen_chunk , & count ) ;
2007-10-16 01:27:08 -07:00
if ( n = = 0 )
return count + 1 ;
2013-05-11 15:35:32 +02:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2015-05-12 00:17:28 +02:00
EXPORT_SYMBOL ( __strnlen_user ) ;