This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM623: lock otp row in opteeos

Part Number: AM623

Tool/software:

Hi, TI expert! 

I have a development environment for am6234, with our own evaluation board.

The software SDK version is PROCESSOR-SDK-LINUX-RT-AM62X-08.06.00.42.

optee version is 3.20.0

I only want to execute hw_read_lock on extend otp rows, but in actual testing, not only is the row read locked, but the row write is also locked, as shown below:

1.Log of locking external otp row

2.Locking status of external OTP rows:

The corresponding test code is as follows:

// ti-processor-sdk-linux-rt-am62xx-evm-08.06.00.42\board-support\optee_os-3.20.0\core\pta\k3\otp.c
static TEE_Result lock_otp_row(uint32_t param_types, TEE_Param params[4])
{
	TEE_Result ret = TEE_SUCCESS;
	uint8_t hw_write_lock = 0;
	uint8_t hw_read_lock = 0;
	uint8_t soft_lock = 0;
	const uint32_t exp_param_types =
		TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE,
				TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE);

	/*
	 * Safely get the invocation parameters
	 */
	if (param_types != exp_param_types)
		return TEE_ERROR_BAD_PARAMETERS;

	if (params[0].value.b & K3_OTP_KEYWRITING_SOFT_LOCK)
		soft_lock = 0x5A;
	if (params[0].value.b & K3_OTP_KEYWRITING_HW_READ_LOCK)
		hw_read_lock = 0x5A;
	if (params[0].value.b & K3_OTP_KEYWRITING_HW_WRITE_LOCK)
		hw_write_lock = 0x5A;

	DMSG("hw_write_lock: 0x%x", hw_write_lock);
	DMSG("hw_read_lock: 0x%x", hw_read_lock);
	DMSG("soft_lock: 0x%x", soft_lock);

	ret = ti_sci_lock_otp_row(params[0].value.a, hw_write_lock,
				  hw_read_lock, soft_lock);

	if (ret)
		return ret;

	DMSG("Locked the row: 0x%08"PRIx32, params[1].value.a);

	return TEE_SUCCESS;
}

static TEE_Result get_otp_row_lock_status(uint32_t param_types, TEE_Param params[4])
{
    TEE_Result ret = TEE_SUCCESS;
    uint8_t global_soft_lock, hw_write_lock, hw_read_lock, row_soft_lock;
    const uint32_t exp_param_types =
        TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_VALUE_OUTPUT,
                        TEE_PARAM_TYPE_VALUE_OUTPUT, TEE_PARAM_TYPE_VALUE_OUTPUT);

    /*
     * Safely get the invocation parameters
     */
    if (param_types != exp_param_types)
        return TEE_ERROR_BAD_PARAMETERS;

    ret = ti_sci_get_otp_row_lock_status(params[0].value.a, &global_soft_lock,
                                         &hw_write_lock, &hw_read_lock, &row_soft_lock);
    if (ret)
        return ret;

    params[1].value.a = global_soft_lock;
    params[2].value.a = hw_write_lock;
    params[3].value.a = hw_read_lock;
    params[3].value.b = row_soft_lock;

    DMSG("OTP row %u lock status: global_soft_lock=0x%02x, hw_write_lock=0x%02x, hw_read_lock=0x%02x, row_soft_lock=0x%02x",
         params[0].value.a, global_soft_lock, hw_write_lock, hw_read_lock, row_soft_lock);

    return TEE_SUCCESS;
}


// ti-processor-sdk-linux-rt-am62xx-evm-08.06.00.42\board-support\optee_os-3.20.0\core\arch\arm\plat-k3\drivers\ti_sci.c
int ti_sci_lock_otp_row(uint8_t row_idx, uint8_t hw_write_lock,
			uint8_t hw_read_lock, uint8_t row_soft_lock)
{
	struct ti_sci_msg_req_lock_otp_row req = { };
	struct ti_sci_msg_resp_lock_otp_row resp = { };
	struct ti_sci_xfer xfer = { };
	int ret = 0;

	ret = ti_sci_setup_xfer(TI_SCI_MSG_LOCK_OTP_ROW, 0,
				&req, sizeof(req), &resp, sizeof(resp), &xfer);
	if (ret)
		return ret;

	req.row_idx = row_idx;
	req.hw_write_lock = hw_write_lock;
	req.hw_read_lock = hw_read_lock;
	req.row_soft_lock = row_soft_lock;

	DMSG("row_idx: 0x%x", req.row_idx);
	DMSG("hw_write_lock: 0x%x", req.hw_write_lock);
	DMSG("hw_read_lock: 0x%x", req.hw_read_lock);
	DMSG("soft_lock: 0x%x", req.row_soft_lock);

	ret = ti_sci_do_xfer(&xfer);
	if (ret)
		return ret;

	return 0;
}

int ti_sci_get_otp_row_lock_status(uint8_t row_idx, uint8_t *global_soft_lock,
                                  uint8_t *hw_write_lock, uint8_t *hw_read_lock,
                                  uint8_t *row_soft_lock)
{
	struct ti_sci_msg_req_get_otp_row_lock_status req = { };
	struct ti_sci_msg_resp_get_otp_row_lock_status resp = { };
	struct ti_sci_xfer xfer = { };
	int ret = 0;

	ret = ti_sci_setup_xfer(TI_SCI_MSG_GET_OTP_ROW_LOCK_STATUS, 0,
						   &req, sizeof(req), &resp, sizeof(resp), &xfer);
	if (ret)
		return ret;

	req.row_idx = row_idx;

	ret = ti_sci_do_xfer(&xfer);
	if (ret)
		return ret;

	*global_soft_lock = resp.global_soft_lock;
	*hw_write_lock = resp.hw_write_lock;
	*hw_read_lock = resp.hw_read_lock;
	*row_soft_lock = resp.row_soft_lock;

	return 0;
}

Regards,

Li

 

  • Hello,

    I only want to execute hw_read_lock on extend otp rows, but in actual testing, not only is the row read locked, but the row write is also locked, as shown below:

    This is expected. The TIFS also enables the write lock if the read lock is requested for a row.

    Regards,

    Prashant

  • Hello,

    When I only perform the Hardware write lock operation, the Hardware read lock occurs, as shown in the following figure

    But I perform the same operation in the MCU SDK, only Hardware write lock and Hardware read unlock,I am puzzled as to why such an error occurs in Optee.

    Regards,

    Li

  • Hello,

    Have you tried to read the status of the row 3 from the MCU+ SDK?

    This is to see if there is an implementation mistake for getting the status either in optee_os or tee_otp client.

    Regards,

    Prashant

  • Hello,

    I used the MCU SDK to read the lock status of row 3, and the result showed a hard read lock:

    From the above results, it can be inferred that when performing a hard read lock under Optee, the hard write also locks. Similarly, when performing a hard write lock, the hard read locks. How can we solve this problem?

    The above phenomenon does not occur in the MCU SDK.

    At the same time, even when the hardware read has been locked, row 3 can still be read using the CA program in Linux. Why is it possible to read row 3 even after the read is locked?

    Regards,

    Li

  • Hello,

    Similarly, when performing a hard write lock, the hard read locks.

    This part is unexpected. I will once go through the OPTEE implementation of TISCI_MSG_LOCK_OTP_ROW & get back to you.

    Why is it possible to read row 3 even after the read is locked?

    The read is controlled by the following settings

    https://software-dl.ti.com/tisci/esd/latest/3_boardcfg/BOARDCFG_SEC.html#pub-boardcfg-otp-entry

    If you have set the `host_id` to ALL, the corresponding MMR can be read by all hosts.

    Regards,

    Prashant

  • Hello,

    If you have set the `host_id` to ALL, the corresponding MMR can be read by all hosts.

    Does extended otp's row hardware read lock mean that all hosts cannot read the extended otp's row?

    What is the significance of read lock if the host_id is set to all and the row of extended otp can still be read? What are its application scenarios?

    Regards,

    Li

  • Hello,

    The read lock permission bit is an attribute of the low level OTP row & not the MMRs. The Extended OTP rows are always latched to the Extended OTP MMRs on cold reset.

    The TIFS then provides the TISCI_MSG_READ_OTP_MMR API for reading these MMRs instead of the low level OTP rows. The MMR read is then controlled by the host_id field in the secure board configuration.

    Regards,

    Prashant

  • Hello,

    At the same time, even when the hardware read has been locked, row 3 can still be read using the CA program in Linux. Why is it possible to read row 3 even after the read is locked?

    I understand what you mean by that even after the row is locked, using TISCI_MSG_READ_OTP_MMR  can still read mmr normally. However, the MCU SDK provides an interface ext_otp_readRow for row read, which calls the mmr read interface ext_otp_readMmr. So, it can be understood that even after the row is locked, both row read and mmr read can read otp normally.

    My question is, after the row is locked, reading otp is not affected in any way. So what is the purpose of this row lock API interface? Under what circumstances will this row lock function be used?

    The interface for row read and mmr read is as follows:

    // src\zy\ti-processor-sdk-mcu-plus-am62x-evm-08.06.00.18\examples\drivers\ipc\ipc_rpmsg_echo_linux\ext_otp.c
    /**
     *  \brief Function to read OTP MMR.
     *  	   Note: Host should have read permission to row.
     *  	   Read permissions to perticular row can be set via
     *  	   board configuration security.
     *
     *  \param mmrIdx   [IN] MMR index to read [0,31]
     *  \param mmrVal   [IN] Pointer to store read value
     *
     *  \return status [out] SystemP_SUCCESS on success
     */
    int32_t ext_otp_readMmr(uint8_t mmrIdx, uint32_t *mmrVal)
    {
    	int32_t status = SystemP_SUCCESS;
    	Sciclient_ReqPrm_t reqParam;
    	Sciclient_RespPrm_t respParam;
    	struct tisci_msg_read_otp_mmr_req request;
    	struct tisci_msg_read_otp_mmr_resp response;
    
    	request.mmr_idx           = mmrIdx;
    
    	reqParam.messageType	  = (uint16_t) TISCI_MSG_READ_OTP_MMR;
    	reqParam.flags            = (uint32_t) TISCI_MSG_FLAG_AOP;
    	reqParam.pReqPayload      = (const uint8_t *) &request;
    	reqParam.reqPayloadSize   = (uint32_t) sizeof (request);
    	reqParam.timeout          = (uint32_t) SystemP_WAIT_FOREVER;
    
    	respParam.flags           = (uint32_t) 0;   /* Populated by the API */
    	respParam.pRespPayload    = (uint8_t *) &response;
    	respParam.respPayloadSize = (uint32_t) sizeof (response);
    
    	status = Sciclient_service(&reqParam, &respParam);
    	DebugP_logInfo("status: 0x%x respParam.flags:0x%x response.mmr_val:0x%x\r\n", status, respParam.flags, response.mmr_val);
    	if ( (status==SystemP_SUCCESS) && ((respParam.flags & TISCI_MSG_FLAG_ACK) == TISCI_MSG_FLAG_ACK) )
    	{
    		*mmrVal = response.mmr_val;
    		DebugP_logInfo("Success Reading OTP MMR %d\r\n", mmrIdx);
    		DebugP_logInfo("OTP MMR Value reported :0x%x \r\n", (uint32_t) *mmrVal);
    	}
    	else
    	{
    		DebugP_logError("Error Reading OTP MMR %d, status:%d resFlags:%d\r\n", mmrIdx, status, respParam.flags);
    		status = SystemP_FAILURE;
    	}
    
    	return status;
    }
    
    /**
     *  \brief Function to read OTP ROM row value.
     *
     *  \param rowIdx  [IN] Row index to read
     *  \param rowVal  [OUT] Pointer to store read value
     *
     *  \return status [out] SystemP_SUCCESS on success
     */
    int32_t ext_otp_readRow(uint8_t rowIdx, uint32_t *rowVal)
    {
        int32_t status = SystemP_SUCCESS;
        uint32_t mmrIdx, mmrOffset;
        uint32_t mmrVal1 = 0, mmrVal2 = 0;
        uint32_t resultVal = 0;
    
        /* Calculate which MMR contains this row */
        mmrIdx = (rowIdx * NUM_BITS_PER_OTP_ROW - OTP_COL_OFFSET) / MMR_SIZE_BITS;
    
        /* Calculate bit offset within the MMR(s) */
        mmrOffset = (rowIdx * NUM_BITS_PER_OTP_ROW - OTP_COL_OFFSET) % MMR_SIZE_BITS;
    
        /* Read first MMR */
        status = ext_otp_readMmr(mmrIdx, &mmrVal1);
        if(status != SystemP_SUCCESS) {
            return status;
        }
    
        if(mmrOffset + NUM_BITS_PER_OTP_ROW <= MMR_SIZE_BITS) {
            /* Row fits in single MMR */
            resultVal = (mmrVal1 >> mmrOffset) & OTP_ROW_FULL_MASK;
        } else {
            /* Row spans two MMRs */
            status = ext_otp_readMmr(mmrIdx + 1, &mmrVal2);
            if(status != SystemP_SUCCESS) {
                return status;
            }
    
            /* Combine values from both MMRs */
            uint32_t bits_from_mmr1 = MMR_SIZE_BITS - mmrOffset;
            uint32_t bits_from_mmr2 = NUM_BITS_PER_OTP_ROW - bits_from_mmr1;
    
            resultVal = ((mmrVal1 >> mmrOffset) & ((1U << bits_from_mmr1) - 1)) |
                       ((mmrVal2 & ((1U << bits_from_mmr2) - 1)) << bits_from_mmr1);
            resultVal &= OTP_ROW_FULL_MASK;
        }
    
        *rowVal = resultVal;
    
        DebugP_logInfo("Success Reading OTP ROM row %d\r\n", rowIdx);
        DebugP_logInfo("ROM Value: 0x%x\r\n", resultVal);
    
        return status;
    }

    Regards,
    Li

  • Hello,

    So, it can be understood that even after the row is locked, both row read and mmr read can read otp normally.

    The TIFS doesn't provide any API for reading the low level rows. The `ext_otp_readMmr` function, though meant for reading the rows, just calculates the required MMR indexes to be read & derive the row value from them. So, the low level rows are never read.

    My question is, after the row is locked, reading otp is not affected in any way. So what is the purpose of this row lock API interface? Under what circumstances will this row lock function be used?

    For Extended OTP, there really isn't any use case for the read lock. Hypothetically, if the TIFS provided an API interface for reading the low level rows, it could then have leveraged the read lock permission bit to control the reading in a certain way.

    Like for Secure OTP, the TIFS leverages the read lock permission bit to determine if the KEYCNT, KEYREV could be read back using the TISCI_MSG_READ_KEYCNT_KEYREV.

    Regards,

    Prashant

  • Hello,

    Thank you for your detailed answer!

    Do you have any ideas for the previous question?

    This part is unexpected. I will once go through the OPTEE implementation of TISCI_MSG_LOCK_OTP_ROW & get back to you.

    Regards,
    Li

  • Hello,

    This part is unexpected. I will once go through the OPTEE implementation of TISCI_MSG_LOCK_OTP_ROW & get back to you.

    Is there a solution to this problem?

    Regards,
    Li

  • Hello,

    I have checked the implementation & it's all looking good.

    If possible, can you do the procedure again for a different row & share all the boot logs? More specifically, I would like to see the values of the variables in the `ti_sci_lock_otp_row` function on write lock only.

    Thanks!