Path: blob/master/drivers/accel/habanalabs/common/security.c
26436 views
// SPDX-License-Identifier: GPL-2.012/*3* Copyright 2020 HabanaLabs, Ltd.4* All Rights Reserved.5*/67#include "habanalabs.h"89static const char * const hl_glbl_error_cause[] = {10"Error due to un-priv read",11"Error due to un-secure read",12"Error due to read from unmapped reg",13"Error due to un-priv write",14"Error due to un-secure write",15"Error due to write to unmapped reg",16"N/A",17"N/A",18"N/A",19"N/A",20"N/A",21"N/A",22"N/A",23"N/A",24"N/A",25"N/A",26"External I/F write sec violation",27"External I/F write to un-mapped reg",28"N/A",29"N/A",30"N/A",31"N/A",32"N/A",33"N/A",34"Read to write only",35"Write to read only"36};3738/**39* hl_get_pb_block - return the relevant block within the block array40*41* @hdev: pointer to hl_device structure42* @mm_reg_addr: register address in the desired block43* @pb_blocks: blocks array44* @array_size: blocks array size45*46*/47static int hl_get_pb_block(struct hl_device *hdev, u32 mm_reg_addr,48const u32 pb_blocks[], int array_size)49{50int i;51u32 start_addr, end_addr;5253for (i = 0 ; i < array_size ; i++) {54start_addr = pb_blocks[i];55end_addr = start_addr + HL_BLOCK_SIZE;5657if ((mm_reg_addr >= start_addr) && (mm_reg_addr < end_addr))58return i;59}6061dev_err(hdev->dev, "No protection domain was found for 0x%x\n",62mm_reg_addr);63return -EDOM;64}6566/**67* hl_unset_pb_in_block - clear a specific protection bit in a block68*69* @hdev: pointer to hl_device structure70* @reg_offset: register offset will be converted to bit offset in pb block71* @sgs_entry: pb array72*73*/74static int hl_unset_pb_in_block(struct hl_device *hdev, u32 reg_offset,75struct hl_block_glbl_sec *sgs_entry)76{77if ((reg_offset >= HL_BLOCK_SIZE) || (reg_offset & 0x3)) {78dev_err(hdev->dev,79"Register offset(%d) is out of range(%d) or invalid\n",80reg_offset, HL_BLOCK_SIZE);81return -EINVAL;82}8384UNSET_GLBL_SEC_BIT(sgs_entry->sec_array,85(reg_offset & (HL_BLOCK_SIZE - 1)) >> 2);8687return 0;88}8990/**91* hl_unsecure_register - locate the relevant block for this register and92* remove corresponding protection bit93*94* @hdev: pointer to hl_device structure95* @mm_reg_addr: register address to unsecure96* @offset: additional offset to the register address97* @pb_blocks: blocks array98* @sgs_array: pb array99* @array_size: blocks array size100*101*/102int hl_unsecure_register(struct hl_device *hdev, u32 mm_reg_addr, int offset,103const u32 pb_blocks[], struct hl_block_glbl_sec sgs_array[],104int array_size)105{106u32 reg_offset;107int block_num;108109block_num = hl_get_pb_block(hdev, mm_reg_addr + offset, pb_blocks,110array_size);111if (block_num < 0)112return block_num;113114reg_offset = (mm_reg_addr + offset) - pb_blocks[block_num];115116return hl_unset_pb_in_block(hdev, reg_offset, &sgs_array[block_num]);117}118119/**120* hl_unsecure_register_range - locate the relevant block for this register121* range and remove corresponding protection bit122*123* @hdev: pointer to hl_device structure124* @mm_reg_range: register address range to unsecure125* @offset: additional offset to the register address126* @pb_blocks: blocks array127* @sgs_array: pb array128* @array_size: blocks array size129*130*/131static int hl_unsecure_register_range(struct hl_device *hdev,132struct range mm_reg_range, int offset, const u32 pb_blocks[],133struct hl_block_glbl_sec sgs_array[],134int array_size)135{136u32 reg_offset;137int i, block_num, rc = 0;138139block_num = hl_get_pb_block(hdev,140mm_reg_range.start + offset, pb_blocks,141array_size);142if (block_num < 0)143return block_num;144145for (i = mm_reg_range.start ; i <= mm_reg_range.end ; i += 4) {146reg_offset = (i + offset) - pb_blocks[block_num];147rc |= hl_unset_pb_in_block(hdev, reg_offset,148&sgs_array[block_num]);149}150151return rc;152}153154/**155* hl_unsecure_registers - locate the relevant block for all registers and156* remove corresponding protection bit157*158* @hdev: pointer to hl_device structure159* @mm_reg_array: register address array to unsecure160* @mm_array_size: register array size161* @offset: additional offset to the register address162* @pb_blocks: blocks array163* @sgs_array: pb array164* @blocks_array_size: blocks array size165*166*/167int hl_unsecure_registers(struct hl_device *hdev, const u32 mm_reg_array[],168int mm_array_size, int offset, const u32 pb_blocks[],169struct hl_block_glbl_sec sgs_array[], int blocks_array_size)170{171int i, rc = 0;172173for (i = 0 ; i < mm_array_size ; i++) {174rc = hl_unsecure_register(hdev, mm_reg_array[i], offset,175pb_blocks, sgs_array, blocks_array_size);176177if (rc)178return rc;179}180181return rc;182}183184/**185* hl_unsecure_registers_range - locate the relevant block for all register186* ranges and remove corresponding protection bit187*188* @hdev: pointer to hl_device structure189* @mm_reg_range_array: register address range array to unsecure190* @mm_array_size: register array size191* @offset: additional offset to the register address192* @pb_blocks: blocks array193* @sgs_array: pb array194* @blocks_array_size: blocks array size195*196*/197static int hl_unsecure_registers_range(struct hl_device *hdev,198const struct range mm_reg_range_array[], int mm_array_size,199int offset, const u32 pb_blocks[],200struct hl_block_glbl_sec sgs_array[], int blocks_array_size)201{202int i, rc = 0;203204for (i = 0 ; i < mm_array_size ; i++) {205rc = hl_unsecure_register_range(hdev, mm_reg_range_array[i],206offset, pb_blocks, sgs_array, blocks_array_size);207208if (rc)209return rc;210}211212return rc;213}214215/**216* hl_ack_pb_security_violations - Ack security violation217*218* @hdev: pointer to hl_device structure219* @pb_blocks: blocks array220* @block_offset: additional offset to the block221* @array_size: blocks array size222*223*/224static void hl_ack_pb_security_violations(struct hl_device *hdev,225const u32 pb_blocks[], u32 block_offset, int array_size)226{227int i;228u32 cause, addr, block_base;229230for (i = 0 ; i < array_size ; i++) {231block_base = pb_blocks[i] + block_offset;232cause = RREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE);233if (cause) {234addr = RREG32(block_base + HL_BLOCK_GLBL_ERR_ADDR);235hdev->asic_funcs->pb_print_security_errors(hdev,236block_base, cause, addr);237WREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE, cause);238}239}240}241242/**243* hl_config_glbl_sec - set pb in HW according to given pb array244*245* @hdev: pointer to hl_device structure246* @pb_blocks: blocks array247* @sgs_array: pb array248* @block_offset: additional offset to the block249* @array_size: blocks array size250*251*/252void hl_config_glbl_sec(struct hl_device *hdev, const u32 pb_blocks[],253struct hl_block_glbl_sec sgs_array[], u32 block_offset,254int array_size)255{256int i, j;257u32 sgs_base;258259if (hdev->pldm)260usleep_range(100, 1000);261262for (i = 0 ; i < array_size ; i++) {263sgs_base = block_offset + pb_blocks[i] +264HL_BLOCK_GLBL_SEC_OFFS;265266for (j = 0 ; j < HL_BLOCK_GLBL_SEC_LEN ; j++)267WREG32(sgs_base + j * sizeof(u32),268sgs_array[i].sec_array[j]);269}270}271272/**273* hl_secure_block - locally memsets a block to 0274*275* @hdev: pointer to hl_device structure276* @sgs_array: pb array to clear277* @array_size: blocks array size278*279*/280void hl_secure_block(struct hl_device *hdev,281struct hl_block_glbl_sec sgs_array[], int array_size)282{283int i;284285for (i = 0 ; i < array_size ; i++)286memset((char *)(sgs_array[i].sec_array), 0,287HL_BLOCK_GLBL_SEC_SIZE);288}289290/**291* hl_init_pb_with_mask - set selected pb instances with mask in HW according292* to given configuration293*294* @hdev: pointer to hl_device structure295* @num_dcores: number of decores to apply configuration to296* set to HL_PB_SHARED if need to apply only once297* @dcore_offset: offset between dcores298* @num_instances: number of instances to apply configuration to299* @instance_offset: offset between instances300* @pb_blocks: blocks array301* @blocks_array_size: blocks array size302* @user_regs_array: unsecured register array303* @user_regs_array_size: unsecured register array size304* @mask: enabled instances mask: 1- enabled, 0- disabled305*/306int hl_init_pb_with_mask(struct hl_device *hdev, u32 num_dcores,307u32 dcore_offset, u32 num_instances, u32 instance_offset,308const u32 pb_blocks[], u32 blocks_array_size,309const u32 *user_regs_array, u32 user_regs_array_size, u64 mask)310{311int i, j;312struct hl_block_glbl_sec *glbl_sec;313314glbl_sec = kcalloc(blocks_array_size,315sizeof(struct hl_block_glbl_sec),316GFP_KERNEL);317if (!glbl_sec)318return -ENOMEM;319320hl_secure_block(hdev, glbl_sec, blocks_array_size);321hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size, 0,322pb_blocks, glbl_sec, blocks_array_size);323324/* Fill all blocks with the same configuration */325for (i = 0 ; i < num_dcores ; i++) {326for (j = 0 ; j < num_instances ; j++) {327int seq = i * num_instances + j;328329if (!(mask & BIT_ULL(seq)))330continue;331332hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,333i * dcore_offset + j * instance_offset,334blocks_array_size);335}336}337338kfree(glbl_sec);339340return 0;341}342343/**344* hl_init_pb - set pb in HW according to given configuration345*346* @hdev: pointer to hl_device structure347* @num_dcores: number of decores to apply configuration to348* set to HL_PB_SHARED if need to apply only once349* @dcore_offset: offset between dcores350* @num_instances: number of instances to apply configuration to351* @instance_offset: offset between instances352* @pb_blocks: blocks array353* @blocks_array_size: blocks array size354* @user_regs_array: unsecured register array355* @user_regs_array_size: unsecured register array size356*357*/358int hl_init_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,359u32 num_instances, u32 instance_offset,360const u32 pb_blocks[], u32 blocks_array_size,361const u32 *user_regs_array, u32 user_regs_array_size)362{363return hl_init_pb_with_mask(hdev, num_dcores, dcore_offset,364num_instances, instance_offset, pb_blocks,365blocks_array_size, user_regs_array,366user_regs_array_size, ULLONG_MAX);367}368369/**370* hl_init_pb_ranges_with_mask - set pb instances using mask in HW according to371* given configuration unsecurring registers372* ranges instead of specific registers373*374* @hdev: pointer to hl_device structure375* @num_dcores: number of decores to apply configuration to376* set to HL_PB_SHARED if need to apply only once377* @dcore_offset: offset between dcores378* @num_instances: number of instances to apply configuration to379* @instance_offset: offset between instances380* @pb_blocks: blocks array381* @blocks_array_size: blocks array size382* @user_regs_range_array: unsecured register range array383* @user_regs_range_array_size: unsecured register range array size384* @mask: enabled instances mask: 1- enabled, 0- disabled385*/386int hl_init_pb_ranges_with_mask(struct hl_device *hdev, u32 num_dcores,387u32 dcore_offset, u32 num_instances, u32 instance_offset,388const u32 pb_blocks[], u32 blocks_array_size,389const struct range *user_regs_range_array,390u32 user_regs_range_array_size, u64 mask)391{392int i, j, rc = 0;393struct hl_block_glbl_sec *glbl_sec;394395glbl_sec = kcalloc(blocks_array_size,396sizeof(struct hl_block_glbl_sec),397GFP_KERNEL);398if (!glbl_sec)399return -ENOMEM;400401hl_secure_block(hdev, glbl_sec, blocks_array_size);402rc = hl_unsecure_registers_range(hdev, user_regs_range_array,403user_regs_range_array_size, 0, pb_blocks, glbl_sec,404blocks_array_size);405if (rc)406goto free_glbl_sec;407408/* Fill all blocks with the same configuration */409for (i = 0 ; i < num_dcores ; i++) {410for (j = 0 ; j < num_instances ; j++) {411int seq = i * num_instances + j;412413if (!(mask & BIT_ULL(seq)))414continue;415416hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,417i * dcore_offset + j * instance_offset,418blocks_array_size);419}420}421422free_glbl_sec:423kfree(glbl_sec);424425return rc;426}427428/**429* hl_init_pb_ranges - set pb in HW according to given configuration unsecurring430* registers ranges instead of specific registers431*432* @hdev: pointer to hl_device structure433* @num_dcores: number of decores to apply configuration to434* set to HL_PB_SHARED if need to apply only once435* @dcore_offset: offset between dcores436* @num_instances: number of instances to apply configuration to437* @instance_offset: offset between instances438* @pb_blocks: blocks array439* @blocks_array_size: blocks array size440* @user_regs_range_array: unsecured register range array441* @user_regs_range_array_size: unsecured register range array size442*443*/444int hl_init_pb_ranges(struct hl_device *hdev, u32 num_dcores,445u32 dcore_offset, u32 num_instances, u32 instance_offset,446const u32 pb_blocks[], u32 blocks_array_size,447const struct range *user_regs_range_array,448u32 user_regs_range_array_size)449{450return hl_init_pb_ranges_with_mask(hdev, num_dcores, dcore_offset,451num_instances, instance_offset, pb_blocks,452blocks_array_size, user_regs_range_array,453user_regs_range_array_size, ULLONG_MAX);454}455456/**457* hl_init_pb_single_dcore - set pb for a single docre in HW458* according to given configuration459*460* @hdev: pointer to hl_device structure461* @dcore_offset: offset from the dcore0462* @num_instances: number of instances to apply configuration to463* @instance_offset: offset between instances464* @pb_blocks: blocks array465* @blocks_array_size: blocks array size466* @user_regs_array: unsecured register array467* @user_regs_array_size: unsecured register array size468*469*/470int hl_init_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,471u32 num_instances, u32 instance_offset,472const u32 pb_blocks[], u32 blocks_array_size,473const u32 *user_regs_array, u32 user_regs_array_size)474{475int i, rc = 0;476struct hl_block_glbl_sec *glbl_sec;477478glbl_sec = kcalloc(blocks_array_size,479sizeof(struct hl_block_glbl_sec),480GFP_KERNEL);481if (!glbl_sec)482return -ENOMEM;483484hl_secure_block(hdev, glbl_sec, blocks_array_size);485rc = hl_unsecure_registers(hdev, user_regs_array, user_regs_array_size,4860, pb_blocks, glbl_sec, blocks_array_size);487if (rc)488goto free_glbl_sec;489490/* Fill all blocks with the same configuration */491for (i = 0 ; i < num_instances ; i++)492hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,493dcore_offset + i * instance_offset,494blocks_array_size);495496free_glbl_sec:497kfree(glbl_sec);498499return rc;500}501502/**503* hl_init_pb_ranges_single_dcore - set pb for a single docre in HW according504* to given configuration unsecurring505* registers ranges instead of specific506* registers507*508* @hdev: pointer to hl_device structure509* @dcore_offset: offset from the dcore0510* @num_instances: number of instances to apply configuration to511* @instance_offset: offset between instances512* @pb_blocks: blocks array513* @blocks_array_size: blocks array size514* @user_regs_range_array: unsecured register range array515* @user_regs_range_array_size: unsecured register range array size516*517*/518int hl_init_pb_ranges_single_dcore(struct hl_device *hdev, u32 dcore_offset,519u32 num_instances, u32 instance_offset,520const u32 pb_blocks[], u32 blocks_array_size,521const struct range *user_regs_range_array, u32 user_regs_range_array_size)522{523int i;524struct hl_block_glbl_sec *glbl_sec;525526glbl_sec = kcalloc(blocks_array_size,527sizeof(struct hl_block_glbl_sec),528GFP_KERNEL);529if (!glbl_sec)530return -ENOMEM;531532hl_secure_block(hdev, glbl_sec, blocks_array_size);533hl_unsecure_registers_range(hdev, user_regs_range_array,534user_regs_range_array_size, 0, pb_blocks, glbl_sec,535blocks_array_size);536537/* Fill all blocks with the same configuration */538for (i = 0 ; i < num_instances ; i++)539hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,540dcore_offset + i * instance_offset,541blocks_array_size);542543kfree(glbl_sec);544545return 0;546}547548/**549* hl_ack_pb_with_mask - ack pb with mask in HW according to given configuration550*551* @hdev: pointer to hl_device structure552* @num_dcores: number of decores to apply configuration to553* set to HL_PB_SHARED if need to apply only once554* @dcore_offset: offset between dcores555* @num_instances: number of instances to apply configuration to556* @instance_offset: offset between instances557* @pb_blocks: blocks array558* @blocks_array_size: blocks array size559* @mask: enabled instances mask: 1- enabled, 0- disabled560*561*/562void hl_ack_pb_with_mask(struct hl_device *hdev, u32 num_dcores,563u32 dcore_offset, u32 num_instances, u32 instance_offset,564const u32 pb_blocks[], u32 blocks_array_size, u64 mask)565{566int i, j;567568/* ack all blocks */569for (i = 0 ; i < num_dcores ; i++) {570for (j = 0 ; j < num_instances ; j++) {571int seq = i * num_instances + j;572573if (!(mask & BIT_ULL(seq)))574continue;575576hl_ack_pb_security_violations(hdev, pb_blocks,577i * dcore_offset + j * instance_offset,578blocks_array_size);579}580}581}582583/**584* hl_ack_pb - ack pb in HW according to given configuration585*586* @hdev: pointer to hl_device structure587* @num_dcores: number of decores to apply configuration to588* set to HL_PB_SHARED if need to apply only once589* @dcore_offset: offset between dcores590* @num_instances: number of instances to apply configuration to591* @instance_offset: offset between instances592* @pb_blocks: blocks array593* @blocks_array_size: blocks array size594*595*/596void hl_ack_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,597u32 num_instances, u32 instance_offset,598const u32 pb_blocks[], u32 blocks_array_size)599{600hl_ack_pb_with_mask(hdev, num_dcores, dcore_offset, num_instances,601instance_offset, pb_blocks, blocks_array_size,602ULLONG_MAX);603}604605/**606* hl_ack_pb_single_dcore - ack pb for single docre in HW607* according to given configuration608*609* @hdev: pointer to hl_device structure610* @dcore_offset: offset from dcore0611* @num_instances: number of instances to apply configuration to612* @instance_offset: offset between instances613* @pb_blocks: blocks array614* @blocks_array_size: blocks array size615*616*/617void hl_ack_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,618u32 num_instances, u32 instance_offset,619const u32 pb_blocks[], u32 blocks_array_size)620{621int i;622623/* ack all blocks */624for (i = 0 ; i < num_instances ; i++)625hl_ack_pb_security_violations(hdev, pb_blocks,626dcore_offset + i * instance_offset,627blocks_array_size);628629}630631static u32 hl_automated_get_block_base_addr(struct hl_device *hdev,632struct hl_special_block_info *block_info,633u32 major, u32 minor, u32 sub_minor)634{635u32 fw_block_base_address = block_info->base_addr +636major * block_info->major_offset +637minor * block_info->minor_offset +638sub_minor * block_info->sub_minor_offset;639struct asic_fixed_properties *prop = &hdev->asic_prop;640641/* Calculation above returns an address for FW use, and therefore should642* be casted for driver use.643*/644return (fw_block_base_address - lower_32_bits(prop->cfg_base_address));645}646647static bool hl_check_block_type_exclusion(struct hl_skip_blocks_cfg *skip_blocks_cfg,648int block_type)649{650int i;651652/* Check if block type is listed in the exclusion list of block types */653for (i = 0 ; i < skip_blocks_cfg->block_types_len ; i++)654if (block_type == skip_blocks_cfg->block_types[i])655return true;656657return false;658}659660static bool hl_check_block_range_exclusion(struct hl_device *hdev,661struct hl_skip_blocks_cfg *skip_blocks_cfg,662struct hl_special_block_info *block_info,663u32 major, u32 minor, u32 sub_minor)664{665u32 blocks_in_range, block_base_addr_in_range, block_base_addr;666int i, j;667668block_base_addr = hl_automated_get_block_base_addr(hdev, block_info,669major, minor, sub_minor);670671for (i = 0 ; i < skip_blocks_cfg->block_ranges_len ; i++) {672blocks_in_range = (skip_blocks_cfg->block_ranges[i].end -673skip_blocks_cfg->block_ranges[i].start) /674HL_BLOCK_SIZE + 1;675for (j = 0 ; j < blocks_in_range ; j++) {676block_base_addr_in_range = skip_blocks_cfg->block_ranges[i].start +677j * HL_BLOCK_SIZE;678if (block_base_addr == block_base_addr_in_range)679return true;680}681}682683return false;684}685686static int hl_read_glbl_errors(struct hl_device *hdev,687u32 blk_idx, u32 major, u32 minor, u32 sub_minor, void *data)688{689struct asic_fixed_properties *prop = &hdev->asic_prop;690struct hl_special_block_info *special_blocks = prop->special_blocks;691struct hl_special_block_info *current_block = &special_blocks[blk_idx];692u32 glbl_err_addr, glbl_err_cause, addr_val, cause_val, block_base,693base = current_block->base_addr - lower_32_bits(prop->cfg_base_address);694int i;695696block_base = base + major * current_block->major_offset +697minor * current_block->minor_offset +698sub_minor * current_block->sub_minor_offset;699700glbl_err_cause = block_base + HL_GLBL_ERR_CAUSE_OFFSET;701cause_val = RREG32(glbl_err_cause);702if (!cause_val)703return 0;704705glbl_err_addr = block_base + HL_GLBL_ERR_ADDR_OFFSET;706addr_val = RREG32(glbl_err_addr);707708for (i = 0 ; i <= prop->glbl_err_max_cause_num ; i++) {709if (cause_val & BIT(i))710dev_err_ratelimited(hdev->dev,711"%s, addr %#llx\n",712hl_glbl_error_cause[i],713prop->cfg_base_address + block_base +714FIELD_GET(HL_GLBL_ERR_ADDRESS_MASK, addr_val));715}716717WREG32(glbl_err_cause, cause_val);718719return 0;720}721722void hl_check_for_glbl_errors(struct hl_device *hdev)723{724struct asic_fixed_properties *prop = &hdev->asic_prop;725struct hl_special_blocks_cfg special_blocks_cfg;726struct iterate_special_ctx glbl_err_iter;727int rc;728729memset(&special_blocks_cfg, 0, sizeof(special_blocks_cfg));730special_blocks_cfg.skip_blocks_cfg = &prop->skip_special_blocks_cfg;731732glbl_err_iter.fn = &hl_read_glbl_errors;733glbl_err_iter.data = &special_blocks_cfg;734735rc = hl_iterate_special_blocks(hdev, &glbl_err_iter);736if (rc)737dev_err_ratelimited(hdev->dev,738"Could not iterate special blocks, glbl error check failed\n");739}740741int hl_iterate_special_blocks(struct hl_device *hdev, struct iterate_special_ctx *ctx)742{743struct hl_special_blocks_cfg *special_blocks_cfg =744(struct hl_special_blocks_cfg *)ctx->data;745struct hl_skip_blocks_cfg *skip_blocks_cfg =746special_blocks_cfg->skip_blocks_cfg;747u32 major, minor, sub_minor, blk_idx, num_blocks;748struct hl_special_block_info *block_info_arr;749int rc;750751block_info_arr = hdev->asic_prop.special_blocks;752if (!block_info_arr)753return -EINVAL;754755num_blocks = hdev->asic_prop.num_of_special_blocks;756757for (blk_idx = 0 ; blk_idx < num_blocks ; blk_idx++, block_info_arr++) {758if (hl_check_block_type_exclusion(skip_blocks_cfg, block_info_arr->block_type))759continue;760761for (major = 0 ; major < block_info_arr->major ; major++) {762minor = 0;763do {764sub_minor = 0;765do {766if ((hl_check_block_range_exclusion(hdev,767skip_blocks_cfg, block_info_arr,768major, minor, sub_minor)) ||769(skip_blocks_cfg->skip_block_hook &&770skip_blocks_cfg->skip_block_hook(hdev,771special_blocks_cfg,772blk_idx, major, minor, sub_minor))) {773sub_minor++;774continue;775}776777rc = ctx->fn(hdev, blk_idx, major, minor,778sub_minor, ctx->data);779if (rc)780return rc;781782sub_minor++;783} while (sub_minor < block_info_arr->sub_minor);784785minor++;786} while (minor < block_info_arr->minor);787}788}789790return 0;791}792793794