/* SPDX-License-Identifier: BSD-3-Clause * Copyright 2020 NXP */ #ifndef __DESC_SDAP_H__ #define __DESC_SDAP_H__ #include "rta.h" #include "common.h" #include "pdcp.h" /* The file defines all the functions to do PDCP without protocol support in * SEC */ /* Enable SDAP support */ #define SDAP_SUPPORT #ifdef SDAP_SUPPORT #define SDAP_BYTE_SIZE 1 #define SDAP_BITS_SIZE (SDAP_BYTE_SIZE * 8) #endif static inline void key_loading_opti(struct program *p, struct alginfo *cipherdata, struct alginfo *authdata) { LABEL(lbl_skip_key_loading_jump); REFERENCE(ref_skip_key_loading_jump); /* Optimisation to bypass key loading (and decryption of the keys): * Jump command testing: * - SHRD: Descriptor is shared * - SELF: The shared descriptor is in the same DECO * - BOTH: The Class 1 and 2 CHA have finished * -> If this is true, we jump and skip loading of the keys as they are * already loaded */ ref_skip_key_loading_jump = JUMP(p, lbl_skip_key_loading_jump, LOCAL_JUMP, ALL_TRUE, SHRD | SELF | BOTH); /* Load the keys */ if (cipherdata) { KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, cipherdata->keylen, INLINE_KEY(cipherdata)); } if (authdata) { KEY(p, KEY2, authdata->key_enc_flags, authdata->key, authdata->keylen, INLINE_KEY(authdata)); } /* Save the place where we want the jump to go */ SET_LABEL(p, lbl_skip_key_loading_jump); /* Update the jump command with the position where to jump */ PATCH_JUMP(p, ref_skip_key_loading_jump, lbl_skip_key_loading_jump); } static inline int pdcp_sdap_get_sn_parameters(enum pdcp_sn_size sn_size, bool swap, uint32_t *offset, uint32_t *length, uint32_t *sn_mask) { switch (sn_size) { case PDCP_SN_SIZE_5: *offset = 7; *length = 1; *sn_mask = (swap == false) ? PDCP_C_PLANE_SN_MASK : PDCP_C_PLANE_SN_MASK_BE; break; case PDCP_SN_SIZE_7: *offset = 7; *length = 1; *sn_mask = (swap == false) ? PDCP_7BIT_SN_MASK : PDCP_7BIT_SN_MASK_BE; break; case PDCP_SN_SIZE_12: *offset = 6; *length = 2; *sn_mask = (swap == false) ? PDCP_12BIT_SN_MASK : PDCP_12BIT_SN_MASK_BE; break; case PDCP_SN_SIZE_15: *offset = 6; *length = 2; *sn_mask = (swap == false) ? PDCP_U_PLANE_15BIT_SN_MASK : PDCP_U_PLANE_15BIT_SN_MASK_BE; break; case PDCP_SN_SIZE_18: *offset = 5; *length = 3; *sn_mask = (swap == false) ? PDCP_U_PLANE_18BIT_SN_MASK : PDCP_U_PLANE_18BIT_SN_MASK_BE; break; default: pr_err("Invalid sn_size for %s\n", __func__); return -ENOTSUP; } #ifdef SDAP_SUPPORT *length += SDAP_BYTE_SIZE; *offset -= SDAP_BYTE_SIZE; #endif return 0; } static inline int pdcp_sdap_insert_no_int_op(struct program *p, bool swap __maybe_unused, struct alginfo *cipherdata, unsigned int dir, enum pdcp_sn_size sn_size) { int op; uint32_t sn_mask = 0; uint32_t length = 0; uint32_t offset = 0; if (pdcp_sdap_get_sn_parameters(sn_size, swap, &offset, &length, &sn_mask)) return -ENOTSUP; /* Load key */ key_loading_opti(p, cipherdata, NULL); SEQLOAD(p, MATH0, offset, length, 0); JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); #ifdef SDAP_SUPPORT rta_mathi(p, MATH0, ((swap == true) ? MATH_FUN_RSHIFT : MATH_FUN_LSHIFT), SDAP_BITS_SIZE, MATH1, 8, 0); MATHB(p, MATH1, AND, sn_mask, MATH1, 8, IFB | IMMED2); #else MATHB(p, MATH0, AND, sn_mask, MATH1, 8, IFB | IMMED2); #endif SEQSTORE(p, MATH0, offset, length, 0); MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); MOVEB(p, DESCBUF, 8, MATH2, 0, 8, WAITCOMP | IMMED); MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); MATHB(p, SEQINSZ, SUB, MATH3, VSEQINSZ, 4, 0); MATHB(p, SEQINSZ, SUB, MATH3, VSEQOUTSZ, 4, 0); SEQFIFOSTORE(p, MSG, 0, 0, VLF); op = dir == OP_TYPE_ENCAP_PROTOCOL ? DIR_ENC : DIR_DEC; switch (cipherdata->algtype) { case PDCP_CIPHER_TYPE_SNOW: /* Copy the IV */ MOVEB(p, MATH2, 0, CONTEXT1, 0, 8, WAITCOMP | IMMED); ALG_OPERATION(p, OP_ALG_ALGSEL_SNOW_F8, OP_ALG_AAI_F8, OP_ALG_AS_INITFINAL, ICV_CHECK_DISABLE, op); break; case PDCP_CIPHER_TYPE_AES: /* The first 64 bits are 0 */ MOVEB(p, MATH2, 0, CONTEXT1, 16, 8, WAITCOMP | IMMED); ALG_OPERATION(p, OP_ALG_ALGSEL_AES, OP_ALG_AAI_CTR, OP_ALG_AS_INITFINAL, ICV_CHECK_DISABLE, op); break; case PDCP_CIPHER_TYPE_ZUC: if (rta_sec_era < RTA_SEC_ERA_5) { pr_err("Invalid era for selected algorithm\n"); return -ENOTSUP; } /* The LSB and MSB is the same for ZUC context */ MOVEB(p, MATH2, 0, CONTEXT1, 0, 0x08, IMMED); MOVEB(p, MATH2, 0, CONTEXT1, 0x08, 0x08, WAITCOMP | IMMED); ALG_OPERATION(p, OP_ALG_ALGSEL_ZUCE, OP_ALG_AAI_F8, OP_ALG_AS_INITFINAL, ICV_CHECK_DISABLE, op); break; default: pr_err("%s: Invalid encrypt algorithm selected: %d\n", "pdcp_sdap_insert_15bit_op", cipherdata->algtype); return -EINVAL; } SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); return 0; } static inline int pdcp_sdap_insert_enc_only_op(struct program *p, bool swap __maybe_unused, struct alginfo *cipherdata, struct alginfo *authdata __maybe_unused, unsigned int dir, enum pdcp_sn_size sn_size, unsigned char era_2_sw_hfn_ovrd __maybe_unused) { uint32_t offset = 0, length = 0, sn_mask = 0; if (pdcp_sdap_get_sn_parameters(sn_size, swap, &offset, &length, &sn_mask)) return -ENOTSUP; /* Load key */ key_loading_opti(p, cipherdata, NULL); /* Load header */ SEQLOAD(p, MATH0, offset, length, 0); JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); #ifdef SDAP_SUPPORT rta_mathi(p, MATH0, ((swap == true) ? MATH_FUN_RSHIFT : MATH_FUN_LSHIFT), SDAP_BITS_SIZE, MATH1, 8, 0); MATHB(p, MATH1, AND, sn_mask, MATH1, 8, IFB | IMMED2); #else MATHB(p, MATH0, AND, sn_mask, MATH1, 8, IFB | IMMED2); #endif /* Word (32 bit) swap */ MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); /* Load words from PDB: word 02 (HFN) + word 03 (bearer_dir)*/ MOVEB(p, DESCBUF, 8, MATH2, 0, 8, WAITCOMP | IMMED); /* Create basic IV */ MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); /* Write header */ SEQSTORE(p, MATH0, offset, length, 0); if (rta_sec_era > RTA_SEC_ERA_2) { MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); } else { MATHB(p, SEQINSZ, SUB, ONE, MATH1, 4, 0); MATHB(p, MATH1, ADD, ONE, VSEQINSZ, 4, 0); } if (dir == OP_TYPE_ENCAP_PROTOCOL) MATHB(p, SEQINSZ, ADD, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); else MATHB(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); switch (cipherdata->algtype) { case PDCP_CIPHER_TYPE_SNOW: MOVEB(p, MATH2, 0, CONTEXT1, 0, 8, WAITCOMP | IMMED); SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); ALG_OPERATION(p, OP_ALG_ALGSEL_SNOW_F8, OP_ALG_AAI_F8, OP_ALG_AS_INITFINAL, ICV_CHECK_DISABLE, dir == OP_TYPE_ENCAP_PROTOCOL ? DIR_ENC : DIR_DEC); break; case PDCP_CIPHER_TYPE_AES: MOVEB(p, MATH2, 0, CONTEXT1, 16, 8, WAITCOMP | IMMED); SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); ALG_OPERATION(p, OP_ALG_ALGSEL_AES, OP_ALG_AAI_CTR, OP_ALG_AS_INITFINAL, ICV_CHECK_DISABLE, dir == OP_TYPE_ENCAP_PROTOCOL ? DIR_ENC : DIR_DEC); break; case PDCP_CIPHER_TYPE_ZUC: if (rta_sec_era < RTA_SEC_ERA_5) { pr_err("Invalid era for selected algorithm\n"); return -ENOTSUP; } MOVEB(p, MATH2, 0, CONTEXT1, 0, 0x08, IMMED); MOVEB(p, MATH2, 0, CONTEXT1, 0x08, 0x08, WAITCOMP | IMMED); SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); ALG_OPERATION(p, OP_ALG_ALGSEL_ZUCE, OP_ALG_AAI_F8, OP_ALG_AS_INITFINAL, ICV_CHECK_DISABLE, dir == OP_TYPE_ENCAP_PROTOCOL ? DIR_ENC : DIR_DEC); break; default: pr_err("%s: Invalid encrypt algorithm selected: %d\n", "pdcp_sdap_insert_enc_only_op", cipherdata->algtype); return -EINVAL; } if (dir == OP_TYPE_ENCAP_PROTOCOL) { SEQFIFOLOAD(p, MSG1, 0, VLF); FIFOLOAD(p, MSG1, PDCP_NULL_INT_MAC_I_VAL, 4, LAST1 | FLUSH1 | IMMED); } else { SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); MOVE(p, OFIFO, 0, MATH1, 4, PDCP_MAC_I_LEN, WAITCOMP | IMMED); MATHB(p, MATH1, XOR, PDCP_NULL_INT_MAC_I_VAL, NONE, 4, IMMED2); JUMP(p, PDCP_NULL_INT_ICV_CHECK_FAILED_STATUS, HALT_STATUS, ALL_FALSE, MATH_Z); } return 0; } /* * This function leverage the use of in/out snooping as SNOW and ZUC both * have a class 1 and class 2 CHA. It also supports AES as cipher. * Supported: * - cipher: * - AES-CTR * - SNOW F8 * - ZUC F8 * - authentication * - SNOW F8 * - ZUC F8 */ static inline int pdcp_sdap_insert_snoop_op(struct program *p, bool swap __maybe_unused, struct alginfo *cipherdata, struct alginfo *authdata, unsigned int dir, enum pdcp_sn_size sn_size, unsigned char era_2_sw_hfn_ovrd __maybe_unused) { uint32_t offset = 0, length = 0, sn_mask = 0; uint32_t int_op_alg = 0; uint32_t int_op_aai = 0; uint32_t cipher_op_alg = 0; uint32_t cipher_op_aai = 0; if (authdata->algtype == PDCP_CIPHER_TYPE_ZUC) { if (rta_sec_era < RTA_SEC_ERA_5) { pr_err("Invalid era for selected algorithm\n"); return -ENOTSUP; } } if (pdcp_sdap_get_sn_parameters(sn_size, swap, &offset, &length, &sn_mask)) return -ENOTSUP; if (dir == OP_TYPE_ENCAP_PROTOCOL) MATHB(p, SEQINSZ, SUB, length, VSEQINSZ, 4, IMMED2); key_loading_opti(p, cipherdata, authdata); /* Load the PDCP header from the input data * Note: SEQINSZ is decremented by length */ SEQLOAD(p, MATH0, offset, length, 0); /* Wait the SN is loaded */ JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); /* Pass the PDCP header to integrity block */ MOVEB(p, MATH0, offset, IFIFOAB2, 0, length, IMMED); #ifdef SDAP_SUPPORT /* If SDAP is enabled, the least significant byte is the SDAP header * Remove it by shifting the register */ rta_mathi(p, MATH0, ((swap == true) ? MATH_FUN_RSHIFT : MATH_FUN_LSHIFT), SDAP_BITS_SIZE, MATH1, 8, 0); /* Mask the PDCP header to keep only the SN */ MATHB(p, MATH1, AND, sn_mask, MATH1, 8, IFB | IMMED2); #else /* Mask the PDCP header to keep only the SN */ MATHB(p, MATH0, AND, sn_mask, MATH1, 8, IFB | IMMED2); #endif /* Do a byte swap, it places the SN in upper part of the MATH reg */ MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); /* Load the HFN / Beare / Dir from the PDB * CAAM word are 32bit hence loading 8 byte loads 2 words: * - The HFN at offset 8 * - The Bearer / Dir at offset 12 */ MOVEB(p, DESCBUF, 8, MATH2, 0, 8, WAITCOMP | IMMED); /* Create the 4 first byte of the ICV by oring the math registers */ MATHB(p, MATH1, OR, MATH2, MATH1, 8, 0); /* Set the IV of class 1 CHA */ if (cipherdata->algtype == PDCP_CIPHER_TYPE_AES) { MOVEB(p, MATH1, 0, CONTEXT1, 16, 8, IMMED); } else { /* Set the IV for the confidentiality CHA */ MOVEB(p, MATH1, 0, CONTEXT1, 0, 8, IMMED); } /* Set the IV of class 2 CHA */ if (authdata->algtype == PDCP_AUTH_TYPE_ZUC) { /* Set the IV for the integrity CHA */ MOVEB(p, MATH1, 0, CONTEXT2, 0, 8, WAITCOMP | IMMED); } else if (authdata->algtype == PDCP_AUTH_TYPE_SNOW) { MOVEB(p, MATH1, 0, CONTEXT2, 0, 4, WAITCOMP | IMMED); /* Generate the bottom snow IV for integrity * Note: MATH1 lowest 32bits is as follow: * | bearer (5) | Dir (1) | zero (26) | * the resulting math regs will be: * MATH3 MATH2 * | zero (5) | Dir (1) | zero (26) | | Bearer (5) | zero (27) | */ if (swap == false) { MATHB(p, MATH1, AND, upper_32_bits(PDCP_BEARER_MASK), MATH2, 4, IMMED2); MATHB(p, MATH1, AND, lower_32_bits(PDCP_DIR_MASK), MATH3, 4, IMMED2); } else { MATHB(p, MATH1, AND, lower_32_bits(PDCP_BEARER_MASK_BE), MATH2, 4, IMMED2); MATHB(p, MATH1, AND, upper_32_bits(PDCP_DIR_MASK_BE), MATH3, 4, IMMED2); } /* Word swap MATH3 reg */ MATHB(p, MATH3, SHLD, MATH3, MATH3, 8, 0); /* Don't understand, seems to be doing a move of 12 byte * (read MATH2 and overread MATH3) */ MOVEB(p, MATH2, 4, OFIFO, 0, 12, IMMED); /* Add the rest of the snow IV to the context */ MOVE(p, OFIFO, 0, CONTEXT2, 4, 12, IMMED); } /* Set the variable size of data the register will write */ if (dir == OP_TYPE_ENCAP_PROTOCOL) { /* We will add the interity data so add its length */ MATHI(p, SEQINSZ, ADD, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); } else { /* We will check the interity data so remove its length */ MATHI(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); /* Do not take the ICV in the out-snooping configuration */ MATHI(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, VSEQINSZ, 4, IMMED2); } /* We write the PDCP header to output*/ SEQSTORE(p, MATH0, offset, length, 0); /* Definition of the flow of output data */ if (dir == OP_TYPE_ENCAP_PROTOCOL) { /* We write data according to VSEQOUTSZ */ SEQFIFOSTORE(p, MSG, 0, 0, VLF); } else { /* We write data according to VSEQOUTSZ */ SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); } /* Get parameters for authentication */ if (authdata->algtype == PDCP_AUTH_TYPE_ZUC) { int_op_alg = OP_ALG_ALGSEL_ZUCA; int_op_aai = OP_ALG_AAI_F9; } else if (authdata->algtype == PDCP_AUTH_TYPE_SNOW) { int_op_alg = OP_ALG_ALGSEL_SNOW_F9; int_op_aai = OP_ALG_AAI_F9; } else { pr_err("%s no support for auth alg: %d\n", __func__, authdata->algtype); return -1; } /* Get parameters for ciphering */ if (cipherdata->algtype == PDCP_CIPHER_TYPE_ZUC) { cipher_op_alg = OP_ALG_ALGSEL_ZUCE; cipher_op_aai = OP_ALG_AAI_F8; } else if (cipherdata->algtype == PDCP_CIPHER_TYPE_SNOW) { cipher_op_alg = OP_ALG_ALGSEL_SNOW_F8; cipher_op_aai = OP_ALG_AAI_F8; } else if (cipherdata->algtype == PDCP_CIPHER_TYPE_AES) { cipher_op_alg = OP_ALG_ALGSEL_AES; cipher_op_aai = OP_ALG_AAI_CTR; } else { pr_err("%s no support for cipher alg: %d\n", __func__, authdata->algtype); return -1; } /* Configure the CHA, the class 2 CHA must be configured first or an * error will be generated */ /* Configure the class 2 CHA (integrity )*/ ALG_OPERATION(p, int_op_alg, int_op_aai, OP_ALG_AS_INITFINAL, dir == OP_TYPE_ENCAP_PROTOCOL ? ICV_CHECK_DISABLE : ICV_CHECK_ENABLE, DIR_ENC); /* Configure class 1 CHA (confidentiality)*/ ALG_OPERATION(p, cipher_op_alg, cipher_op_aai, OP_ALG_AS_INITFINAL, ICV_CHECK_DISABLE, dir == OP_TYPE_ENCAP_PROTOCOL ? DIR_ENC : DIR_DEC); /* Definition of the flow of input data */ if (dir == OP_TYPE_ENCAP_PROTOCOL) { /* We read data according to VSEQINSZ * Note: we perform an in-snooping, eg the data will be read * only once. they will be sent to both the integrity CHA and * confidentiality CHA */ SEQFIFOLOAD(p, MSGINSNOOP, 0, VLF | LAST2); /* When the integrity CHA is finished, send the ICV stored in * the context to the confidentiality CHA for encryption */ MOVE(p, CONTEXT2, 0, IFIFOAB1, 0, 4, LAST1 | FLUSH1 | IMMED); } else { /* We read data according to VSEQINSZ * Note: we perform an out-snooping, eg the data will be read * only once. The will first be sent to the confidentiality * CHA for decryption, then the CAAM will direct them to the * integrity CHA to verify the ICV (which is at the end of the * sequence) */ SEQFIFOLOAD(p, MSGOUTSNOOP, 0, VLF | LAST2); /* Process the ICV by class 1 CHA */ SEQFIFOLOAD(p, MSG1, 4, LAST1 | FLUSH1); /* Wait for class 1 CHA to finish, the ICV data are stalling in * the output fifo */ JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CLASS1 | NOP | NIFP); if (rta_sec_era >= RTA_SEC_ERA_6) LOAD(p, 0, DCTRL, 0, LDLEN_RST_CHA_OFIFO_PTR, IMMED); /* Save the content left in the Output FIFO (the ICV) to MATH0 */ MOVE(p, OFIFO, 0, MATH0, 0, 4, WAITCOMP | IMMED); /* Configure a NFIFO entry to take data from the altsource * and send it to the class 2 CHA as an ICV */ NFIFOADD(p, IFIFO, ICV2, 4, LAST2); /* Move the content of MATH0 (OFIFO offset) to altsource * Note: As configured by the altsource, this will send * the */ if (rta_sec_era <= RTA_SEC_ERA_2) { /* Shut off automatic Info FIFO entries */ LOAD(p, 0, DCTRL, LDOFF_DISABLE_AUTO_NFIFO, 0, IMMED); MOVE(p, MATH0, 0, IFIFOAB2, 0, 4, WAITCOMP | IMMED); } else { MOVE(p, MATH0, 0, IFIFO, 0, 4, WAITCOMP | IMMED); } } if (authdata->algtype == PDCP_CIPHER_TYPE_ZUC) { /* Reset ZUCA mode and done interrupt * Note: If it is not done, DECO generate an error: 200031ca * -> ZUCA ICV failed */ LOAD(p, CLRW_CLR_C2MODE, CLRW, 0, 4, IMMED); LOAD(p, CIRQ_ZADI, ICTRL, 0, 4, IMMED); } return 0; } /* Function used when the integrity algorithm is a class 1 CHA so outsnooping * is not possible * Supported: * - cipher: * - AES-CTR * - SNOW F8 * - ZUC F8 * - authentication * - AES-CMAC */ static inline int pdcp_sdap_insert_no_snoop_op( struct program *p, bool swap __maybe_unused, struct alginfo *cipherdata, struct alginfo *authdata, unsigned int dir, enum pdcp_sn_size sn_size, unsigned char era_2_sw_hfn_ovrd __maybe_unused) { uint32_t offset = 0, length = 0, sn_mask = 0; uint32_t cipher_alg_op = 0; uint32_t cipher_alg_aai = 0; if (authdata->algtype == PDCP_CIPHER_TYPE_ZUC) { if (rta_sec_era < RTA_SEC_ERA_5) { pr_err("Invalid era for selected algorithm\n"); return -ENOTSUP; } } if (pdcp_sdap_get_sn_parameters(sn_size, swap, &offset, &length, &sn_mask)) return -ENOTSUP; SEQLOAD(p, MATH0, offset, length, 0); JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); #ifdef SDAP_SUPPORT rta_mathi(p, MATH0, ((swap == true) ? MATH_FUN_RSHIFT : MATH_FUN_LSHIFT), SDAP_BITS_SIZE, MATH1, 8, 0); MATHB(p, MATH1, AND, sn_mask, MATH1, 8, IFB | IMMED2); #else MATHB(p, MATH0, AND, sn_mask, MATH1, 8, IFB | IMMED2); #endif MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); MOVEB(p, DESCBUF, 8, MATH2, 0, 0x08, WAITCOMP | IMMED); MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); SEQSTORE(p, MATH0, offset, length, 0); if (dir == OP_TYPE_ENCAP_PROTOCOL) { /* Load authentication key */ KEY(p, KEY1, authdata->key_enc_flags, authdata->key, authdata->keylen, INLINE_KEY(authdata)); /* Set the iv for AES authentication */ MOVEB(p, MATH2, 0, IFIFOAB1, 0, 8, IMMED); /* Pass the header */ MOVEB(p, MATH0, offset, IFIFOAB1, 0, length, IMMED); /* Configure variable size for I/O */ MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); MATHB(p, VSEQINSZ, ADD, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); /* Perform the authentication */ ALG_OPERATION(p, OP_ALG_ALGSEL_AES, OP_ALG_AAI_CMAC, OP_ALG_AS_INITFINAL, ICV_CHECK_DISABLE, DIR_DEC); /* Configure the read of data */ SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); /* Save the ICV generated */ MOVEB(p, CONTEXT1, 0, MATH3, 0, 4, WAITCOMP | IMMED); /* The CHA will be reused so we need to clear it */ LOAD(p, CLRW_RESET_CLS1_CHA | CLRW_CLR_C1KEY | CLRW_CLR_C1CTX | CLRW_CLR_C1ICV | CLRW_CLR_C1DATAS | CLRW_CLR_C1MODE, CLRW, 0, 4, IMMED); /* Load confidentiality key */ KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, cipherdata->keylen, INLINE_KEY(cipherdata)); /* Load the IV for ciphering */ if (cipherdata->algtype == PDCP_CIPHER_TYPE_AES) { MOVEB(p, MATH2, 0, CONTEXT1, 16, 8, IMMED); cipher_alg_op = OP_ALG_ALGSEL_AES; cipher_alg_aai = OP_ALG_AAI_CTR; } else if (cipherdata->algtype == PDCP_CIPHER_TYPE_ZUC) { /* Set the IV for the confidentiality CHA */ MOVEB(p, MATH2, 0, CONTEXT1, 0, 8, IMMED); cipher_alg_op = OP_ALG_ALGSEL_ZUCE; cipher_alg_aai = OP_ALG_AAI_F8; } else if (cipherdata->algtype == PDCP_CIPHER_TYPE_SNOW) { /* Set the IV for the confidentiality CHA */ MOVEB(p, MATH2, 0, CONTEXT1, 0, 8, IMMED); cipher_alg_op = OP_ALG_ALGSEL_SNOW_F8; cipher_alg_aai = OP_ALG_AAI_F8; } /* Rewind the pointer on input data to reread it */ SEQINPTR(p, 0, PDCP_NULL_MAX_FRAME_LEN, RTO); /* Define the ciphering operation */ ALG_OPERATION(p, cipher_alg_op, cipher_alg_aai, OP_ALG_AS_INITFINAL, ICV_CHECK_DISABLE, DIR_ENC); /* Define the data to write */ SEQFIFOSTORE(p, MSG, 0, 0, VLF); /* Skip the header which does not need to be encrypted */ SEQFIFOLOAD(p, SKIP, length, 0); /* Read the rest of the data */ SEQFIFOLOAD(p, MSG1, 0, VLF); /* Send the ICV stored in MATH3 for encryption */ MOVEB(p, MATH3, 0, IFIFOAB1, 0, 4, LAST1 | FLUSH1 | IMMED); } else { /* Load the IV for ciphering */ if (cipherdata->algtype == PDCP_CIPHER_TYPE_AES) { MOVEB(p, MATH2, 0, CONTEXT1, 16, 8, IMMED); cipher_alg_op = OP_ALG_ALGSEL_AES; cipher_alg_aai = OP_ALG_AAI_CTR; } else if (cipherdata->algtype == PDCP_CIPHER_TYPE_ZUC) { /* Set the IV for the confidentiality CHA */ MOVEB(p, MATH2, 0, CONTEXT1, 0, 8, IMMED); cipher_alg_op = OP_ALG_ALGSEL_ZUCE; cipher_alg_aai = OP_ALG_AAI_F8; } else if (cipherdata->algtype == PDCP_CIPHER_TYPE_SNOW) { /* Set the IV for the confidentiality CHA */ MOVEB(p, MATH2, 0, CONTEXT1, 0, 8, IMMED); cipher_alg_op = OP_ALG_ALGSEL_SNOW_F8; cipher_alg_aai = OP_ALG_AAI_F8; } MOVEB(p, MATH2, 0, CONTEXT2, 0, 8, IMMED); /* Read all the data */ MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); /* Do not write back the ICV */ MATHB(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); /* Load the key for ciphering */ KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, cipherdata->keylen, INLINE_KEY(cipherdata)); /* Write all the data */ SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); /* Define the ciphering algorithm */ ALG_OPERATION(p, cipher_alg_op, cipher_alg_aai, OP_ALG_AS_INITFINAL, ICV_CHECK_DISABLE, DIR_DEC); /* Read all the data */ SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); /* Save the ICV which is stalling in output FIFO to MATH3 */ MOVEB(p, OFIFO, 0, MATH3, 0, 4, IMMED); /* Reset class 1 CHA */ LOAD(p, CLRW_RESET_CLS1_CHA | CLRW_CLR_C1KEY | CLRW_CLR_C1CTX | CLRW_CLR_C1ICV | CLRW_CLR_C1DATAS | CLRW_CLR_C1MODE, CLRW, 0, 4, IMMED); /* Load the key for authentcation */ KEY(p, KEY1, authdata->key_enc_flags, authdata->key, authdata->keylen, INLINE_KEY(authdata)); /* Start a new sequence */ SEQINPTR(p, 0, 0, SOP); /* Define the operation to verify the ICV */ ALG_OPERATION(p, OP_ALG_ALGSEL_AES, OP_ALG_AAI_CMAC, OP_ALG_AS_INITFINAL, ICV_CHECK_ENABLE, DIR_DEC); /* Set the variable size input */ MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); MOVE(p, CONTEXT2, 0, IFIFOAB1, 0, 8, IMMED); SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); /* Define an NFIFO entry to load the ICV saved */ LOAD(p, NFIFOENTRY_STYPE_ALTSOURCE | NFIFOENTRY_DEST_CLASS1 | NFIFOENTRY_DTYPE_ICV | NFIFOENTRY_LC1 | NFIFOENTRY_FC1 | 4, NFIFO_SZL, 0, 4, IMMED); /* Load the ICV */ MOVEB(p, MATH3, 0, ALTSOURCE, 0, 4, IMMED); } return 0; } static int pdcp_sdap_insert_with_int_op( struct program *p, bool swap __maybe_unused, struct alginfo *cipherdata, struct alginfo *authdata, enum pdcp_sn_size sn_size, unsigned char era_2_sw_hfn_ovrd, unsigned int dir) { static int ( *pdcp_cp_fp[PDCP_CIPHER_TYPE_INVALID][PDCP_AUTH_TYPE_INVALID])( struct program *, bool swap, struct alginfo *, struct alginfo *, unsigned int, enum pdcp_sn_size, unsigned char __maybe_unused) = { { /* NULL */ pdcp_insert_cplane_null_op, /* NULL */ pdcp_insert_cplane_int_only_op, /* SNOW f9 */ pdcp_insert_cplane_int_only_op, /* AES CMAC */ pdcp_insert_cplane_int_only_op /* ZUC-I */ }, { /* SNOW f8 */ pdcp_sdap_insert_enc_only_op, /* NULL */ pdcp_sdap_insert_snoop_op, /* SNOW f9 */ pdcp_sdap_insert_no_snoop_op, /* AES CMAC */ pdcp_sdap_insert_snoop_op /* ZUC-I */ }, { /* AES CTR */ pdcp_sdap_insert_enc_only_op, /* NULL */ pdcp_sdap_insert_snoop_op, /* SNOW f9 */ pdcp_sdap_insert_no_snoop_op, /* AES CMAC */ pdcp_sdap_insert_snoop_op /* ZUC-I */ }, { /* ZUC-E */ pdcp_sdap_insert_enc_only_op, /* NULL */ pdcp_sdap_insert_snoop_op, /* SNOW f9 */ pdcp_sdap_insert_no_snoop_op, /* AES CMAC */ pdcp_sdap_insert_snoop_op /* ZUC-I */ }, }; int err; err = pdcp_cp_fp[cipherdata->algtype] [authdata->algtype](p, swap, cipherdata, authdata, dir, sn_size, era_2_sw_hfn_ovrd); if (err) return err; return 0; } static inline int cnstr_shdsc_pdcp_sdap_u_plane(uint32_t *descbuf, bool ps, bool swap, enum pdcp_sn_size sn_size, uint32_t hfn, unsigned short bearer, unsigned short direction, uint32_t hfn_threshold, struct alginfo *cipherdata, struct alginfo *authdata, unsigned char era_2_sw_hfn_ovrd, uint32_t caps_mode) { struct program prg; struct program *p = &prg; int err; enum pdb_type_e pdb_type; static enum rta_share_type desc_share[PDCP_CIPHER_TYPE_INVALID][PDCP_AUTH_TYPE_INVALID] = { { /* NULL */ SHR_WAIT, /* NULL */ SHR_ALWAYS, /* SNOW f9 */ SHR_ALWAYS, /* AES CMAC */ SHR_ALWAYS /* ZUC-I */ }, { /* SNOW f8 */ SHR_ALWAYS, /* NULL */ SHR_ALWAYS, /* SNOW f9 */ SHR_WAIT, /* AES CMAC */ SHR_WAIT /* ZUC-I */ }, { /* AES CTR */ SHR_ALWAYS, /* NULL */ SHR_ALWAYS, /* SNOW f9 */ SHR_ALWAYS, /* AES CMAC */ SHR_WAIT /* ZUC-I */ }, { /* ZUC-E */ SHR_ALWAYS, /* NULL */ SHR_WAIT, /* SNOW f9 */ SHR_WAIT, /* AES CMAC */ SHR_WAIT /* ZUC-I */ }, }; LABEL(pdb_end); /* Check HFN override for ERA 2 */ if (rta_sec_era != RTA_SEC_ERA_2 && era_2_sw_hfn_ovrd) { pr_err("Cannot select SW HFN ovrd for other era than 2"); return -EINVAL; } /* Check the confidentiality algorithm is supported by the code */ switch (cipherdata->algtype) { case PDCP_CIPHER_TYPE_NULL: case PDCP_CIPHER_TYPE_SNOW: case PDCP_CIPHER_TYPE_AES: case PDCP_CIPHER_TYPE_ZUC: break; default: pr_err("Cipher algorithm not supported: %d\n", cipherdata->algtype); return -ENOTSUP; } /* Check the authentication algorithm is supported by the code */ if (authdata) { switch (authdata->algtype) { case PDCP_AUTH_TYPE_NULL: case PDCP_AUTH_TYPE_SNOW: case PDCP_AUTH_TYPE_AES: case PDCP_AUTH_TYPE_ZUC: break; default: pr_err("Auth algorithm not supported: %d\n", authdata->algtype); return -ENOTSUP; } } /* Check the Sequence Number size is supported by the code */ switch (sn_size) { case PDCP_SN_SIZE_5: case PDCP_SN_SIZE_7: case PDCP_SN_SIZE_12: case PDCP_SN_SIZE_15: case PDCP_SN_SIZE_18: break; default: pr_err("SN size not supported: %d\n", sn_size); return -ENOTSUP; } /* Check that we are not performing ZUC algo on old platforms */ if (cipherdata->algtype == PDCP_CIPHER_TYPE_ZUC && rta_sec_era < RTA_SEC_ERA_5) { pr_err("ZUC algorithm not supported for era: %d\n", rta_sec_era); return -ENOTSUP; } /* Initialize the program */ PROGRAM_CNTXT_INIT(p, descbuf, 0); if (swap) PROGRAM_SET_BSWAP(p); if (ps) PROGRAM_SET_36BIT_ADDR(p); /* Select the shared descriptor sharing mode */ if (authdata) SHR_HDR(p, desc_share[cipherdata->algtype][authdata->algtype], 0, 0); else SHR_HDR(p, SHR_ALWAYS, 0, 0); /* Construct the PDB */ pdb_type = cnstr_pdcp_u_plane_pdb(p, sn_size, hfn, bearer, direction, hfn_threshold, cipherdata, authdata); if (pdb_type == PDCP_PDB_TYPE_INVALID) { pr_err("Error creating PDCP UPlane PDB\n"); return -EINVAL; } SET_LABEL(p, pdb_end); /* Inser the HFN override operation */ err = insert_hfn_ov_op(p, sn_size, pdb_type, era_2_sw_hfn_ovrd); if (err) return err; /* Create the descriptor */ if (!authdata) { if (cipherdata->algtype == PDCP_CIPHER_TYPE_NULL) { insert_copy_frame_op(p, cipherdata, OP_TYPE_ENCAP_PROTOCOL); } else { err = pdcp_sdap_insert_no_int_op(p, swap, cipherdata, caps_mode, sn_size); if (err) { pr_err("Fail pdcp_sdap_insert_no_int_op\n"); return err; } } } else { err = pdcp_sdap_insert_with_int_op(p, swap, cipherdata, authdata, sn_size, era_2_sw_hfn_ovrd, caps_mode); if (err) { pr_err("Fail pdcp_sdap_insert_with_int_op\n"); return err; } } PATCH_HDR(p, 0, pdb_end); return PROGRAM_FINALIZE(p); } /** * cnstr_shdsc_pdcp_sdap_u_plane_encap - Function for creating a PDCP-SDAP * User Plane encapsulation descriptor. * @descbuf: pointer to buffer for descriptor construction * @ps: if 36/40bit addressing is desired, this parameter must be true * @swap: must be true when core endianness doesn't match SEC endianness * @sn_size: selects Sequence Number Size: 7/12/15 bits * @hfn: starting Hyper Frame Number to be used together with the SN from the * PDCP frames. * @bearer: radio bearer ID * @direction: the direction of the PDCP frame (UL/DL) * @hfn_threshold: HFN value that once reached triggers a warning from SEC that * keys should be renegotiated at the earliest convenience. * @cipherdata: pointer to block cipher transform definitions * Valid algorithm values are those from cipher_type_pdcp enum. * @era_2_sw_hfn_ovrd: if software HFN override mechanism is desired for * this descriptor. Note: Can only be used for * SEC ERA 2. * * Return: size of descriptor written in words or negative number on error. * Once the function returns, the value of this parameter can be used * for reclaiming the space that wasn't used for the descriptor. * * Note: descbuf must be large enough to contain a full 256 byte long * descriptor; after the function returns, by subtracting the actual number of * bytes used, the user can reuse the remaining buffer space for other purposes. */ static inline int cnstr_shdsc_pdcp_sdap_u_plane_encap(uint32_t *descbuf, bool ps, bool swap, enum pdcp_sn_size sn_size, uint32_t hfn, unsigned short bearer, unsigned short direction, uint32_t hfn_threshold, struct alginfo *cipherdata, struct alginfo *authdata, unsigned char era_2_sw_hfn_ovrd) { return cnstr_shdsc_pdcp_sdap_u_plane(descbuf, ps, swap, sn_size, hfn, bearer, direction, hfn_threshold, cipherdata, authdata, era_2_sw_hfn_ovrd, OP_TYPE_ENCAP_PROTOCOL); } /** * cnstr_shdsc_pdcp_sdap_u_plane_decap - Function for creating a PDCP-SDAP * User Plane decapsulation descriptor. * @descbuf: pointer to buffer for descriptor construction * @ps: if 36/40bit addressing is desired, this parameter must be true * @swap: must be true when core endianness doesn't match SEC endianness * @sn_size: selects Sequence Number Size: 7/12/15 bits * @hfn: starting Hyper Frame Number to be used together with the SN from the * PDCP frames. * @bearer: radio bearer ID * @direction: the direction of the PDCP frame (UL/DL) * @hfn_threshold: HFN value that once reached triggers a warning from SEC that * keys should be renegotiated at the earliest convenience. * @cipherdata: pointer to block cipher transform definitions * Valid algorithm values are those from cipher_type_pdcp enum. * @era_2_sw_hfn_ovrd: if software HFN override mechanism is desired for * this descriptor. Note: Can only be used for * SEC ERA 2. * * Return: size of descriptor written in words or negative number on error. * Once the function returns, the value of this parameter can be used * for reclaiming the space that wasn't used for the descriptor. * * Note: descbuf must be large enough to contain a full 256 byte long * descriptor; after the function returns, by subtracting the actual number of * bytes used, the user can reuse the remaining buffer space for other purposes. */ static inline int cnstr_shdsc_pdcp_sdap_u_plane_decap(uint32_t *descbuf, bool ps, bool swap, enum pdcp_sn_size sn_size, uint32_t hfn, unsigned short bearer, unsigned short direction, uint32_t hfn_threshold, struct alginfo *cipherdata, struct alginfo *authdata, unsigned char era_2_sw_hfn_ovrd) { return cnstr_shdsc_pdcp_sdap_u_plane(descbuf, ps, swap, sn_size, hfn, bearer, direction, hfn_threshold, cipherdata, authdata, era_2_sw_hfn_ovrd, OP_TYPE_DECAP_PROTOCOL); } #endif /* __DESC_SDAP_H__ */