2012-03-15 21:58:34 +00:00
/*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License , version 2 , as
* published by the Free Software Foundation .
*
* 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 , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* Copyright 2010 Paul Mackerras , IBM Corp . < paulus @ au1 . ibm . com >
* Copyright 2011 David Gibson , IBM Corporation < dwg @ au1 . ibm . com >
*/
# include <linux/types.h>
# include <linux/string.h>
# include <linux/kvm.h>
# include <linux/kvm_host.h>
# include <linux/highmem.h>
# include <linux/gfp.h>
# include <linux/slab.h>
# include <linux/hugetlb.h>
# include <linux/list.h>
# include <linux/anon_inodes.h>
# include <asm/tlbflush.h>
# include <asm/kvm_ppc.h>
# include <asm/kvm_book3s.h>
# include <asm/mmu-hash64.h>
# include <asm/hvcall.h>
# include <asm/synch.h>
# include <asm/ppc-opcode.h>
# include <asm/kvm_host.h>
# include <asm/udbg.h>
# define TCES_PER_PAGE (PAGE_SIZE / sizeof(u64))
static long kvmppc_stt_npages ( unsigned long window_size )
{
return ALIGN ( ( window_size > > SPAPR_TCE_SHIFT )
* sizeof ( u64 ) , PAGE_SIZE ) / PAGE_SIZE ;
}
static void release_spapr_tce_table ( struct kvmppc_spapr_tce_table * stt )
{
struct kvm * kvm = stt - > kvm ;
int i ;
mutex_lock ( & kvm - > lock ) ;
list_del ( & stt - > list ) ;
for ( i = 0 ; i < kvmppc_stt_npages ( stt - > window_size ) ; i + + )
__free_page ( stt - > pages [ i ] ) ;
kfree ( stt ) ;
mutex_unlock ( & kvm - > lock ) ;
kvm_put_kvm ( kvm ) ;
}
static int kvm_spapr_tce_fault ( struct vm_area_struct * vma , struct vm_fault * vmf )
{
struct kvmppc_spapr_tce_table * stt = vma - > vm_file - > private_data ;
struct page * page ;
if ( vmf - > pgoff > = kvmppc_stt_npages ( stt - > window_size ) )
return VM_FAULT_SIGBUS ;
page = stt - > pages [ vmf - > pgoff ] ;
get_page ( page ) ;
vmf - > page = page ;
return 0 ;
}
static const struct vm_operations_struct kvm_spapr_tce_vm_ops = {
. fault = kvm_spapr_tce_fault ,
} ;
static int kvm_spapr_tce_mmap ( struct file * file , struct vm_area_struct * vma )
{
vma - > vm_ops = & kvm_spapr_tce_vm_ops ;
return 0 ;
}
static int kvm_spapr_tce_release ( struct inode * inode , struct file * filp )
{
struct kvmppc_spapr_tce_table * stt = filp - > private_data ;
release_spapr_tce_table ( stt ) ;
return 0 ;
}
2013-04-04 19:09:41 -04:00
static const struct file_operations kvm_spapr_tce_fops = {
2012-03-15 21:58:34 +00:00
. mmap = kvm_spapr_tce_mmap ,
. release = kvm_spapr_tce_release ,
} ;
long kvm_vm_ioctl_create_spapr_tce ( struct kvm * kvm ,
struct kvm_create_spapr_tce * args )
{
struct kvmppc_spapr_tce_table * stt = NULL ;
long npages ;
int ret = - ENOMEM ;
int i ;
/* Check this LIOBN hasn't been previously allocated */
list_for_each_entry ( stt , & kvm - > arch . spapr_tce_tables , list ) {
if ( stt - > liobn = = args - > liobn )
return - EBUSY ;
}
npages = kvmppc_stt_npages ( args - > window_size ) ;
stt = kzalloc ( sizeof ( * stt ) + npages * sizeof ( struct page * ) ,
GFP_KERNEL ) ;
if ( ! stt )
goto fail ;
stt - > liobn = args - > liobn ;
stt - > window_size = args - > window_size ;
stt - > kvm = kvm ;
for ( i = 0 ; i < npages ; i + + ) {
stt - > pages [ i ] = alloc_page ( GFP_KERNEL | __GFP_ZERO ) ;
if ( ! stt - > pages [ i ] )
goto fail ;
}
kvm_get_kvm ( kvm ) ;
mutex_lock ( & kvm - > lock ) ;
list_add ( & stt - > list , & kvm - > arch . spapr_tce_tables ) ;
mutex_unlock ( & kvm - > lock ) ;
return anon_inode_getfd ( " kvm-spapr-tce " , & kvm_spapr_tce_fops ,
2013-08-24 22:14:08 +02:00
stt , O_RDWR | O_CLOEXEC ) ;
2012-03-15 21:58:34 +00:00
fail :
if ( stt ) {
for ( i = 0 ; i < npages ; i + + )
if ( stt - > pages [ i ] )
__free_page ( stt - > pages [ i ] ) ;
kfree ( stt ) ;
}
return ret ;
}