xref: /linux/drivers/accel/habanalabs/common/security.c (revision d6296cb65320be16dbf20f2fd584ddc25f3437cd)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 /*
4  * Copyright 2020 HabanaLabs, Ltd.
5  * All Rights Reserved.
6  */
7 
8 #include "habanalabs.h"
9 
10 static const char * const hl_glbl_error_cause[HL_MAX_NUM_OF_GLBL_ERR_CAUSE] = {
11 	"Error due to un-priv read",
12 	"Error due to un-secure read",
13 	"Error due to read from unmapped reg",
14 	"Error due to un-priv write",
15 	"Error due to un-secure write",
16 	"Error due to write to unmapped reg",
17 	"External I/F write sec violation",
18 	"External I/F write to un-mapped reg",
19 	"Read to write only",
20 	"Write to read only"
21 };
22 
23 /**
24  * hl_get_pb_block - return the relevant block within the block array
25  *
26  * @hdev: pointer to hl_device structure
27  * @mm_reg_addr: register address in the desired block
28  * @pb_blocks: blocks array
29  * @array_size: blocks array size
30  *
31  */
32 static int hl_get_pb_block(struct hl_device *hdev, u32 mm_reg_addr,
33 		const u32 pb_blocks[], int array_size)
34 {
35 	int i;
36 	u32 start_addr, end_addr;
37 
38 	for (i = 0 ; i < array_size ; i++) {
39 		start_addr = pb_blocks[i];
40 		end_addr = start_addr + HL_BLOCK_SIZE;
41 
42 		if ((mm_reg_addr >= start_addr) && (mm_reg_addr < end_addr))
43 			return i;
44 	}
45 
46 	dev_err(hdev->dev, "No protection domain was found for 0x%x\n",
47 			mm_reg_addr);
48 	return -EDOM;
49 }
50 
51 /**
52  * hl_unset_pb_in_block - clear a specific protection bit in a block
53  *
54  * @hdev: pointer to hl_device structure
55  * @reg_offset: register offset will be converted to bit offset in pb block
56  * @sgs_entry: pb array
57  *
58  */
59 static int hl_unset_pb_in_block(struct hl_device *hdev, u32 reg_offset,
60 				struct hl_block_glbl_sec *sgs_entry)
61 {
62 	if ((reg_offset >= HL_BLOCK_SIZE) || (reg_offset & 0x3)) {
63 		dev_err(hdev->dev,
64 			"Register offset(%d) is out of range(%d) or invalid\n",
65 			reg_offset, HL_BLOCK_SIZE);
66 		return -EINVAL;
67 	}
68 
69 	UNSET_GLBL_SEC_BIT(sgs_entry->sec_array,
70 			 (reg_offset & (HL_BLOCK_SIZE - 1)) >> 2);
71 
72 	return 0;
73 }
74 
75 /**
76  * hl_unsecure_register - locate the relevant block for this register and
77  *                        remove corresponding protection bit
78  *
79  * @hdev: pointer to hl_device structure
80  * @mm_reg_addr: register address to unsecure
81  * @offset: additional offset to the register address
82  * @pb_blocks: blocks array
83  * @sgs_array: pb array
84  * @array_size: blocks array size
85  *
86  */
87 int hl_unsecure_register(struct hl_device *hdev, u32 mm_reg_addr, int offset,
88 		const u32 pb_blocks[], struct hl_block_glbl_sec sgs_array[],
89 		int array_size)
90 {
91 	u32 reg_offset;
92 	int block_num;
93 
94 	block_num = hl_get_pb_block(hdev, mm_reg_addr + offset, pb_blocks,
95 			array_size);
96 	if (block_num < 0)
97 		return block_num;
98 
99 	reg_offset = (mm_reg_addr + offset) - pb_blocks[block_num];
100 
101 	return hl_unset_pb_in_block(hdev, reg_offset, &sgs_array[block_num]);
102 }
103 
104 /**
105  * hl_unsecure_register_range - locate the relevant block for this register
106  *                              range and remove corresponding protection bit
107  *
108  * @hdev: pointer to hl_device structure
109  * @mm_reg_range: register address range to unsecure
110  * @offset: additional offset to the register address
111  * @pb_blocks: blocks array
112  * @sgs_array: pb array
113  * @array_size: blocks array size
114  *
115  */
116 static int hl_unsecure_register_range(struct hl_device *hdev,
117 		struct range mm_reg_range, int offset, const u32 pb_blocks[],
118 		struct hl_block_glbl_sec sgs_array[],
119 		int array_size)
120 {
121 	u32 reg_offset;
122 	int i, block_num, rc = 0;
123 
124 	block_num = hl_get_pb_block(hdev,
125 			mm_reg_range.start + offset, pb_blocks,
126 			array_size);
127 	if (block_num < 0)
128 		return block_num;
129 
130 	for (i = mm_reg_range.start ; i <= mm_reg_range.end ; i += 4) {
131 		reg_offset = (i + offset) - pb_blocks[block_num];
132 		rc |= hl_unset_pb_in_block(hdev, reg_offset,
133 					&sgs_array[block_num]);
134 	}
135 
136 	return rc;
137 }
138 
139 /**
140  * hl_unsecure_registers - locate the relevant block for all registers and
141  *                        remove corresponding protection bit
142  *
143  * @hdev: pointer to hl_device structure
144  * @mm_reg_array: register address array to unsecure
145  * @mm_array_size: register array size
146  * @offset: additional offset to the register address
147  * @pb_blocks: blocks array
148  * @sgs_array: pb array
149  * @blocks_array_size: blocks array size
150  *
151  */
152 int hl_unsecure_registers(struct hl_device *hdev, const u32 mm_reg_array[],
153 		int mm_array_size, int offset, const u32 pb_blocks[],
154 		struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
155 {
156 	int i, rc = 0;
157 
158 	for (i = 0 ; i < mm_array_size ; i++) {
159 		rc = hl_unsecure_register(hdev, mm_reg_array[i], offset,
160 				pb_blocks, sgs_array, blocks_array_size);
161 
162 		if (rc)
163 			return rc;
164 	}
165 
166 	return rc;
167 }
168 
169 /**
170  * hl_unsecure_registers_range - locate the relevant block for all register
171  *                        ranges and remove corresponding protection bit
172  *
173  * @hdev: pointer to hl_device structure
174  * @mm_reg_range_array: register address range array to unsecure
175  * @mm_array_size: register array size
176  * @offset: additional offset to the register address
177  * @pb_blocks: blocks array
178  * @sgs_array: pb array
179  * @blocks_array_size: blocks array size
180  *
181  */
182 static int hl_unsecure_registers_range(struct hl_device *hdev,
183 		const struct range mm_reg_range_array[], int mm_array_size,
184 		int offset, const u32 pb_blocks[],
185 		struct hl_block_glbl_sec sgs_array[], int blocks_array_size)
186 {
187 	int i, rc = 0;
188 
189 	for (i = 0 ; i < mm_array_size ; i++) {
190 		rc = hl_unsecure_register_range(hdev, mm_reg_range_array[i],
191 			offset, pb_blocks, sgs_array, blocks_array_size);
192 
193 		if (rc)
194 			return rc;
195 	}
196 
197 	return rc;
198 }
199 
200 /**
201  * hl_ack_pb_security_violations - Ack security violation
202  *
203  * @hdev: pointer to hl_device structure
204  * @pb_blocks: blocks array
205  * @block_offset: additional offset to the block
206  * @array_size: blocks array size
207  *
208  */
209 static void hl_ack_pb_security_violations(struct hl_device *hdev,
210 		const u32 pb_blocks[], u32 block_offset, int array_size)
211 {
212 	int i;
213 	u32 cause, addr, block_base;
214 
215 	for (i = 0 ; i < array_size ; i++) {
216 		block_base = pb_blocks[i] + block_offset;
217 		cause = RREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE);
218 		if (cause) {
219 			addr = RREG32(block_base + HL_BLOCK_GLBL_ERR_ADDR);
220 			hdev->asic_funcs->pb_print_security_errors(hdev,
221 					block_base, cause, addr);
222 			WREG32(block_base + HL_BLOCK_GLBL_ERR_CAUSE, cause);
223 		}
224 	}
225 }
226 
227 /**
228  * hl_config_glbl_sec - set pb in HW according to given pb array
229  *
230  * @hdev: pointer to hl_device structure
231  * @pb_blocks: blocks array
232  * @sgs_array: pb array
233  * @block_offset: additional offset to the block
234  * @array_size: blocks array size
235  *
236  */
237 void hl_config_glbl_sec(struct hl_device *hdev, const u32 pb_blocks[],
238 		struct hl_block_glbl_sec sgs_array[], u32 block_offset,
239 		int array_size)
240 {
241 	int i, j;
242 	u32 sgs_base;
243 
244 	if (hdev->pldm)
245 		usleep_range(100, 1000);
246 
247 	for (i = 0 ; i < array_size ; i++) {
248 		sgs_base = block_offset + pb_blocks[i] +
249 				HL_BLOCK_GLBL_SEC_OFFS;
250 
251 		for (j = 0 ; j < HL_BLOCK_GLBL_SEC_LEN ; j++)
252 			WREG32(sgs_base + j * sizeof(u32),
253 				sgs_array[i].sec_array[j]);
254 	}
255 }
256 
257 /**
258  * hl_secure_block - locally memsets a block to 0
259  *
260  * @hdev: pointer to hl_device structure
261  * @sgs_array: pb array to clear
262  * @array_size: blocks array size
263  *
264  */
265 void hl_secure_block(struct hl_device *hdev,
266 		struct hl_block_glbl_sec sgs_array[], int array_size)
267 {
268 	int i;
269 
270 	for (i = 0 ; i < array_size ; i++)
271 		memset((char *)(sgs_array[i].sec_array), 0,
272 			HL_BLOCK_GLBL_SEC_SIZE);
273 }
274 
275 /**
276  * hl_init_pb_with_mask - set selected pb instances with mask in HW according
277  *                        to given configuration
278  *
279  * @hdev: pointer to hl_device structure
280  * @num_dcores: number of decores to apply configuration to
281  *              set to HL_PB_SHARED if need to apply only once
282  * @dcore_offset: offset between dcores
283  * @num_instances: number of instances to apply configuration to
284  * @instance_offset: offset between instances
285  * @pb_blocks: blocks array
286  * @blocks_array_size: blocks array size
287  * @regs_array: register array
288  * @regs_array_size: register array size
289  * @mask: enabled instances mask: 1- enabled, 0- disabled
290  */
291 int hl_init_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
292 		u32 dcore_offset, u32 num_instances, u32 instance_offset,
293 		const u32 pb_blocks[], u32 blocks_array_size,
294 		const u32 *regs_array, u32 regs_array_size, u64 mask)
295 {
296 	int i, j;
297 	struct hl_block_glbl_sec *glbl_sec;
298 
299 	glbl_sec = kcalloc(blocks_array_size,
300 			sizeof(struct hl_block_glbl_sec),
301 			GFP_KERNEL);
302 	if (!glbl_sec)
303 		return -ENOMEM;
304 
305 	hl_secure_block(hdev, glbl_sec, blocks_array_size);
306 	hl_unsecure_registers(hdev, regs_array, regs_array_size, 0, pb_blocks,
307 			glbl_sec, blocks_array_size);
308 
309 	/* Fill all blocks with the same configuration */
310 	for (i = 0 ; i < num_dcores ; i++) {
311 		for (j = 0 ; j < num_instances ; j++) {
312 			int seq = i * num_instances + j;
313 
314 			if (!(mask & BIT_ULL(seq)))
315 				continue;
316 
317 			hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
318 					i * dcore_offset + j * instance_offset,
319 					blocks_array_size);
320 		}
321 	}
322 
323 	kfree(glbl_sec);
324 
325 	return 0;
326 }
327 
328 /**
329  * hl_init_pb - set pb in HW according to given configuration
330  *
331  * @hdev: pointer to hl_device structure
332  * @num_dcores: number of decores to apply configuration to
333  *              set to HL_PB_SHARED if need to apply only once
334  * @dcore_offset: offset between dcores
335  * @num_instances: number of instances to apply configuration to
336  * @instance_offset: offset between instances
337  * @pb_blocks: blocks array
338  * @blocks_array_size: blocks array size
339  * @regs_array: register array
340  * @regs_array_size: register array size
341  *
342  */
343 int hl_init_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
344 		u32 num_instances, u32 instance_offset,
345 		const u32 pb_blocks[], u32 blocks_array_size,
346 		const u32 *regs_array, u32 regs_array_size)
347 {
348 	return hl_init_pb_with_mask(hdev, num_dcores, dcore_offset,
349 			num_instances, instance_offset, pb_blocks,
350 			blocks_array_size, regs_array, regs_array_size,
351 			ULLONG_MAX);
352 }
353 
354 /**
355  * hl_init_pb_ranges_with_mask - set pb instances using mask in HW according to
356  *                               given configuration unsecurring registers
357  *                               ranges instead of specific registers
358  *
359  * @hdev: pointer to hl_device structure
360  * @num_dcores: number of decores to apply configuration to
361  *              set to HL_PB_SHARED if need to apply only once
362  * @dcore_offset: offset between dcores
363  * @num_instances: number of instances to apply configuration to
364  * @instance_offset: offset between instances
365  * @pb_blocks: blocks array
366  * @blocks_array_size: blocks array size
367  * @regs_range_array: register range array
368  * @regs_range_array_size: register range array size
369  * @mask: enabled instances mask: 1- enabled, 0- disabled
370  */
371 int hl_init_pb_ranges_with_mask(struct hl_device *hdev, u32 num_dcores,
372 		u32 dcore_offset, u32 num_instances, u32 instance_offset,
373 		const u32 pb_blocks[], u32 blocks_array_size,
374 		const struct range *regs_range_array, u32 regs_range_array_size,
375 		u64 mask)
376 {
377 	int i, j, rc = 0;
378 	struct hl_block_glbl_sec *glbl_sec;
379 
380 	glbl_sec = kcalloc(blocks_array_size,
381 			sizeof(struct hl_block_glbl_sec),
382 			GFP_KERNEL);
383 	if (!glbl_sec)
384 		return -ENOMEM;
385 
386 	hl_secure_block(hdev, glbl_sec, blocks_array_size);
387 	rc = hl_unsecure_registers_range(hdev, regs_range_array,
388 			regs_range_array_size, 0, pb_blocks, glbl_sec,
389 			blocks_array_size);
390 	if (rc)
391 		goto free_glbl_sec;
392 
393 	/* Fill all blocks with the same configuration */
394 	for (i = 0 ; i < num_dcores ; i++) {
395 		for (j = 0 ; j < num_instances ; j++) {
396 			int seq = i * num_instances + j;
397 
398 			if (!(mask & BIT_ULL(seq)))
399 				continue;
400 
401 			hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
402 					i * dcore_offset + j * instance_offset,
403 					blocks_array_size);
404 		}
405 	}
406 
407 free_glbl_sec:
408 	kfree(glbl_sec);
409 
410 	return rc;
411 }
412 
413 /**
414  * hl_init_pb_ranges - set pb in HW according to given configuration unsecurring
415  *                     registers ranges instead of specific registers
416  *
417  * @hdev: pointer to hl_device structure
418  * @num_dcores: number of decores to apply configuration to
419  *              set to HL_PB_SHARED if need to apply only once
420  * @dcore_offset: offset between dcores
421  * @num_instances: number of instances to apply configuration to
422  * @instance_offset: offset between instances
423  * @pb_blocks: blocks array
424  * @blocks_array_size: blocks array size
425  * @regs_range_array: register range array
426  * @regs_range_array_size: register range array size
427  *
428  */
429 int hl_init_pb_ranges(struct hl_device *hdev, u32 num_dcores,
430 		u32 dcore_offset, u32 num_instances, u32 instance_offset,
431 		const u32 pb_blocks[], u32 blocks_array_size,
432 		const struct range *regs_range_array, u32 regs_range_array_size)
433 {
434 	return hl_init_pb_ranges_with_mask(hdev, num_dcores, dcore_offset,
435 			num_instances, instance_offset, pb_blocks,
436 			blocks_array_size, regs_range_array,
437 			regs_range_array_size, ULLONG_MAX);
438 }
439 
440 /**
441  * hl_init_pb_single_dcore - set pb for a single docre in HW
442  * according to given configuration
443  *
444  * @hdev: pointer to hl_device structure
445  * @dcore_offset: offset from the dcore0
446  * @num_instances: number of instances to apply configuration to
447  * @instance_offset: offset between instances
448  * @pb_blocks: blocks array
449  * @blocks_array_size: blocks array size
450  * @regs_array: register array
451  * @regs_array_size: register array size
452  *
453  */
454 int hl_init_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
455 		u32 num_instances, u32 instance_offset,
456 		const u32 pb_blocks[], u32 blocks_array_size,
457 		const u32 *regs_array, u32 regs_array_size)
458 {
459 	int i, rc = 0;
460 	struct hl_block_glbl_sec *glbl_sec;
461 
462 	glbl_sec = kcalloc(blocks_array_size,
463 			sizeof(struct hl_block_glbl_sec),
464 			GFP_KERNEL);
465 	if (!glbl_sec)
466 		return -ENOMEM;
467 
468 	hl_secure_block(hdev, glbl_sec, blocks_array_size);
469 	rc = hl_unsecure_registers(hdev, regs_array, regs_array_size, 0,
470 			pb_blocks, glbl_sec, blocks_array_size);
471 	if (rc)
472 		goto free_glbl_sec;
473 
474 	/* Fill all blocks with the same configuration */
475 	for (i = 0 ; i < num_instances ; i++)
476 		hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
477 				dcore_offset + i * instance_offset,
478 				blocks_array_size);
479 
480 free_glbl_sec:
481 	kfree(glbl_sec);
482 
483 	return rc;
484 }
485 
486 /**
487  * hl_init_pb_ranges_single_dcore - set pb for a single docre in HW according
488  *                                  to given configuration unsecurring
489  *                                  registers ranges instead of specific
490  *                                  registers
491  *
492  * @hdev: pointer to hl_device structure
493  * @dcore_offset: offset from the dcore0
494  * @num_instances: number of instances to apply configuration to
495  * @instance_offset: offset between instances
496  * @pb_blocks: blocks array
497  * @blocks_array_size: blocks array size
498  * @regs_range_array: register range array
499  * @regs_range_array_size: register range array size
500  *
501  */
502 int hl_init_pb_ranges_single_dcore(struct hl_device *hdev, u32 dcore_offset,
503 		u32 num_instances, u32 instance_offset,
504 		const u32 pb_blocks[], u32 blocks_array_size,
505 		const struct range *regs_range_array, u32 regs_range_array_size)
506 {
507 	int i;
508 	struct hl_block_glbl_sec *glbl_sec;
509 
510 	glbl_sec = kcalloc(blocks_array_size,
511 			sizeof(struct hl_block_glbl_sec),
512 			GFP_KERNEL);
513 	if (!glbl_sec)
514 		return -ENOMEM;
515 
516 	hl_secure_block(hdev, glbl_sec, blocks_array_size);
517 	hl_unsecure_registers_range(hdev, regs_range_array,
518 			regs_range_array_size, 0, pb_blocks, glbl_sec,
519 			blocks_array_size);
520 
521 	/* Fill all blocks with the same configuration */
522 	for (i = 0 ; i < num_instances ; i++)
523 		hl_config_glbl_sec(hdev, pb_blocks, glbl_sec,
524 				dcore_offset + i * instance_offset,
525 				blocks_array_size);
526 
527 	kfree(glbl_sec);
528 
529 	return 0;
530 }
531 
532 /**
533  * hl_ack_pb_with_mask - ack pb with mask in HW according to given configuration
534  *
535  * @hdev: pointer to hl_device structure
536  * @num_dcores: number of decores to apply configuration to
537  *              set to HL_PB_SHARED if need to apply only once
538  * @dcore_offset: offset between dcores
539  * @num_instances: number of instances to apply configuration to
540  * @instance_offset: offset between instances
541  * @pb_blocks: blocks array
542  * @blocks_array_size: blocks array size
543  * @mask: enabled instances mask: 1- enabled, 0- disabled
544  *
545  */
546 void hl_ack_pb_with_mask(struct hl_device *hdev, u32 num_dcores,
547 		u32 dcore_offset, u32 num_instances, u32 instance_offset,
548 		const u32 pb_blocks[], u32 blocks_array_size, u64 mask)
549 {
550 	int i, j;
551 
552 	/* ack all blocks */
553 	for (i = 0 ; i < num_dcores ; i++) {
554 		for (j = 0 ; j < num_instances ; j++) {
555 			int seq = i * num_instances + j;
556 
557 			if (!(mask & BIT_ULL(seq)))
558 				continue;
559 
560 			hl_ack_pb_security_violations(hdev, pb_blocks,
561 					i * dcore_offset + j * instance_offset,
562 					blocks_array_size);
563 		}
564 	}
565 }
566 
567 /**
568  * hl_ack_pb - ack pb in HW according to given configuration
569  *
570  * @hdev: pointer to hl_device structure
571  * @num_dcores: number of decores to apply configuration to
572  *              set to HL_PB_SHARED if need to apply only once
573  * @dcore_offset: offset between dcores
574  * @num_instances: number of instances to apply configuration to
575  * @instance_offset: offset between instances
576  * @pb_blocks: blocks array
577  * @blocks_array_size: blocks array size
578  *
579  */
580 void hl_ack_pb(struct hl_device *hdev, u32 num_dcores, u32 dcore_offset,
581 		u32 num_instances, u32 instance_offset,
582 		const u32 pb_blocks[], u32 blocks_array_size)
583 {
584 	hl_ack_pb_with_mask(hdev, num_dcores, dcore_offset, num_instances,
585 			instance_offset, pb_blocks, blocks_array_size,
586 			ULLONG_MAX);
587 }
588 
589 /**
590  * hl_ack_pb_single_dcore - ack pb for single docre in HW
591  * according to given configuration
592  *
593  * @hdev: pointer to hl_device structure
594  * @dcore_offset: offset from dcore0
595  * @num_instances: number of instances to apply configuration to
596  * @instance_offset: offset between instances
597  * @pb_blocks: blocks array
598  * @blocks_array_size: blocks array size
599  *
600  */
601 void hl_ack_pb_single_dcore(struct hl_device *hdev, u32 dcore_offset,
602 		u32 num_instances, u32 instance_offset,
603 		const u32 pb_blocks[], u32 blocks_array_size)
604 {
605 	int i;
606 
607 	/* ack all blocks */
608 	for (i = 0 ; i < num_instances ; i++)
609 		hl_ack_pb_security_violations(hdev, pb_blocks,
610 				dcore_offset + i * instance_offset,
611 				blocks_array_size);
612 
613 }
614 
615 static u32 hl_automated_get_block_base_addr(struct hl_device *hdev,
616 		struct hl_special_block_info *block_info,
617 		u32 major, u32 minor, u32 sub_minor)
618 {
619 	u32 fw_block_base_address = block_info->base_addr +
620 			major * block_info->major_offset +
621 			minor * block_info->minor_offset +
622 			sub_minor * block_info->sub_minor_offset;
623 	struct asic_fixed_properties *prop = &hdev->asic_prop;
624 
625 	/* Calculation above returns an address for FW use, and therefore should
626 	 * be casted for driver use.
627 	 */
628 	return (fw_block_base_address - lower_32_bits(prop->cfg_base_address));
629 }
630 
631 static bool hl_check_block_type_exclusion(struct hl_skip_blocks_cfg *skip_blocks_cfg,
632 		int block_type)
633 {
634 	int i;
635 
636 	/* Check if block type is listed in the exclusion list of block types */
637 	for (i = 0 ; i < skip_blocks_cfg->block_types_len ; i++)
638 		if (block_type == skip_blocks_cfg->block_types[i])
639 			return true;
640 
641 	return false;
642 }
643 
644 static bool hl_check_block_range_exclusion(struct hl_device *hdev,
645 		struct hl_skip_blocks_cfg *skip_blocks_cfg,
646 		struct hl_special_block_info *block_info,
647 		u32 major, u32 minor, u32 sub_minor)
648 {
649 	u32 blocks_in_range, block_base_addr_in_range, block_base_addr;
650 	int i, j;
651 
652 	block_base_addr = hl_automated_get_block_base_addr(hdev, block_info,
653 			major, minor, sub_minor);
654 
655 	for (i = 0 ; i < skip_blocks_cfg->block_ranges_len ; i++) {
656 		blocks_in_range = (skip_blocks_cfg->block_ranges[i].end -
657 				skip_blocks_cfg->block_ranges[i].start) /
658 				HL_BLOCK_SIZE + 1;
659 		for (j = 0 ; j < blocks_in_range ; j++) {
660 			block_base_addr_in_range = skip_blocks_cfg->block_ranges[i].start +
661 					j * HL_BLOCK_SIZE;
662 			if (block_base_addr == block_base_addr_in_range)
663 				return true;
664 		}
665 	}
666 
667 	return false;
668 }
669 
670 static int hl_read_glbl_errors(struct hl_device *hdev,
671 		u32 blk_idx, u32 major, u32 minor, u32 sub_minor, void *data)
672 {
673 	struct hl_special_block_info *special_blocks = hdev->asic_prop.special_blocks;
674 	struct hl_special_block_info *current_block = &special_blocks[blk_idx];
675 	u32 glbl_err_addr, glbl_err_cause, addr_val, cause_val, block_base,
676 		base = current_block->base_addr - lower_32_bits(hdev->asic_prop.cfg_base_address);
677 	int i;
678 
679 	block_base = base + major * current_block->major_offset +
680 			minor * current_block->minor_offset +
681 			sub_minor * current_block->sub_minor_offset;
682 
683 	glbl_err_cause = block_base + HL_GLBL_ERR_CAUSE_OFFSET;
684 	cause_val = RREG32(glbl_err_cause);
685 	if (!cause_val)
686 		return 0;
687 
688 	glbl_err_addr = block_base + HL_GLBL_ERR_ADDR_OFFSET;
689 	addr_val = RREG32(glbl_err_addr);
690 
691 	for (i = 0 ; i < hdev->asic_prop.glbl_err_cause_num ; i++) {
692 		if (cause_val & BIT(i))
693 			dev_err_ratelimited(hdev->dev,
694 				"%s, addr %#llx\n",
695 				hl_glbl_error_cause[i],
696 				hdev->asic_prop.cfg_base_address + block_base +
697 				FIELD_GET(HL_GLBL_ERR_ADDRESS_MASK, addr_val));
698 	}
699 
700 	WREG32(glbl_err_cause, cause_val);
701 
702 	return 0;
703 }
704 
705 void hl_check_for_glbl_errors(struct hl_device *hdev)
706 {
707 	struct asic_fixed_properties *prop = &hdev->asic_prop;
708 	struct hl_special_blocks_cfg special_blocks_cfg;
709 	struct iterate_special_ctx glbl_err_iter;
710 	int rc;
711 
712 	memset(&special_blocks_cfg, 0, sizeof(special_blocks_cfg));
713 	special_blocks_cfg.skip_blocks_cfg = &prop->skip_special_blocks_cfg;
714 
715 	glbl_err_iter.fn = &hl_read_glbl_errors;
716 	glbl_err_iter.data = &special_blocks_cfg;
717 
718 	rc = hl_iterate_special_blocks(hdev, &glbl_err_iter);
719 	if (rc)
720 		dev_err_ratelimited(hdev->dev,
721 			"Could not iterate special blocks, glbl error check failed\n");
722 }
723 
724 int hl_iterate_special_blocks(struct hl_device *hdev, struct iterate_special_ctx *ctx)
725 {
726 	struct hl_special_blocks_cfg *special_blocks_cfg =
727 			(struct hl_special_blocks_cfg *)ctx->data;
728 	struct hl_skip_blocks_cfg *skip_blocks_cfg =
729 			special_blocks_cfg->skip_blocks_cfg;
730 	u32 major, minor, sub_minor, blk_idx, num_blocks;
731 	struct hl_special_block_info *block_info_arr;
732 	int rc;
733 
734 	block_info_arr = hdev->asic_prop.special_blocks;
735 	if (!block_info_arr)
736 		return -EINVAL;
737 
738 	num_blocks = hdev->asic_prop.num_of_special_blocks;
739 
740 	for (blk_idx = 0 ; blk_idx < num_blocks ; blk_idx++, block_info_arr++) {
741 		if (hl_check_block_type_exclusion(skip_blocks_cfg, block_info_arr->block_type))
742 			continue;
743 
744 		for (major = 0 ; major < block_info_arr->major ; major++) {
745 			minor = 0;
746 			do {
747 				sub_minor = 0;
748 				do {
749 					if ((hl_check_block_range_exclusion(hdev,
750 							skip_blocks_cfg, block_info_arr,
751 							major, minor, sub_minor)) ||
752 						(skip_blocks_cfg->skip_block_hook &&
753 						skip_blocks_cfg->skip_block_hook(hdev,
754 							special_blocks_cfg,
755 							blk_idx, major, minor, sub_minor))) {
756 						sub_minor++;
757 						continue;
758 					}
759 
760 					rc = ctx->fn(hdev, blk_idx, major, minor,
761 								sub_minor, ctx->data);
762 					if (rc)
763 						return rc;
764 
765 					sub_minor++;
766 				} while (sub_minor < block_info_arr->sub_minor);
767 
768 				minor++;
769 			} while (minor < block_info_arr->minor);
770 		}
771 	}
772 
773 	return 0;
774 }
775