2016-07-20 01:14:38 -04:00
/*
* Copyright ( c ) 2011 - 2016 Intel Corporation . All rights reserved .
*
* Permission is hereby granted , free of charge , to any person obtaining a
* copy of this software and associated documentation files ( the " Software " ) ,
* to deal in the Software without restriction , including without limitation
* the rights to use , copy , modify , merge , publish , distribute , sublicense ,
* and / or sell copies of the Software , and to permit persons to whom the
* Software is furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice ( including the next
* paragraph ) shall be included in all copies or substantial portions of the
* Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE .
*/
# include <linux/acpi.h>
# include "i915_drv.h"
2016-10-20 17:15:03 +08:00
# include "gvt.h"
2016-07-20 01:14:38 -04:00
2017-09-08 21:37:48 +08:00
/*
* Note : Only for GVT - g virtual VBT generation , other usage must
* not do like this .
*/
# define _INTEL_BIOS_PRIVATE
2019-06-13 11:44:16 +03:00
# include "display/intel_vbt_defs.h"
2017-09-08 21:37:48 +08:00
# define OPREGION_SIGNATURE "IntelGraphicsMem"
# define MBOX_VBT (1<<3)
/* device handle */
# define DEVICE_TYPE_CRT 0x01
# define DEVICE_TYPE_EFP1 0x04
# define DEVICE_TYPE_EFP2 0x40
# define DEVICE_TYPE_EFP3 0x20
# define DEVICE_TYPE_EFP4 0x10
struct opregion_header {
u8 signature [ 16 ] ;
u32 size ;
u32 opregion_ver ;
u8 bios_ver [ 32 ] ;
u8 vbios_ver [ 16 ] ;
u8 driver_ver [ 16 ] ;
u32 mboxes ;
u32 driver_model ;
u32 pcon ;
u8 dver [ 32 ] ;
u8 rsvd [ 124 ] ;
} __packed ;
struct bdb_data_header {
u8 id ;
u16 size ; /* data size */
} __packed ;
2018-09-04 14:13:43 +08:00
/* For supporting windows guest with opregion, here hardcode the emulated
* bdb header version as ' 186 ' , and the corresponding child_device_config
* length should be ' 33 ' but not ' 38 ' .
*/
2017-10-20 21:52:29 +08:00
struct efp_child_device_config {
u16 handle ;
u16 device_type ;
u16 device_class ;
u8 i2c_speed ;
u8 dp_onboard_redriver ; /* 158 */
u8 dp_ondock_redriver ; /* 158 */
u8 hdmi_level_shifter_value : 4 ; /* 169 */
u8 hdmi_max_data_rate : 4 ; /* 204 */
u16 dtd_buf_ptr ; /* 161 */
u8 edidless_efp : 1 ; /* 161 */
u8 compression_enable : 1 ; /* 198 */
u8 compression_method : 1 ; /* 198 */
u8 ganged_edp : 1 ; /* 202 */
u8 skip0 : 4 ;
u8 compression_structure_index : 4 ; /* 198 */
u8 skip1 : 4 ;
u8 slave_port ; /* 202 */
u8 skip2 ;
u8 dvo_port ;
u8 i2c_pin ; /* for add-in card */
u8 slave_addr ; /* for add-in card */
u8 ddc_pin ;
u16 edid_ptr ;
u8 dvo_config ;
u8 efp_docked_port : 1 ; /* 158 */
u8 lane_reversal : 1 ; /* 184 */
u8 onboard_lspcon : 1 ; /* 192 */
u8 iboost_enable : 1 ; /* 196 */
u8 hpd_invert : 1 ; /* BXT 196 */
u8 slip3 : 3 ;
u8 hdmi_compat : 1 ;
u8 dp_compat : 1 ;
u8 tmds_compat : 1 ;
u8 skip4 : 5 ;
u8 aux_channel ;
u8 dongle_detect ;
u8 pipe_cap : 2 ;
u8 sdvo_stall : 1 ; /* 158 */
u8 hpd_status : 2 ;
u8 integrated_encoder : 1 ;
u8 skip5 : 2 ;
u8 dvo_wiring ;
u8 mipi_bridge_type ; /* 171 */
u16 device_class_ext ;
u8 dvo_function ;
} __packed ;
2017-09-08 21:37:48 +08:00
struct vbt {
/* header->bdb_offset point to bdb_header offset */
struct vbt_header header ;
struct bdb_header bdb_header ;
struct bdb_data_header general_features_header ;
struct bdb_general_features general_features ;
struct bdb_data_header general_definitions_header ;
struct bdb_general_definitions general_definitions ;
2017-10-20 21:52:29 +08:00
struct efp_child_device_config child0 ;
struct efp_child_device_config child1 ;
struct efp_child_device_config child2 ;
struct efp_child_device_config child3 ;
2017-09-08 21:37:48 +08:00
struct bdb_data_header driver_features_header ;
struct bdb_driver_features driver_features ;
} ;
static void virt_vbt_generation ( struct vbt * v )
{
int num_child ;
memset ( v , 0 , sizeof ( struct vbt ) ) ;
v - > header . signature [ 0 ] = ' $ ' ;
v - > header . signature [ 1 ] = ' V ' ;
v - > header . signature [ 2 ] = ' B ' ;
v - > header . signature [ 3 ] = ' T ' ;
/* there's features depending on version! */
v - > header . version = 155 ;
v - > header . header_size = sizeof ( v - > header ) ;
2020-03-05 21:15:59 +08:00
v - > header . vbt_size = sizeof ( struct vbt ) ;
2017-09-08 21:37:48 +08:00
v - > header . bdb_offset = offsetof ( struct vbt , bdb_header ) ;
strcpy ( & v - > bdb_header . signature [ 0 ] , " BIOS_DATA_BLOCK " ) ;
2018-09-04 14:13:43 +08:00
v - > bdb_header . version = 186 ; /* child_dev_size = 33 */
2017-09-08 21:37:48 +08:00
v - > bdb_header . header_size = sizeof ( v - > bdb_header ) ;
2020-03-05 21:15:59 +08:00
v - > bdb_header . bdb_size = sizeof ( struct vbt ) - sizeof ( struct vbt_header ) ;
2017-09-08 21:37:48 +08:00
/* general features */
v - > general_features_header . id = BDB_GENERAL_FEATURES ;
v - > general_features_header . size = sizeof ( struct bdb_general_features ) ;
v - > general_features . int_crt_support = 0 ;
v - > general_features . int_tv_support = 0 ;
/* child device */
num_child = 4 ; /* each port has one child */
2018-09-04 14:13:43 +08:00
v - > general_definitions . child_dev_size =
sizeof ( struct efp_child_device_config ) ;
2017-09-08 21:37:48 +08:00
v - > general_definitions_header . id = BDB_GENERAL_DEFINITIONS ;
/* size will include child devices */
v - > general_definitions_header . size =
2018-09-04 14:13:43 +08:00
sizeof ( struct bdb_general_definitions ) +
num_child * v - > general_definitions . child_dev_size ;
2017-09-08 21:37:48 +08:00
/* portA */
v - > child0 . handle = DEVICE_TYPE_EFP1 ;
v - > child0 . device_type = DEVICE_TYPE_DP ;
v - > child0 . dvo_port = DVO_PORT_DPA ;
v - > child0 . aux_channel = DP_AUX_A ;
2017-10-20 21:52:29 +08:00
v - > child0 . dp_compat = true ;
v - > child0 . integrated_encoder = true ;
2017-09-08 21:37:48 +08:00
/* portB */
v - > child1 . handle = DEVICE_TYPE_EFP2 ;
v - > child1 . device_type = DEVICE_TYPE_DP ;
v - > child1 . dvo_port = DVO_PORT_DPB ;
v - > child1 . aux_channel = DP_AUX_B ;
2017-10-20 21:52:29 +08:00
v - > child1 . dp_compat = true ;
v - > child1 . integrated_encoder = true ;
2017-09-08 21:37:48 +08:00
/* portC */
v - > child2 . handle = DEVICE_TYPE_EFP3 ;
v - > child2 . device_type = DEVICE_TYPE_DP ;
v - > child2 . dvo_port = DVO_PORT_DPC ;
v - > child2 . aux_channel = DP_AUX_C ;
2017-10-20 21:52:29 +08:00
v - > child2 . dp_compat = true ;
v - > child2 . integrated_encoder = true ;
2017-09-08 21:37:48 +08:00
/* portD */
v - > child3 . handle = DEVICE_TYPE_EFP4 ;
v - > child3 . device_type = DEVICE_TYPE_DP ;
v - > child3 . dvo_port = DVO_PORT_DPD ;
v - > child3 . aux_channel = DP_AUX_D ;
2017-10-20 21:52:29 +08:00
v - > child3 . dp_compat = true ;
v - > child3 . integrated_encoder = true ;
2017-09-08 21:37:48 +08:00
/* driver features */
v - > driver_features_header . id = BDB_DRIVER_FEATURES ;
v - > driver_features_header . size = sizeof ( struct bdb_driver_features ) ;
v - > driver_features . lvds_config = BDB_DRIVER_FEATURE_NO_LVDS ;
}
2017-11-20 15:31:15 +08:00
/**
* intel_vgpu_init_opregion - initialize the stuff used to emulate opregion
* @ vgpu : a vGPU
*
* Returns :
* Zero on success , negative error code if failed .
*/
int intel_vgpu_init_opregion ( struct intel_vgpu * vgpu )
2017-11-08 00:45:21 +08:00
{
u8 * buf ;
struct opregion_header * header ;
struct vbt v ;
2017-12-04 11:23:44 +08:00
const char opregion_signature [ 16 ] = OPREGION_SIGNATURE ;
2017-11-08 00:45:21 +08:00
gvt_dbg_core ( " init vgpu%d opregion \n " , vgpu - > id ) ;
vgpu_opregion ( vgpu ) - > va = ( void * ) __get_free_pages ( GFP_KERNEL |
__GFP_ZERO ,
get_order ( INTEL_GVT_OPREGION_SIZE ) ) ;
if ( ! vgpu_opregion ( vgpu ) - > va ) {
gvt_err ( " fail to get memory for vgpu virt opregion \n " ) ;
return - ENOMEM ;
}
/* emulated opregion with VBT mailbox only */
buf = ( u8 * ) vgpu_opregion ( vgpu ) - > va ;
header = ( struct opregion_header * ) buf ;
2017-12-04 11:23:44 +08:00
memcpy ( header - > signature , opregion_signature ,
sizeof ( opregion_signature ) ) ;
2017-11-08 00:45:21 +08:00
header - > size = 0x8 ;
header - > opregion_ver = 0x02000000 ;
header - > mboxes = MBOX_VBT ;
/* for unknown reason, the value in LID field is incorrect
* which block the windows guest , so workaround it by force
* setting it to " OPEN "
*/
buf [ INTEL_GVT_OPREGION_CLID ] = 0x3 ;
/* emulated vbt from virt vbt generation */
virt_vbt_generation ( & v ) ;
memcpy ( buf + INTEL_GVT_OPREGION_VBT_OFFSET , & v , sizeof ( struct vbt ) ) ;
return 0 ;
}
2016-07-20 01:14:38 -04:00
static int map_vgpu_opregion ( struct intel_vgpu * vgpu , bool map )
{
u64 mfn ;
int i , ret ;
for ( i = 0 ; i < INTEL_GVT_OPREGION_PAGES ; i + + ) {
2016-12-05 10:53:25 +08:00
mfn = intel_gvt_hypervisor_virt_to_mfn ( vgpu_opregion ( vgpu ) - > va
2016-07-20 01:14:38 -04:00
+ i * PAGE_SIZE ) ;
if ( mfn = = INTEL_GVT_INVALID_ADDR ) {
2017-03-10 04:26:53 -05:00
gvt_vgpu_err ( " fail to get MFN from VA \n " ) ;
2016-07-20 01:14:38 -04:00
return - EINVAL ;
}
ret = intel_gvt_hypervisor_map_gfn_to_mfn ( vgpu ,
vgpu_opregion ( vgpu ) - > gfn [ i ] ,
2016-11-03 18:38:32 +08:00
mfn , 1 , map ) ;
2016-07-20 01:14:38 -04:00
if ( ret ) {
2017-03-10 04:26:53 -05:00
gvt_vgpu_err ( " fail to map GFN to MFN, errno: %d \n " ,
ret ) ;
2016-07-20 01:14:38 -04:00
return ret ;
}
}
2017-11-20 15:31:15 +08:00
vgpu_opregion ( vgpu ) - > mapped = map ;
2016-07-20 01:14:38 -04:00
return 0 ;
}
/**
2017-11-20 15:31:15 +08:00
* intel_vgpu_opregion_base_write_handler - Opregion base register write handler
*
2016-07-20 01:14:38 -04:00
* @ vgpu : a vGPU
2017-11-20 15:31:15 +08:00
* @ gpa : guest physical address of opregion
2016-07-20 01:14:38 -04:00
*
2017-11-20 15:31:15 +08:00
* Returns :
* Zero on success , negative error code if failed .
2016-07-20 01:14:38 -04:00
*/
2017-11-20 15:31:15 +08:00
int intel_vgpu_opregion_base_write_handler ( struct intel_vgpu * vgpu , u32 gpa )
2016-07-20 01:14:38 -04:00
{
2017-11-20 15:31:16 +08:00
int i , ret = 0 ;
gvt_dbg_core ( " emulate opregion from kernel \n " ) ;
switch ( intel_gvt_host . hypervisor_type ) {
case INTEL_GVT_HYPERVISOR_KVM :
2018-01-31 14:42:44 +08:00
for ( i = 0 ; i < INTEL_GVT_OPREGION_PAGES ; i + + )
vgpu_opregion ( vgpu ) - > gfn [ i ] = ( gpa > > PAGE_SHIFT ) + i ;
2017-11-20 15:31:16 +08:00
break ;
case INTEL_GVT_HYPERVISOR_XEN :
/**
* Wins guest on Xengt will write this register twice : xen
* hvmloader and windows graphic driver .
*/
if ( vgpu_opregion ( vgpu ) - > mapped )
map_vgpu_opregion ( vgpu , false ) ;
2016-07-20 01:14:38 -04:00
2017-11-20 15:31:16 +08:00
for ( i = 0 ; i < INTEL_GVT_OPREGION_PAGES ; i + + )
vgpu_opregion ( vgpu ) - > gfn [ i ] = ( gpa > > PAGE_SHIFT ) + i ;
2017-11-20 15:31:15 +08:00
2017-11-20 15:31:16 +08:00
ret = map_vgpu_opregion ( vgpu , true ) ;
break ;
default :
ret = - EINVAL ;
gvt_vgpu_err ( " not supported hypervisor \n " ) ;
}
2017-11-20 15:31:15 +08:00
return ret ;
2016-07-20 01:14:38 -04:00
}
/**
2017-11-20 15:31:15 +08:00
* intel_vgpu_clean_opregion - clean the stuff used to emulate opregion
2016-07-20 01:14:38 -04:00
* @ vgpu : a vGPU
*
*/
2017-11-20 15:31:15 +08:00
void intel_vgpu_clean_opregion ( struct intel_vgpu * vgpu )
2016-07-20 01:14:38 -04:00
{
2017-11-20 15:31:15 +08:00
gvt_dbg_core ( " vgpu%d: clean vgpu opregion \n " , vgpu - > id ) ;
2016-07-20 01:14:38 -04:00
2017-11-20 15:31:15 +08:00
if ( ! vgpu_opregion ( vgpu ) - > va )
return ;
2016-07-20 01:14:38 -04:00
2017-11-20 15:31:16 +08:00
if ( intel_gvt_host . hypervisor_type = = INTEL_GVT_HYPERVISOR_XEN ) {
if ( vgpu_opregion ( vgpu ) - > mapped )
map_vgpu_opregion ( vgpu , false ) ;
} else if ( intel_gvt_host . hypervisor_type = = INTEL_GVT_HYPERVISOR_KVM ) {
2018-01-31 14:42:44 +08:00
/* Guest opregion is released by VFIO */
2017-11-20 15:31:16 +08:00
}
2017-11-20 15:31:15 +08:00
free_pages ( ( unsigned long ) vgpu_opregion ( vgpu ) - > va ,
2017-11-20 15:31:16 +08:00
get_order ( INTEL_GVT_OPREGION_SIZE ) ) ;
2016-11-03 18:38:32 +08:00
2017-11-20 15:31:15 +08:00
vgpu_opregion ( vgpu ) - > va = NULL ;
2017-11-20 15:31:16 +08:00
2016-07-20 01:14:38 -04:00
}
2017-11-20 15:31:15 +08:00
2016-07-20 01:14:38 -04:00
# define GVT_OPREGION_FUNC(scic) \
( { \
u32 __ret ; \
__ret = ( scic & OPREGION_SCIC_FUNC_MASK ) > > \
OPREGION_SCIC_FUNC_SHIFT ; \
__ret ; \
} )
# define GVT_OPREGION_SUBFUNC(scic) \
( { \
u32 __ret ; \
__ret = ( scic & OPREGION_SCIC_SUBFUNC_MASK ) > > \
OPREGION_SCIC_SUBFUNC_SHIFT ; \
__ret ; \
} )
static const char * opregion_func_name ( u32 func )
{
const char * name = NULL ;
switch ( func ) {
case 0 . . . 3 :
case 5 :
case 7 . . . 15 :
name = " Reserved " ;
break ;
case 4 :
name = " Get BIOS Data " ;
break ;
case 6 :
name = " System BIOS Callbacks " ;
break ;
default :
name = " Unknown " ;
break ;
}
return name ;
}
static const char * opregion_subfunc_name ( u32 subfunc )
{
const char * name = NULL ;
switch ( subfunc ) {
case 0 :
name = " Supported Calls " ;
break ;
case 1 :
name = " Requested Callbacks " ;
break ;
case 2 . . . 3 :
case 8 . . . 9 :
name = " Reserved " ;
break ;
case 5 :
name = " Boot Display " ;
break ;
case 6 :
name = " TV-Standard/Video-Connector " ;
break ;
case 7 :
name = " Internal Graphics " ;
break ;
case 10 :
name = " Spread Spectrum Clocks " ;
break ;
case 11 :
name = " Get AKSV " ;
break ;
default :
name = " Unknown " ;
break ;
}
return name ;
} ;
static bool querying_capabilities ( u32 scic )
{
u32 func , subfunc ;
func = GVT_OPREGION_FUNC ( scic ) ;
subfunc = GVT_OPREGION_SUBFUNC ( scic ) ;
if ( ( func = = INTEL_GVT_OPREGION_SCIC_F_GETBIOSDATA & &
subfunc = = INTEL_GVT_OPREGION_SCIC_SF_SUPPRTEDCALLS )
| | ( func = = INTEL_GVT_OPREGION_SCIC_F_GETBIOSDATA & &
subfunc = = INTEL_GVT_OPREGION_SCIC_SF_REQEUSTEDCALLBACKS )
| | ( func = = INTEL_GVT_OPREGION_SCIC_F_GETBIOSCALLBACKS & &
subfunc = = INTEL_GVT_OPREGION_SCIC_SF_SUPPRTEDCALLS ) ) {
return true ;
}
return false ;
}
/**
* intel_vgpu_emulate_opregion_request - emulating OpRegion request
* @ vgpu : a vGPU
* @ swsci : SWSCI request
*
* Returns :
* Zero on success , negative error code if failed
*/
int intel_vgpu_emulate_opregion_request ( struct intel_vgpu * vgpu , u32 swsci )
{
2018-01-31 14:42:44 +08:00
u32 scic , parm ;
2016-07-20 01:14:38 -04:00
u32 func , subfunc ;
2018-01-31 14:42:44 +08:00
u64 scic_pa = 0 , parm_pa = 0 ;
int ret ;
2016-07-20 01:14:38 -04:00
2017-11-20 15:31:16 +08:00
switch ( intel_gvt_host . hypervisor_type ) {
case INTEL_GVT_HYPERVISOR_XEN :
2018-01-31 14:42:44 +08:00
scic = * ( ( u32 * ) vgpu_opregion ( vgpu ) - > va +
INTEL_GVT_OPREGION_SCIC ) ;
parm = * ( ( u32 * ) vgpu_opregion ( vgpu ) - > va +
INTEL_GVT_OPREGION_PARM ) ;
2017-11-20 15:31:16 +08:00
break ;
case INTEL_GVT_HYPERVISOR_KVM :
2018-01-31 14:42:44 +08:00
scic_pa = ( vgpu_opregion ( vgpu ) - > gfn [ 0 ] < < PAGE_SHIFT ) +
INTEL_GVT_OPREGION_SCIC ;
parm_pa = ( vgpu_opregion ( vgpu ) - > gfn [ 0 ] < < PAGE_SHIFT ) +
INTEL_GVT_OPREGION_PARM ;
ret = intel_gvt_hypervisor_read_gpa ( vgpu , scic_pa ,
& scic , sizeof ( scic ) ) ;
if ( ret ) {
gvt_vgpu_err ( " guest opregion read error %d, gpa 0x%llx, len %lu \n " ,
ret , scic_pa , sizeof ( scic ) ) ;
return ret ;
}
ret = intel_gvt_hypervisor_read_gpa ( vgpu , parm_pa ,
& parm , sizeof ( parm ) ) ;
if ( ret ) {
gvt_vgpu_err ( " guest opregion read error %d, gpa 0x%llx, len %lu \n " ,
ret , scic_pa , sizeof ( scic ) ) ;
return ret ;
}
2017-11-20 15:31:16 +08:00
break ;
default :
gvt_vgpu_err ( " not supported hypervisor \n " ) ;
return - EINVAL ;
}
2016-07-20 01:14:38 -04:00
if ( ! ( swsci & SWSCI_SCI_SELECT ) ) {
2017-03-10 04:26:53 -05:00
gvt_vgpu_err ( " requesting SMI service \n " ) ;
2016-07-20 01:14:38 -04:00
return 0 ;
}
/* ignore non 0->1 trasitions */
if ( ( vgpu_cfg_space ( vgpu ) [ INTEL_GVT_PCI_SWSCI ]
& SWSCI_SCI_TRIGGER ) | |
! ( swsci & SWSCI_SCI_TRIGGER ) ) {
return 0 ;
}
2018-01-31 14:42:44 +08:00
func = GVT_OPREGION_FUNC ( scic ) ;
subfunc = GVT_OPREGION_SUBFUNC ( scic ) ;
if ( ! querying_capabilities ( scic ) ) {
2017-03-10 04:26:53 -05:00
gvt_vgpu_err ( " requesting runtime service: func \" %s \" , "
2016-07-20 01:14:38 -04:00
" subfunc \" %s \" \n " ,
opregion_func_name ( func ) ,
opregion_subfunc_name ( subfunc ) ) ;
/*
* emulate exit status of function call , ' 0 ' means
* " failure, generic, unsupported or unknown cause "
*/
2018-01-31 14:42:44 +08:00
scic & = ~ OPREGION_SCIC_EXIT_MASK ;
goto out ;
}
scic = 0 ;
parm = 0 ;
out :
switch ( intel_gvt_host . hypervisor_type ) {
case INTEL_GVT_HYPERVISOR_XEN :
* ( ( u32 * ) vgpu_opregion ( vgpu ) - > va +
INTEL_GVT_OPREGION_SCIC ) = scic ;
* ( ( u32 * ) vgpu_opregion ( vgpu ) - > va +
INTEL_GVT_OPREGION_PARM ) = parm ;
break ;
case INTEL_GVT_HYPERVISOR_KVM :
ret = intel_gvt_hypervisor_write_gpa ( vgpu , scic_pa ,
& scic , sizeof ( scic ) ) ;
if ( ret ) {
gvt_vgpu_err ( " guest opregion write error %d, gpa 0x%llx, len %lu \n " ,
ret , scic_pa , sizeof ( scic ) ) ;
return ret ;
}
ret = intel_gvt_hypervisor_write_gpa ( vgpu , parm_pa ,
& parm , sizeof ( parm ) ) ;
if ( ret ) {
gvt_vgpu_err ( " guest opregion write error %d, gpa 0x%llx, len %lu \n " ,
ret , scic_pa , sizeof ( scic ) ) ;
return ret ;
}
break ;
default :
gvt_vgpu_err ( " not supported hypervisor \n " ) ;
return - EINVAL ;
2016-07-20 01:14:38 -04:00
}
return 0 ;
}