2005-04-16 15:20:36 -07:00
/*
* PARISC specific syscalls
*
* Copyright ( C ) 1999 - 2003 Matthew Wilcox < willy at parisc - linux . org >
* Copyright ( C ) 2000 - 2003 Paul Bame < bame at parisc - linux . org >
* Copyright ( C ) 2001 Thomas Bogendoerfer < tsbogend at parisc - linux . org >
2014-01-31 22:19:52 +01:00
* Copyright ( C ) 1999 - 2014 Helge Deller < deller @ gmx . de >
2005-04-16 15:20:36 -07:00
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <asm/uaccess.h>
2014-01-31 22:19:52 +01:00
# include <asm/elf.h>
2005-04-16 15:20:36 -07:00
# include <linux/file.h>
# include <linux/fs.h>
# include <linux/linkage.h>
# include <linux/mm.h>
# include <linux/mman.h>
# include <linux/shm.h>
# include <linux/syscalls.h>
2006-08-27 11:12:13 -04:00
# include <linux/utsname.h>
# include <linux/personality.h>
2014-01-31 22:19:52 +01:00
# include <linux/random.h>
2005-04-16 15:20:36 -07:00
2014-01-31 22:19:52 +01:00
/* we construct an artificial offset for the mapping based on the physical
* address of the kernel mapping variable */
# define GET_LAST_MMAP(filp) \
( filp ? ( ( unsigned long ) filp - > f_mapping ) > > 8 : 0UL )
# define SET_LAST_MMAP(filp, val) \
{ /* nothing */ }
static int get_offset ( unsigned int last_mmap )
2005-04-16 15:20:36 -07:00
{
2014-01-31 22:19:52 +01:00
return ( last_mmap & ( SHMLBA - 1 ) ) > > PAGE_SHIFT ;
}
2005-04-16 15:20:36 -07:00
2014-01-31 22:19:52 +01:00
static unsigned long shared_align_offset ( unsigned int last_mmap ,
unsigned long pgoff )
{
return ( get_offset ( last_mmap ) + pgoff ) < < PAGE_SHIFT ;
2005-04-16 15:20:36 -07:00
}
2014-01-31 22:19:52 +01:00
static inline unsigned long COLOR_ALIGN ( unsigned long addr ,
unsigned int last_mmap , unsigned long pgoff )
2005-04-16 15:20:36 -07:00
{
2014-01-31 22:19:52 +01:00
unsigned long base = ( addr + SHMLBA - 1 ) & ~ ( SHMLBA - 1 ) ;
unsigned long off = ( SHMLBA - 1 ) &
( shared_align_offset ( last_mmap , pgoff ) < < PAGE_SHIFT ) ;
return base + off ;
2005-04-16 15:20:36 -07:00
}
2014-01-31 22:19:52 +01:00
/*
* Top of mmap area ( just below the process stack ) .
*/
static unsigned long mmap_upper_limit ( void )
parisc: fix mmap(MAP_FIXED|MAP_SHARED) to already mmapped address
locale-gen on Debian showed a strange problem on parisc:
mmap2(NULL, 536870912, PROT_NONE, MAP_SHARED, 3, 0) = 0x42a54000
mmap2(0x42a54000, 103860, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, 3, 0) = -1 EINVAL (Invalid argument)
Basically it was just trying to re-mmap() a file at the same address
which it was given by a previous mmap() call. But this remapping failed
with EINVAL.
The problem is, that when MAP_FIXED and MAP_SHARED flags were used, we didn't
included the mapping-based offset when we verified the alignment of the given
fixed address against the offset which we calculated it in the previous call.
Signed-off-by: Helge Deller <deller@gmx.de>
Cc: <stable@vger.kernel.org> # 3.10+
2013-11-20 23:07:42 +01:00
{
2014-01-31 22:19:52 +01:00
unsigned long stack_base ;
parisc: fix mmap(MAP_FIXED|MAP_SHARED) to already mmapped address
locale-gen on Debian showed a strange problem on parisc:
mmap2(NULL, 536870912, PROT_NONE, MAP_SHARED, 3, 0) = 0x42a54000
mmap2(0x42a54000, 103860, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, 3, 0) = -1 EINVAL (Invalid argument)
Basically it was just trying to re-mmap() a file at the same address
which it was given by a previous mmap() call. But this remapping failed
with EINVAL.
The problem is, that when MAP_FIXED and MAP_SHARED flags were used, we didn't
included the mapping-based offset when we verified the alignment of the given
fixed address against the offset which we calculated it in the previous call.
Signed-off-by: Helge Deller <deller@gmx.de>
Cc: <stable@vger.kernel.org> # 3.10+
2013-11-20 23:07:42 +01:00
2014-01-31 22:19:52 +01:00
/* Limit stack size to 1GB - see setup_arg_pages() in fs/exec.c */
stack_base = rlimit_max ( RLIMIT_STACK ) ;
if ( stack_base > ( 1 < < 30 ) )
stack_base = 1 < < 30 ;
return PAGE_ALIGN ( STACK_TOP - stack_base ) ;
parisc: fix mmap(MAP_FIXED|MAP_SHARED) to already mmapped address
locale-gen on Debian showed a strange problem on parisc:
mmap2(NULL, 536870912, PROT_NONE, MAP_SHARED, 3, 0) = 0x42a54000
mmap2(0x42a54000, 103860, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, 3, 0) = -1 EINVAL (Invalid argument)
Basically it was just trying to re-mmap() a file at the same address
which it was given by a previous mmap() call. But this remapping failed
with EINVAL.
The problem is, that when MAP_FIXED and MAP_SHARED flags were used, we didn't
included the mapping-based offset when we verified the alignment of the given
fixed address against the offset which we calculated it in the previous call.
Signed-off-by: Helge Deller <deller@gmx.de>
Cc: <stable@vger.kernel.org> # 3.10+
2013-11-20 23:07:42 +01:00
}
2014-01-31 22:19:52 +01:00
unsigned long arch_get_unmapped_area ( struct file * filp , unsigned long addr ,
unsigned long len , unsigned long pgoff , unsigned long flags )
2005-04-16 15:20:36 -07:00
{
2014-01-31 22:19:52 +01:00
struct mm_struct * mm = current - > mm ;
struct vm_area_struct * vma ;
unsigned long task_size = TASK_SIZE ;
int do_color_align , last_mmap ;
2013-02-27 17:02:40 -08:00
struct vm_unmapped_area_info info ;
2005-04-16 15:20:36 -07:00
2014-01-31 22:19:52 +01:00
if ( len > task_size )
return - ENOMEM ;
do_color_align = 0 ;
if ( filp | | ( flags & MAP_SHARED ) )
do_color_align = 1 ;
last_mmap = GET_LAST_MMAP ( filp ) ;
if ( flags & MAP_FIXED ) {
if ( ( flags & MAP_SHARED ) & & last_mmap & &
( addr - shared_align_offset ( last_mmap , pgoff ) )
& ( SHMLBA - 1 ) )
return - EINVAL ;
goto found_addr ;
}
if ( addr ) {
if ( do_color_align & & last_mmap )
addr = COLOR_ALIGN ( addr , last_mmap , pgoff ) ;
else
addr = PAGE_ALIGN ( addr ) ;
vma = find_vma ( mm , addr ) ;
if ( task_size - len > = addr & &
( ! vma | | addr + len < = vma - > vm_start ) )
goto found_addr ;
}
2013-02-27 17:02:40 -08:00
info . flags = 0 ;
info . length = len ;
2014-01-31 22:19:52 +01:00
info . low_limit = mm - > mmap_legacy_base ;
info . high_limit = mmap_upper_limit ( ) ;
info . align_mask = last_mmap ? ( PAGE_MASK & ( SHMLBA - 1 ) ) : 0 ;
info . align_offset = shared_align_offset ( last_mmap , pgoff ) ;
addr = vm_unmapped_area ( & info ) ;
found_addr :
if ( do_color_align & & ! last_mmap & & ! ( addr & ~ PAGE_MASK ) )
SET_LAST_MMAP ( filp , addr - ( pgoff < < PAGE_SHIFT ) ) ;
return addr ;
2005-04-16 15:20:36 -07:00
}
2014-01-31 22:19:52 +01:00
unsigned long
arch_get_unmapped_area_topdown ( struct file * filp , const unsigned long addr0 ,
const unsigned long len , const unsigned long pgoff ,
const unsigned long flags )
2005-04-16 15:20:36 -07:00
{
2014-01-31 22:19:52 +01:00
struct vm_area_struct * vma ;
struct mm_struct * mm = current - > mm ;
unsigned long addr = addr0 ;
int do_color_align , last_mmap ;
struct vm_unmapped_area_info info ;
# ifdef CONFIG_64BIT
/* This should only ever run for 32-bit processes. */
BUG_ON ( ! test_thread_flag ( TIF_32BIT ) ) ;
# endif
/* requested length too big for entire address space */
2005-04-16 15:20:36 -07:00
if ( len > TASK_SIZE )
return - ENOMEM ;
2014-01-31 22:19:52 +01:00
do_color_align = 0 ;
if ( filp | | ( flags & MAP_SHARED ) )
do_color_align = 1 ;
last_mmap = GET_LAST_MMAP ( filp ) ;
2013-02-02 23:44:59 +00:00
if ( flags & MAP_FIXED ) {
2014-01-31 22:19:52 +01:00
if ( ( flags & MAP_SHARED ) & & last_mmap & &
( addr - shared_align_offset ( last_mmap , pgoff ) )
& ( SHMLBA - 1 ) )
2013-02-02 23:44:59 +00:00
return - EINVAL ;
2014-01-31 22:19:52 +01:00
goto found_addr ;
2013-02-02 23:44:59 +00:00
}
2005-04-16 15:20:36 -07:00
2014-01-31 22:19:52 +01:00
/* requesting a specific address */
if ( addr ) {
if ( do_color_align & & last_mmap )
addr = COLOR_ALIGN ( addr , last_mmap , pgoff ) ;
else
addr = PAGE_ALIGN ( addr ) ;
vma = find_vma ( mm , addr ) ;
if ( TASK_SIZE - len > = addr & &
( ! vma | | addr + len < = vma - > vm_start ) )
goto found_addr ;
}
info . flags = VM_UNMAPPED_AREA_TOPDOWN ;
info . length = len ;
info . low_limit = PAGE_SIZE ;
info . high_limit = mm - > mmap_base ;
info . align_mask = last_mmap ? ( PAGE_MASK & ( SHMLBA - 1 ) ) : 0 ;
info . align_offset = shared_align_offset ( last_mmap , pgoff ) ;
addr = vm_unmapped_area ( & info ) ;
if ( ! ( addr & ~ PAGE_MASK ) )
goto found_addr ;
VM_BUG_ON ( addr ! = - ENOMEM ) ;
/*
* A failed mmap ( ) very likely causes application failure ,
* so fall back to the bottom - up function here . This scenario
* can happen with large stack limits and large mmap ( )
* allocations .
*/
return arch_get_unmapped_area ( filp , addr0 , len , pgoff , flags ) ;
found_addr :
if ( do_color_align & & ! last_mmap & & ! ( addr & ~ PAGE_MASK ) )
SET_LAST_MMAP ( filp , addr - ( pgoff < < PAGE_SHIFT ) ) ;
parisc: fix mmap(MAP_FIXED|MAP_SHARED) to already mmapped address
locale-gen on Debian showed a strange problem on parisc:
mmap2(NULL, 536870912, PROT_NONE, MAP_SHARED, 3, 0) = 0x42a54000
mmap2(0x42a54000, 103860, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_FIXED, 3, 0) = -1 EINVAL (Invalid argument)
Basically it was just trying to re-mmap() a file at the same address
which it was given by a previous mmap() call. But this remapping failed
with EINVAL.
The problem is, that when MAP_FIXED and MAP_SHARED flags were used, we didn't
included the mapping-based offset when we verified the alignment of the given
fixed address against the offset which we calculated it in the previous call.
Signed-off-by: Helge Deller <deller@gmx.de>
Cc: <stable@vger.kernel.org> # 3.10+
2013-11-20 23:07:42 +01:00
2005-04-16 15:20:36 -07:00
return addr ;
}
2014-01-31 22:19:52 +01:00
static int mmap_is_legacy ( void )
{
if ( current - > personality & ADDR_COMPAT_LAYOUT )
return 1 ;
/* parisc stack always grows up - so a unlimited stack should
* not be an indicator to use the legacy memory layout .
* if ( rlimit ( RLIMIT_STACK ) = = RLIM_INFINITY )
* return 1 ;
*/
return sysctl_legacy_va_layout ;
}
static unsigned long mmap_rnd ( void )
{
unsigned long rnd = 0 ;
/*
* 8 bits of randomness in 32 bit mmaps , 20 address space bits
* 28 bits of randomness in 64 bit mmaps , 40 address space bits
*/
if ( current - > flags & PF_RANDOMIZE ) {
if ( is_32bit_task ( ) )
rnd = get_random_int ( ) % ( 1 < < 8 ) ;
else
rnd = get_random_int ( ) % ( 1 < < 28 ) ;
}
return rnd < < PAGE_SHIFT ;
}
static unsigned long mmap_legacy_base ( void )
{
return TASK_UNMAPPED_BASE + mmap_rnd ( ) ;
}
/*
* This function , called very early during the creation of a new
* process VM image , sets up which VM layout function to use :
*/
void arch_pick_mmap_layout ( struct mm_struct * mm )
{
mm - > mmap_legacy_base = mmap_legacy_base ( ) ;
mm - > mmap_base = mmap_upper_limit ( ) ;
if ( mmap_is_legacy ( ) ) {
mm - > mmap_base = mm - > mmap_legacy_base ;
mm - > get_unmapped_area = arch_get_unmapped_area ;
} else {
mm - > get_unmapped_area = arch_get_unmapped_area_topdown ;
}
}
2005-04-16 15:20:36 -07:00
asmlinkage unsigned long sys_mmap2 ( unsigned long addr , unsigned long len ,
unsigned long prot , unsigned long flags , unsigned long fd ,
unsigned long pgoff )
{
/* Make sure the shift for mmap2 is constant (12), no matter what PAGE_SIZE
we have . */
2009-11-30 17:37:04 -05:00
return sys_mmap_pgoff ( addr , len , prot , flags , fd ,
pgoff > > ( PAGE_SHIFT - 12 ) ) ;
2005-04-16 15:20:36 -07:00
}
asmlinkage unsigned long sys_mmap ( unsigned long addr , unsigned long len ,
unsigned long prot , unsigned long flags , unsigned long fd ,
unsigned long offset )
{
if ( ! ( offset & ~ PAGE_MASK ) ) {
2009-11-30 17:37:04 -05:00
return sys_mmap_pgoff ( addr , len , prot , flags , fd ,
offset > > PAGE_SHIFT ) ;
2005-04-16 15:20:36 -07:00
} else {
return - EINVAL ;
}
}
/* Fucking broken ABI */
# ifdef CONFIG_64BIT
asmlinkage long parisc_truncate64 ( const char __user * path ,
unsigned int high , unsigned int low )
{
return sys_truncate ( path , ( long ) high < < 32 | low ) ;
}
asmlinkage long parisc_ftruncate64 ( unsigned int fd ,
unsigned int high , unsigned int low )
{
return sys_ftruncate ( fd , ( long ) high < < 32 | low ) ;
}
/* stubs for the benefit of the syscall_table since truncate64 and truncate
* are identical on LP64 */
asmlinkage long sys_truncate64 ( const char __user * path , unsigned long length )
{
return sys_truncate ( path , length ) ;
}
asmlinkage long sys_ftruncate64 ( unsigned int fd , unsigned long length )
{
return sys_ftruncate ( fd , length ) ;
}
asmlinkage long sys_fcntl64 ( unsigned int fd , unsigned int cmd , unsigned long arg )
{
return sys_fcntl ( fd , cmd , arg ) ;
}
# else
asmlinkage long parisc_truncate64 ( const char __user * path ,
unsigned int high , unsigned int low )
{
return sys_truncate64 ( path , ( loff_t ) high < < 32 | low ) ;
}
asmlinkage long parisc_ftruncate64 ( unsigned int fd ,
unsigned int high , unsigned int low )
{
return sys_ftruncate64 ( fd , ( loff_t ) high < < 32 | low ) ;
}
# endif
asmlinkage ssize_t parisc_pread64 ( unsigned int fd , char __user * buf , size_t count ,
unsigned int high , unsigned int low )
{
return sys_pread64 ( fd , buf , count , ( loff_t ) high < < 32 | low ) ;
}
asmlinkage ssize_t parisc_pwrite64 ( unsigned int fd , const char __user * buf ,
size_t count , unsigned int high , unsigned int low )
{
return sys_pwrite64 ( fd , buf , count , ( loff_t ) high < < 32 | low ) ;
}
asmlinkage ssize_t parisc_readahead ( int fd , unsigned int high , unsigned int low ,
size_t count )
{
return sys_readahead ( fd , ( loff_t ) high < < 32 | low , count ) ;
}
asmlinkage long parisc_fadvise64_64 ( int fd ,
unsigned int high_off , unsigned int low_off ,
unsigned int high_len , unsigned int low_len , int advice )
{
return sys_fadvise64_64 ( fd , ( loff_t ) high_off < < 32 | low_off ,
( loff_t ) high_len < < 32 | low_len , advice ) ;
}
2006-04-20 04:44:07 +00:00
asmlinkage long parisc_sync_file_range ( int fd ,
u32 hi_off , u32 lo_off , u32 hi_nbytes , u32 lo_nbytes ,
unsigned int flags )
{
return sys_sync_file_range ( fd , ( loff_t ) hi_off < < 32 | lo_off ,
( loff_t ) hi_nbytes < < 32 | lo_nbytes , flags ) ;
}
2013-02-19 21:23:59 +01:00
asmlinkage long parisc_fallocate ( int fd , int mode , u32 offhi , u32 offlo ,
u32 lenhi , u32 lenlo )
{
return sys_fallocate ( fd , mode , ( ( u64 ) offhi < < 32 ) | offlo ,
( ( u64 ) lenhi < < 32 ) | lenlo ) ;
}
2005-04-16 15:20:36 -07:00
asmlinkage unsigned long sys_alloc_hugepages ( int key , unsigned long addr , unsigned long len , int prot , int flag )
{
return - ENOMEM ;
}
asmlinkage int sys_free_hugepages ( unsigned long addr )
{
return - EINVAL ;
}
2006-08-27 11:12:13 -04:00
long parisc_personality ( unsigned long personality )
{
long err ;
if ( personality ( current - > personality ) = = PER_LINUX32
2012-08-02 15:33:59 +02:00
& & personality ( personality ) = = PER_LINUX )
personality = ( personality & ~ PER_MASK ) | PER_LINUX32 ;
2006-08-27 11:12:13 -04:00
err = sys_personality ( personality ) ;
2012-08-02 15:33:59 +02:00
if ( personality ( err ) = = PER_LINUX32 )
err = ( err & ~ PER_MASK ) | PER_LINUX ;
2006-08-27 11:12:13 -04:00
return err ;
}