// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2020 HabanaLabs , Ltd .
* All Rights Reserved .
*/
#include "habanalabs.h"
static const char * const hl_glbl_error_cause[] = {
"Error due to un-priv read" ,
"Error due to un-secure read" ,
"Error due to read from unmapped reg" ,
"Error due to un-priv write" ,
"Error due to un-secure write" ,
"Error due to write to unmapped reg" ,
"N/A" ,
"N/A" ,
"N/A" ,
"N/A" ,
"N/A" ,
"N/A" ,
"N/A" ,
"N/A" ,
"N/A" ,
"N/A" ,
"External I/F write sec violation" ,
"External I/F write to un-mapped reg" ,
"N/A" ,
"N/A" ,
"N/A" ,
"N/A" ,
"N/A" ,
"N/A" ,
"Read to write only" ,
"Write to read only"
};
/**
* hl_get_pb_block - return the relevant block within the block array
*
* @ hdev : pointer to hl_device structure
* @ mm_reg_addr : register address in the desired block
* @ pb_blocks : blocks array
* @ array_size : blocks array size
*
*/
static int hl_get_pb_block(struct hl_device *hdev, u32 mm_reg_addr,
const u32 pb_blocks[], int array_size)
{
int i;
u32 start_addr, end_addr;
for (i = 0 ; i < array_size ; i++) {
start_addr = pb_blocks[i];
end_addr = start_addr + HL_BLOCK_SIZE;
if ((mm_reg_addr >= start_addr) && (mm_reg_addr < end_addr))
return i;
}
dev_err(hdev->dev, "No protection domain was found for 0x%x\n" ,
mm_reg_addr);
return -EDOM;
}
/**
* hl_unset_pb_in_block - clear a specific protection bit in a block
*
* @ hdev : pointer to hl_device structure
* @ reg_offset : register offset will be converted to bit offset in pb block
* @ sgs_entry : pb array
*
*/
static int hl_unset_pb_in_block(struct hl_device *hdev, u32 reg_offset,
struct hl_block_glbl_sec *sgs_entry)
{
if ((reg_offset >= HL_BLOCK_SIZE) || (reg_offset & 0 x3)) {
dev_err(hdev->dev,
"Register offset(%d) is out of range(%d) or invalid\n" ,
reg_offset, HL_BLOCK_SIZE);
return -EINVAL;
}
UNSET_GLBL_SEC_BIT(sgs_entry->sec_array,
(reg_offset & (HL_BLOCK_SIZE - 1 )) >> 2 );
return 0 ;
}
/**
* hl_unsecure_register - locate the relevant block for this register and
* remove corresponding protection bit
*
* @ hdev : pointer to hl_device structure
* @ mm_reg_addr : register address to unsecure
* @ offset : additional offset to the register address
* @ pb_blocks : blocks array
* @ sgs_array : pb array
* @ array_size : blocks array size
*
*/
int hl_unsecure_register(struct hl_device *hdev, u32 mm_reg_addr, int offset,
const u32 pb_blocks[], struct hl_block_glbl_sec sgs_array[],
int array_size)
{
u32 reg_offset;
int block_num;
block_num = hl_get_pb_block(hdev, mm_reg_addr + offset, pb_blocks,
array_size);
if (block_num < 0 )
return block_num;
reg_offset = (mm_reg_addr + offset) - pb_blocks[block_num];
return hl_unset_pb_in_block(hdev, reg_offset, &sgs_array[block_num]);
}
/**
* hl_unsecure_register_range - locate the relevant block for this register
* range and remove corresponding protection bit
*
* @ hdev : pointer to hl_device structure
* @ mm_reg_range : register address range to unsecure
* @ offset : additional offset to the register address
* @ pb_blocks : blocks array
* @ sgs_array : pb array
* @ array_size : blocks array size
*
*/
static int hl_unsecure_register_range(struct hl_device *hdev,
struct range mm_reg_range, int offset, const u32 pb_blocks[],
struct hl_block_glbl_sec sgs_array[],
int array_size)
{
u32 reg_offset;
int i, block_num, rc = 0 ;
block_num = hl_get_pb_block(hdev,
mm_reg_range.start + offset, pb_blocks,
array_size);
if (block_num < 0 )
return block_num;
for (i = mm_reg_range.start ; i <= mm_reg_range.end ; i += 4 ) {
reg_offset = (i + offset) - pb_blocks[block_num];
rc |= hl_unset_pb_in_block(hdev, reg_offset,
&sgs_array[block_num]);
}
return rc;
}
/**
* hl_unsecure_registers - locate the relevant block for all registers and
* remove corresponding protection bit
*
* @ hdev : pointer to hl_device structure
* @ mm_reg_array : register address array to unsecure
* @ mm_array_size : register array size
* @ offset : additional offset to the register address
* @ pb_blocks : blocks array
* @ sgs_array : pb array
* @ blocks_array_size : blocks array size
*
*/
int hl_unsecure_registers(struct hl_device *hdev, const u32 mm_reg_array[],
int mm_array_size, int offset, const u32 pb_blocks[],
struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
{
int i, rc = 0 ;
for (i = 0 ; i < mm_array_size ; i++) {
rc = hl_unsecure_register(hdev, mm_reg_array[i], offset,
pb_blocks, sgs_array, blocks_array_size);
if (rc)
return rc;
}
return rc;
}
/**
* hl_unsecure_registers_range - locate the relevant block for all register
* ranges and remove corresponding protection bit
*
* @ hdev : pointer to hl_device structure
* @ mm_reg_range_array : register address range array to unsecure
* @ mm_array_size : register array size
* @ offset : additional offset to the register address
* @ pb_blocks : blocks array
* @ sgs_array : pb array
* @ blocks_array_size : blocks array size
*
*/
static int hl_unsecure_registers_range(struct hl_device *hdev,
const struct range mm_reg_range_array[], int mm_array_size,
int offset, const u32 pb_blocks[],
struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
{
int i, rc = 0 ;
for (i = 0 ; i < mm_array_size ; i++) {
rc = hl_unsecure_register_range(hdev, mm_reg_range_array[i],
offset, pb_blocks, sgs_array, blocks_array_size);
if (rc)
return rc;
}
return rc;
}
/**
* hl_ack_pb_security_violations - Ack security violation
*
* @ hdev : pointer to hl_device structure
* @ pb_blocks : blocks array
* @ block_offset : additional offset to the block
* @ array_size : blocks array size
*
*/
static void hl_ack_pb_security_violations(struct hl_device *hdev,
const u32 pb_blocks[], u32 block_offset, int array_size)
{
int i;
u32 cause, addr, block_base;
for (i = 0 ; i < array_size ; i++) {
block_base = pb_blocks[i] + block_offset;
cause = RREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE);
if (cause) {
addr = RREG32(block_base + HL_BLOCK_GLBL_ERR_ADDR);
hdev->asic_funcs->pb_print_security_errors(hdev,
block_base, cause, addr);
WREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE, cause);
}
}
}
/**
* hl_config_glbl_sec - set pb in HW according to given pb array
*
* @ hdev : pointer to hl_device structure
* @ pb_blocks : blocks array
* @ sgs_array : pb array
* @ block_offset : additional offset to the block
* @ array_size : blocks array size
*
*/
void hl_config_glbl_sec(struct hl_device *hdev, const u32 pb_blocks[],
struct hl_block_glbl_sec sgs_array[], u32 block_offset,
int array_size)
{
int i, j;
u32 sgs_base;
if (hdev->pldm)
usleep_range(100 , 1000 );
for (i = 0 ; i < array_size ; i++) {
sgs_base = block_offset + pb_blocks[i] +
HL_BLOCK_GLBL_SEC_OFFS;
for (j = 0 ; j < HL_BLOCK_GLBL_SEC_LEN ; j++)
WREG32(sgs_base + j * sizeof (u32),
sgs_array[i].sec_array[j]);
}
}
/**
* hl_secure_block - locally memsets a block to 0
*
* @ hdev : pointer to hl_device structure
* @ sgs_array : pb array to clear
* @ array_size : blocks array size
*
*/
void hl_secure_block(struct hl_device *hdev,
struct hl_block_glbl_sec sgs_array[], int array_size)
{
int i;
for (i = 0 ; i < array_size ; i++)
memset((char *)(sgs_array[i].sec_array), 0 ,
HL_BLOCK_GLBL_SEC_SIZE);
}
/**
* hl_init_pb_with_mask - set selected pb instances with mask in HW according
* to given configuration
*
* @ hdev : pointer to hl_device structure
* @ num_dcores : number of decores to apply configuration to
* set to HL_PB_SHARED if need to apply only once
* @ dcore_offset : offset between dcores
* @ num_instances : number of instances to apply configuration to
* @ instance_offset : offset between instances
* @ pb_blocks : blocks array
* @ blocks_array_size : blocks array size
* @ user_regs_array : unsecured register array
* @ user_regs_array_size : unsecured register array size
* @ mask : enabled instances mask : 1 - enabled , 0 - disabled
*/
int hl_init_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
u32 dcore_offset, u32 num_instances, u32 instance_offset,
const u32 pb_blocks[], u32 blocks_array_size,
const u32 *user_regs_array, u32 user_regs_array_size, u64 mask)
{
int i, j;
struct hl_block_glbl_sec *glbl_sec;
glbl_sec = kcalloc(blocks_array_size,
sizeof (struct hl_block_glbl_sec),
GFP_KERNEL);
if (!glbl_sec)
return -ENOMEM;
hl_secure_block(hdev, glbl_sec, blocks_array_size);
hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size, 0 ,
pb_blocks, glbl_sec, blocks_array_size);
/* Fill all blocks with the same configuration */
for (i = 0 ; i < num_dcores ; i++) {
for (j = 0 ; j < num_instances ; j++) {
int seq = i * num_instances + j;
if (!(mask & BIT_ULL(seq)))
continue ;
hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
i * dcore_offset + j * instance_offset,
blocks_array_size);
}
}
kfree(glbl_sec);
return 0 ;
}
/**
* hl_init_pb - set pb in HW according to given configuration
*
* @ hdev : pointer to hl_device structure
* @ num_dcores : number of decores to apply configuration to
* set to HL_PB_SHARED if need to apply only once
* @ dcore_offset : offset between dcores
* @ num_instances : number of instances to apply configuration to
* @ instance_offset : offset between instances
* @ pb_blocks : blocks array
* @ blocks_array_size : blocks array size
* @ user_regs_array : unsecured register array
* @ user_regs_array_size : unsecured register array size
*
*/
int hl_init_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
u32 num_instances, u32 instance_offset,
const u32 pb_blocks[], u32 blocks_array_size,
const u32 *user_regs_array, u32 user_regs_array_size)
{
return hl_init_pb_with_mask(hdev, num_dcores, dcore_offset,
num_instances, instance_offset, pb_blocks,
blocks_array_size, user_regs_array,
user_regs_array_size, ULLONG_MAX);
}
/**
* hl_init_pb_ranges_with_mask - set pb instances using mask in HW according to
* given configuration unsecurring registers
* ranges instead of specific registers
*
* @ hdev : pointer to hl_device structure
* @ num_dcores : number of decores to apply configuration to
* set to HL_PB_SHARED if need to apply only once
* @ dcore_offset : offset between dcores
* @ num_instances : number of instances to apply configuration to
* @ instance_offset : offset between instances
* @ pb_blocks : blocks array
* @ blocks_array_size : blocks array size
* @ user_regs_range_array : unsecured register range array
* @ user_regs_range_array_size : unsecured register range array size
* @ mask : enabled instances mask : 1 - enabled , 0 - disabled
*/
int hl_init_pb_ranges_with_mask(struct hl_device *hdev, u32 num_dcores,
u32 dcore_offset, u32 num_instances, u32 instance_offset,
const u32 pb_blocks[], u32 blocks_array_size,
const struct range *user_regs_range_array,
u32 user_regs_range_array_size, u64 mask)
{
int i, j, rc = 0 ;
struct hl_block_glbl_sec *glbl_sec;
glbl_sec = kcalloc(blocks_array_size,
sizeof (struct hl_block_glbl_sec),
GFP_KERNEL);
if (!glbl_sec)
return -ENOMEM;
hl_secure_block(hdev, glbl_sec, blocks_array_size);
rc = hl_unsecure_registers_range(hdev, user_regs_range_array,
user_regs_range_array_size, 0 , pb_blocks, glbl_sec,
blocks_array_size);
if (rc)
goto free_glbl_sec;
/* Fill all blocks with the same configuration */
for (i = 0 ; i < num_dcores ; i++) {
for (j = 0 ; j < num_instances ; j++) {
int seq = i * num_instances + j;
if (!(mask & BIT_ULL(seq)))
continue ;
hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
i * dcore_offset + j * instance_offset,
blocks_array_size);
}
}
free_glbl_sec:
kfree(glbl_sec);
return rc;
}
/**
* hl_init_pb_ranges - set pb in HW according to given configuration unsecurring
* registers ranges instead of specific registers
*
* @ hdev : pointer to hl_device structure
* @ num_dcores : number of decores to apply configuration to
* set to HL_PB_SHARED if need to apply only once
* @ dcore_offset : offset between dcores
* @ num_instances : number of instances to apply configuration to
* @ instance_offset : offset between instances
* @ pb_blocks : blocks array
* @ blocks_array_size : blocks array size
* @ user_regs_range_array : unsecured register range array
* @ user_regs_range_array_size : unsecured register range array size
*
*/
int hl_init_pb_ranges(struct hl_device *hdev, u32 num_dcores,
u32 dcore_offset, u32 num_instances, u32 instance_offset,
const u32 pb_blocks[], u32 blocks_array_size,
const struct range *user_regs_range_array,
u32 user_regs_range_array_size)
{
return hl_init_pb_ranges_with_mask(hdev, num_dcores, dcore_offset,
num_instances, instance_offset, pb_blocks,
blocks_array_size, user_regs_range_array,
user_regs_range_array_size, ULLONG_MAX);
}
/**
* hl_init_pb_single_dcore - set pb for a single docre in HW
* according to given configuration
*
* @ hdev : pointer to hl_device structure
* @ dcore_offset : offset from the dcore0
* @ num_instances : number of instances to apply configuration to
* @ instance_offset : offset between instances
* @ pb_blocks : blocks array
* @ blocks_array_size : blocks array size
* @ user_regs_array : unsecured register array
* @ user_regs_array_size : unsecured register array size
*
*/
int hl_init_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
u32 num_instances, u32 instance_offset,
const u32 pb_blocks[], u32 blocks_array_size,
const u32 *user_regs_array, u32 user_regs_array_size)
{
int i, rc = 0 ;
struct hl_block_glbl_sec *glbl_sec;
glbl_sec = kcalloc(blocks_array_size,
sizeof (struct hl_block_glbl_sec),
GFP_KERNEL);
if (!glbl_sec)
return -ENOMEM;
hl_secure_block(hdev, glbl_sec, blocks_array_size);
rc = hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size,
0 , pb_blocks, glbl_sec, blocks_array_size);
if (rc)
goto free_glbl_sec;
/* Fill all blocks with the same configuration */
for (i = 0 ; i < num_instances ; i++)
hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
dcore_offset + i * instance_offset,
blocks_array_size);
free_glbl_sec:
kfree(glbl_sec);
return rc;
}
/**
* hl_init_pb_ranges_single_dcore - set pb for a single docre in HW according
* to given configuration unsecurring
* registers ranges instead of specific
* registers
*
* @ hdev : pointer to hl_device structure
* @ dcore_offset : offset from the dcore0
* @ num_instances : number of instances to apply configuration to
* @ instance_offset : offset between instances
* @ pb_blocks : blocks array
* @ blocks_array_size : blocks array size
* @ user_regs_range_array : unsecured register range array
* @ user_regs_range_array_size : unsecured register range array size
*
*/
int hl_init_pb_ranges_single_dcore(struct hl_device *hdev, u32 dcore_offset,
u32 num_instances, u32 instance_offset,
const u32 pb_blocks[], u32 blocks_array_size,
const struct range *user_regs_range_array, u32 user_regs_range_array_size)
{
int i;
struct hl_block_glbl_sec *glbl_sec;
glbl_sec = kcalloc(blocks_array_size,
sizeof (struct hl_block_glbl_sec),
GFP_KERNEL);
if (!glbl_sec)
return -ENOMEM;
hl_secure_block(hdev, glbl_sec, blocks_array_size);
hl_unsecure_registers_range(hdev, user_regs_range_array,
user_regs_range_array_size, 0 , pb_blocks, glbl_sec,
blocks_array_size);
/* Fill all blocks with the same configuration */
for (i = 0 ; i < num_instances ; i++)
hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
dcore_offset + i * instance_offset,
blocks_array_size);
kfree(glbl_sec);
return 0 ;
}
/**
* hl_ack_pb_with_mask - ack pb with mask in HW according to given configuration
*
* @ hdev : pointer to hl_device structure
* @ num_dcores : number of decores to apply configuration to
* set to HL_PB_SHARED if need to apply only once
* @ dcore_offset : offset between dcores
* @ num_instances : number of instances to apply configuration to
* @ instance_offset : offset between instances
* @ pb_blocks : blocks array
* @ blocks_array_size : blocks array size
* @ mask : enabled instances mask : 1 - enabled , 0 - disabled
*
*/
void hl_ack_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
u32 dcore_offset, u32 num_instances, u32 instance_offset,
const u32 pb_blocks[], u32 blocks_array_size, u64 mask)
{
int i, j;
/* ack all blocks */
for (i = 0 ; i < num_dcores ; i++) {
for (j = 0 ; j < num_instances ; j++) {
int seq = i * num_instances + j;
if (!(mask & BIT_ULL(seq)))
continue ;
hl_ack_pb_security_violations(hdev, pb_blocks,
i * dcore_offset + j * instance_offset,
blocks_array_size);
}
}
}
/**
* hl_ack_pb - ack pb in HW according to given configuration
*
* @ hdev : pointer to hl_device structure
* @ num_dcores : number of decores to apply configuration to
* set to HL_PB_SHARED if need to apply only once
* @ dcore_offset : offset between dcores
* @ num_instances : number of instances to apply configuration to
* @ instance_offset : offset between instances
* @ pb_blocks : blocks array
* @ blocks_array_size : blocks array size
*
*/
void hl_ack_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
u32 num_instances, u32 instance_offset,
const u32 pb_blocks[], u32 blocks_array_size)
{
hl_ack_pb_with_mask(hdev, num_dcores, dcore_offset, num_instances,
instance_offset, pb_blocks, blocks_array_size,
ULLONG_MAX);
}
/**
* hl_ack_pb_single_dcore - ack pb for single docre in HW
* according to given configuration
*
* @ hdev : pointer to hl_device structure
* @ dcore_offset : offset from dcore0
* @ num_instances : number of instances to apply configuration to
* @ instance_offset : offset between instances
* @ pb_blocks : blocks array
* @ blocks_array_size : blocks array size
*
*/
void hl_ack_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
u32 num_instances, u32 instance_offset,
const u32 pb_blocks[], u32 blocks_array_size)
{
int i;
/* ack all blocks */
for (i = 0 ; i < num_instances ; i++)
hl_ack_pb_security_violations(hdev, pb_blocks,
dcore_offset + i * instance_offset,
blocks_array_size);
}
static u32 hl_automated_get_block_base_addr(struct hl_device *hdev,
struct hl_special_block_info *block_info,
u32 major, u32 minor, u32 sub_minor)
{
u32 fw_block_base_address = block_info->base_addr +
major * block_info->major_offset +
minor * block_info->minor_offset +
sub_minor * block_info->sub_minor_offset;
struct asic_fixed_properties *prop = &hdev->asic_prop;
/* Calculation above returns an address for FW use, and therefore should
* be casted for driver use .
*/
return (fw_block_base_address - lower_32_bits(prop->cfg_base_address));
}
static bool hl_check_block_type_exclusion(struct hl_skip_blocks_cfg *skip_blocks_cfg,
int block_type)
{
int i;
/* Check if block type is listed in the exclusion list of block types */
for (i = 0 ; i < skip_blocks_cfg->block_types_len ; i++)
if (block_type == skip_blocks_cfg->block_types[i])
return true ;
return false ;
}
static bool hl_check_block_range_exclusion(struct hl_device *hdev,
struct hl_skip_blocks_cfg *skip_blocks_cfg,
struct hl_special_block_info *block_info,
u32 major, u32 minor, u32 sub_minor)
{
u32 blocks_in_range, block_base_addr_in_range, block_base_addr;
int i, j;
block_base_addr = hl_automated_get_block_base_addr(hdev, block_info,
major, minor, sub_minor);
for (i = 0 ; i < skip_blocks_cfg->block_ranges_len ; i++) {
blocks_in_range = (skip_blocks_cfg->block_ranges[i].end -
skip_blocks_cfg->block_ranges[i].start) /
HL_BLOCK_SIZE + 1 ;
for (j = 0 ; j < blocks_in_range ; j++) {
block_base_addr_in_range = skip_blocks_cfg->block_ranges[i].start +
j * HL_BLOCK_SIZE;
if (block_base_addr == block_base_addr_in_range)
return true ;
}
}
return false ;
}
static int hl_read_glbl_errors(struct hl_device *hdev,
u32 blk_idx, u32 major, u32 minor, u32 sub_minor, void *data)
{
struct asic_fixed_properties *prop = &hdev->asic_prop;
struct hl_special_block_info *special_blocks = prop->special_blocks;
struct hl_special_block_info *current_block = &special_blocks[blk_idx];
u32 glbl_err_addr, glbl_err_cause, addr_val, cause_val, block_base,
base = current_block->base_addr - lower_32_bits(prop->cfg_base_address);
int i;
block_base = base + major * current_block->major_offset +
minor * current_block->minor_offset +
sub_minor * current_block->sub_minor_offset;
glbl_err_cause = block_base + HL_GLBL_ERR_CAUSE_OFFSET;
cause_val = RREG32(glbl_err_cause);
if (!cause_val)
return 0 ;
glbl_err_addr = block_base + HL_GLBL_ERR_ADDR_OFFSET;
addr_val = RREG32(glbl_err_addr);
for (i = 0 ; i <= prop->glbl_err_max_cause_num ; i++) {
if (cause_val & BIT(i))
dev_err_ratelimited(hdev->dev,
"%s, addr %#llx\n" ,
hl_glbl_error_cause[i],
prop->cfg_base_address + block_base +
FIELD_GET(HL_GLBL_ERR_ADDRESS_MASK, addr_val));
}
WREG32(glbl_err_cause, cause_val);
return 0 ;
}
void hl_check_for_glbl_errors(struct hl_device *hdev)
{
struct asic_fixed_properties *prop = &hdev->asic_prop;
struct hl_special_blocks_cfg special_blocks_cfg;
struct iterate_special_ctx glbl_err_iter;
int rc;
memset(&special_blocks_cfg, 0 , sizeof (special_blocks_cfg));
special_blocks_cfg.skip_blocks_cfg = &prop->skip_special_blocks_cfg;
glbl_err_iter.fn = &hl_read_glbl_errors;
glbl_err_iter.data = &special_blocks_cfg;
rc = hl_iterate_special_blocks(hdev, &glbl_err_iter);
if (rc)
dev_err_ratelimited(hdev->dev,
"Could not iterate special blocks, glbl error check failed\n" );
}
int hl_iterate_special_blocks(struct hl_device *hdev, struct iterate_special_ctx *ctx)
{
struct hl_special_blocks_cfg *special_blocks_cfg =
(struct hl_special_blocks_cfg *)ctx->data;
struct hl_skip_blocks_cfg *skip_blocks_cfg =
special_blocks_cfg->skip_blocks_cfg;
u32 major, minor, sub_minor, blk_idx, num_blocks;
struct hl_special_block_info *block_info_arr;
int rc;
block_info_arr = hdev->asic_prop.special_blocks;
if (!block_info_arr)
return -EINVAL;
num_blocks = hdev->asic_prop.num_of_special_blocks;
for (blk_idx = 0 ; blk_idx < num_blocks ; blk_idx++, block_info_arr++) {
if (hl_check_block_type_exclusion(skip_blocks_cfg, block_info_arr->block_type))
continue ;
for (major = 0 ; major < block_info_arr->major ; major++) {
minor = 0 ;
do {
sub_minor = 0 ;
do {
if ((hl_check_block_range_exclusion(hdev,
skip_blocks_cfg, block_info_arr,
major, minor, sub_minor)) ||
(skip_blocks_cfg->skip_block_hook &&
skip_blocks_cfg->skip_block_hook(hdev,
special_blocks_cfg,
blk_idx, major, minor, sub_minor))) {
sub_minor++;
continue ;
}
rc = ctx->fn(hdev, blk_idx, major, minor,
sub_minor, ctx->data);
if (rc)
return rc;
sub_minor++;
} while (sub_minor < block_info_arr->sub_minor);
minor++;
} while (minor < block_info_arr->minor);
}
}
return 0 ;
}
Messung V0.5 in Prozent C=96 H=85 G=90
¤ Dauer der Verarbeitung: 0.19 Sekunden
¤
*© Formatika GbR, Deutschland