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 .
*
* Authors :
* Eddie Dong < eddie . dong @ intel . com >
* Jike Song < jike . song @ intel . com >
*
* Contributors :
* Zhi Wang < zhi . a . wang @ intel . com >
* Min He < min . he @ intel . com >
* Bing Niu < bing . niu @ intel . com >
*
*/
# include "i915_drv.h"
2016-10-20 17:15:03 +08:00
# include "gvt.h"
2016-07-20 01:14:38 -04:00
enum {
INTEL_GVT_PCI_BAR_GTTMMIO = 0 ,
INTEL_GVT_PCI_BAR_APERTURE ,
INTEL_GVT_PCI_BAR_PIO ,
INTEL_GVT_PCI_BAR_MAX ,
} ;
2017-03-06 17:08:30 +08:00
/* bitmap for writable bits (RW or RW1C bits, but cannot co-exist in one
* byte ) byte by byte in standard pci configuration space . ( not the full
* 256 bytes . )
*/
static const u8 pci_cfg_space_rw_bmp [ PCI_INTERRUPT_LINE + 4 ] = {
[ PCI_COMMAND ] = 0xff , 0x07 ,
[ PCI_STATUS ] = 0x00 , 0xf9 , /* the only one RW1C byte */
[ PCI_CACHE_LINE_SIZE ] = 0xff ,
[ PCI_BASE_ADDRESS_0 . . . PCI_CARDBUS_CIS - 1 ] = 0xff ,
[ PCI_ROM_ADDRESS ] = 0x01 , 0xf8 , 0xff , 0xff ,
[ PCI_INTERRUPT_LINE ] = 0xff ,
} ;
/**
* vgpu_pci_cfg_mem_write - write virtual cfg space memory
*
* Use this function to write virtual cfg space memory .
* For standard cfg space , only RW bits can be changed ,
* and we emulates the RW1C behavior of PCI_STATUS register .
*/
static void vgpu_pci_cfg_mem_write ( struct intel_vgpu * vgpu , unsigned int off ,
u8 * src , unsigned int bytes )
{
u8 * cfg_base = vgpu_cfg_space ( vgpu ) ;
u8 mask , new , old ;
int i = 0 ;
for ( ; i < bytes & & ( off + i < sizeof ( pci_cfg_space_rw_bmp ) ) ; i + + ) {
mask = pci_cfg_space_rw_bmp [ off + i ] ;
old = cfg_base [ off + i ] ;
new = src [ i ] & mask ;
/**
* The PCI_STATUS high byte has RW1C bits , here
* emulates clear by writing 1 for these bits .
* Writing a 0 b to RW1C bits has no effect .
*/
if ( off + i = = PCI_STATUS + 1 )
new = ( ~ new & old ) & mask ;
cfg_base [ off + i ] = ( old & ~ mask ) | new ;
}
/* For other configuration space directly copy as it is. */
if ( i < bytes )
memcpy ( cfg_base + off + i , src + i , bytes - i ) ;
}
2016-07-20 01:14:38 -04:00
/**
* intel_vgpu_emulate_cfg_read - emulate vGPU configuration space read
*
* Returns :
* Zero on success , negative error code if failed .
*/
2016-11-03 18:38:35 +08:00
int intel_vgpu_emulate_cfg_read ( struct intel_vgpu * vgpu , unsigned int offset ,
2016-07-20 01:14:38 -04:00
void * p_data , unsigned int bytes )
{
if ( WARN_ON ( bytes > 4 ) )
return - EINVAL ;
2017-08-23 14:08:10 +08:00
if ( WARN_ON ( offset + bytes > vgpu - > gvt - > device_info . cfg_space_size ) )
2016-07-20 01:14:38 -04:00
return - EINVAL ;
memcpy ( p_data , vgpu_cfg_space ( vgpu ) + offset , bytes ) ;
return 0 ;
}
static int map_aperture ( struct intel_vgpu * vgpu , bool map )
{
2017-08-15 13:14:04 +08:00
phys_addr_t aperture_pa = vgpu_aperture_pa_base ( vgpu ) ;
unsigned long aperture_sz = vgpu_aperture_sz ( vgpu ) ;
u64 first_gfn ;
2016-07-20 01:14:38 -04:00
u64 val ;
int ret ;
if ( map = = vgpu - > cfg_space . bar [ INTEL_GVT_PCI_BAR_APERTURE ] . tracked )
return 0 ;
val = vgpu_cfg_space ( vgpu ) [ PCI_BASE_ADDRESS_2 ] ;
if ( val & PCI_BASE_ADDRESS_MEM_TYPE_64 )
val = * ( u64 * ) ( vgpu_cfg_space ( vgpu ) + PCI_BASE_ADDRESS_2 ) ;
else
val = * ( u32 * ) ( vgpu_cfg_space ( vgpu ) + PCI_BASE_ADDRESS_2 ) ;
first_gfn = ( val + vgpu_aperture_offset ( vgpu ) ) > > PAGE_SHIFT ;
ret = intel_gvt_hypervisor_map_gfn_to_mfn ( vgpu , first_gfn ,
2017-08-15 13:14:04 +08:00
aperture_pa > > PAGE_SHIFT ,
aperture_sz > > PAGE_SHIFT ,
map ) ;
2018-01-30 13:51:31 +08:00
if ( ret )
2016-07-20 01:14:38 -04:00
return ret ;
vgpu - > cfg_space . bar [ INTEL_GVT_PCI_BAR_APERTURE ] . tracked = map ;
return 0 ;
}
static int trap_gttmmio ( struct intel_vgpu * vgpu , bool trap )
{
u64 start , end ;
u64 val ;
int ret ;
if ( trap = = vgpu - > cfg_space . bar [ INTEL_GVT_PCI_BAR_GTTMMIO ] . tracked )
return 0 ;
val = vgpu_cfg_space ( vgpu ) [ PCI_BASE_ADDRESS_0 ] ;
if ( val & PCI_BASE_ADDRESS_MEM_TYPE_64 )
start = * ( u64 * ) ( vgpu_cfg_space ( vgpu ) + PCI_BASE_ADDRESS_0 ) ;
else
start = * ( u32 * ) ( vgpu_cfg_space ( vgpu ) + PCI_BASE_ADDRESS_0 ) ;
start & = ~ GENMASK ( 3 , 0 ) ;
end = start + vgpu - > cfg_space . bar [ INTEL_GVT_PCI_BAR_GTTMMIO ] . size - 1 ;
ret = intel_gvt_hypervisor_set_trap_area ( vgpu , start , end , trap ) ;
if ( ret )
return ret ;
vgpu - > cfg_space . bar [ INTEL_GVT_PCI_BAR_GTTMMIO ] . tracked = trap ;
return 0 ;
}
static int emulate_pci_command_write ( struct intel_vgpu * vgpu ,
unsigned int offset , void * p_data , unsigned int bytes )
{
u8 old = vgpu_cfg_space ( vgpu ) [ offset ] ;
u8 new = * ( u8 * ) p_data ;
u8 changed = old ^ new ;
int ret ;
2017-03-06 17:08:30 +08:00
vgpu_pci_cfg_mem_write ( vgpu , offset , p_data , bytes ) ;
2016-07-20 01:14:38 -04:00
if ( ! ( changed & PCI_COMMAND_MEMORY ) )
return 0 ;
if ( old & PCI_COMMAND_MEMORY ) {
ret = trap_gttmmio ( vgpu , false ) ;
if ( ret )
return ret ;
ret = map_aperture ( vgpu , false ) ;
if ( ret )
return ret ;
} else {
ret = trap_gttmmio ( vgpu , true ) ;
if ( ret )
return ret ;
ret = map_aperture ( vgpu , true ) ;
if ( ret )
return ret ;
}
return 0 ;
}
2017-11-02 13:33:42 +08:00
static int emulate_pci_rom_bar_write ( struct intel_vgpu * vgpu ,
unsigned int offset , void * p_data , unsigned int bytes )
{
u32 * pval = ( u32 * ) ( vgpu_cfg_space ( vgpu ) + offset ) ;
u32 new = * ( u32 * ) ( p_data ) ;
if ( ( new & PCI_ROM_ADDRESS_MASK ) = = PCI_ROM_ADDRESS_MASK )
/* We don't have rom, return size of 0. */
* pval = 0 ;
else
vgpu_pci_cfg_mem_write ( vgpu , offset , p_data , bytes ) ;
return 0 ;
}
2016-07-20 01:14:38 -04:00
static int emulate_pci_bar_write ( struct intel_vgpu * vgpu , unsigned int offset ,
void * p_data , unsigned int bytes )
{
u32 new = * ( u32 * ) ( p_data ) ;
bool lo = IS_ALIGNED ( offset , 8 ) ;
u64 size ;
int ret = 0 ;
bool mmio_enabled =
vgpu_cfg_space ( vgpu ) [ PCI_COMMAND ] & PCI_COMMAND_MEMORY ;
2017-08-18 17:49:58 +08:00
struct intel_vgpu_pci_bar * bars = vgpu - > cfg_space . bar ;
2016-07-20 01:14:38 -04:00
2017-08-18 17:49:58 +08:00
/*
* Power - up software can determine how much address
* space the device requires by writing a value of
* all 1 ' s to the register and then reading the value
* back . The device will return 0 ' s in all don ' t - care
* address bits .
*/
2016-07-20 01:14:38 -04:00
if ( new = = 0xffffffff ) {
2017-08-18 17:49:58 +08:00
switch ( offset ) {
case PCI_BASE_ADDRESS_0 :
case PCI_BASE_ADDRESS_1 :
size = ~ ( bars [ INTEL_GVT_PCI_BAR_GTTMMIO ] . size - 1 ) ;
intel_vgpu_write_pci_bar ( vgpu , offset ,
size > > ( lo ? 0 : 32 ) , lo ) ;
/*
* Untrap the BAR , since guest hasn ' t configured a
* valid GPA
2016-07-20 01:14:38 -04:00
*/
ret = trap_gttmmio ( vgpu , false ) ;
break ;
2017-08-18 17:49:58 +08:00
case PCI_BASE_ADDRESS_2 :
case PCI_BASE_ADDRESS_3 :
size = ~ ( bars [ INTEL_GVT_PCI_BAR_APERTURE ] . size - 1 ) ;
intel_vgpu_write_pci_bar ( vgpu , offset ,
size > > ( lo ? 0 : 32 ) , lo ) ;
2016-07-20 01:14:38 -04:00
ret = map_aperture ( vgpu , false ) ;
break ;
2017-08-18 17:49:58 +08:00
default :
/* Unimplemented BARs */
intel_vgpu_write_pci_bar ( vgpu , offset , 0x0 , false ) ;
2016-07-20 01:14:38 -04:00
}
} else {
2017-08-18 17:49:58 +08:00
switch ( offset ) {
case PCI_BASE_ADDRESS_0 :
case PCI_BASE_ADDRESS_1 :
/*
* Untrap the old BAR first , since guest has
* re - configured the BAR
*/
trap_gttmmio ( vgpu , false ) ;
intel_vgpu_write_pci_bar ( vgpu , offset , new , lo ) ;
ret = trap_gttmmio ( vgpu , mmio_enabled ) ;
2016-07-20 01:14:38 -04:00
break ;
2017-08-18 17:49:58 +08:00
case PCI_BASE_ADDRESS_2 :
case PCI_BASE_ADDRESS_3 :
map_aperture ( vgpu , false ) ;
intel_vgpu_write_pci_bar ( vgpu , offset , new , lo ) ;
ret = map_aperture ( vgpu , mmio_enabled ) ;
2016-07-20 01:14:38 -04:00
break ;
2017-08-18 17:49:58 +08:00
default :
intel_vgpu_write_pci_bar ( vgpu , offset , new , lo ) ;
2016-07-20 01:14:38 -04:00
}
}
return ret ;
}
/**
* intel_vgpu_emulate_cfg_read - emulate vGPU configuration space write
*
* Returns :
* Zero on success , negative error code if failed .
*/
2016-11-03 18:38:35 +08:00
int intel_vgpu_emulate_cfg_write ( struct intel_vgpu * vgpu , unsigned int offset ,
2016-07-20 01:14:38 -04:00
void * p_data , unsigned int bytes )
{
int ret ;
if ( WARN_ON ( bytes > 4 ) )
return - EINVAL ;
2017-08-23 14:08:10 +08:00
if ( WARN_ON ( offset + bytes > vgpu - > gvt - > device_info . cfg_space_size ) )
2016-07-20 01:14:38 -04:00
return - EINVAL ;
/* First check if it's PCI_COMMAND */
if ( IS_ALIGNED ( offset , 2 ) & & offset = = PCI_COMMAND ) {
if ( WARN_ON ( bytes > 2 ) )
return - EINVAL ;
return emulate_pci_command_write ( vgpu , offset , p_data , bytes ) ;
}
switch ( rounddown ( offset , 4 ) ) {
2017-11-02 13:33:42 +08:00
case PCI_ROM_ADDRESS :
if ( WARN_ON ( ! IS_ALIGNED ( offset , 4 ) ) )
return - EINVAL ;
return emulate_pci_rom_bar_write ( vgpu , offset , p_data , bytes ) ;
2017-08-18 17:49:58 +08:00
case PCI_BASE_ADDRESS_0 . . . PCI_BASE_ADDRESS_5 :
2016-07-20 01:14:38 -04:00
if ( WARN_ON ( ! IS_ALIGNED ( offset , 4 ) ) )
return - EINVAL ;
return emulate_pci_bar_write ( vgpu , offset , p_data , bytes ) ;
case INTEL_GVT_PCI_SWSCI :
if ( WARN_ON ( ! IS_ALIGNED ( offset , 4 ) ) )
return - EINVAL ;
ret = intel_vgpu_emulate_opregion_request ( vgpu , * ( u32 * ) p_data ) ;
if ( ret )
return ret ;
break ;
case INTEL_GVT_PCI_OPREGION :
if ( WARN_ON ( ! IS_ALIGNED ( offset , 4 ) ) )
return - EINVAL ;
2017-11-20 15:31:15 +08:00
ret = intel_vgpu_opregion_base_write_handler ( vgpu ,
* ( u32 * ) p_data ) ;
2016-07-20 01:14:38 -04:00
if ( ret )
return ret ;
2017-03-06 17:08:30 +08:00
vgpu_pci_cfg_mem_write ( vgpu , offset , p_data , bytes ) ;
2016-07-20 01:14:38 -04:00
break ;
default :
2017-03-06 17:08:30 +08:00
vgpu_pci_cfg_mem_write ( vgpu , offset , p_data , bytes ) ;
2016-07-20 01:14:38 -04:00
break ;
}
return 0 ;
}
2017-01-13 11:15:58 +08:00
/**
* intel_vgpu_init_cfg_space - init vGPU configuration space when create vGPU
*
* @ vgpu : a vGPU
* @ primary : is the vGPU presented as primary
*
*/
void intel_vgpu_init_cfg_space ( struct intel_vgpu * vgpu ,
bool primary )
{
struct intel_gvt * gvt = vgpu - > gvt ;
const struct intel_gvt_device_info * info = & gvt - > device_info ;
u16 * gmch_ctl ;
memcpy ( vgpu_cfg_space ( vgpu ) , gvt - > firmware . cfg_space ,
info - > cfg_space_size ) ;
if ( ! primary ) {
vgpu_cfg_space ( vgpu ) [ PCI_CLASS_DEVICE ] =
INTEL_GVT_PCI_CLASS_VGA_OTHER ;
vgpu_cfg_space ( vgpu ) [ PCI_CLASS_PROG ] =
INTEL_GVT_PCI_CLASS_VGA_OTHER ;
}
/* Show guest that there isn't any stolen memory.*/
gmch_ctl = ( u16 * ) ( vgpu_cfg_space ( vgpu ) + INTEL_GVT_PCI_GMCH_CONTROL ) ;
* gmch_ctl & = ~ ( BDW_GMCH_GMS_MASK < < BDW_GMCH_GMS_SHIFT ) ;
intel_vgpu_write_pci_bar ( vgpu , PCI_BASE_ADDRESS_2 ,
gvt_aperture_pa_base ( gvt ) , true ) ;
vgpu_cfg_space ( vgpu ) [ PCI_COMMAND ] & = ~ ( PCI_COMMAND_IO
| PCI_COMMAND_MEMORY
| PCI_COMMAND_MASTER ) ;
/*
* Clear the bar upper 32 bit and let guest to assign the new value
*/
memset ( vgpu_cfg_space ( vgpu ) + PCI_BASE_ADDRESS_1 , 0 , 4 ) ;
memset ( vgpu_cfg_space ( vgpu ) + PCI_BASE_ADDRESS_3 , 0 , 4 ) ;
2017-08-18 17:49:58 +08:00
memset ( vgpu_cfg_space ( vgpu ) + PCI_BASE_ADDRESS_4 , 0 , 8 ) ;
2017-01-13 11:15:58 +08:00
memset ( vgpu_cfg_space ( vgpu ) + INTEL_GVT_PCI_OPREGION , 0 , 4 ) ;
2017-08-18 17:49:58 +08:00
vgpu - > cfg_space . bar [ INTEL_GVT_PCI_BAR_GTTMMIO ] . size =
pci_resource_len ( gvt - > dev_priv - > drm . pdev , 0 ) ;
vgpu - > cfg_space . bar [ INTEL_GVT_PCI_BAR_APERTURE ] . size =
pci_resource_len ( gvt - > dev_priv - > drm . pdev , 2 ) ;
2017-11-02 13:33:42 +08:00
memset ( vgpu_cfg_space ( vgpu ) + PCI_ROM_ADDRESS , 0 , 4 ) ;
2017-01-13 11:15:58 +08:00
}
2017-01-13 11:15:59 +08:00
/**
* intel_vgpu_reset_cfg_space - reset vGPU configuration space
*
* @ vgpu : a vGPU
*
*/
void intel_vgpu_reset_cfg_space ( struct intel_vgpu * vgpu )
{
u8 cmd = vgpu_cfg_space ( vgpu ) [ PCI_COMMAND ] ;
bool primary = vgpu_cfg_space ( vgpu ) [ PCI_CLASS_DEVICE ] ! =
INTEL_GVT_PCI_CLASS_VGA_OTHER ;
if ( cmd & PCI_COMMAND_MEMORY ) {
trap_gttmmio ( vgpu , false ) ;
map_aperture ( vgpu , false ) ;
}
/**
* Currently we only do such reset when vGPU is not
* owned by any VM , so we simply restore entire cfg
* space to default value .
*/
intel_vgpu_init_cfg_space ( vgpu , primary ) ;
}