2007-05-16 04:03:54 +04:00
/* hvapi.c: Hypervisor API management.
*
* Copyright ( C ) 2007 David S . Miller < davem @ davemloft . net >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <asm/hypervisor.h>
# include <asm/oplib.h>
/* If the hypervisor indicates that the API setting
* calls are unsupported , by returning HV_EBADTRAP or
* HV_ENOTSUPPORTED , we assume that API groups with the
* PRE_API flag set are major 1 minor 0.
*/
struct api_info {
unsigned long group ;
unsigned long major ;
unsigned long minor ;
unsigned int refcnt ;
unsigned int flags ;
# define FLAG_PRE_API 0x00000001
} ;
static struct api_info api_table [ ] = {
{ . group = HV_GRP_SUN4V , . flags = FLAG_PRE_API } ,
{ . group = HV_GRP_CORE , . flags = FLAG_PRE_API } ,
{ . group = HV_GRP_INTR , } ,
{ . group = HV_GRP_SOFT_STATE , } ,
{ . group = HV_GRP_PCI , . flags = FLAG_PRE_API } ,
{ . group = HV_GRP_LDOM , } ,
{ . group = HV_GRP_SVC_CHAN , . flags = FLAG_PRE_API } ,
{ . group = HV_GRP_NCS , . flags = FLAG_PRE_API } ,
2008-07-18 11:43:52 +04:00
{ . group = HV_GRP_RNG , } ,
2007-05-16 04:03:54 +04:00
{ . group = HV_GRP_NIAG_PERF , . flags = FLAG_PRE_API } ,
{ . group = HV_GRP_FIRE_PERF , } ,
2008-07-18 11:43:52 +04:00
{ . group = HV_GRP_N2_CPU , } ,
{ . group = HV_GRP_NIU , } ,
{ . group = HV_GRP_VF_CPU , } ,
2007-05-16 04:03:54 +04:00
{ . group = HV_GRP_DIAG , . flags = FLAG_PRE_API } ,
} ;
static DEFINE_SPINLOCK ( hvapi_lock ) ;
static struct api_info * __get_info ( unsigned long group )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( api_table ) ; i + + ) {
if ( api_table [ i ] . group = = group )
return & api_table [ i ] ;
}
return NULL ;
}
static void __get_ref ( struct api_info * p )
{
p - > refcnt + + ;
}
static void __put_ref ( struct api_info * p )
{
if ( - - p - > refcnt = = 0 ) {
unsigned long ignore ;
sun4v_set_version ( p - > group , 0 , 0 , & ignore ) ;
p - > major = p - > minor = 0 ;
}
}
/* Register a hypervisor API specification. It indicates the
* API group and desired major + minor .
*
* If an existing API registration exists ' 0 ' ( success ) will
* be returned if it is compatible with the one being registered .
* Otherwise a negative error code will be returned .
*
* Otherwise an attempt will be made to negotiate the requested
* API group / major / minor with the hypervisor , and errors returned
* if that does not succeed .
*/
int sun4v_hvapi_register ( unsigned long group , unsigned long major ,
unsigned long * minor )
{
struct api_info * p ;
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & hvapi_lock , flags ) ;
p = __get_info ( group ) ;
ret = - EINVAL ;
if ( p ) {
if ( p - > refcnt ) {
ret = - EINVAL ;
if ( p - > major = = major ) {
* minor = p - > minor ;
ret = 0 ;
}
} else {
unsigned long actual_minor ;
unsigned long hv_ret ;
hv_ret = sun4v_set_version ( group , major , * minor ,
& actual_minor ) ;
ret = - EINVAL ;
if ( hv_ret = = HV_EOK ) {
* minor = actual_minor ;
p - > major = major ;
p - > minor = actual_minor ;
ret = 0 ;
} else if ( hv_ret = = HV_EBADTRAP | |
2007-05-25 11:33:49 +04:00
hv_ret = = HV_ENOTSUPPORTED ) {
2007-05-16 04:03:54 +04:00
if ( p - > flags & FLAG_PRE_API ) {
if ( major = = 1 ) {
p - > major = 1 ;
p - > minor = 0 ;
* minor = 0 ;
ret = 0 ;
}
}
}
}
if ( ret = = 0 )
__get_ref ( p ) ;
}
spin_unlock_irqrestore ( & hvapi_lock , flags ) ;
return ret ;
}
EXPORT_SYMBOL ( sun4v_hvapi_register ) ;
void sun4v_hvapi_unregister ( unsigned long group )
{
struct api_info * p ;
unsigned long flags ;
spin_lock_irqsave ( & hvapi_lock , flags ) ;
p = __get_info ( group ) ;
if ( p )
__put_ref ( p ) ;
spin_unlock_irqrestore ( & hvapi_lock , flags ) ;
}
EXPORT_SYMBOL ( sun4v_hvapi_unregister ) ;
int sun4v_hvapi_get ( unsigned long group ,
unsigned long * major ,
unsigned long * minor )
{
struct api_info * p ;
unsigned long flags ;
int ret ;
spin_lock_irqsave ( & hvapi_lock , flags ) ;
ret = - EINVAL ;
p = __get_info ( group ) ;
if ( p & & p - > refcnt ) {
* major = p - > major ;
* minor = p - > minor ;
ret = 0 ;
}
spin_unlock_irqrestore ( & hvapi_lock , flags ) ;
return ret ;
}
EXPORT_SYMBOL ( sun4v_hvapi_get ) ;
void __init sun4v_hvapi_init ( void )
{
unsigned long group , major , minor ;
group = HV_GRP_SUN4V ;
major = 1 ;
minor = 0 ;
if ( sun4v_hvapi_register ( group , major , & minor ) )
goto bad ;
group = HV_GRP_CORE ;
major = 1 ;
minor = 1 ;
if ( sun4v_hvapi_register ( group , major , & minor ) )
goto bad ;
return ;
bad :
prom_printf ( " HVAPI: Cannot register API group "
" %lx with major(%u) minor(%u) \n " ,
group , major , minor ) ;
prom_halt ( ) ;
}