/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include extern void dsb(void); extern void ce_async_reset(); void wr_ce(uint32_t val,uint32_t reg) { uint32_t platform_id; platform_id = board_platform_id(); if((platform_id == APQ8064) || (platform_id == APQ8064AA) || (platform_id == APQ8064AB)) writel(val,CRYPTO_ENG_REG(CE3_CRYPTO4_BASE, reg)); else writel(val,CRYPTO_ENG_REG(CE1_CRYPTO4_BASE, reg)); } uint32_t rd_ce(uint32_t reg) { uint32_t val; uint32_t platform_id; platform_id = board_platform_id(); if((platform_id == APQ8064) || (platform_id == APQ8064AA) || (platform_id == APQ8064AB)) val = readl(CRYPTO_ENG_REG(CE3_CRYPTO4_BASE, reg)); else val = readl(CRYPTO_ENG_REG(CE1_CRYPTO4_BASE, reg)); return val; } /* * Function to reset the crypto engine. */ void crypto_eng_reset(void) { ce_async_reset(); return; } /* Function to switch the CE1 context * from register to ADM */ void crypto_eng_cleanup(void) { unsigned int val; enum ap_ce_channel_type chn = AP_CE_ADM_USE; /* Make a SMC call to TZ to make CE1 use ADM interface for HLOS*/ val = switch_ce_chn_cmd(chn); dprintf(INFO, "TZ channel swith returned %d\n", val); } /* * Function to initialize the crypto engine for a new session. It enables the * auto shutdown feature of CRYPTO and mask various interrupts since we use * polling. We are not using DMOV now. */ void crypto_eng_init(void) { unsigned int val; enum ap_ce_channel_type chn = AP_CE_REGISTER_USE; /* Make a SMC call to TZ to make CE1 use register interface for HLOS*/ val = switch_ce_chn_cmd(chn); dprintf(INFO, "TZ channel swith returned %d\n", val); } /* * Function to set various SHAx registers in CRYPTO based on algorithm type. */ void crypto_set_sha_ctx(void *ctx_ptr, unsigned int bytes_to_write, crypto_auth_alg_type auth_alg, bool first, bool last) { crypto_SHA1_ctx *sha1_ctx = (crypto_SHA1_ctx *) ctx_ptr; crypto_SHA256_ctx *sha256_ctx = (crypto_SHA256_ctx *) ctx_ptr; unsigned int i = 0; unsigned int iv_len = 0; unsigned int *auth_iv; unsigned int seg_cfg_val; seg_cfg_val = SEG_CFG_AUTH_ALG_SHA; if (auth_alg == CRYPTO_AUTH_ALG_SHA1) { seg_cfg_val |= SEG_CFG_AUTH_SIZE_SHA1; if (last) { seg_cfg_val |= SEG_CFG_LAST; } iv_len = SHA1_INIT_VECTOR_SIZE; auth_iv = sha1_ctx->auth_iv; } else if (auth_alg == CRYPTO_AUTH_ALG_SHA256) { seg_cfg_val |= SEG_CFG_AUTH_SIZE_SHA256; if (last) { seg_cfg_val |= SEG_CFG_LAST; } iv_len = SHA256_INIT_VECTOR_SIZE; auth_iv = sha256_ctx->auth_iv; } else { dprintf(CRITICAL, "crypto_set_sha_ctx invalid auth algorithm\n"); return; } for (i = 0; i < iv_len; i++) { wr_ce(*(auth_iv + i), CRYPTO_AUTH_IVn(i)); } wr_ce(seg_cfg_val, CRYPTO_AUTH_SEG_CFG); /* Typecast with crypto_SHA1_ctx because offset of auth_bytecnt in both crypto_SHA1_ctx and crypto_SHA256_ctx are same */ wr_ce(((crypto_SHA1_ctx *) ctx_ptr)->auth_bytecnt[0], CRYPTO_AUTH_BYTECNTn(0)); wr_ce(((crypto_SHA1_ctx *) ctx_ptr)->auth_bytecnt[1], CRYPTO_AUTH_BYTECNTn(1)); wr_ce(bytes_to_write, CRYPTO_AUTH_SEG_SIZE); wr_ce(bytes_to_write, CRYPTO_SEG_SIZE); /* * Ensure previous instructions (any writes to config registers) * are completed. * * TODO: Revisit dsb. */ dsb(); wr_ce(GOPROC_GO, CRYPTO_GOPROC); return; } /* * Function to send data to CRYPTO. This is non-DMOV implementation and uses * polling to send the requested amount of data. */ void crypto_send_data(void *ctx_ptr, unsigned char *data_ptr, unsigned int buff_size, unsigned int bytes_to_write, unsigned int *ret_status) { crypto_SHA1_ctx *sha1_ctx = (crypto_SHA1_ctx *) ctx_ptr; unsigned int bytes_left = 0; unsigned int i = 0; unsigned int ce_status = 0; unsigned int ce_err_bmsk = 0; unsigned int is_not_aligned = FALSE; unsigned char data[4]; unsigned char *buff_ptr = data_ptr; /* Check if the buff_ptr is aligned */ if (!(IS_ALIGNED(buff_ptr))) { is_not_aligned = TRUE; } /* Fill the saved_buff with data from buff_ptr. First we have to write all the data from the saved_buff and then we will write data from buff_ptr. We will update bytes_left and buff_ptr in the while loop once are done writing all the data from saved_buff. */ if (sha1_ctx->saved_buff_indx != 0) { memcpy(sha1_ctx->saved_buff + sha1_ctx->saved_buff_indx, buff_ptr, (((buff_size + sha1_ctx->saved_buff_indx) <= CRYPTO_SHA_BLOCK_SIZE) ? buff_size : (CRYPTO_SHA_BLOCK_SIZE - sha1_ctx->saved_buff_indx))); if (bytes_to_write >= CRYPTO_SHA_BLOCK_SIZE) { bytes_left = CRYPTO_SHA_BLOCK_SIZE; } else { bytes_left = bytes_to_write; } } else { bytes_left = bytes_to_write; } /* Error bitmask to check crypto engine status */ ce_err_bmsk = (SW_ERR | DIN_RDY | DIN_SIZE_AVAIL); while (bytes_left >= 4) { ce_status = rd_ce(CRYPTO_STATUS); ce_status &= ce_err_bmsk; if (ce_status & SW_ERR) { /* If there is SW_ERR, reset the engine */ crypto_eng_reset(); *ret_status = CRYPTO_ERR_FAIL; dprintf(CRITICAL, "crypto_send_data sw error\n"); return; } /* We can write data now - 4 bytes at a time in network byte order */ if ((ce_status & DIN_RDY) && ((ce_status & DIN_SIZE_AVAIL) >= 4)) { if (sha1_ctx->saved_buff_indx != 0) { /* Write from saved_buff */ wr_ce(htonl (* ((unsigned int *)(sha1_ctx->saved_buff) + i)), CRYPTO_DATA_IN); } else { if (!is_not_aligned) { /* Write from buff_ptr aligned */ wr_ce(htonl (*((unsigned int *)buff_ptr + i)), CRYPTO_DATA_IN); } else { /* If buff_ptr is not aligned write byte by byte */ data[0] = *(buff_ptr + i); data[1] = *(buff_ptr + i + 1); data[2] = *(buff_ptr + i + 2); data[3] = *(buff_ptr + i + 3); /* i will incremented by 1 in outside block */ i += 3; wr_ce(htonl(*(unsigned int *)data), CRYPTO_DATA_IN); memset(data, 0, 4); } } i++; bytes_left -= 4; /* Check if we have written from saved_buff. Adjust buff_ptr and bytes_left accordingly */ if ((sha1_ctx->saved_buff_indx != 0) && (bytes_left == 0) && (bytes_to_write > CRYPTO_SHA_BLOCK_SIZE)) { bytes_left = (bytes_to_write - CRYPTO_SHA_BLOCK_SIZE); buff_ptr = (unsigned char *)((unsigned char *)data_ptr + CRYPTO_SHA_BLOCK_SIZE - sha1_ctx-> saved_buff_indx); i = 0; sha1_ctx->saved_buff_indx = 0; if (!(IS_ALIGNED(buff_ptr))) { is_not_aligned = TRUE; } } } } /* We might have bytes_left < 4. Write them now if available */ if (bytes_left) { memset(data, 0, sizeof(unsigned int)); if (sha1_ctx->saved_buff_indx) buff_ptr = (sha1_ctx->saved_buff + bytes_to_write - 1); else buff_ptr = (((unsigned char *)data_ptr) + buff_size - 1); for (i = 0; i < bytes_left; i++) { data[3 - i] = *(buff_ptr - bytes_left + i + 1); } ce_status = rd_ce(CRYPTO_STATUS); ce_status &= ce_err_bmsk; if (ce_status & SW_ERR) { crypto_eng_reset(); *ret_status = CRYPTO_ERR_FAIL; dprintf(CRITICAL, "crypto_send_data sw error 2\n"); return; } if ((ce_status & DIN_RDY) && ((ce_status & DIN_SIZE_AVAIL) >= 4)) { wr_ce(*(unsigned int *)data, CRYPTO_DATA_IN); } } *ret_status = CRYPTO_ERR_NONE; return; } /* * Function to get digest from CRYPTO. We poll for AUTH_DONE from CRYPTO. */ void crypto_get_digest(unsigned char *digest_ptr, unsigned int *ret_status, crypto_auth_alg_type auth_alg, bool last) { unsigned int ce_status = 0; unsigned int ce_err_bmsk = 0; unsigned int i = 0; unsigned int digest_len = 0; ce_err_bmsk = (OPERATION_DONE | SW_ERR); do { ce_status = rd_ce(CRYPTO_STATUS); ce_status &= ce_err_bmsk; } while (ce_status == 0); if (ce_status & SW_ERR) { crypto_eng_reset(); *ret_status = CRYPTO_ERR_FAIL; dprintf(CRITICAL, "crypto_get_digest sw error\n"); return; } /* Digest length depends on auth_alg */ if (auth_alg == CRYPTO_AUTH_ALG_SHA1) { digest_len = SHA1_INIT_VECTOR_SIZE; } else if (auth_alg == CRYPTO_AUTH_ALG_SHA256) { digest_len = SHA256_INIT_VECTOR_SIZE; } /* Retrieve digest from CRYPTO */ for (i = 0; i < digest_len; i++) { unsigned int auth_iv = rd_ce(CRYPTO_AUTH_IVn(i)); if (last) { *((unsigned int *)digest_ptr + i) = htonl(auth_iv); } else { *((unsigned int *)digest_ptr + i) = auth_iv; } } *ret_status = CRYPTO_ERR_NONE; return; } /* Function to restore auth_bytecnt registers for ctx_ptr */ void crypto_get_ctx(void *ctx_ptr) { ((crypto_SHA1_ctx *) ctx_ptr)->auth_bytecnt[0] = rd_ce(CRYPTO_AUTH_BYTECNTn(0)); ((crypto_SHA1_ctx *) ctx_ptr)->auth_bytecnt[1] = rd_ce(CRYPTO_AUTH_BYTECNTn(1)); return; } /* Returns the max authentication block size */ uint32_t crypto_get_max_auth_blk_size() { return 0xFA00; }