2012-10-31 13:37:59 +01:00
/*
* ePAPR hcall interface
*
* Copyright 2008 - 2011 Freescale Semiconductor , Inc .
*
* Author : Timur Tabi < timur @ freescale . com >
*
* This file is provided under a dual BSD / GPL license . When using or
* redistributing this file , you may do so under either license .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
* * Neither the name of Freescale Semiconductor nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission .
*
*
* ALTERNATIVELY , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) as published by the Free Software
* Foundation , either version 2 of that License or ( at your option ) any
* later version .
*
* THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ` ` AS IS ' ' AND ANY
* EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
* DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
* LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
/* A "hypercall" is an "sc 1" instruction. This header file file provides C
* wrapper functions for the ePAPR hypervisor interface . It is inteded
* for use by Linux device drivers and other operating systems .
*
* The hypercalls are implemented as inline assembly , rather than assembly
* language functions in a . S file , for optimization . It allows
* the caller to issue the hypercall instruction directly , improving both
* performance and memory footprint .
*/
# ifndef _EPAPR_HCALLS_H
# define _EPAPR_HCALLS_H
# include <uapi/asm/epapr_hcalls.h>
# ifndef __ASSEMBLY__
# include <linux/types.h>
# include <linux/errno.h>
# include <asm/byteorder.h>
/*
* Hypercall register clobber list
*
* These macros are used to define the list of clobbered registers during a
* hypercall . Technically , registers r0 and r3 - r12 are always clobbered ,
* but the gcc inline assembly syntax does not allow us to specify registers
* on the clobber list that are also on the input / output list . Therefore ,
* the lists of clobbered registers depends on the number of register
* parmeters ( " +r " and " =r " ) passed to the hypercall .
*
* Each assembly block should use one of the HCALL_CLOBBERSx macros . As a
* general rule , ' x ' is the number of parameters passed to the assembly
* block * except * for r11 .
*
* If you ' re not sure , just use the smallest value of ' x ' that does not
* generate a compilation error . Because these are static inline functions ,
* the compiler will only check the clobber list for a function if you
* compile code that calls that function .
*
* r3 and r11 are not included in any clobbers list because they are always
* listed as output registers .
*
* XER , CTR , and LR are currently listed as clobbers because it ' s uncertain
* whether they will be clobbered .
*
* Note that r11 can be used as an output parameter .
*
* The " memory " clobber is only necessary for hcalls where the Hypervisor
* will read or write guest memory . However , we add it to all hcalls because
* the impact is minimal , and we want to ensure that it ' s present for the
* hcalls that need it .
*/
/* List of common clobbered registers. Do not use this macro. */
# define EV_HCALL_CLOBBERS "r0", "r12", "xer", "ctr", "lr", "cc", "memory"
# define EV_HCALL_CLOBBERS8 EV_HCALL_CLOBBERS
# define EV_HCALL_CLOBBERS7 EV_HCALL_CLOBBERS8, "r10"
# define EV_HCALL_CLOBBERS6 EV_HCALL_CLOBBERS7, "r9"
# define EV_HCALL_CLOBBERS5 EV_HCALL_CLOBBERS6, "r8"
# define EV_HCALL_CLOBBERS4 EV_HCALL_CLOBBERS5, "r7"
# define EV_HCALL_CLOBBERS3 EV_HCALL_CLOBBERS4, "r6"
# define EV_HCALL_CLOBBERS2 EV_HCALL_CLOBBERS3, "r5"
# define EV_HCALL_CLOBBERS1 EV_HCALL_CLOBBERS2, "r4"
extern bool epapr_paravirt_enabled ;
extern u32 epapr_hypercall_start [ ] ;
2013-07-03 17:13:15 +03:00
# ifdef CONFIG_EPAPR_PARAVIRT
int __init epapr_paravirt_early_init ( void ) ;
# else
static inline int epapr_paravirt_early_init ( void ) { return 0 ; }
# endif
2012-10-31 13:37:59 +01:00
/*
* We use " uintptr_t " to define a register because it ' s guaranteed to be a
* 32 - bit integer on a 32 - bit platform , and a 64 - bit integer on a 64 - bit
* platform .
*
* All registers are either input / output or output only . Registers that are
* initialized before making the hypercall are input / output . All
* input / output registers are represented with " +r " . Output - only registers
* are represented with " =r " . Do not specify any unused registers . The
* clobber list will tell the compiler that the hypercall modifies those
* registers , which is good enough .
*/
/**
* ev_int_set_config - configure the specified interrupt
* @ interrupt : the interrupt number
* @ config : configuration for this interrupt
* @ priority : interrupt priority
* @ destination : destination CPU number
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_int_set_config ( unsigned int interrupt ,
uint32_t config , unsigned int priority , uint32_t destination )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
register uintptr_t r4 __asm__ ( " r4 " ) ;
register uintptr_t r5 __asm__ ( " r5 " ) ;
register uintptr_t r6 __asm__ ( " r6 " ) ;
r11 = EV_HCALL_TOKEN ( EV_INT_SET_CONFIG ) ;
r3 = interrupt ;
r4 = config ;
r5 = priority ;
r6 = destination ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " +r " ( r3 ) , " +r " ( r4 ) , " +r " ( r5 ) , " +r " ( r6 )
: : EV_HCALL_CLOBBERS4
) ;
return r3 ;
}
/**
* ev_int_get_config - return the config of the specified interrupt
* @ interrupt : the interrupt number
* @ config : returned configuration for this interrupt
* @ priority : returned interrupt priority
* @ destination : returned destination CPU number
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_int_get_config ( unsigned int interrupt ,
uint32_t * config , unsigned int * priority , uint32_t * destination )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
register uintptr_t r4 __asm__ ( " r4 " ) ;
register uintptr_t r5 __asm__ ( " r5 " ) ;
register uintptr_t r6 __asm__ ( " r6 " ) ;
r11 = EV_HCALL_TOKEN ( EV_INT_GET_CONFIG ) ;
r3 = interrupt ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " +r " ( r3 ) , " =r " ( r4 ) , " =r " ( r5 ) , " =r " ( r6 )
: : EV_HCALL_CLOBBERS4
) ;
* config = r4 ;
* priority = r5 ;
* destination = r6 ;
return r3 ;
}
/**
* ev_int_set_mask - sets the mask for the specified interrupt source
* @ interrupt : the interrupt number
* @ mask : 0 = enable interrupts , 1 = disable interrupts
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_int_set_mask ( unsigned int interrupt ,
unsigned int mask )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
register uintptr_t r4 __asm__ ( " r4 " ) ;
r11 = EV_HCALL_TOKEN ( EV_INT_SET_MASK ) ;
r3 = interrupt ;
r4 = mask ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " +r " ( r3 ) , " +r " ( r4 )
: : EV_HCALL_CLOBBERS2
) ;
return r3 ;
}
/**
* ev_int_get_mask - returns the mask for the specified interrupt source
* @ interrupt : the interrupt number
* @ mask : returned mask for this interrupt ( 0 = enabled , 1 = disabled )
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_int_get_mask ( unsigned int interrupt ,
unsigned int * mask )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
register uintptr_t r4 __asm__ ( " r4 " ) ;
r11 = EV_HCALL_TOKEN ( EV_INT_GET_MASK ) ;
r3 = interrupt ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " +r " ( r3 ) , " =r " ( r4 )
: : EV_HCALL_CLOBBERS2
) ;
* mask = r4 ;
return r3 ;
}
/**
* ev_int_eoi - signal the end of interrupt processing
* @ interrupt : the interrupt number
*
* This function signals the end of processing for the the specified
* interrupt , which must be the interrupt currently in service . By
* definition , this is also the highest - priority interrupt .
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_int_eoi ( unsigned int interrupt )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
r11 = EV_HCALL_TOKEN ( EV_INT_EOI ) ;
r3 = interrupt ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " +r " ( r3 )
: : EV_HCALL_CLOBBERS1
) ;
return r3 ;
}
/**
* ev_byte_channel_send - send characters to a byte stream
* @ handle : byte stream handle
* @ count : ( input ) num of chars to send , ( output ) num chars sent
* @ buffer : pointer to a 16 - byte buffer
*
* @ buffer must be at least 16 bytes long , because all 16 bytes will be
* read from memory into registers , even if count < 16.
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_byte_channel_send ( unsigned int handle ,
unsigned int * count , const char buffer [ EV_BYTE_CHANNEL_MAX_BYTES ] )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
register uintptr_t r4 __asm__ ( " r4 " ) ;
register uintptr_t r5 __asm__ ( " r5 " ) ;
register uintptr_t r6 __asm__ ( " r6 " ) ;
register uintptr_t r7 __asm__ ( " r7 " ) ;
register uintptr_t r8 __asm__ ( " r8 " ) ;
const uint32_t * p = ( const uint32_t * ) buffer ;
r11 = EV_HCALL_TOKEN ( EV_BYTE_CHANNEL_SEND ) ;
r3 = handle ;
r4 = * count ;
r5 = be32_to_cpu ( p [ 0 ] ) ;
r6 = be32_to_cpu ( p [ 1 ] ) ;
r7 = be32_to_cpu ( p [ 2 ] ) ;
r8 = be32_to_cpu ( p [ 3 ] ) ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " +r " ( r3 ) ,
" +r " ( r4 ) , " +r " ( r5 ) , " +r " ( r6 ) , " +r " ( r7 ) , " +r " ( r8 )
: : EV_HCALL_CLOBBERS6
) ;
* count = r4 ;
return r3 ;
}
/**
* ev_byte_channel_receive - fetch characters from a byte channel
* @ handle : byte channel handle
* @ count : ( input ) max num of chars to receive , ( output ) num chars received
* @ buffer : pointer to a 16 - byte buffer
*
* The size of @ buffer must be at least 16 bytes , even if you request fewer
* than 16 characters , because we always write 16 bytes to @ buffer . This is
* for performance reasons .
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_byte_channel_receive ( unsigned int handle ,
unsigned int * count , char buffer [ EV_BYTE_CHANNEL_MAX_BYTES ] )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
register uintptr_t r4 __asm__ ( " r4 " ) ;
register uintptr_t r5 __asm__ ( " r5 " ) ;
register uintptr_t r6 __asm__ ( " r6 " ) ;
register uintptr_t r7 __asm__ ( " r7 " ) ;
register uintptr_t r8 __asm__ ( " r8 " ) ;
uint32_t * p = ( uint32_t * ) buffer ;
r11 = EV_HCALL_TOKEN ( EV_BYTE_CHANNEL_RECEIVE ) ;
r3 = handle ;
r4 = * count ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " +r " ( r3 ) , " +r " ( r4 ) ,
" =r " ( r5 ) , " =r " ( r6 ) , " =r " ( r7 ) , " =r " ( r8 )
: : EV_HCALL_CLOBBERS6
) ;
* count = r4 ;
p [ 0 ] = cpu_to_be32 ( r5 ) ;
p [ 1 ] = cpu_to_be32 ( r6 ) ;
p [ 2 ] = cpu_to_be32 ( r7 ) ;
p [ 3 ] = cpu_to_be32 ( r8 ) ;
return r3 ;
}
/**
* ev_byte_channel_poll - returns the status of the byte channel buffers
* @ handle : byte channel handle
* @ rx_count : returned count of bytes in receive queue
* @ tx_count : returned count of free space in transmit queue
*
* This function reports the amount of data in the receive queue ( i . e . the
* number of bytes you can read ) , and the amount of free space in the transmit
* queue ( i . e . the number of bytes you can write ) .
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_byte_channel_poll ( unsigned int handle ,
unsigned int * rx_count , unsigned int * tx_count )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
register uintptr_t r4 __asm__ ( " r4 " ) ;
register uintptr_t r5 __asm__ ( " r5 " ) ;
r11 = EV_HCALL_TOKEN ( EV_BYTE_CHANNEL_POLL ) ;
r3 = handle ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " +r " ( r3 ) , " =r " ( r4 ) , " =r " ( r5 )
: : EV_HCALL_CLOBBERS3
) ;
* rx_count = r4 ;
* tx_count = r5 ;
return r3 ;
}
/**
* ev_int_iack - acknowledge an interrupt
* @ handle : handle to the target interrupt controller
* @ vector : returned interrupt vector
*
* If handle is zero , the function returns the next interrupt source
* number to be handled irrespective of the hierarchy or cascading
* of interrupt controllers . If non - zero , specifies a handle to the
* interrupt controller that is the target of the acknowledge .
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_int_iack ( unsigned int handle ,
unsigned int * vector )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
register uintptr_t r4 __asm__ ( " r4 " ) ;
r11 = EV_HCALL_TOKEN ( EV_INT_IACK ) ;
r3 = handle ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " +r " ( r3 ) , " =r " ( r4 )
: : EV_HCALL_CLOBBERS2
) ;
* vector = r4 ;
return r3 ;
}
/**
* ev_doorbell_send - send a doorbell to another partition
* @ handle : doorbell send handle
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_doorbell_send ( unsigned int handle )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
r11 = EV_HCALL_TOKEN ( EV_DOORBELL_SEND ) ;
r3 = handle ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " +r " ( r3 )
: : EV_HCALL_CLOBBERS1
) ;
return r3 ;
}
/**
* ev_idle - - wait for next interrupt on this core
*
* Returns 0 for success , or an error code .
*/
static inline unsigned int ev_idle ( void )
{
register uintptr_t r11 __asm__ ( " r11 " ) ;
register uintptr_t r3 __asm__ ( " r3 " ) ;
r11 = EV_HCALL_TOKEN ( EV_IDLE ) ;
asm volatile ( " bl epapr_hypercall_start "
: " +r " ( r11 ) , " =r " ( r3 )
: : EV_HCALL_CLOBBERS1
) ;
return r3 ;
}
2013-10-08 09:32:19 +05:30
# ifdef CONFIG_EPAPR_PARAVIRT
static inline unsigned long epapr_hypercall ( unsigned long * in ,
unsigned long * out ,
unsigned long nr )
{
unsigned long register r0 asm ( " r0 " ) ;
unsigned long register r3 asm ( " r3 " ) = in [ 0 ] ;
unsigned long register r4 asm ( " r4 " ) = in [ 1 ] ;
unsigned long register r5 asm ( " r5 " ) = in [ 2 ] ;
unsigned long register r6 asm ( " r6 " ) = in [ 3 ] ;
unsigned long register r7 asm ( " r7 " ) = in [ 4 ] ;
unsigned long register r8 asm ( " r8 " ) = in [ 5 ] ;
unsigned long register r9 asm ( " r9 " ) = in [ 6 ] ;
unsigned long register r10 asm ( " r10 " ) = in [ 7 ] ;
unsigned long register r11 asm ( " r11 " ) = nr ;
unsigned long register r12 asm ( " r12 " ) ;
asm volatile ( " bl epapr_hypercall_start "
: " =r " ( r0 ) , " =r " ( r3 ) , " =r " ( r4 ) , " =r " ( r5 ) , " =r " ( r6 ) ,
" =r " ( r7 ) , " =r " ( r8 ) , " =r " ( r9 ) , " =r " ( r10 ) , " =r " ( r11 ) ,
" =r " ( r12 )
: " r " ( r3 ) , " r " ( r4 ) , " r " ( r5 ) , " r " ( r6 ) , " r " ( r7 ) , " r " ( r8 ) ,
" r " ( r9 ) , " r " ( r10 ) , " r " ( r11 )
: " memory " , " cc " , " xer " , " ctr " , " lr " ) ;
out [ 0 ] = r4 ;
out [ 1 ] = r5 ;
out [ 2 ] = r6 ;
out [ 3 ] = r7 ;
out [ 4 ] = r8 ;
out [ 5 ] = r9 ;
out [ 6 ] = r10 ;
out [ 7 ] = r11 ;
return r3 ;
}
# else
static unsigned long epapr_hypercall ( unsigned long * in ,
unsigned long * out ,
unsigned long nr )
{
return EV_UNIMPLEMENTED ;
}
# endif
2013-10-08 09:32:20 +05:30
static inline long epapr_hypercall0_1 ( unsigned int nr , unsigned long * r2 )
{
unsigned long in [ 8 ] ;
unsigned long out [ 8 ] ;
unsigned long r ;
r = epapr_hypercall ( in , out , nr ) ;
* r2 = out [ 0 ] ;
return r ;
}
static inline long epapr_hypercall0 ( unsigned int nr )
{
unsigned long in [ 8 ] ;
unsigned long out [ 8 ] ;
return epapr_hypercall ( in , out , nr ) ;
}
static inline long epapr_hypercall1 ( unsigned int nr , unsigned long p1 )
{
unsigned long in [ 8 ] ;
unsigned long out [ 8 ] ;
in [ 0 ] = p1 ;
return epapr_hypercall ( in , out , nr ) ;
}
static inline long epapr_hypercall2 ( unsigned int nr , unsigned long p1 ,
unsigned long p2 )
{
unsigned long in [ 8 ] ;
unsigned long out [ 8 ] ;
in [ 0 ] = p1 ;
in [ 1 ] = p2 ;
return epapr_hypercall ( in , out , nr ) ;
}
static inline long epapr_hypercall3 ( unsigned int nr , unsigned long p1 ,
unsigned long p2 , unsigned long p3 )
{
unsigned long in [ 8 ] ;
unsigned long out [ 8 ] ;
in [ 0 ] = p1 ;
in [ 1 ] = p2 ;
in [ 2 ] = p3 ;
return epapr_hypercall ( in , out , nr ) ;
}
static inline long epapr_hypercall4 ( unsigned int nr , unsigned long p1 ,
unsigned long p2 , unsigned long p3 ,
unsigned long p4 )
{
unsigned long in [ 8 ] ;
unsigned long out [ 8 ] ;
in [ 0 ] = p1 ;
in [ 1 ] = p2 ;
in [ 2 ] = p3 ;
in [ 3 ] = p4 ;
return epapr_hypercall ( in , out , nr ) ;
}
2012-10-31 13:37:59 +01:00
# endif /* !__ASSEMBLY__ */
# endif /* _EPAPR_HCALLS_H */