From 3ba2e519eafaa0cf90ec209aaccfa47d8913b3ec Mon Sep 17 00:00:00 2001 From: Franck Lenormand Date: Mon, 12 Oct 2020 19:39:59 +0530 Subject: [PATCH] common/dpaax/caamflib: support PDCP-SDAP The SDAP is a protocol in the LTE stack on top of PDCP. It is dedicated to QoS. The difficulty of implementing this protocol is because the PDCP change behavior regarding encryption and authentication of the SDU it receives. In effect PDCP shall not encrypt the SDAP SDU but must authenticate it (when encryption and authentication is enabled). The current version of SEC does not support the SDAP and the change of behavior of PDCP prevent the use of the PDCP protocol command available. The way to do it is to reuse the PDCP implementation but to not use the PDCP protocol and to have descriptors which performs the PDCP protocol. It is implemented by doing small changes of code: #ifdef SDAP_SUPPORT length += SDAP_BYTE_SIZE; offset -= SDAP_BYTE_SIZE; #endif after having computed the size of the SN to read from the input data, then #ifdef SDAP_SUPPORT MATHI(p, MATH0, LSHIFT, 8, 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 It will keep the SN and the SDAP header in MATH0, then shift it to remove the SDAP header and store the result in MATH1. Signed-off-by: Franck Lenormand Signed-off-by: Akhil Goyal --- drivers/common/dpaax/caamflib/desc/pdcp.h | 8 + drivers/common/dpaax/caamflib/desc/sdap.h | 1063 +++++++++++++++++++++ 2 files changed, 1071 insertions(+) create mode 100644 drivers/common/dpaax/caamflib/desc/sdap.h diff --git a/drivers/common/dpaax/caamflib/desc/pdcp.h b/drivers/common/dpaax/caamflib/desc/pdcp.h index 95d1416c2f..f084cf1de0 100644 --- a/drivers/common/dpaax/caamflib/desc/pdcp.h +++ b/drivers/common/dpaax/caamflib/desc/pdcp.h @@ -43,6 +43,14 @@ #define PDCP_C_PLANE_SN_MASK 0x1F000000 #define PDCP_C_PLANE_SN_MASK_BE 0x0000001F +/** + * PDCP_7BIT_SN_MASK - This mask is used in the PDCP descriptors for + * extracting the sequence number (SN) from the + * PDCP User Plane header. + */ +#define PDCP_7BIT_SN_MASK 0x7F000000 +#define PDCP_7BIT_SN_MASK_BE 0x0000007F + /** * PDCP_12BIT_SN_MASK - This mask is used in the PDCP descriptors for * extracting the sequence number (SN) from the diff --git a/drivers/common/dpaax/caamflib/desc/sdap.h b/drivers/common/dpaax/caamflib/desc/sdap.h new file mode 100644 index 0000000000..6523db1733 --- /dev/null +++ b/drivers/common/dpaax/caamflib/desc/sdap.h @@ -0,0 +1,1063 @@ +/* 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__ */ -- 2.20.1