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
* Licensed under the GPL
*/
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>
2005-04-16 15:20:36 -07:00
# include "kern_util.h"
2006-01-18 17:42:41 -08:00
# 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 ;
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
pud = pud_offset ( pgd , addr ) ;
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 )
{
2008-02-04 22:31:07 -08:00
jmp_buf buf ;
2005-04-16 15:20:36 -07:00
struct page * page ;
2008-02-04 22:30:55 -08:00
pte_t * pte ;
2008-02-04 22:31:07 -08:00
int n , faulted ;
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 ) ;
2007-10-16 01:27:08 -07:00
addr = ( unsigned long ) kmap_atomic ( page , KM_UML_USERCOPY ) +
( addr & ~ PAGE_MASK ) ;
2006-07-01 04:36:19 -07:00
2008-02-04 22:31:07 -08:00
current - > thread . fault_catcher = & buf ;
faulted = UML_SETJMP ( & buf ) ;
if ( faulted = = 0 )
n = ( * op ) ( addr , len , arg ) ;
else
n = - 1 ;
current - > thread . fault_catcher = NULL ;
2006-07-01 04:36:19 -07:00
2010-06-04 14:14:58 -07:00
kunmap_atomic ( ( void * ) addr , KM_UML_USERCOPY ) ;
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
}
2008-02-04 22:31:07 -08:00
static int 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
{
2008-02-04 22:31:07 -08:00
int size , remain , n ;
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
}
2007-10-16 01:26:56 -07:00
int copy_from_user ( void * to , const void __user * from , int n )
2005-04-16 15:20:36 -07:00
{
2007-10-16 01:27:08 -07:00
if ( segment_eq ( get_fs ( ) , KERNEL_DS ) ) {
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
}
2007-10-16 01:27:08 -07:00
return access_ok ( VERIFY_READ , from , n ) ?
2005-04-16 15:20:36 -07:00
buffer_op ( ( unsigned long ) from , n , 0 , copy_chunk_from_user , & to ) :
2007-10-16 01:27:08 -07:00
n ;
2005-04-16 15:20:36 -07:00
}
2011-08-18 20:14:10 +01:00
EXPORT_SYMBOL ( 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
}
2007-10-16 01:26:56 -07:00
int copy_to_user ( void __user * to , const void * from , int n )
2005-04-16 15:20:36 -07:00
{
2007-10-16 01:27:08 -07:00
if ( segment_eq ( get_fs ( ) , KERNEL_DS ) ) {
memcpy ( ( __force void * ) to , from , n ) ;
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-10-16 01:27:08 -07:00
return access_ok ( VERIFY_WRITE , to , n ) ?
2005-04-16 15:20:36 -07:00
buffer_op ( ( unsigned long ) to , n , 1 , copy_chunk_to_user , & from ) :
2007-10-16 01:27:08 -07:00
n ;
2005-04-16 15:20:36 -07:00
}
2011-08-18 20:14:10 +01:00
EXPORT_SYMBOL ( 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
}
2007-10-16 01:26:56 -07:00
int strncpy_from_user ( char * dst , const char __user * src , int count )
2005-04-16 15:20:36 -07:00
{
int n ;
char * ptr = dst ;
2007-10-16 01:27:08 -07:00
if ( segment_eq ( get_fs ( ) , KERNEL_DS ) ) {
strncpy ( dst , ( __force void * ) src , count ) ;
return strnlen ( dst , count ) ;
2005-04-16 15:20:36 -07:00
}
2007-10-16 01:27:08 -07:00
if ( ! access_ok ( VERIFY_READ , src , 1 ) )
return - EFAULT ;
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
}
2011-08-18 20:14:10 +01: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
}
2007-10-16 01:26:56 -07:00
int __clear_user ( void __user * mem , int len )
2005-04-16 15:20:36 -07:00
{
2007-10-16 01:27:08 -07:00
return buffer_op ( ( unsigned long ) mem , len , 1 , clear_chunk , NULL ) ;
2005-04-16 15:20:36 -07:00
}
2007-10-16 01:26:56 -07:00
int clear_user ( void __user * mem , int len )
2005-04-16 15:20:36 -07:00
{
2007-10-16 01:27:08 -07:00
if ( segment_eq ( get_fs ( ) , KERNEL_DS ) ) {
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
}
2007-10-16 01:27:08 -07:00
return access_ok ( VERIFY_WRITE , mem , len ) ?
buffer_op ( ( unsigned long ) mem , len , 1 , clear_chunk , NULL ) : len ;
2005-04-16 15:20:36 -07:00
}
2011-08-18 20:14:10 +01: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
}
2007-10-16 01:26:56 -07:00
int strnlen_user ( const void __user * str , int len )
2005-04-16 15:20:36 -07:00
{
int count = 0 , n ;
2007-10-16 01:27:08 -07:00
if ( segment_eq ( get_fs ( ) , KERNEL_DS ) )
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 ;
return - EFAULT ;
2005-04-16 15:20:36 -07:00
}
2011-08-18 20:14:10 +01:00
EXPORT_SYMBOL ( strnlen_user ) ;