@ -1135,6 +1135,10 @@ enum CXL_SUPPORTED_FEATURES_LIST {
CXL_FEATURE_ECS ,
CXL_FEATURE_SPPR ,
CXL_FEATURE_HPPR ,
CXL_FEATURE_CACHELINE_SPARING ,
CXL_FEATURE_ROW_SPARING ,
CXL_FEATURE_BANK_SPARING ,
CXL_FEATURE_RANK_SPARING ,
CXL_FEATURE_MAX
} ;
@ -1223,6 +1227,35 @@ typedef struct CXLMemECSSetFeature {
CXLMemECSWriteAttrs feat_data [ ] ;
} QEMU_PACKED QEMU_ALIGNED ( 16 ) CXLMemECSSetFeature ;
/*
* CXL r3 .2 section 8.2 .10 .7 .2 .3 :
* Memory Sparing Features Discovery and Configuration
*/
static const QemuUUID cacheline_sparing_uuid = {
. data = UUID ( 0x96C33386 , 0x91dd , 0x44c7 , 0x9e , 0xcb ,
0xfd , 0xaf , 0x65 , 0x03 , 0xba , 0xc4 )
} ;
static const QemuUUID row_sparing_uuid = {
. data = UUID ( 0x450ebf67 , 0xb135 , 0x4f97 , 0xa4 , 0x98 ,
0xc2 , 0xd5 , 0x7f , 0x27 , 0x9b , 0xed )
} ;
static const QemuUUID bank_sparing_uuid = {
. data = UUID ( 0x78b79636 , 0x90ac , 0x4b64 , 0xa4 , 0xef ,
0xfa , 0xac , 0x5d , 0x18 , 0xa8 , 0x63 )
} ;
static const QemuUUID rank_sparing_uuid = {
. data = UUID ( 0x34dbaff5 , 0x0552 , 0x4281 , 0x8f , 0x76 ,
0xda , 0x0b , 0x5e , 0x7a , 0x76 , 0xa7 )
} ;
typedef struct CXLMemSparingSetFeature {
CXLSetFeatureInHeader hdr ;
CXLMemSparingWriteAttrs feat_data ;
} QEMU_PACKED QEMU_ALIGNED ( 16 ) CXLMemSparingSetFeature ;
/* CXL r3.1 section 8.2.9.6.1: Get Supported Features (Opcode 0500h) */
static CXLRetCode cmd_features_get_supported ( const struct cxl_cmd * cmd ,
uint8_t * payload_in ,
@ -1323,6 +1356,70 @@ static CXLRetCode cmd_features_get_supported(const struct cxl_cmd *cmd,
CXL_FEAT_ENTRY_SFE_CEL_VALID ,
} ;
break ;
case CXL_FEATURE_CACHELINE_SPARING :
/* Fill supported feature entry for Cacheline Memory Sparing */
get_feats_out - > feat_entries [ entry + + ] =
( struct CXLSupportedFeatureEntry ) {
. uuid = cacheline_sparing_uuid ,
. feat_index = index ,
. get_feat_size = sizeof ( CXLMemSparingReadAttrs ) ,
. set_feat_size = sizeof ( CXLMemSparingWriteAttrs ) ,
. attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL ,
. get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION ,
. set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION ,
. set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
CXL_FEAT_ENTRY_SFE_CEL_VALID ,
} ;
break ;
case CXL_FEATURE_ROW_SPARING :
/* Fill supported feature entry for Row Memory Sparing */
get_feats_out - > feat_entries [ entry + + ] =
( struct CXLSupportedFeatureEntry ) {
. uuid = row_sparing_uuid ,
. feat_index = index ,
. get_feat_size = sizeof ( CXLMemSparingReadAttrs ) ,
. set_feat_size = sizeof ( CXLMemSparingWriteAttrs ) ,
. attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL ,
. get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION ,
. set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION ,
. set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
CXL_FEAT_ENTRY_SFE_CEL_VALID ,
} ;
break ;
case CXL_FEATURE_BANK_SPARING :
/* Fill supported feature entry for Bank Memory Sparing */
get_feats_out - > feat_entries [ entry + + ] =
( struct CXLSupportedFeatureEntry ) {
. uuid = bank_sparing_uuid ,
. feat_index = index ,
. get_feat_size = sizeof ( CXLMemSparingReadAttrs ) ,
. set_feat_size = sizeof ( CXLMemSparingWriteAttrs ) ,
. attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL ,
. get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION ,
. set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION ,
. set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
CXL_FEAT_ENTRY_SFE_CEL_VALID ,
} ;
break ;
case CXL_FEATURE_RANK_SPARING :
/* Fill supported feature entry for Rank Memory Sparing */
get_feats_out - > feat_entries [ entry + + ] =
( struct CXLSupportedFeatureEntry ) {
. uuid = rank_sparing_uuid ,
. feat_index = index ,
. get_feat_size = sizeof ( CXLMemSparingReadAttrs ) ,
. set_feat_size = sizeof ( CXLMemSparingWriteAttrs ) ,
. attr_flags = CXL_FEAT_ENTRY_ATTR_FLAG_CHANGABLE |
CXL_FEAT_ENTRY_ATTR_FLAG_SUPPORT_DEFAULT_SEL ,
. get_feat_version = CXL_MEMDEV_SPARING_GET_FEATURE_VERSION ,
. set_feat_version = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION ,
. set_feat_effects = CXL_FEAT_ENTRY_SFE_IMMEDIATE_CONFIG_CHANGE |
CXL_FEAT_ENTRY_SFE_CEL_VALID ,
} ;
break ;
default :
__builtin_unreachable ( ) ;
}
@ -1411,6 +1508,47 @@ static CXLRetCode cmd_features_get_feature(const struct cxl_cmd *cmd,
memcpy ( payload_out ,
( uint8_t * ) & ct3d - > hard_ppr_attrs + get_feature - > offset ,
bytes_to_copy ) ;
} else if ( qemu_uuid_is_equal ( & get_feature - > uuid ,
& cacheline_sparing_uuid ) ) {
if ( get_feature - > offset > = sizeof ( CXLMemSparingReadAttrs ) ) {
return CXL_MBOX_INVALID_INPUT ;
}
bytes_to_copy = sizeof ( CXLMemSparingReadAttrs ) -
get_feature - > offset ;
bytes_to_copy = MIN ( bytes_to_copy , get_feature - > count ) ;
memcpy ( payload_out ,
( uint8_t * ) & ct3d - > cacheline_sparing_attrs + get_feature - > offset ,
bytes_to_copy ) ;
} else if ( qemu_uuid_is_equal ( & get_feature - > uuid , & row_sparing_uuid ) ) {
if ( get_feature - > offset > = sizeof ( CXLMemSparingReadAttrs ) ) {
return CXL_MBOX_INVALID_INPUT ;
}
bytes_to_copy = sizeof ( CXLMemSparingReadAttrs ) -
get_feature - > offset ;
bytes_to_copy = MIN ( bytes_to_copy , get_feature - > count ) ;
memcpy ( payload_out ,
( uint8_t * ) & ct3d - > row_sparing_attrs + get_feature - > offset ,
bytes_to_copy ) ;
} else if ( qemu_uuid_is_equal ( & get_feature - > uuid , & bank_sparing_uuid ) ) {
if ( get_feature - > offset > = sizeof ( CXLMemSparingReadAttrs ) ) {
return CXL_MBOX_INVALID_INPUT ;
}
bytes_to_copy = sizeof ( CXLMemSparingReadAttrs ) -
get_feature - > offset ;
bytes_to_copy = MIN ( bytes_to_copy , get_feature - > count ) ;
memcpy ( payload_out ,
( uint8_t * ) & ct3d - > bank_sparing_attrs + get_feature - > offset ,
bytes_to_copy ) ;
} else if ( qemu_uuid_is_equal ( & get_feature - > uuid , & rank_sparing_uuid ) ) {
if ( get_feature - > offset > = sizeof ( CXLMemSparingReadAttrs ) ) {
return CXL_MBOX_INVALID_INPUT ;
}
bytes_to_copy = sizeof ( CXLMemSparingReadAttrs ) -
get_feature - > offset ;
bytes_to_copy = MIN ( bytes_to_copy , get_feature - > count ) ;
memcpy ( payload_out ,
( uint8_t * ) & ct3d - > rank_sparing_attrs + get_feature - > offset ,
bytes_to_copy ) ;
} else {
return CXL_MBOX_UNSUPPORTED ;
}
@ -1560,6 +1698,78 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
ct3d - > hard_ppr_attrs . hppr_op_mode =
ct3d - > hard_ppr_wr_attrs . hppr_op_mode ;
}
} else if ( qemu_uuid_is_equal ( & hdr - > uuid , & cacheline_sparing_uuid ) ) {
CXLMemSparingSetFeature * mem_sparing_set_feature = ( void * ) payload_in ;
CXLMemSparingWriteAttrs * mem_sparing_write_attrs =
& mem_sparing_set_feature - > feat_data ;
if ( hdr - > version ! = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION ) {
return CXL_MBOX_UNSUPPORTED ;
}
memcpy ( ( uint8_t * ) & ct3d - > cacheline_sparing_wr_attrs + hdr - > offset ,
mem_sparing_write_attrs , bytes_to_copy ) ;
set_feat_info - > data_size + = bytes_to_copy ;
if ( data_transfer_flag = = CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER | |
data_transfer_flag = = CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER ) {
ct3d - > cacheline_sparing_attrs . op_mode =
ct3d - > cacheline_sparing_wr_attrs . op_mode ;
}
} else if ( qemu_uuid_is_equal ( & hdr - > uuid , & row_sparing_uuid ) ) {
CXLMemSparingSetFeature * mem_sparing_set_feature = ( void * ) payload_in ;
CXLMemSparingWriteAttrs * mem_sparing_write_attrs =
& mem_sparing_set_feature - > feat_data ;
if ( hdr - > version ! = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION ) {
return CXL_MBOX_UNSUPPORTED ;
}
memcpy ( ( uint8_t * ) & ct3d - > row_sparing_wr_attrs + hdr - > offset ,
mem_sparing_write_attrs , bytes_to_copy ) ;
set_feat_info - > data_size + = bytes_to_copy ;
if ( data_transfer_flag = = CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER | |
data_transfer_flag = = CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER ) {
ct3d - > row_sparing_attrs . op_mode =
ct3d - > row_sparing_wr_attrs . op_mode ;
}
} else if ( qemu_uuid_is_equal ( & hdr - > uuid , & bank_sparing_uuid ) ) {
CXLMemSparingSetFeature * mem_sparing_set_feature = ( void * ) payload_in ;
CXLMemSparingWriteAttrs * mem_sparing_write_attrs =
& mem_sparing_set_feature - > feat_data ;
if ( hdr - > version ! = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION ) {
return CXL_MBOX_UNSUPPORTED ;
}
memcpy ( ( uint8_t * ) & ct3d - > bank_sparing_wr_attrs + hdr - > offset ,
mem_sparing_write_attrs , bytes_to_copy ) ;
set_feat_info - > data_size + = bytes_to_copy ;
if ( data_transfer_flag = = CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER | |
data_transfer_flag = = CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER ) {
ct3d - > bank_sparing_attrs . op_mode =
ct3d - > bank_sparing_wr_attrs . op_mode ;
}
} else if ( qemu_uuid_is_equal ( & hdr - > uuid , & rank_sparing_uuid ) ) {
CXLMemSparingSetFeature * mem_sparing_set_feature = ( void * ) payload_in ;
CXLMemSparingWriteAttrs * mem_sparing_write_attrs =
& mem_sparing_set_feature - > feat_data ;
if ( hdr - > version ! = CXL_MEMDEV_SPARING_SET_FEATURE_VERSION ) {
return CXL_MBOX_UNSUPPORTED ;
}
memcpy ( ( uint8_t * ) & ct3d - > rank_sparing_wr_attrs + hdr - > offset ,
mem_sparing_write_attrs , bytes_to_copy ) ;
set_feat_info - > data_size + = bytes_to_copy ;
if ( data_transfer_flag = = CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER | |
data_transfer_flag = = CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER ) {
ct3d - > rank_sparing_attrs . op_mode =
ct3d - > rank_sparing_wr_attrs . op_mode ;
}
} else {
return CXL_MBOX_UNSUPPORTED ;
}
@ -1576,6 +1786,15 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
memset ( & ct3d - > soft_ppr_wr_attrs , 0 , set_feat_info - > data_size ) ;
} else if ( qemu_uuid_is_equal ( & hdr - > uuid , & hard_ppr_uuid ) ) {
memset ( & ct3d - > hard_ppr_wr_attrs , 0 , set_feat_info - > data_size ) ;
} else if ( qemu_uuid_is_equal ( & hdr - > uuid , & cacheline_sparing_uuid ) ) {
memset ( & ct3d - > cacheline_sparing_wr_attrs , 0 ,
set_feat_info - > data_size ) ;
} else if ( qemu_uuid_is_equal ( & hdr - > uuid , & row_sparing_uuid ) ) {
memset ( & ct3d - > row_sparing_wr_attrs , 0 , set_feat_info - > data_size ) ;
} else if ( qemu_uuid_is_equal ( & hdr - > uuid , & bank_sparing_uuid ) ) {
memset ( & ct3d - > bank_sparing_wr_attrs , 0 , set_feat_info - > data_size ) ;
} else if ( qemu_uuid_is_equal ( & hdr - > uuid , & rank_sparing_uuid ) ) {
memset ( & ct3d - > rank_sparing_wr_attrs , 0 , set_feat_info - > data_size ) ;
}
set_feat_info - > data_transfer_flag = 0 ;
set_feat_info - > data_saved_across_reset = false ;
@ -1586,9 +1805,27 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,
return CXL_MBOX_SUCCESS ;
}
static void cxl_mbox_create_mem_sparing_event_records ( CXLType3Dev * ct3d ,
# define CXL_MEM_SPARING_FLAGS_QUERY_RESOURCES BIT(0)
# define CXL_MEM_SPARING_FLAGS_HARD_SPARING BIT(1)
# define CXL_MEM_SPARING_FLAGS_SUB_CHANNEL_VALID BIT(2)
# define CXL_MEM_SPARING_FLAGS_NIB_MASK_VALID BIT(3)
typedef struct CXLMemSparingMaintInPayload {
uint8_t flags ;
uint8_t channel ;
uint8_t rank ;
uint8_t nibble_mask [ 3 ] ;
uint8_t bank_group ;
uint8_t bank ;
uint8_t row [ 3 ] ;
uint16_t column ;
uint8_t sub_channel ;
} QEMU_PACKED CXLMemSparingMaintInPayload ;
static void cxl_create_mem_sparing_event_records ( CXLType3Dev * ct3d ,
uint8_t maint_op_class , uint8_t maint_op_sub_class ,
CXLMaintenance * ent )
CXLMaintenance * ent ,
CXLMemSparingMaintInPayload * sparing_pi )
{
CXLEventSparing event_rec = { } ;
@ -1616,6 +1853,31 @@ static void cxl_mbox_create_mem_sparing_event_records(CXLType3Dev *ct3d,
strncpy ( ( char * ) event_rec . component_id , ( char * ) ent - > component_id ,
sizeof ( event_rec . component_id ) ) ;
}
} else if ( sparing_pi ) {
event_rec . flags = CXL_MSER_FLAGS_QUERY_RESOURCES ;
event_rec . result = 0 ;
event_rec . validity_flags = CXL_MSER_VALID_CHANNEL |
CXL_MSER_VALID_RANK |
CXL_MSER_VALID_NIB_MASK |
CXL_MSER_VALID_BANK_GROUP |
CXL_MSER_VALID_BANK |
CXL_MSER_VALID_ROW |
CXL_MSER_VALID_COLUMN ;
event_rec . res_avail = 1 ;
event_rec . channel = sparing_pi - > channel ;
event_rec . rank = sparing_pi - > rank ;
if ( sparing_pi - > flags & CXL_MEM_SPARING_FLAGS_NIB_MASK_VALID ) {
memcpy ( event_rec . nibble_mask , sparing_pi - > nibble_mask ,
sizeof ( sparing_pi - > nibble_mask ) ) ;
}
event_rec . bank_group = sparing_pi - > bank_group ;
event_rec . bank = sparing_pi - > bank ;
event_rec . column = sparing_pi - > column ;
memcpy ( event_rec . row , sparing_pi - > row , sizeof ( sparing_pi - > row ) ) ;
if ( sparing_pi - > flags & CXL_MEM_SPARING_FLAGS_SUB_CHANNEL_VALID ) {
event_rec . sub_channel = sparing_pi - > sub_channel ;
event_rec . validity_flags | = CXL_MSER_VALID_SUB_CHANNEL ;
}
} else {
return ;
}
@ -1627,6 +1889,26 @@ static void cxl_mbox_create_mem_sparing_event_records(CXLType3Dev *ct3d,
}
}
static CXLRetCode cxl_perform_mem_sparing ( CXLType3Dev * ct3d , uint8_t sub_class ,
void * maint_pi )
{
switch ( sub_class ) {
case CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING :
qemu_log ( " Cacheline Memory Sparing \n " ) ;
return CXL_MBOX_SUCCESS ;
case CXL_MEMDEV_MAINT_SUBCLASS_ROW_SPARING :
qemu_log ( " Row Memory Sparing \n " ) ;
return CXL_MBOX_SUCCESS ;
case CXL_MEMDEV_MAINT_SUBCLASS_BANK_SPARING :
qemu_log ( " Bank Memory Sparing \n " ) ;
return CXL_MBOX_SUCCESS ;
case CXL_MEMDEV_MAINT_SUBCLASS_RANK_SPARING :
qemu_log ( " Rank Memory Sparing \n " ) ;
return CXL_MBOX_SUCCESS ;
default :
return CXL_MBOX_INVALID_INPUT ;
}
}
static void cxl_perform_ppr ( CXLType3Dev * ct3d , uint64_t dpa )
{
@ -1637,10 +1919,10 @@ static void cxl_perform_ppr(CXLType3Dev *ct3d, uint64_t dpa)
/* Produce a Memory Sparing Event Record */
if ( ct3d - > soft_ppr_attrs . sppr_op_mode &
CXL_MEMDEV_SPPR_OP_MODE_MEM_SPARING_EV_REC_EN ) {
cxl_mbox_ create_mem_sparing_event_records ( ct3d ,
cxl_create_mem_sparing_event_records ( ct3d ,
CXL_MEMDEV_MAINT_CLASS_SPARING ,
CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING ,
ent ) ;
ent , NULL ) ;
}
break ;
}
@ -1664,6 +1946,7 @@ static CXLRetCode cmd_media_perform_maintenance(const struct cxl_cmd *cmd,
uint64_t dpa ;
uint8_t nibble_mask [ 3 ] ;
} QEMU_PACKED ppr ;
CXLMemSparingMaintInPayload mem_sparing_pi ;
} ;
} QEMU_PACKED * maint_in = ( void * ) payload_in ;
CXLType3Dev * ct3d = CXL_TYPE3 ( cci - > d ) ;
@ -1689,6 +1972,32 @@ static CXLRetCode cmd_media_perform_maintenance(const struct cxl_cmd *cmd,
return CXL_MBOX_INVALID_INPUT ;
}
break ;
case CXL_MEMDEV_MAINT_CLASS_SPARING :
if ( maint_in - > mem_sparing_pi . flags &
CXL_MEM_SPARING_FLAGS_QUERY_RESOURCES ) {
/*
* CXL r3 .2 sect 8.2 .10 .7 .1 .4 - Memory Sparing Maintenance Operation
* Produce Memory Sparing Event record to report resources
* availability .
*/
cxl_create_mem_sparing_event_records ( ct3d , maint_in - > class ,
maint_in - > subclass , NULL ,
& maint_in - > mem_sparing_pi ) ;
return CXL_MBOX_SUCCESS ;
}
switch ( maint_in - > subclass ) {
case CXL_MEMDEV_MAINT_SUBCLASS_CACHELINE_SPARING :
case CXL_MEMDEV_MAINT_SUBCLASS_ROW_SPARING :
case CXL_MEMDEV_MAINT_SUBCLASS_BANK_SPARING :
case CXL_MEMDEV_MAINT_SUBCLASS_RANK_SPARING :
return cxl_perform_mem_sparing ( ct3d , maint_in - > subclass ,
& maint_in - > mem_sparing_pi ) ;
default :
return CXL_MBOX_INVALID_INPUT ;
}
break ;
default :
return CXL_MBOX_INVALID_INPUT ;
}