601 lines
17 KiB
C
601 lines
17 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
* Copyright 2020 HabanaLabs, Ltd.
|
|
* All Rights Reserved.
|
|
*/
|
|
|
|
#include "habanalabs.h"
|
|
|
|
/**
|
|
* 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 & 0x3)) {
|
|
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
|
|
* @regs_array: register array
|
|
* @regs_array_size: 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 *regs_array, u32 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, regs_array, 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
|
|
* @regs_array: register array
|
|
* @regs_array_size: 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 *regs_array, u32 regs_array_size)
|
|
{
|
|
return hl_init_pb_with_mask(hdev, num_dcores, dcore_offset,
|
|
num_instances, instance_offset, pb_blocks,
|
|
blocks_array_size, regs_array, 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
|
|
* @regs_range_array: register range array
|
|
* @regs_range_array_size: 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 *regs_range_array, u32 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, regs_range_array,
|
|
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
|
|
* @regs_range_array: register range array
|
|
* @regs_range_array_size: 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 *regs_range_array, u32 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, regs_range_array,
|
|
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
|
|
* @regs_array: register array
|
|
* @regs_array_size: 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 *regs_array, u32 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, regs_array, 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
|
|
* @regs_range_array: register range array
|
|
* @regs_range_array_size: 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 *regs_range_array, u32 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, regs_range_array,
|
|
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);
|
|
|
|
}
|