@ -12,9 +12,246 @@
# include "qemu/range.h"
# include "qemu/rcu.h"
# include "sysemu/hostmem.h"
# include "sysemu/numa.h"
# include "hw/cxl/cxl.h"
# include "hw/pci/msix.h"
# define DWORD_BYTE 4
/* Default CDAT entries for a memory region */
enum {
CT3_CDAT_DSMAS ,
CT3_CDAT_DSLBIS0 ,
CT3_CDAT_DSLBIS1 ,
CT3_CDAT_DSLBIS2 ,
CT3_CDAT_DSLBIS3 ,
CT3_CDAT_DSEMTS ,
CT3_CDAT_NUM_ENTRIES
} ;
static int ct3_build_cdat_entries_for_mr ( CDATSubHeader * * cdat_table ,
int dsmad_handle , MemoryRegion * mr )
{
g_autofree CDATDsmas * dsmas = NULL ;
g_autofree CDATDslbis * dslbis0 = NULL ;
g_autofree CDATDslbis * dslbis1 = NULL ;
g_autofree CDATDslbis * dslbis2 = NULL ;
g_autofree CDATDslbis * dslbis3 = NULL ;
g_autofree CDATDsemts * dsemts = NULL ;
dsmas = g_malloc ( sizeof ( * dsmas ) ) ;
if ( ! dsmas ) {
return - ENOMEM ;
}
* dsmas = ( CDATDsmas ) {
. header = {
. type = CDAT_TYPE_DSMAS ,
. length = sizeof ( * dsmas ) ,
} ,
. DSMADhandle = dsmad_handle ,
. flags = CDAT_DSMAS_FLAG_NV ,
. DPA_base = 0 ,
. DPA_length = int128_get64 ( mr - > size ) ,
} ;
/* For now, no memory side cache, plausiblish numbers */
dslbis0 = g_malloc ( sizeof ( * dslbis0 ) ) ;
if ( ! dslbis0 ) {
return - ENOMEM ;
}
* dslbis0 = ( CDATDslbis ) {
. header = {
. type = CDAT_TYPE_DSLBIS ,
. length = sizeof ( * dslbis0 ) ,
} ,
. handle = dsmad_handle ,
. flags = HMAT_LB_MEM_MEMORY ,
. data_type = HMAT_LB_DATA_READ_LATENCY ,
. entry_base_unit = 10000 , /* 10ns base */
. entry [ 0 ] = 15 , /* 150ns */
} ;
dslbis1 = g_malloc ( sizeof ( * dslbis1 ) ) ;
if ( ! dslbis1 ) {
return - ENOMEM ;
}
* dslbis1 = ( CDATDslbis ) {
. header = {
. type = CDAT_TYPE_DSLBIS ,
. length = sizeof ( * dslbis1 ) ,
} ,
. handle = dsmad_handle ,
. flags = HMAT_LB_MEM_MEMORY ,
. data_type = HMAT_LB_DATA_WRITE_LATENCY ,
. entry_base_unit = 10000 ,
. entry [ 0 ] = 25 , /* 250ns */
} ;
dslbis2 = g_malloc ( sizeof ( * dslbis2 ) ) ;
if ( ! dslbis2 ) {
return - ENOMEM ;
}
* dslbis2 = ( CDATDslbis ) {
. header = {
. type = CDAT_TYPE_DSLBIS ,
. length = sizeof ( * dslbis2 ) ,
} ,
. handle = dsmad_handle ,
. flags = HMAT_LB_MEM_MEMORY ,
. data_type = HMAT_LB_DATA_READ_BANDWIDTH ,
. entry_base_unit = 1000 , /* GB/s */
. entry [ 0 ] = 16 ,
} ;
dslbis3 = g_malloc ( sizeof ( * dslbis3 ) ) ;
if ( ! dslbis3 ) {
return - ENOMEM ;
}
* dslbis3 = ( CDATDslbis ) {
. header = {
. type = CDAT_TYPE_DSLBIS ,
. length = sizeof ( * dslbis3 ) ,
} ,
. handle = dsmad_handle ,
. flags = HMAT_LB_MEM_MEMORY ,
. data_type = HMAT_LB_DATA_WRITE_BANDWIDTH ,
. entry_base_unit = 1000 , /* GB/s */
. entry [ 0 ] = 16 ,
} ;
dsemts = g_malloc ( sizeof ( * dsemts ) ) ;
if ( ! dsemts ) {
return - ENOMEM ;
}
* dsemts = ( CDATDsemts ) {
. header = {
. type = CDAT_TYPE_DSEMTS ,
. length = sizeof ( * dsemts ) ,
} ,
. DSMAS_handle = dsmad_handle ,
/* Reserved - the non volatile from DSMAS matters */
. EFI_memory_type_attr = 2 ,
. DPA_offset = 0 ,
. DPA_length = int128_get64 ( mr - > size ) ,
} ;
/* Header always at start of structure */
cdat_table [ CT3_CDAT_DSMAS ] = g_steal_pointer ( & dsmas ) ;
cdat_table [ CT3_CDAT_DSLBIS0 ] = g_steal_pointer ( & dslbis0 ) ;
cdat_table [ CT3_CDAT_DSLBIS1 ] = g_steal_pointer ( & dslbis1 ) ;
cdat_table [ CT3_CDAT_DSLBIS2 ] = g_steal_pointer ( & dslbis2 ) ;
cdat_table [ CT3_CDAT_DSLBIS3 ] = g_steal_pointer ( & dslbis3 ) ;
cdat_table [ CT3_CDAT_DSEMTS ] = g_steal_pointer ( & dsemts ) ;
return 0 ;
}
static int ct3_build_cdat_table ( CDATSubHeader * * * cdat_table , void * priv )
{
g_autofree CDATSubHeader * * table = NULL ;
MemoryRegion * nonvolatile_mr ;
CXLType3Dev * ct3d = priv ;
int dsmad_handle = 0 ;
int rc ;
if ( ! ct3d - > hostmem ) {
return 0 ;
}
nonvolatile_mr = host_memory_backend_get_memory ( ct3d - > hostmem ) ;
if ( ! nonvolatile_mr ) {
return - EINVAL ;
}
table = g_malloc0 ( CT3_CDAT_NUM_ENTRIES * sizeof ( * table ) ) ;
if ( ! table ) {
return - ENOMEM ;
}
rc = ct3_build_cdat_entries_for_mr ( table , dsmad_handle + + , nonvolatile_mr ) ;
if ( rc < 0 ) {
return rc ;
}
* cdat_table = g_steal_pointer ( & table ) ;
return CT3_CDAT_NUM_ENTRIES ;
}
static void ct3_free_cdat_table ( CDATSubHeader * * cdat_table , int num , void * priv )
{
int i ;
for ( i = 0 ; i < num ; i + + ) {
g_free ( cdat_table [ i ] ) ;
}
g_free ( cdat_table ) ;
}
static bool cxl_doe_cdat_rsp ( DOECap * doe_cap )
{
CDATObject * cdat = & CXL_TYPE3 ( doe_cap - > pdev ) - > cxl_cstate . cdat ;
uint16_t ent ;
void * base ;
uint32_t len ;
CDATReq * req = pcie_doe_get_write_mbox_ptr ( doe_cap ) ;
CDATRsp rsp ;
assert ( cdat - > entry_len ) ;
/* Discard if request length mismatched */
if ( pcie_doe_get_obj_len ( req ) <
DIV_ROUND_UP ( sizeof ( CDATReq ) , DWORD_BYTE ) ) {
return false ;
}
ent = req - > entry_handle ;
base = cdat - > entry [ ent ] . base ;
len = cdat - > entry [ ent ] . length ;
rsp = ( CDATRsp ) {
. header = {
. vendor_id = CXL_VENDOR_ID ,
. data_obj_type = CXL_DOE_TABLE_ACCESS ,
. reserved = 0x0 ,
. length = DIV_ROUND_UP ( ( sizeof ( rsp ) + len ) , DWORD_BYTE ) ,
} ,
. rsp_code = CXL_DOE_TAB_RSP ,
. table_type = CXL_DOE_TAB_TYPE_CDAT ,
. entry_handle = ( ent < cdat - > entry_len - 1 ) ?
ent + 1 : CXL_DOE_TAB_ENT_MAX ,
} ;
memcpy ( doe_cap - > read_mbox , & rsp , sizeof ( rsp ) ) ;
memcpy ( doe_cap - > read_mbox + DIV_ROUND_UP ( sizeof ( rsp ) , DWORD_BYTE ) ,
base , len ) ;
doe_cap - > read_mbox_len + = rsp . header . length ;
return true ;
}
static uint32_t ct3d_config_read ( PCIDevice * pci_dev , uint32_t addr , int size )
{
CXLType3Dev * ct3d = CXL_TYPE3 ( pci_dev ) ;
uint32_t val ;
if ( pcie_doe_read_config ( & ct3d - > doe_cdat , addr , size , & val ) ) {
return val ;
}
return pci_default_read_config ( pci_dev , addr , size ) ;
}
static void ct3d_config_write ( PCIDevice * pci_dev , uint32_t addr , uint32_t val ,
int size )
{
CXLType3Dev * ct3d = CXL_TYPE3 ( pci_dev ) ;
pcie_doe_write_config ( & ct3d - > doe_cdat , addr , val , size ) ;
pci_default_write_config ( pci_dev , addr , val , size ) ;
}
/*
* Null value of all Fs suggested by IEEE RA guidelines for use of
* EU , OUI and CID
@ -140,6 +377,11 @@ static bool cxl_setup_memory(CXLType3Dev *ct3d, Error **errp)
return true ;
}
static DOEProtocol doe_cdat_prot [ ] = {
{ CXL_VENDOR_ID , CXL_DOE_TABLE_ACCESS , cxl_doe_cdat_rsp } ,
{ }
} ;
static void ct3_realize ( PCIDevice * pci_dev , Error * * errp )
{
CXLType3Dev * ct3d = CXL_TYPE3 ( pci_dev ) ;
@ -189,6 +431,14 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp)
for ( i = 0 ; i < msix_num ; i + + ) {
msix_vector_use ( pci_dev , i ) ;
}
/* DOE Initailization */
pcie_doe_init ( pci_dev , & ct3d - > doe_cdat , 0x190 , doe_cdat_prot , true , 0 ) ;
cxl_cstate - > cdat . build_cdat_table = ct3_build_cdat_table ;
cxl_cstate - > cdat . free_cdat_table = ct3_free_cdat_table ;
cxl_cstate - > cdat . private = ct3d ;
cxl_doe_cdat_init ( cxl_cstate , errp ) ;
}
static void ct3_exit ( PCIDevice * pci_dev )
@ -197,6 +447,7 @@ static void ct3_exit(PCIDevice *pci_dev)
CXLComponentState * cxl_cstate = & ct3d - > cxl_cstate ;
ComponentRegisters * regs = & cxl_cstate - > crb ;
cxl_doe_cdat_release ( cxl_cstate ) ;
g_free ( regs - > special_ops ) ;
address_space_destroy ( & ct3d - > hostmem_as ) ;
}
@ -296,6 +547,7 @@ static Property ct3_props[] = {
DEFINE_PROP_LINK ( " lsa " , CXLType3Dev , lsa , TYPE_MEMORY_BACKEND ,
HostMemoryBackend * ) ,
DEFINE_PROP_UINT64 ( " sn " , CXLType3Dev , sn , UI64_NULL ) ,
DEFINE_PROP_STRING ( " cdat " , CXLType3Dev , cxl_cstate . cdat . filename ) ,
DEFINE_PROP_END_OF_LIST ( ) ,
} ;
@ -361,6 +613,9 @@ static void ct3_class_init(ObjectClass *oc, void *data)
pc - > device_id = 0xd93 ; /* LVF for now */
pc - > revision = 1 ;
pc - > config_write = ct3d_config_write ;
pc - > config_read = ct3d_config_read ;
set_bit ( DEVICE_CATEGORY_STORAGE , dc - > categories ) ;
dc - > desc = " CXL PMEM Device (Type 3) " ;
dc - > reset = ct3d_reset ;