From: Akhil Goyal Date: Mon, 22 Oct 2018 07:12:09 +0000 (+0530) Subject: crypto/dpaa2_sec: add sample PDCP descriptor APIs X-Git-Url: http://git.droids-corp.org/?a=commitdiff_plain;h=73a24060cd70;p=dpdk.git crypto/dpaa2_sec: add sample PDCP descriptor APIs DPAA2 SEC platform can support look aside protocol offload for PDCP protocol. The relevant APIs for configuring the hardware for PDCP is added for various modes and crypto algorithms. Signed-off-by: Hemant Agrawal Signed-off-by: Horia Geanta Neag Signed-off-by: Alex Porosanu Signed-off-by: Akhil Goyal --- diff --git a/drivers/crypto/dpaa2_sec/hw/desc.h b/drivers/crypto/dpaa2_sec/hw/desc.h index ca94ea3ff3..5d99dd8af6 100644 --- a/drivers/crypto/dpaa2_sec/hw/desc.h +++ b/drivers/crypto/dpaa2_sec/hw/desc.h @@ -868,7 +868,7 @@ #define OP_PCL_LTE_MIXED_AUTH_SHIFT 0 #define OP_PCL_LTE_MIXED_AUTH_MASK (3 << OP_PCL_LTE_MIXED_AUTH_SHIFT) #define OP_PCL_LTE_MIXED_ENC_SHIFT 8 -#define OP_PCL_LTE_MIXED_ENC_MASK (3 < OP_PCL_LTE_MIXED_ENC_SHIFT) +#define OP_PCL_LTE_MIXED_ENC_MASK (3 << OP_PCL_LTE_MIXED_ENC_SHIFT) #define OP_PCL_LTE_MIXED_AUTH_NULL (OP_PCL_LTE_NULL << \ OP_PCL_LTE_MIXED_AUTH_SHIFT) #define OP_PCL_LTE_MIXED_AUTH_SNOW (OP_PCL_LTE_SNOW << \ diff --git a/drivers/crypto/dpaa2_sec/hw/desc/pdcp.h b/drivers/crypto/dpaa2_sec/hw/desc/pdcp.h new file mode 100644 index 0000000000..719ef605fd --- /dev/null +++ b/drivers/crypto/dpaa2_sec/hw/desc/pdcp.h @@ -0,0 +1,2796 @@ +/* + * Copyright 2008-2013 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause or GPL-2.0+ + */ + +#ifndef __DESC_PDCP_H__ +#define __DESC_PDCP_H__ + +#include "hw/rta.h" +#include "common.h" + +/** + * DOC: PDCP Shared Descriptor Constructors + * + * Shared descriptors for PDCP protocol. + */ + +/** + * PDCP_NULL_MAX_FRAME_LEN - The maximum frame frame length that is supported by + * PDCP NULL protocol. + */ +#define PDCP_NULL_MAX_FRAME_LEN 0x00002FFF + +/** + * PDCP_MAC_I_LEN - The length of the MAC-I for PDCP protocol operation + */ +#define PDCP_MAC_I_LEN 0x00000004 + +/** + * PDCP_MAX_FRAME_LEN_STATUS - The status returned in FD status/command field in + * case the input frame is larger than + * PDCP_NULL_MAX_FRAME_LEN. + */ +#define PDCP_MAX_FRAME_LEN_STATUS 0xF1 + +/** + * PDCP_C_PLANE_SN_MASK - This mask is used in the PDCP descriptors for + * extracting the sequence number (SN) from the PDCP + * Control Plane header. For PDCP Control Plane, the SN + * is constant (5 bits) as opposed to PDCP Data Plane + * (7/12/15 bits). + */ +#define PDCP_C_PLANE_SN_MASK 0x1F000000 +#define PDCP_C_PLANE_SN_MASK_BE 0x0000001F + +/** + * PDCP_U_PLANE_15BIT_SN_MASK - This mask is used in the PDCP descriptors for + * extracting the sequence number (SN) from the + * PDCP User Plane header. For PDCP Control Plane, + * the SN is constant (5 bits) as opposed to PDCP + * Data Plane (7/12/15 bits). + */ +#define PDCP_U_PLANE_15BIT_SN_MASK 0xFF7F0000 +#define PDCP_U_PLANE_15BIT_SN_MASK_BE 0x00007FFF + +/** + * PDCP_BEARER_MASK - This mask is used masking out the bearer for PDCP + * processing with SNOW f9 in LTE. + * + * The value on which this mask is applied is formatted as below: + * Count-C (32 bit) | Bearer (5 bit) | Direction (1 bit) | 0 (26 bits) + * + * Applying this mask is done for creating the upper 64 bits of the IV needed + * for SNOW f9. + * + * The lower 32 bits of the mask are used for masking the direction for AES + * CMAC IV. + */ +#define PDCP_BEARER_MASK 0x00000004FFFFFFFFull +#define PDCP_BEARER_MASK_BE 0xFFFFFFFF04000000ull + +/** + * PDCP_DIR_MASK - This mask is used masking out the direction for PDCP + * processing with SNOW f9 in LTE. + * + * The value on which this mask is applied is formatted as below: + * Bearer (5 bit) | Direction (1 bit) | 0 (26 bits) + * + * Applying this mask is done for creating the lower 32 bits of the IV needed + * for SNOW f9. + * + * The upper 32 bits of the mask are used for masking the direction for AES + * CMAC IV. + */ +#define PDCP_DIR_MASK 0x00000000000000F8ull +#define PDCP_DIR_MASK_BE 0xF800000000000000ull + +/** + * PDCP_NULL_INT_MAC_I_VAL - The value of the PDCP PDU MAC-I in case NULL + * integrity is used. + */ + +#define PDCP_NULL_INT_MAC_I_VAL 0x00000000 + +/** + * PDCP_NULL_INT_ICV_CHECK_FAILED_STATUS - The status used to report ICV check + * failed in case of NULL integrity + * Control Plane processing. + */ +#define PDCP_NULL_INT_ICV_CHECK_FAILED_STATUS 0x0A +/** + * PDCP_DPOVRD_HFN_OV_EN - Value to be used in the FD status/cmd field to + * indicate the HFN override mechanism is active for the + * frame. + */ +#define PDCP_DPOVRD_HFN_OV_EN 0x80000000 + +/** + * PDCP_P4080REV2_HFN_OV_BUFLEN - The length in bytes of the supplementary space + * that must be provided by the user at the + * beginning of the input frame buffer for + * P4080 REV 2. + * + * The format of the frame buffer is the following: + * + * |<---PDCP_P4080REV2_HFN_OV_BUFLEN-->| + * //===================================||============||==============\\ + * || PDCP_DPOVRD_HFN_OV_EN | HFN value || PDCP Header|| PDCP Payload || + * \\===================================||============||==============// + * + * If HFN override mechanism is not desired, then the MSB of the first 4 bytes + * must be set to 0b. + */ +#define PDCP_P4080REV2_HFN_OV_BUFLEN 4 + +/** + * enum cipher_type_pdcp - Type selectors for cipher types in PDCP protocol OP + * instructions. + * @PDCP_CIPHER_TYPE_NULL: NULL + * @PDCP_CIPHER_TYPE_SNOW: SNOW F8 + * @PDCP_CIPHER_TYPE_AES: AES + * @PDCP_CIPHER_TYPE_ZUC: ZUCE + * @PDCP_CIPHER_TYPE_INVALID: invalid option + */ +enum cipher_type_pdcp { + PDCP_CIPHER_TYPE_NULL, + PDCP_CIPHER_TYPE_SNOW, + PDCP_CIPHER_TYPE_AES, + PDCP_CIPHER_TYPE_ZUC, + PDCP_CIPHER_TYPE_INVALID +}; + +/** + * enum auth_type_pdcp - Type selectors for integrity types in PDCP protocol OP + * instructions. + * @PDCP_AUTH_TYPE_NULL: NULL + * @PDCP_AUTH_TYPE_SNOW: SNOW F9 + * @PDCP_AUTH_TYPE_AES: AES CMAC + * @PDCP_AUTH_TYPE_ZUC: ZUCA + * @PDCP_AUTH_TYPE_INVALID: invalid option + */ +enum auth_type_pdcp { + PDCP_AUTH_TYPE_NULL, + PDCP_AUTH_TYPE_SNOW, + PDCP_AUTH_TYPE_AES, + PDCP_AUTH_TYPE_ZUC, + PDCP_AUTH_TYPE_INVALID +}; + +/** + * enum pdcp_dir - Type selectors for direction for PDCP protocol + * @PDCP_DIR_UPLINK: uplink direction + * @PDCP_DIR_DOWNLINK: downlink direction + * @PDCP_DIR_INVALID: invalid option + */ +enum pdcp_dir { + PDCP_DIR_UPLINK = 0, + PDCP_DIR_DOWNLINK = 1, + PDCP_DIR_INVALID +}; + +/** + * enum pdcp_plane - PDCP domain selectors + * @PDCP_CONTROL_PLANE: Control Plane + * @PDCP_DATA_PLANE: Data Plane + * @PDCP_SHORT_MAC: Short MAC + */ +enum pdcp_plane { + PDCP_CONTROL_PLANE, + PDCP_DATA_PLANE, + PDCP_SHORT_MAC +}; + +/** + * enum pdcp_sn_size - Sequence Number Size selectors for PDCP protocol + * @PDCP_SN_SIZE_5: 5bit sequence number + * @PDCP_SN_SIZE_7: 7bit sequence number + * @PDCP_SN_SIZE_12: 12bit sequence number + * @PDCP_SN_SIZE_15: 15bit sequence number + * @PDCP_SN_SIZE_18: 18bit sequence number + */ +enum pdcp_sn_size { + PDCP_SN_SIZE_5 = 5, + PDCP_SN_SIZE_7 = 7, + PDCP_SN_SIZE_12 = 12, + PDCP_SN_SIZE_15 = 15 +}; + +/* + * PDCP Control Plane Protocol Data Blocks + */ +#define PDCP_C_PLANE_PDB_HFN_SHIFT 5 +#define PDCP_C_PLANE_PDB_BEARER_SHIFT 27 +#define PDCP_C_PLANE_PDB_DIR_SHIFT 26 +#define PDCP_C_PLANE_PDB_HFN_THR_SHIFT 5 + +#define PDCP_U_PLANE_PDB_OPT_SHORT_SN 0x2 +#define PDCP_U_PLANE_PDB_OPT_15B_SN 0x4 +#define PDCP_U_PLANE_PDB_SHORT_SN_HFN_SHIFT 7 +#define PDCP_U_PLANE_PDB_LONG_SN_HFN_SHIFT 12 +#define PDCP_U_PLANE_PDB_15BIT_SN_HFN_SHIFT 15 +#define PDCP_U_PLANE_PDB_BEARER_SHIFT 27 +#define PDCP_U_PLANE_PDB_DIR_SHIFT 26 +#define PDCP_U_PLANE_PDB_SHORT_SN_HFN_THR_SHIFT 7 +#define PDCP_U_PLANE_PDB_LONG_SN_HFN_THR_SHIFT 12 +#define PDCP_U_PLANE_PDB_15BIT_SN_HFN_THR_SHIFT 15 + +struct pdcp_pdb { + union { + uint32_t opt; + uint32_t rsvd; + } opt_res; + uint32_t hfn_res; /* HyperFrame number,(27, 25 or 21 bits), + * left aligned & right-padded with zeros. + */ + uint32_t bearer_dir_res;/* Bearer(5 bits), packet direction (1 bit), + * left aligned & right-padded with zeros. + */ + uint32_t hfn_thr_res; /* HyperFrame number threshold (27, 25 or 21 + * bits), left aligned & right-padded with + * zeros. + */ +}; + +/* + * PDCP internal PDB types + */ +enum pdb_type_e { + PDCP_PDB_TYPE_NO_PDB, + PDCP_PDB_TYPE_FULL_PDB, + PDCP_PDB_TYPE_REDUCED_PDB, + PDCP_PDB_TYPE_INVALID +}; + +/* + * Function for appending the portion of a PDCP Control Plane shared descriptor + * which performs NULL encryption and integrity (i.e. copies the input frame + * to the output frame, appending 32 bits of zeros at the end (MAC-I for + * NULL integrity). + */ +static inline int +pdcp_insert_cplane_null_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata __maybe_unused, + struct alginfo *authdata __maybe_unused, + unsigned int dir, + unsigned char era_2_sw_hfn_ovrd __maybe_unused) +{ + LABEL(local_offset); + REFERENCE(move_cmd_read_descbuf); + REFERENCE(move_cmd_write_descbuf); + + if (rta_sec_era > RTA_SEC_ERA_2) { + MATHB(p, SEQINSZ, ADD, ZERO, 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); + } else { + MATHB(p, SEQINSZ, ADD, ONE, VSEQINSZ, 4, 0); + MATHB(p, VSEQINSZ, SUB, ONE, VSEQINSZ, 4, 0); + + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + MATHB(p, SEQINSZ, ADD, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, + IMMED2); + MATHB(p, VSEQINSZ, SUB, ONE, MATH0, 4, 0); + } else { + MATHB(p, VSEQINSZ, SUB, PDCP_MAC_I_LEN, VSEQINSZ, 4, + IMMED2); + MATHB(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, + IMMED2); + MATHB(p, VSEQOUTSZ, SUB, ONE, MATH0, 4, 0); + } + + MATHB(p, MATH0, ADD, ONE, MATH0, 4, 0); + + /* + * Since MOVELEN is available only starting with + * SEC ERA 3, use poor man's MOVELEN: create a MOVE + * command dynamically by writing the length from M1 by + * OR-ing the command in the M1 register and MOVE the + * result into the descriptor buffer. Care must be taken + * wrt. the location of the command because of SEC + * pipelining. The actual MOVEs are written at the end + * of the descriptor due to calculations needed on the + * offset in the descriptor for the MOVE command. + */ + move_cmd_read_descbuf = MOVE(p, DESCBUF, 0, MATH0, 0, 6, + IMMED); + move_cmd_write_descbuf = MOVE(p, MATH0, 0, DESCBUF, 0, 8, + WAITCOMP | IMMED); + } + MATHB(p, VSEQINSZ, SUB, PDCP_NULL_MAX_FRAME_LEN, NONE, 4, + IMMED2); + JUMP(p, PDCP_MAX_FRAME_LEN_STATUS, HALT_STATUS, ALL_FALSE, MATH_N); + + if (rta_sec_era > RTA_SEC_ERA_2) { + if (dir == OP_TYPE_ENCAP_PROTOCOL) + MATHB(p, VSEQINSZ, ADD, ZERO, MATH0, 4, 0); + else + MATHB(p, VSEQOUTSZ, ADD, ZERO, MATH0, 4, 0); + } + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); + + if (rta_sec_era > RTA_SEC_ERA_2) { + MOVE(p, AB1, 0, OFIFO, 0, MATH0, 0); + } else { + SET_LABEL(p, local_offset); + + /* Shut off automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_DISABLE_AUTO_NFIFO, 0, IMMED); + /* Placeholder for MOVE command with length from M1 register */ + MOVE(p, IFIFOAB1, 0, OFIFO, 0, 0, IMMED); + /* Enable automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_ENABLE_AUTO_NFIFO, 0, IMMED); + } + + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + MATHB(p, MATH1, XOR, MATH1, MATH0, 8, 0); + MOVE(p, MATH0, 0, OFIFO, 0, 4, IMMED); + } + + if (rta_sec_era < RTA_SEC_ERA_3) { + PATCH_MOVE(p, move_cmd_read_descbuf, local_offset); + PATCH_MOVE(p, move_cmd_write_descbuf, local_offset); + } + + return 0; +} + +static inline int +insert_copy_frame_op(struct program *p, + struct alginfo *cipherdata __maybe_unused, + unsigned int dir __maybe_unused) +{ + LABEL(local_offset); + REFERENCE(move_cmd_read_descbuf); + REFERENCE(move_cmd_write_descbuf); + + if (rta_sec_era > RTA_SEC_ERA_2) { + MATHB(p, SEQINSZ, ADD, ZERO, VSEQINSZ, 4, 0); + MATHB(p, SEQINSZ, ADD, ZERO, VSEQOUTSZ, 4, 0); + } else { + MATHB(p, SEQINSZ, ADD, ONE, VSEQINSZ, 4, 0); + MATHB(p, VSEQINSZ, SUB, ONE, VSEQINSZ, 4, 0); + MATHB(p, SEQINSZ, ADD, ONE, VSEQOUTSZ, 4, 0); + MATHB(p, VSEQOUTSZ, SUB, ONE, VSEQOUTSZ, 4, 0); + MATHB(p, VSEQINSZ, SUB, ONE, MATH0, 4, 0); + MATHB(p, MATH0, ADD, ONE, MATH0, 4, 0); + + /* + * Since MOVELEN is available only starting with + * SEC ERA 3, use poor man's MOVELEN: create a MOVE + * command dynamically by writing the length from M1 by + * OR-ing the command in the M1 register and MOVE the + * result into the descriptor buffer. Care must be taken + * wrt. the location of the command because of SEC + * pipelining. The actual MOVEs are written at the end + * of the descriptor due to calculations needed on the + * offset in the descriptor for the MOVE command. + */ + move_cmd_read_descbuf = MOVE(p, DESCBUF, 0, MATH0, 0, 6, + IMMED); + move_cmd_write_descbuf = MOVE(p, MATH0, 0, DESCBUF, 0, 8, + WAITCOMP | IMMED); + } + MATHB(p, SEQINSZ, SUB, PDCP_NULL_MAX_FRAME_LEN, NONE, 4, + IFB | IMMED2); + JUMP(p, PDCP_MAX_FRAME_LEN_STATUS, HALT_STATUS, ALL_FALSE, MATH_N); + + if (rta_sec_era > RTA_SEC_ERA_2) + MATHB(p, VSEQINSZ, ADD, ZERO, MATH0, 4, 0); + + SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); + if (rta_sec_era > RTA_SEC_ERA_2) { + MOVE(p, AB1, 0, OFIFO, 0, MATH0, 0); + } else { + SET_LABEL(p, local_offset); + + /* Shut off automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_DISABLE_AUTO_NFIFO, 0, IMMED); + + /* Placeholder for MOVE command with length from M0 register */ + MOVE(p, IFIFOAB1, 0, OFIFO, 0, 0, IMMED); + + /* Enable automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_ENABLE_AUTO_NFIFO, 0, IMMED); + } + + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + + if (rta_sec_era < RTA_SEC_ERA_3) { + PATCH_MOVE(p, move_cmd_read_descbuf, local_offset); + PATCH_MOVE(p, move_cmd_write_descbuf, local_offset); + } + return 0; +} + +static inline int +pdcp_insert_cplane_int_only_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata __maybe_unused, + struct alginfo *authdata, unsigned int dir, + unsigned char era_2_sw_hfn_ovrd) +{ + LABEL(local_offset); + REFERENCE(move_cmd_read_descbuf); + REFERENCE(move_cmd_write_descbuf); + + switch (authdata->algtype) { + case PDCP_AUTH_TYPE_SNOW: + /* Insert Auth Key */ + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + SEQLOAD(p, MATH0, 7, 1, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + + if (rta_sec_era > RTA_SEC_ERA_2 || + (rta_sec_era == RTA_SEC_ERA_2 && + era_2_sw_hfn_ovrd == 0)) { + SEQINPTR(p, 0, 1, RTO); + } else { + SEQINPTR(p, 0, 5, RTO); + SEQFIFOLOAD(p, SKIP, 4, 0); + } + + if (swap == false) { + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + + MOVEB(p, DESCBUF, 8, MATH2, 0, 8, WAITCOMP | IMMED); + + MATHB(p, MATH2, AND, PDCP_BEARER_MASK, MATH2, 8, + IMMED2); + MOVEB(p, DESCBUF, 0x0C, MATH3, 0, 4, WAITCOMP | IMMED); + MATHB(p, MATH3, AND, PDCP_DIR_MASK, MATH3, 8, IMMED2); + MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); + MOVEB(p, MATH2, 0, CONTEXT2, 0, 0x0C, WAITCOMP | IMMED); + } else { + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK_BE, MATH1, 8, + IFB | IMMED2); + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + + MOVE(p, DESCBUF, 8, MATH2, 0, 8, WAITCOMP | IMMED); + MATHB(p, MATH2, AND, PDCP_BEARER_MASK_BE, MATH2, 8, + IMMED2); + + MOVE(p, DESCBUF, 0x0C, MATH3, 0, 4, WAITCOMP | IMMED); + MATHB(p, MATH3, AND, PDCP_DIR_MASK_BE, MATH3, 8, + IMMED2); + MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); + MOVE(p, MATH2, 0, CONTEXT2, 0, 0x0C, WAITCOMP | IMMED); + } + + if (dir == OP_TYPE_DECAP_PROTOCOL) { + MATHB(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, MATH1, 4, + IMMED2); + } else { + if (rta_sec_era > RTA_SEC_ERA_2) { + MATHB(p, SEQINSZ, SUB, ZERO, MATH1, 4, + 0); + } else { + MATHB(p, SEQINSZ, ADD, ONE, MATH1, 4, + 0); + MATHB(p, MATH1, SUB, ONE, MATH1, 4, + 0); + } + } + + if (rta_sec_era > RTA_SEC_ERA_2) { + MATHB(p, MATH1, SUB, ZERO, VSEQINSZ, 4, 0); + MATHB(p, MATH1, SUB, ZERO, VSEQOUTSZ, 4, 0); + } else { + MATHB(p, ZERO, ADD, MATH1, VSEQINSZ, 4, 0); + MATHB(p, ZERO, ADD, MATH1, VSEQOUTSZ, 4, 0); + + /* + * Since MOVELEN is available only starting with + * SEC ERA 3, use poor man's MOVELEN: create a MOVE + * command dynamically by writing the length from M1 by + * OR-ing the command in the M1 register and MOVE the + * result into the descriptor buffer. Care must be taken + * wrt. the location of the command because of SEC + * pipelining. The actual MOVEs are written at the end + * of the descriptor due to calculations needed on the + * offset in the descriptor for the MOVE command. + */ + move_cmd_read_descbuf = MOVE(p, DESCBUF, 0, MATH1, 0, 6, + IMMED); + move_cmd_write_descbuf = MOVE(p, MATH1, 0, DESCBUF, 0, + 8, WAITCOMP | IMMED); + } + + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + ALG_OPERATION(p, OP_ALG_ALGSEL_SNOW_F9, OP_ALG_AAI_F9, + OP_ALG_AS_INITFINAL, + dir == OP_TYPE_ENCAP_PROTOCOL ? + ICV_CHECK_DISABLE : ICV_CHECK_ENABLE, + DIR_ENC); + + if (rta_sec_era > RTA_SEC_ERA_2) { + SEQFIFOLOAD(p, MSGINSNOOP, 0, + VLF | LAST1 | LAST2 | FLUSH1); + MOVE(p, AB1, 0, OFIFO, 0, MATH1, 0); + } else { + SEQFIFOLOAD(p, MSGINSNOOP, 0, + VLF | LAST1 | LAST2 | FLUSH1); + SET_LABEL(p, local_offset); + + /* Shut off automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_DISABLE_AUTO_NFIFO, 0, IMMED); + /* + * Placeholder for MOVE command with length from M1 + * register + */ + MOVE(p, IFIFOAB1, 0, OFIFO, 0, 0, IMMED); + /* Enable automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_ENABLE_AUTO_NFIFO, 0, IMMED); + } + + if (dir == OP_TYPE_DECAP_PROTOCOL) + SEQFIFOLOAD(p, ICV2, 4, LAST2); + else + SEQSTORE(p, CONTEXT2, 0, 4, 0); + + break; + + case PDCP_AUTH_TYPE_AES: + /* Insert Auth Key */ + KEY(p, KEY1, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + SEQLOAD(p, MATH0, 7, 1, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + if (rta_sec_era > RTA_SEC_ERA_2 || + (rta_sec_era == RTA_SEC_ERA_2 && + era_2_sw_hfn_ovrd == 0)) { + SEQINPTR(p, 0, 1, RTO); + } else { + SEQINPTR(p, 0, 5, RTO); + SEQFIFOLOAD(p, SKIP, 4, 0); + } + + if (swap == false) { + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + 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); + MOVEB(p, MATH2, 0, IFIFOAB1, 0, 8, IMMED); + } else { + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK_BE, MATH1, 8, + IFB | IMMED2); + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + + MOVE(p, DESCBUF, 8, MATH2, 0, 8, WAITCOMP | IMMED); + MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); + MOVE(p, MATH2, 0, IFIFOAB1, 0, 8, IMMED); + } + + if (dir == OP_TYPE_DECAP_PROTOCOL) { + MATHB(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, MATH1, 4, + IMMED2); + } else { + if (rta_sec_era > RTA_SEC_ERA_2) { + MATHB(p, SEQINSZ, SUB, ZERO, MATH1, 4, + 0); + } else { + MATHB(p, SEQINSZ, ADD, ONE, MATH1, 4, + 0); + MATHB(p, MATH1, SUB, ONE, MATH1, 4, + 0); + } + } + + if (rta_sec_era > RTA_SEC_ERA_2) { + MATHB(p, MATH1, SUB, ZERO, VSEQINSZ, 4, 0); + MATHB(p, MATH1, SUB, ZERO, VSEQOUTSZ, 4, 0); + } else { + MATHB(p, ZERO, ADD, MATH1, VSEQINSZ, 4, 0); + MATHB(p, ZERO, ADD, MATH1, VSEQOUTSZ, 4, 0); + + /* + * Since MOVELEN is available only starting with + * SEC ERA 3, use poor man's MOVELEN: create a MOVE + * command dynamically by writing the length from M1 by + * OR-ing the command in the M1 register and MOVE the + * result into the descriptor buffer. Care must be taken + * wrt. the location of the command because of SEC + * pipelining. The actual MOVEs are written at the end + * of the descriptor due to calculations needed on the + * offset in the descriptor for the MOVE command. + */ + move_cmd_read_descbuf = MOVE(p, DESCBUF, 0, MATH1, 0, 6, + IMMED); + move_cmd_write_descbuf = MOVE(p, MATH1, 0, DESCBUF, 0, + 8, WAITCOMP | IMMED); + } + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + ALG_OPERATION(p, OP_ALG_ALGSEL_AES, + OP_ALG_AAI_CMAC, + OP_ALG_AS_INITFINAL, + dir == OP_TYPE_ENCAP_PROTOCOL ? + ICV_CHECK_DISABLE : ICV_CHECK_ENABLE, + DIR_ENC); + + if (rta_sec_era > RTA_SEC_ERA_2) { + MOVE(p, AB2, 0, OFIFO, 0, MATH1, 0); + SEQFIFOLOAD(p, MSGINSNOOP, 0, + VLF | LAST1 | LAST2 | FLUSH1); + } else { + SEQFIFOLOAD(p, MSGINSNOOP, 0, + VLF | LAST1 | LAST2 | FLUSH1); + SET_LABEL(p, local_offset); + + /* Shut off automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_DISABLE_AUTO_NFIFO, 0, IMMED); + + /* + * Placeholder for MOVE command with length from + * M1 register + */ + MOVE(p, IFIFOAB2, 0, OFIFO, 0, 0, IMMED); + + /* Enable automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_ENABLE_AUTO_NFIFO, 0, IMMED); + } + + if (dir == OP_TYPE_DECAP_PROTOCOL) + SEQFIFOLOAD(p, ICV1, 4, LAST1 | FLUSH1); + else + SEQSTORE(p, CONTEXT1, 0, 4, 0); + + break; + + case PDCP_AUTH_TYPE_ZUC: + if (rta_sec_era < RTA_SEC_ERA_5) { + pr_err("Invalid era for selected algorithm\n"); + return -ENOTSUP; + } + /* Insert Auth Key */ + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + SEQLOAD(p, MATH0, 7, 1, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + SEQINPTR(p, 0, 1, RTO); + if (swap == false) { + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + 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); + MOVEB(p, MATH2, 0, CONTEXT2, 0, 8, IMMED); + + } else { + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK_BE, MATH1, 8, + IFB | IMMED2); + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + + MOVE(p, DESCBUF, 8, MATH2, 0, 8, WAITCOMP | IMMED); + MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); + MOVE(p, MATH2, 0, CONTEXT2, 0, 8, IMMED); + } + if (dir == OP_TYPE_DECAP_PROTOCOL) + MATHB(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, MATH1, 4, + IMMED2); + else + MATHB(p, SEQINSZ, SUB, ZERO, MATH1, 4, 0); + + MATHB(p, MATH1, SUB, ZERO, VSEQINSZ, 4, 0); + MATHB(p, MATH1, SUB, ZERO, VSEQOUTSZ, 4, 0); + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + ALG_OPERATION(p, OP_ALG_ALGSEL_ZUCA, + OP_ALG_AAI_F9, + OP_ALG_AS_INITFINAL, + dir == OP_TYPE_ENCAP_PROTOCOL ? + ICV_CHECK_DISABLE : ICV_CHECK_ENABLE, + DIR_ENC); + SEQFIFOLOAD(p, MSGINSNOOP, 0, VLF | LAST1 | LAST2 | FLUSH1); + MOVE(p, AB1, 0, OFIFO, 0, MATH1, 0); + + if (dir == OP_TYPE_DECAP_PROTOCOL) + SEQFIFOLOAD(p, ICV2, 4, LAST2); + else + SEQSTORE(p, CONTEXT2, 0, 4, 0); + + break; + + default: + pr_err("%s: Invalid integrity algorithm selected: %d\n", + "pdcp_insert_cplane_int_only_op", authdata->algtype); + return -EINVAL; + } + + if (rta_sec_era < RTA_SEC_ERA_3) { + PATCH_MOVE(p, move_cmd_read_descbuf, local_offset); + PATCH_MOVE(p, move_cmd_write_descbuf, local_offset); + } + + return 0; +} + +static inline int +pdcp_insert_cplane_enc_only_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata, + struct alginfo *authdata __maybe_unused, + unsigned int dir, + unsigned char era_2_sw_hfn_ovrd __maybe_unused) +{ + /* Insert Cipher Key */ + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + + if (rta_sec_era >= RTA_SEC_ERA_8) { + PROTOCOL(p, dir, OP_PCLID_LTE_PDCP_CTRL_MIXED, + (uint16_t)cipherdata->algtype << 8); + return 0; + } + + SEQLOAD(p, MATH0, 7, 1, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + if (swap == false) + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + else + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK_BE, MATH1, 8, + IFB | IMMED2); + SEQSTORE(p, MATH0, 7, 1, 0); + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + MOVE(p, DESCBUF, 8, MATH2, 0, 8, WAITCOMP | IMMED); + MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); + + switch (cipherdata->algtype) { + case PDCP_CIPHER_TYPE_SNOW: + MOVE(p, MATH2, 0, CONTEXT1, 0, 8, WAITCOMP | IMMED); + + 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); + 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: + MOVE(p, MATH2, 0, CONTEXT1, 0x10, 0x10, WAITCOMP | IMMED); + + 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); + + 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; + } + + MOVE(p, MATH2, 0, CONTEXT1, 0, 0x08, IMMED); + MOVE(p, MATH2, 0, CONTEXT1, 0x08, 0x08, WAITCOMP | IMMED); + MATHB(p, SEQINSZ, SUB, ZERO, 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); + + 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_insert_cplane_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; +} + +static inline int +pdcp_insert_cplane_acc_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata, + struct alginfo *authdata, + unsigned int dir, + unsigned char era_2_hfn_ovrd __maybe_unused) +{ + /* Insert Auth Key */ + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, authdata->keylen, + INLINE_KEY(authdata)); + + /* Insert Cipher Key */ + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + PROTOCOL(p, dir, OP_PCLID_LTE_PDCP_CTRL, (uint16_t)cipherdata->algtype); + + return 0; +} + +static inline int +pdcp_insert_cplane_snow_aes_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata, + struct alginfo *authdata, + unsigned int dir, + unsigned char era_2_sw_hfn_ovrd) +{ + LABEL(back_to_sd_offset); + LABEL(end_desc); + LABEL(local_offset); + LABEL(jump_to_beginning); + LABEL(fifo_load_mac_i_offset); + REFERENCE(seqin_ptr_read); + REFERENCE(seqin_ptr_write); + REFERENCE(seq_out_read); + REFERENCE(jump_back_to_sd_cmd); + REFERENCE(move_mac_i_to_desc_buf); + + if (rta_sec_era >= RTA_SEC_ERA_8) { + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + + PROTOCOL(p, dir, OP_PCLID_LTE_PDCP_CTRL_MIXED, + ((uint16_t)cipherdata->algtype << 8) | + (uint16_t)authdata->algtype); + + return 0; + } + + SEQLOAD(p, MATH0, 7, 1, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + if (swap == false) + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + else + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK_BE, MATH1, 8, + IFB | IMMED2); + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + MOVE(p, DESCBUF, 4, MATH2, 0, 0x08, WAITCOMP | IMMED); + MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); + SEQSTORE(p, MATH0, 7, 1, 0); + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + if (rta_sec_era > RTA_SEC_ERA_2 || + (rta_sec_era == RTA_SEC_ERA_2 && + era_2_sw_hfn_ovrd == 0)) { + SEQINPTR(p, 0, 1, RTO); + } else { + SEQINPTR(p, 0, 5, RTO); + SEQFIFOLOAD(p, SKIP, 4, 0); + } + KEY(p, KEY1, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + MOVE(p, MATH2, 0, IFIFOAB1, 0, 0x08, IMMED); + + if (rta_sec_era > RTA_SEC_ERA_2) { + MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); + MATHB(p, SEQINSZ, SUB, ZERO, MATH1, 4, 0); + MATHB(p, VSEQINSZ, ADD, PDCP_MAC_I_LEN - 1, VSEQOUTSZ, + 4, IMMED2); + } else { + MATHB(p, SEQINSZ, SUB, MATH3, VSEQINSZ, 4, 0); + MATHB(p, VSEQINSZ, ADD, PDCP_MAC_I_LEN - 1, VSEQOUTSZ, + 4, IMMED2); + /* + * Note: Although the calculations below might seem a + * little off, the logic is the following: + * + * - SEQ IN PTR RTO below needs the full length of the + * frame; in case of P4080_REV_2_HFN_OV_WORKAROUND, + * this means the length of the frame to be processed + * + 4 bytes (the HFN override flag and value). + * The length of the frame to be processed minus 1 + * byte is in the VSIL register (because + * VSIL = SIL + 3, due to 1 byte, the header being + * already written by the SEQ STORE above). So for + * calculating the length to use in RTO, I add one + * to the VSIL value in order to obtain the total + * frame length. This helps in case of P4080 which + * can have the value 0 as an operand in a MATH + * command only as SRC1 When the HFN override + * workaround is not enabled, the length of the + * frame is given by the SIL register; the + * calculation is similar to the one in the SEC 4.2 + * and SEC 5.3 cases. + */ + if (era_2_sw_hfn_ovrd) + MATHB(p, VSEQOUTSZ, ADD, ONE, MATH1, 4, + 0); + else + MATHB(p, SEQINSZ, ADD, MATH3, MATH1, 4, + 0); + } + /* + * Placeholder for filling the length in + * SEQIN PTR RTO below + */ + seqin_ptr_read = MOVE(p, DESCBUF, 0, MATH1, 0, 6, IMMED); + seqin_ptr_write = MOVE(p, MATH1, 0, DESCBUF, 0, 8, + WAITCOMP | IMMED); + ALG_OPERATION(p, OP_ALG_ALGSEL_AES, + OP_ALG_AAI_CMAC, + OP_ALG_AS_INITFINAL, + ICV_CHECK_DISABLE, + DIR_DEC); + SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); + MOVE(p, CONTEXT1, 0, MATH3, 0, 4, WAITCOMP | IMMED); + if (rta_sec_era <= RTA_SEC_ERA_3) + LOAD(p, CLRW_CLR_C1KEY | + CLRW_CLR_C1CTX | + CLRW_CLR_C1ICV | + CLRW_CLR_C1DATAS | + CLRW_CLR_C1MODE, + CLRW, 0, 4, IMMED); + else + 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); + + if (rta_sec_era <= RTA_SEC_ERA_3) + LOAD(p, CCTRL_RESET_CHA_ALL, CCTRL, 0, 4, IMMED); + + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + SET_LABEL(p, local_offset); + MOVE(p, MATH2, 0, CONTEXT1, 0, 8, IMMED); + SEQINPTR(p, 0, 0, RTO); + + if (rta_sec_era == RTA_SEC_ERA_2 && era_2_sw_hfn_ovrd) { + SEQFIFOLOAD(p, SKIP, 5, 0); + MATHB(p, SEQINSZ, ADD, ONE, SEQINSZ, 4, 0); + } + + MATHB(p, SEQINSZ, SUB, ONE, VSEQINSZ, 4, 0); + ALG_OPERATION(p, OP_ALG_ALGSEL_SNOW_F8, + OP_ALG_AAI_F8, + OP_ALG_AS_INITFINAL, + ICV_CHECK_DISABLE, + DIR_ENC); + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + + if (rta_sec_era > RTA_SEC_ERA_2 || + (rta_sec_era == RTA_SEC_ERA_2 && + era_2_sw_hfn_ovrd == 0)) + SEQFIFOLOAD(p, SKIP, 1, 0); + + SEQFIFOLOAD(p, MSG1, 0, VLF); + MOVE(p, MATH3, 0, IFIFOAB1, 0, 4, LAST1 | FLUSH1 | IMMED); + PATCH_MOVE(p, seqin_ptr_read, local_offset); + PATCH_MOVE(p, seqin_ptr_write, local_offset); + } else { + MOVE(p, MATH2, 0, CONTEXT1, 0, 8, IMMED); + + if (rta_sec_era >= RTA_SEC_ERA_5) + MOVE(p, CONTEXT1, 0, CONTEXT2, 0, 8, IMMED); + + if (rta_sec_era > RTA_SEC_ERA_2) + MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); + else + MATHB(p, SEQINSZ, SUB, MATH3, VSEQINSZ, 4, 0); + + MATHB(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); +/* + * TODO: To be changed when proper support is added in RTA (can't load a + * command that is also written by RTA (or patch it for that matter). + * Change when proper RTA support is added. + */ + if (p->ps) + WORD(p, 0x168B0004); + else + WORD(p, 0x16880404); + + jump_back_to_sd_cmd = JUMP(p, 0, LOCAL_JUMP, ALL_TRUE, 0); + /* + * Placeholder for command reading the SEQ OUT command in + * JD. Done for rereading the decrypted data and performing + * the integrity check + */ +/* + * TODO: RTA currently doesn't support patching of length of a MOVE command + * Thus, it is inserted as a raw word, as per PS setting. + */ + if (p->ps) + seq_out_read = MOVE(p, DESCBUF, 0, MATH1, 0, 20, + WAITCOMP | IMMED); + else + seq_out_read = MOVE(p, DESCBUF, 0, MATH1, 0, 16, + WAITCOMP | IMMED); + + MATHB(p, MATH1, XOR, CMD_SEQ_IN_PTR ^ CMD_SEQ_OUT_PTR, MATH1, 4, + IMMED2); + /* Placeholder for overwriting the SEQ IN with SEQ OUT */ +/* + * TODO: RTA currently doesn't support patching of length of a MOVE command + * Thus, it is inserted as a raw word, as per PS setting. + */ + if (p->ps) + MOVE(p, MATH1, 0, DESCBUF, 0, 24, IMMED); + else + MOVE(p, MATH1, 0, DESCBUF, 0, 20, IMMED); + + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + + if (rta_sec_era >= RTA_SEC_ERA_4) + MOVE(p, CONTEXT1, 0, CONTEXT2, 0, 8, IMMED); + else + MOVE(p, CONTEXT1, 0, MATH3, 0, 8, IMMED); + + ALG_OPERATION(p, OP_ALG_ALGSEL_SNOW_F8, + OP_ALG_AAI_F8, + OP_ALG_AS_INITFINAL, + ICV_CHECK_DISABLE, + DIR_DEC); + SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); + SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); + + if (rta_sec_era <= RTA_SEC_ERA_3) + move_mac_i_to_desc_buf = MOVE(p, OFIFO, 0, DESCBUF, 0, + 4, WAITCOMP | IMMED); + else + MOVE(p, OFIFO, 0, MATH3, 0, 4, IMMED); + + if (rta_sec_era <= RTA_SEC_ERA_3) + LOAD(p, CCTRL_RESET_CHA_ALL, CCTRL, 0, 4, IMMED); + else + 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); + + KEY(p, KEY1, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + /* + * Placeholder for jump in SD for executing the new SEQ IN PTR + * command (which is actually the old SEQ OUT PTR command + * copied over from JD. + */ + SET_LABEL(p, jump_to_beginning); + JUMP(p, 1 - jump_to_beginning, LOCAL_JUMP, ALL_TRUE, 0); + SET_LABEL(p, back_to_sd_offset); + ALG_OPERATION(p, OP_ALG_ALGSEL_AES, + OP_ALG_AAI_CMAC, + OP_ALG_AS_INITFINAL, + ICV_CHECK_ENABLE, + DIR_DEC); + + /* Read the # of bytes written in the output buffer + 1 (HDR) */ + MATHB(p, VSEQOUTSZ, ADD, ONE, VSEQINSZ, 4, 0); + + if (rta_sec_era <= RTA_SEC_ERA_3) + MOVE(p, MATH3, 0, IFIFOAB1, 0, 8, IMMED); + else + MOVE(p, CONTEXT2, 0, IFIFOAB1, 0, 8, IMMED); + + if (rta_sec_era == RTA_SEC_ERA_2 && era_2_sw_hfn_ovrd) + SEQFIFOLOAD(p, SKIP, 4, 0); + + SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); + + if (rta_sec_era >= RTA_SEC_ERA_4) { + LOAD(p, NFIFOENTRY_STYPE_ALTSOURCE | + NFIFOENTRY_DEST_CLASS1 | + NFIFOENTRY_DTYPE_ICV | + NFIFOENTRY_LC1 | + NFIFOENTRY_FC1 | 4, NFIFO_SZL, 0, 4, IMMED); + MOVE(p, MATH3, 0, ALTSOURCE, 0, 4, IMMED); + } else { + SET_LABEL(p, fifo_load_mac_i_offset); + FIFOLOAD(p, ICV1, fifo_load_mac_i_offset, 4, + LAST1 | FLUSH1 | IMMED); + } + + SET_LABEL(p, end_desc); + + if (!p->ps) { + PATCH_MOVE(p, seq_out_read, end_desc + 1); + PATCH_JUMP(p, jump_back_to_sd_cmd, + back_to_sd_offset + jump_back_to_sd_cmd - 5); + + if (rta_sec_era <= RTA_SEC_ERA_3) + PATCH_MOVE(p, move_mac_i_to_desc_buf, + fifo_load_mac_i_offset + 1); + } else { + PATCH_MOVE(p, seq_out_read, end_desc + 2); + PATCH_JUMP(p, jump_back_to_sd_cmd, + back_to_sd_offset + jump_back_to_sd_cmd - 5); + + if (rta_sec_era <= RTA_SEC_ERA_3) + PATCH_MOVE(p, move_mac_i_to_desc_buf, + fifo_load_mac_i_offset + 1); + } + } + + return 0; +} + +static inline int +pdcp_insert_cplane_aes_snow_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata, + struct alginfo *authdata, + unsigned int dir, + unsigned char era_2_sw_hfn_ovrd __maybe_unused) +{ + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, authdata->keylen, + INLINE_KEY(authdata)); + + if (rta_sec_era >= RTA_SEC_ERA_8) { + PROTOCOL(p, dir, OP_PCLID_LTE_PDCP_CTRL_MIXED, + ((uint16_t)cipherdata->algtype << 8) | + (uint16_t)authdata->algtype); + + return 0; + } + + if (dir == OP_TYPE_ENCAP_PROTOCOL) + MATHB(p, SEQINSZ, SUB, ONE, VSEQINSZ, 4, 0); + + SEQLOAD(p, MATH0, 7, 1, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + MOVE(p, MATH0, 7, IFIFOAB2, 0, 1, IMMED); + if (swap == false) + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + else + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK_BE, MATH1, 8, + IFB | IMMED2); + + SEQSTORE(p, MATH0, 7, 1, 0); + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + MOVE(p, DESCBUF, 4, MATH2, 0, 8, WAITCOMP | IMMED); + MATHB(p, MATH1, OR, MATH2, MATH1, 8, 0); + MOVE(p, MATH1, 0, CONTEXT1, 16, 8, IMMED); + MOVE(p, MATH1, 0, CONTEXT2, 0, 4, IMMED); + if (swap == false) { + MATHB(p, MATH1, AND, lower_32_bits(PDCP_BEARER_MASK), MATH2, 4, + IMMED2); + MATHB(p, MATH1, AND, upper_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); + } + MATHB(p, MATH3, SHLD, MATH3, MATH3, 8, 0); + MOVE(p, MATH2, 4, OFIFO, 0, 12, IMMED); + MOVE(p, OFIFO, 0, CONTEXT2, 4, 12, IMMED); + 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, MATH1, 4, IMMED2); + + MATHB(p, ZERO, ADD, MATH1, VSEQOUTSZ, 4, 0); + MATHB(p, ZERO, ADD, MATH1, VSEQINSZ, 4, 0); + } + + if (dir == OP_TYPE_ENCAP_PROTOCOL) + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + else + SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); + + ALG_OPERATION(p, OP_ALG_ALGSEL_SNOW_F9, + OP_ALG_AAI_F9, + OP_ALG_AS_INITFINAL, + dir == OP_TYPE_ENCAP_PROTOCOL ? + ICV_CHECK_DISABLE : ICV_CHECK_ENABLE, + DIR_DEC); + 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); + + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + SEQFIFOLOAD(p, MSGINSNOOP, 0, VLF | LAST2); + MOVE(p, CONTEXT2, 0, IFIFOAB1, 0, 4, LAST1 | FLUSH1 | IMMED); + } else { + SEQFIFOLOAD(p, MSGOUTSNOOP, 0, VLF | LAST2); + SEQFIFOLOAD(p, MSG1, 4, LAST1 | FLUSH1); + 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); + + MOVE(p, OFIFO, 0, MATH0, 0, 4, WAITCOMP | IMMED); + + NFIFOADD(p, IFIFO, ICV2, 4, LAST2); + + 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); + } + } + + return 0; +} + +static inline int +pdcp_insert_cplane_snow_zuc_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata, + struct alginfo *authdata, + unsigned int dir, + unsigned char era_2_sw_hfn_ovrd __maybe_unused) +{ + LABEL(keyjump); + REFERENCE(pkeyjump); + + if (rta_sec_era < RTA_SEC_ERA_5) { + pr_err("Invalid era for selected algorithm\n"); + return -ENOTSUP; + } + + pkeyjump = JUMP(p, keyjump, LOCAL_JUMP, ALL_TRUE, SHRD | SELF | BOTH); + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, authdata->keylen, + INLINE_KEY(authdata)); + + SET_LABEL(p, keyjump); + + if (rta_sec_era >= RTA_SEC_ERA_8) { + PROTOCOL(p, dir, OP_PCLID_LTE_PDCP_CTRL_MIXED, + ((uint16_t)cipherdata->algtype << 8) | + (uint16_t)authdata->algtype); + return 0; + } + + SEQLOAD(p, MATH0, 7, 1, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + MOVE(p, MATH0, 7, IFIFOAB2, 0, 1, IMMED); + if (swap == false) + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + else + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + MOVE(p, DESCBUF, 4, MATH2, 0, 8, WAITCOMP | IMMED); + MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); + MOVE(p, MATH2, 0, CONTEXT1, 0, 8, IMMED); + MOVE(p, MATH2, 0, CONTEXT2, 0, 8, WAITCOMP | IMMED); + + 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); + + MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); + SEQSTORE(p, MATH0, 7, 1, 0); + + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + SEQFIFOLOAD(p, MSGINSNOOP, 0, VLF | LAST2); + } else { + SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); + SEQFIFOLOAD(p, MSGOUTSNOOP, 0, VLF | LAST1 | FLUSH1); + } + + ALG_OPERATION(p, OP_ALG_ALGSEL_ZUCA, + OP_ALG_AAI_F9, + OP_ALG_AS_INITFINAL, + dir == OP_TYPE_ENCAP_PROTOCOL ? + ICV_CHECK_DISABLE : ICV_CHECK_ENABLE, + DIR_ENC); + + 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); + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + MOVE(p, CONTEXT2, 0, IFIFOAB1, 0, 4, LAST1 | FLUSH1 | IMMED); + } else { + /* Save ICV */ + MOVE(p, OFIFO, 0, MATH0, 0, 4, IMMED); + LOAD(p, NFIFOENTRY_STYPE_ALTSOURCE | + NFIFOENTRY_DEST_CLASS2 | + NFIFOENTRY_DTYPE_ICV | + NFIFOENTRY_LC2 | 4, NFIFO_SZL, 0, 4, IMMED); + MOVE(p, MATH0, 0, ALTSOURCE, 0, 4, WAITCOMP | IMMED); + } + + /* Reset ZUCA mode and done interrupt */ + LOAD(p, CLRW_CLR_C2MODE, CLRW, 0, 4, IMMED); + LOAD(p, CIRQ_ZADI, ICTRL, 0, 4, IMMED); + + PATCH_JUMP(p, pkeyjump, keyjump); + return 0; +} + +static inline int +pdcp_insert_cplane_aes_zuc_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata, + struct alginfo *authdata, + unsigned int dir, + unsigned char era_2_sw_hfn_ovrd __maybe_unused) +{ + LABEL(keyjump); + REFERENCE(pkeyjump); + + if (rta_sec_era < RTA_SEC_ERA_5) { + pr_err("Invalid era for selected algorithm\n"); + return -ENOTSUP; + } + + pkeyjump = JUMP(p, keyjump, LOCAL_JUMP, ALL_TRUE, SHRD | SELF | BOTH); + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, authdata->keylen, + INLINE_KEY(authdata)); + + if (rta_sec_era >= RTA_SEC_ERA_8) { + PROTOCOL(p, dir, OP_PCLID_LTE_PDCP_CTRL_MIXED, + ((uint16_t)cipherdata->algtype << 8) | + (uint16_t)authdata->algtype); + + return 0; + } + + SET_LABEL(p, keyjump); + SEQLOAD(p, MATH0, 7, 1, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + MOVE(p, MATH0, 7, IFIFOAB2, 0, 1, IMMED); + if (swap == false) + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + else + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + MOVE(p, DESCBUF, 4, MATH2, 0, 8, WAITCOMP | IMMED); + MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); + MOVE(p, MATH2, 0, CONTEXT1, 16, 8, IMMED); + MOVE(p, MATH2, 0, CONTEXT2, 0, 8, WAITCOMP | IMMED); + + 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); + + MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); + SEQSTORE(p, MATH0, 7, 1, 0); + + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + SEQFIFOLOAD(p, MSGINSNOOP, 0, VLF | LAST2); + } else { + SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); + SEQFIFOLOAD(p, MSGOUTSNOOP, 0, VLF | LAST1 | FLUSH1); + } + + ALG_OPERATION(p, OP_ALG_ALGSEL_ZUCA, + OP_ALG_AAI_F9, + OP_ALG_AS_INITFINAL, + dir == OP_TYPE_ENCAP_PROTOCOL ? + ICV_CHECK_DISABLE : ICV_CHECK_ENABLE, + DIR_ENC); + + 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); + + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + MOVE(p, CONTEXT2, 0, IFIFOAB1, 0, 4, LAST1 | FLUSH1 | IMMED); + } else { + /* Save ICV */ + MOVE(p, OFIFO, 0, MATH0, 0, 4, IMMED); + + LOAD(p, NFIFOENTRY_STYPE_ALTSOURCE | + NFIFOENTRY_DEST_CLASS2 | + NFIFOENTRY_DTYPE_ICV | + NFIFOENTRY_LC2 | 4, NFIFO_SZL, 0, 4, IMMED); + MOVE(p, MATH0, 0, ALTSOURCE, 0, 4, WAITCOMP | IMMED); + } + + /* Reset ZUCA mode and done interrupt */ + LOAD(p, CLRW_CLR_C2MODE, CLRW, 0, 4, IMMED); + LOAD(p, CIRQ_ZADI, ICTRL, 0, 4, IMMED); + + PATCH_JUMP(p, pkeyjump, keyjump); + + return 0; +} + +static inline int +pdcp_insert_cplane_zuc_snow_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata, + struct alginfo *authdata, + unsigned int dir, + unsigned char era_2_sw_hfn_ovrd __maybe_unused) +{ + LABEL(keyjump); + REFERENCE(pkeyjump); + + if (rta_sec_era < RTA_SEC_ERA_5) { + pr_err("Invalid era for selected algorithm\n"); + return -ENOTSUP; + } + + pkeyjump = JUMP(p, keyjump, LOCAL_JUMP, ALL_TRUE, SHRD | SELF | BOTH); + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, authdata->keylen, + INLINE_KEY(authdata)); + + if (rta_sec_era >= RTA_SEC_ERA_8) { + PROTOCOL(p, dir, OP_PCLID_LTE_PDCP_CTRL_MIXED, + ((uint16_t)cipherdata->algtype << 8) | + (uint16_t)authdata->algtype); + + return 0; + } + + SET_LABEL(p, keyjump); + SEQLOAD(p, MATH0, 7, 1, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + MOVE(p, MATH0, 7, IFIFOAB2, 0, 1, IMMED); + if (swap == false) + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + else + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + MOVE(p, DESCBUF, 4, MATH2, 0, 8, WAITCOMP | IMMED); + MATHB(p, MATH1, OR, MATH2, MATH1, 8, 0); + MOVE(p, MATH1, 0, CONTEXT1, 0, 8, IMMED); + MOVE(p, MATH1, 0, CONTEXT2, 0, 4, IMMED); + if (swap == false) { + MATHB(p, MATH1, AND, lower_32_bits(PDCP_BEARER_MASK), MATH2, + 4, IMMED2); + MATHB(p, MATH1, AND, upper_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); + } + MATHB(p, MATH3, SHLD, MATH3, MATH3, 8, 0); + MOVE(p, MATH2, 4, OFIFO, 0, 12, IMMED); + MOVE(p, OFIFO, 0, CONTEXT2, 4, 12, IMMED); + + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + MATHB(p, SEQINSZ, ADD, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); + MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); + } else { + MATHB(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); + MATHB(p, VSEQOUTSZ, SUB, ZERO, VSEQINSZ, 4, 0); + } + + SEQSTORE(p, MATH0, 7, 1, 0); + + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + SEQFIFOLOAD(p, MSGINSNOOP, 0, VLF | LAST2); + } else { + SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); + SEQFIFOLOAD(p, MSGOUTSNOOP, 0, VLF | LAST2); + } + + ALG_OPERATION(p, OP_ALG_ALGSEL_SNOW_F9, + OP_ALG_AAI_F9, + OP_ALG_AS_INITFINAL, + dir == OP_TYPE_ENCAP_PROTOCOL ? + ICV_CHECK_DISABLE : ICV_CHECK_ENABLE, + DIR_DEC); + + 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); + + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + MOVE(p, CONTEXT2, 0, IFIFOAB1, 0, 4, LAST1 | FLUSH1 | IMMED); + } else { + SEQFIFOLOAD(p, MSG1, 4, LAST1 | FLUSH1); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CLASS1 | NOP | NIFP); + + if (rta_sec_era >= RTA_SEC_ERA_6) + /* + * For SEC ERA 6, there's a problem with the OFIFO + * pointer, and thus it needs to be reset here before + * moving to M0. + */ + LOAD(p, 0, DCTRL, 0, LDLEN_RST_CHA_OFIFO_PTR, IMMED); + + /* Put ICV to M0 before sending it to C2 for comparison. */ + MOVE(p, OFIFO, 0, MATH0, 0, 4, WAITCOMP | IMMED); + + LOAD(p, NFIFOENTRY_STYPE_ALTSOURCE | + NFIFOENTRY_DEST_CLASS2 | + NFIFOENTRY_DTYPE_ICV | + NFIFOENTRY_LC2 | 4, NFIFO_SZL, 0, 4, IMMED); + MOVE(p, MATH0, 0, ALTSOURCE, 0, 4, IMMED); + } + + PATCH_JUMP(p, pkeyjump, keyjump); + return 0; +} + +static inline int +pdcp_insert_cplane_zuc_aes_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata, + struct alginfo *authdata, + unsigned int dir, + unsigned char era_2_sw_hfn_ovrd __maybe_unused) +{ + if (rta_sec_era < RTA_SEC_ERA_5) { + pr_err("Invalid era for selected algorithm\n"); + return -ENOTSUP; + } + + if (rta_sec_era >= RTA_SEC_ERA_8) { + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + + PROTOCOL(p, dir, OP_PCLID_LTE_PDCP_CTRL_MIXED, + ((uint16_t)cipherdata->algtype << 8) | + (uint16_t)authdata->algtype); + return 0; + } + + SEQLOAD(p, MATH0, 7, 1, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + if (swap == false) + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + else + MATHB(p, MATH0, AND, PDCP_C_PLANE_SN_MASK, MATH1, 8, + IFB | IMMED2); + + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + MOVE(p, DESCBUF, 4, MATH2, 0, 0x08, WAITCOMP | IMMED); + MATHB(p, MATH1, OR, MATH2, MATH2, 8, 0); + SEQSTORE(p, MATH0, 7, 1, 0); + if (dir == OP_TYPE_ENCAP_PROTOCOL) { + KEY(p, KEY1, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + MOVE(p, MATH2, 0, IFIFOAB1, 0, 0x08, IMMED); + MOVE(p, MATH0, 7, IFIFOAB1, 0, 1, IMMED); + + MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); + MATHB(p, VSEQINSZ, ADD, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); + + ALG_OPERATION(p, OP_ALG_ALGSEL_AES, + OP_ALG_AAI_CMAC, + OP_ALG_AS_INITFINAL, + ICV_CHECK_DISABLE, + DIR_DEC); + SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); + MOVE(p, CONTEXT1, 0, MATH3, 0, 4, WAITCOMP | IMMED); + 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); + + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + + MOVE(p, MATH2, 0, CONTEXT1, 0, 8, IMMED); + SEQINPTR(p, 0, PDCP_NULL_MAX_FRAME_LEN, RTO); + + ALG_OPERATION(p, OP_ALG_ALGSEL_ZUCE, + OP_ALG_AAI_F8, + OP_ALG_AS_INITFINAL, + ICV_CHECK_DISABLE, + DIR_ENC); + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + + SEQFIFOLOAD(p, SKIP, 1, 0); + + SEQFIFOLOAD(p, MSG1, 0, VLF); + MOVE(p, MATH3, 0, IFIFOAB1, 0, 4, LAST1 | FLUSH1 | IMMED); + } else { + MOVE(p, MATH2, 0, CONTEXT1, 0, 8, IMMED); + + MOVE(p, CONTEXT1, 0, CONTEXT2, 0, 8, IMMED); + + MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); + + MATHB(p, SEQINSZ, SUB, PDCP_MAC_I_LEN, VSEQOUTSZ, 4, IMMED2); + + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + + MOVE(p, CONTEXT1, 0, CONTEXT2, 0, 8, IMMED); + + ALG_OPERATION(p, OP_ALG_ALGSEL_ZUCE, + OP_ALG_AAI_F8, + OP_ALG_AS_INITFINAL, + ICV_CHECK_DISABLE, + DIR_DEC); + SEQFIFOSTORE(p, MSG, 0, 0, VLF | CONT); + SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); + + MOVE(p, OFIFO, 0, MATH3, 0, 4, IMMED); + + 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); + + KEY(p, KEY1, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + + SEQINPTR(p, 0, 0, SOP); + + ALG_OPERATION(p, OP_ALG_ALGSEL_AES, + OP_ALG_AAI_CMAC, + OP_ALG_AS_INITFINAL, + ICV_CHECK_ENABLE, + DIR_DEC); + + MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); + + MOVE(p, CONTEXT2, 0, IFIFOAB1, 0, 8, IMMED); + + SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); + + LOAD(p, NFIFOENTRY_STYPE_ALTSOURCE | + NFIFOENTRY_DEST_CLASS1 | + NFIFOENTRY_DTYPE_ICV | + NFIFOENTRY_LC1 | + NFIFOENTRY_FC1 | 4, NFIFO_SZL, 0, 4, IMMED); + MOVE(p, MATH3, 0, ALTSOURCE, 0, 4, IMMED); + } + + return 0; +} + +static inline int +pdcp_insert_uplane_15bit_op(struct program *p, + bool swap __maybe_unused, + struct alginfo *cipherdata, + unsigned int dir) +{ + int op; + /* Insert Cipher Key */ + KEY(p, KEY1, cipherdata->key_enc_flags, cipherdata->key, + cipherdata->keylen, INLINE_KEY(cipherdata)); + + if (rta_sec_era >= RTA_SEC_ERA_8) { + PROTOCOL(p, dir, OP_PCLID_LTE_PDCP_USER, + (uint16_t)cipherdata->algtype); + return 0; + } + + SEQLOAD(p, MATH0, 6, 2, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + if (swap == false) + MATHB(p, MATH0, AND, PDCP_U_PLANE_15BIT_SN_MASK, MATH1, 8, + IFB | IMMED2); + else + MATHB(p, MATH0, AND, PDCP_U_PLANE_15BIT_SN_MASK_BE, MATH1, 8, + IFB | IMMED2); + SEQSTORE(p, MATH0, 6, 2, 0); + MATHB(p, MATH1, SHLD, MATH1, MATH1, 8, 0); + MOVE(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: + MOVE(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: + MOVE(p, MATH2, 0, CONTEXT1, 0x10, 0x10, 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; + } + MOVE(p, MATH2, 0, CONTEXT1, 0, 0x08, IMMED); + MOVE(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_insert_uplane_15bit_op", cipherdata->algtype); + return -EINVAL; + } + + SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | FLUSH1); + + return 0; +} + +/* + * Function for inserting the snippet of code responsible for creating + * the HFN override code via either DPOVRD or via the input frame. + */ +static inline int +insert_hfn_ov_op(struct program *p, + uint32_t shift, + enum pdb_type_e pdb_type, + unsigned char era_2_sw_hfn_ovrd) +{ + uint32_t imm = PDCP_DPOVRD_HFN_OV_EN; + uint16_t hfn_pdb_offset; + + if (rta_sec_era == RTA_SEC_ERA_2 && !era_2_sw_hfn_ovrd) + return 0; + + switch (pdb_type) { + case PDCP_PDB_TYPE_NO_PDB: + /* + * If there is no PDB, then HFN override mechanism does not + * make any sense, thus in this case the function will + * return the pointer to the current position in the + * descriptor buffer + */ + return 0; + + case PDCP_PDB_TYPE_REDUCED_PDB: + hfn_pdb_offset = 4; + break; + + case PDCP_PDB_TYPE_FULL_PDB: + hfn_pdb_offset = 8; + break; + + default: + return -EINVAL; + } + + if (rta_sec_era > RTA_SEC_ERA_2) { + MATHB(p, DPOVRD, AND, imm, NONE, 8, IFB | IMMED2); + } else { + SEQLOAD(p, MATH0, 4, 4, 0); + JUMP(p, 1, LOCAL_JUMP, ALL_TRUE, CALM); + MATHB(p, MATH0, AND, imm, NONE, 8, IFB | IMMED2); + SEQSTORE(p, MATH0, 4, 4, 0); + } + + if (rta_sec_era >= RTA_SEC_ERA_8) + JUMP(p, 6, LOCAL_JUMP, ALL_TRUE, MATH_Z); + else + JUMP(p, 5, LOCAL_JUMP, ALL_TRUE, MATH_Z); + + if (rta_sec_era > RTA_SEC_ERA_2) + MATHB(p, DPOVRD, LSHIFT, shift, MATH0, 4, IMMED2); + else + MATHB(p, MATH0, LSHIFT, shift, MATH0, 4, IMMED2); + + MATHB(p, MATH0, SHLD, MATH0, MATH0, 8, 0); + MOVE(p, MATH0, 0, DESCBUF, hfn_pdb_offset, 4, IMMED); + + if (rta_sec_era >= RTA_SEC_ERA_8) + /* + * For ERA8, DPOVRD could be handled by the PROTOCOL command + * itself. For now, this is not done. Thus, clear DPOVRD here + * to alleviate any side-effects. + */ + MATHB(p, DPOVRD, AND, ZERO, DPOVRD, 4, STL); + + return 0; +} + +/* + * PDCP Control PDB creation function + */ +static inline enum pdb_type_e +cnstr_pdcp_c_plane_pdb(struct program *p, + uint32_t hfn, + unsigned char bearer, + unsigned char direction, + uint32_t hfn_threshold, + struct alginfo *cipherdata, + struct alginfo *authdata) +{ + struct pdcp_pdb pdb; + enum pdb_type_e + pdb_mask[PDCP_CIPHER_TYPE_INVALID][PDCP_AUTH_TYPE_INVALID] = { + { /* NULL */ + PDCP_PDB_TYPE_NO_PDB, /* NULL */ + PDCP_PDB_TYPE_FULL_PDB, /* SNOW f9 */ + PDCP_PDB_TYPE_FULL_PDB, /* AES CMAC */ + PDCP_PDB_TYPE_FULL_PDB /* ZUC-I */ + }, + { /* SNOW f8 */ + PDCP_PDB_TYPE_FULL_PDB, /* NULL */ + PDCP_PDB_TYPE_FULL_PDB, /* SNOW f9 */ + PDCP_PDB_TYPE_REDUCED_PDB, /* AES CMAC */ + PDCP_PDB_TYPE_REDUCED_PDB /* ZUC-I */ + }, + { /* AES CTR */ + PDCP_PDB_TYPE_FULL_PDB, /* NULL */ + PDCP_PDB_TYPE_REDUCED_PDB, /* SNOW f9 */ + PDCP_PDB_TYPE_FULL_PDB, /* AES CMAC */ + PDCP_PDB_TYPE_REDUCED_PDB /* ZUC-I */ + }, + { /* ZUC-E */ + PDCP_PDB_TYPE_FULL_PDB, /* NULL */ + PDCP_PDB_TYPE_REDUCED_PDB, /* SNOW f9 */ + PDCP_PDB_TYPE_REDUCED_PDB, /* AES CMAC */ + PDCP_PDB_TYPE_FULL_PDB /* ZUC-I */ + }, + }; + + if (rta_sec_era >= RTA_SEC_ERA_8) { + memset(&pdb, 0x00, sizeof(struct pdcp_pdb)); + + /* This is a HW issue. Bit 2 should be set to zero, + * but it does not work this way. Override here. + */ + pdb.opt_res.rsvd = 0x00000002; + + /* Copy relevant information from user to PDB */ + pdb.hfn_res = hfn << PDCP_C_PLANE_PDB_HFN_SHIFT; + pdb.bearer_dir_res = (uint32_t) + ((bearer << PDCP_C_PLANE_PDB_BEARER_SHIFT) | + (direction << PDCP_C_PLANE_PDB_DIR_SHIFT)); + pdb.hfn_thr_res = + hfn_threshold << PDCP_C_PLANE_PDB_HFN_THR_SHIFT; + + /* copy PDB in descriptor*/ + __rta_out32(p, pdb.opt_res.opt); + __rta_out32(p, pdb.hfn_res); + __rta_out32(p, pdb.bearer_dir_res); + __rta_out32(p, pdb.hfn_thr_res); + + return PDCP_PDB_TYPE_FULL_PDB; + } + + switch (pdb_mask[cipherdata->algtype][authdata->algtype]) { + case PDCP_PDB_TYPE_NO_PDB: + break; + + case PDCP_PDB_TYPE_REDUCED_PDB: + __rta_out32(p, (hfn << PDCP_C_PLANE_PDB_HFN_SHIFT)); + __rta_out32(p, + (uint32_t)((bearer << + PDCP_C_PLANE_PDB_BEARER_SHIFT) | + (direction << + PDCP_C_PLANE_PDB_DIR_SHIFT))); + break; + + case PDCP_PDB_TYPE_FULL_PDB: + memset(&pdb, 0x00, sizeof(struct pdcp_pdb)); + + /* This is a HW issue. Bit 2 should be set to zero, + * but it does not work this way. Override here. + */ + pdb.opt_res.rsvd = 0x00000002; + + /* Copy relevant information from user to PDB */ + pdb.hfn_res = hfn << PDCP_C_PLANE_PDB_HFN_SHIFT; + pdb.bearer_dir_res = (uint32_t) + ((bearer << PDCP_C_PLANE_PDB_BEARER_SHIFT) | + (direction << PDCP_C_PLANE_PDB_DIR_SHIFT)); + pdb.hfn_thr_res = + hfn_threshold << PDCP_C_PLANE_PDB_HFN_THR_SHIFT; + + /* copy PDB in descriptor*/ + __rta_out32(p, pdb.opt_res.opt); + __rta_out32(p, pdb.hfn_res); + __rta_out32(p, pdb.bearer_dir_res); + __rta_out32(p, pdb.hfn_thr_res); + + break; + + default: + return PDCP_PDB_TYPE_INVALID; + } + + return pdb_mask[cipherdata->algtype][authdata->algtype]; +} + +/* + * PDCP UPlane PDB creation function + */ +static inline int +cnstr_pdcp_u_plane_pdb(struct program *p, + enum pdcp_sn_size sn_size, + uint32_t hfn, unsigned short bearer, + unsigned short direction, + uint32_t hfn_threshold) +{ + struct pdcp_pdb pdb; + /* Read options from user */ + /* Depending on sequence number length, the HFN and HFN threshold + * have different lengths. + */ + memset(&pdb, 0x00, sizeof(struct pdcp_pdb)); + + switch (sn_size) { + case PDCP_SN_SIZE_7: + pdb.opt_res.opt |= PDCP_U_PLANE_PDB_OPT_SHORT_SN; + pdb.hfn_res = hfn << PDCP_U_PLANE_PDB_SHORT_SN_HFN_SHIFT; + pdb.hfn_thr_res = + hfn_threshold<algtype][authdata->algtype], 0, 0); + + pdb_type = cnstr_pdcp_c_plane_pdb(p, + hfn, + bearer, + direction, + hfn_threshold, + cipherdata, + authdata); + + SET_LABEL(p, pdb_end); + + err = insert_hfn_ov_op(p, PDCP_SN_SIZE_5, pdb_type, + era_2_sw_hfn_ovrd); + if (err) + return err; + + err = pdcp_cp_fp[cipherdata->algtype][authdata->algtype](p, + swap, + cipherdata, + authdata, + OP_TYPE_ENCAP_PROTOCOL, + era_2_sw_hfn_ovrd); + if (err) + return err; + + PATCH_HDR(p, 0, pdb_end); + + return PROGRAM_FINALIZE(p); +} + +/** + * cnstr_shdsc_pdcp_c_plane_decap - Function for creating a PDCP Control 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 + * @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. + * @authdata: pointer to authentication transform definitions + * Valid algorithm values are those from auth_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_c_plane_decap(uint32_t *descbuf, + bool ps, + bool swap, + uint32_t hfn, + unsigned char bearer, + unsigned char direction, + uint32_t hfn_threshold, + struct alginfo *cipherdata, + struct alginfo *authdata, + unsigned char era_2_sw_hfn_ovrd) +{ + static int + (*pdcp_cp_fp[PDCP_CIPHER_TYPE_INVALID][PDCP_AUTH_TYPE_INVALID]) + (struct program*, bool swap, struct alginfo *, + struct alginfo *, unsigned int, unsigned char) = { + { /* 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_insert_cplane_enc_only_op, /* NULL */ + pdcp_insert_cplane_acc_op, /* SNOW f9 */ + pdcp_insert_cplane_snow_aes_op, /* AES CMAC */ + pdcp_insert_cplane_snow_zuc_op /* ZUC-I */ + }, + { /* AES CTR */ + pdcp_insert_cplane_enc_only_op, /* NULL */ + pdcp_insert_cplane_aes_snow_op, /* SNOW f9 */ + pdcp_insert_cplane_acc_op, /* AES CMAC */ + pdcp_insert_cplane_aes_zuc_op /* ZUC-I */ + }, + { /* ZUC-E */ + pdcp_insert_cplane_enc_only_op, /* NULL */ + pdcp_insert_cplane_zuc_snow_op, /* SNOW f9 */ + pdcp_insert_cplane_zuc_aes_op, /* AES CMAC */ + pdcp_insert_cplane_acc_op /* ZUC-I */ + }, + }; + 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_ALWAYS /* ZUC-I */ + }, + }; + enum pdb_type_e pdb_type; + struct program prg; + struct program *p = &prg; + int err; + LABEL(pdb_end); + + if (rta_sec_era != RTA_SEC_ERA_2 && era_2_sw_hfn_ovrd) { + pr_err("Cannot select SW HFN override for other era than 2"); + return -EINVAL; + } + + PROGRAM_CNTXT_INIT(p, descbuf, 0); + if (swap) + PROGRAM_SET_BSWAP(p); + if (ps) + PROGRAM_SET_36BIT_ADDR(p); + + SHR_HDR(p, desc_share[cipherdata->algtype][authdata->algtype], 0, 0); + + pdb_type = cnstr_pdcp_c_plane_pdb(p, + hfn, + bearer, + direction, + hfn_threshold, + cipherdata, + authdata); + + SET_LABEL(p, pdb_end); + + err = insert_hfn_ov_op(p, PDCP_SN_SIZE_5, pdb_type, + era_2_sw_hfn_ovrd); + if (err) + return err; + + err = pdcp_cp_fp[cipherdata->algtype][authdata->algtype](p, + swap, + cipherdata, + authdata, + OP_TYPE_DECAP_PROTOCOL, + era_2_sw_hfn_ovrd); + if (err) + return err; + + PATCH_HDR(p, 0, pdb_end); + + return PROGRAM_FINALIZE(p); +} + +/** + * cnstr_shdsc_pdcp_u_plane_encap - Function for creating a PDCP 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_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, + unsigned char era_2_sw_hfn_ovrd) +{ + struct program prg; + struct program *p = &prg; + int err; + LABEL(pdb_end); + + 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; + } + + PROGRAM_CNTXT_INIT(p, descbuf, 0); + if (swap) + PROGRAM_SET_BSWAP(p); + if (ps) + PROGRAM_SET_36BIT_ADDR(p); + + SHR_HDR(p, SHR_ALWAYS, 0, 0); + if (cnstr_pdcp_u_plane_pdb(p, sn_size, hfn, bearer, direction, + hfn_threshold)) { + pr_err("Error creating PDCP UPlane PDB\n"); + return -EINVAL; + } + SET_LABEL(p, pdb_end); + + err = insert_hfn_ov_op(p, sn_size, PDCP_PDB_TYPE_FULL_PDB, + era_2_sw_hfn_ovrd); + if (err) + return err; + + switch (sn_size) { + case PDCP_SN_SIZE_7: + case PDCP_SN_SIZE_12: + switch (cipherdata->algtype) { + case PDCP_CIPHER_TYPE_ZUC: + if (rta_sec_era < RTA_SEC_ERA_5) { + pr_err("Invalid era for selected algorithm\n"); + return -ENOTSUP; + } + case PDCP_CIPHER_TYPE_AES: + case PDCP_CIPHER_TYPE_SNOW: + /* Insert Cipher Key */ + KEY(p, KEY1, cipherdata->key_enc_flags, + (uint64_t)cipherdata->key, cipherdata->keylen, + INLINE_KEY(cipherdata)); + PROTOCOL(p, OP_TYPE_ENCAP_PROTOCOL, + OP_PCLID_LTE_PDCP_USER, + (uint16_t)cipherdata->algtype); + break; + case PDCP_CIPHER_TYPE_NULL: + insert_copy_frame_op(p, + cipherdata, + OP_TYPE_ENCAP_PROTOCOL); + break; + default: + pr_err("%s: Invalid encrypt algorithm selected: %d\n", + "cnstr_pcl_shdsc_pdcp_u_plane_decap", + cipherdata->algtype); + return -EINVAL; + } + break; + + case PDCP_SN_SIZE_15: + switch (cipherdata->algtype) { + case PDCP_CIPHER_TYPE_NULL: + insert_copy_frame_op(p, + cipherdata, + OP_TYPE_ENCAP_PROTOCOL); + break; + + default: + err = pdcp_insert_uplane_15bit_op(p, swap, cipherdata, + OP_TYPE_ENCAP_PROTOCOL); + if (err) + return err; + break; + } + break; + + case PDCP_SN_SIZE_5: + default: + pr_err("Invalid SN size selected\n"); + return -ENOTSUP; + } + + PATCH_HDR(p, 0, pdb_end); + return PROGRAM_FINALIZE(p); +} + +/** + * cnstr_shdsc_pdcp_u_plane_decap - Function for creating a PDCP 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_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, + unsigned char era_2_sw_hfn_ovrd) +{ + struct program prg; + struct program *p = &prg; + int err; + LABEL(pdb_end); + + if (rta_sec_era != RTA_SEC_ERA_2 && era_2_sw_hfn_ovrd) { + pr_err("Cannot select SW HFN override for other era than 2"); + return -EINVAL; + } + + PROGRAM_CNTXT_INIT(p, descbuf, 0); + if (swap) + PROGRAM_SET_BSWAP(p); + if (ps) + PROGRAM_SET_36BIT_ADDR(p); + + SHR_HDR(p, SHR_ALWAYS, 0, 0); + if (cnstr_pdcp_u_plane_pdb(p, sn_size, hfn, bearer, direction, + hfn_threshold)) { + pr_err("Error creating PDCP UPlane PDB\n"); + return -EINVAL; + } + SET_LABEL(p, pdb_end); + + err = insert_hfn_ov_op(p, sn_size, PDCP_PDB_TYPE_FULL_PDB, + era_2_sw_hfn_ovrd); + if (err) + return err; + + switch (sn_size) { + case PDCP_SN_SIZE_7: + case PDCP_SN_SIZE_12: + switch (cipherdata->algtype) { + case PDCP_CIPHER_TYPE_ZUC: + if (rta_sec_era < RTA_SEC_ERA_5) { + pr_err("Invalid era for selected algorithm\n"); + return -ENOTSUP; + } + case PDCP_CIPHER_TYPE_AES: + case PDCP_CIPHER_TYPE_SNOW: + /* Insert Cipher Key */ + KEY(p, KEY1, cipherdata->key_enc_flags, + cipherdata->key, cipherdata->keylen, + INLINE_KEY(cipherdata)); + PROTOCOL(p, OP_TYPE_DECAP_PROTOCOL, + OP_PCLID_LTE_PDCP_USER, + (uint16_t)cipherdata->algtype); + break; + case PDCP_CIPHER_TYPE_NULL: + insert_copy_frame_op(p, + cipherdata, + OP_TYPE_DECAP_PROTOCOL); + break; + default: + pr_err("%s: Invalid encrypt algorithm selected: %d\n", + "cnstr_pcl_shdsc_pdcp_u_plane_decap", + cipherdata->algtype); + return -EINVAL; + } + break; + + case PDCP_SN_SIZE_15: + switch (cipherdata->algtype) { + case PDCP_CIPHER_TYPE_NULL: + insert_copy_frame_op(p, + cipherdata, + OP_TYPE_DECAP_PROTOCOL); + break; + + default: + err = pdcp_insert_uplane_15bit_op(p, swap, cipherdata, + OP_TYPE_DECAP_PROTOCOL); + if (err) + return err; + break; + } + break; + + case PDCP_SN_SIZE_5: + default: + pr_err("Invalid SN size selected\n"); + return -ENOTSUP; + } + + PATCH_HDR(p, 0, pdb_end); + return PROGRAM_FINALIZE(p); +} + +/** + * cnstr_shdsc_pdcp_short_mac - Function for creating a PDCP Short MAC + * 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 + * @authdata: pointer to authentication transform definitions + * Valid algorithm values are those from auth_type_pdcp enum. + * + * 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_short_mac(uint32_t *descbuf, + bool ps, + bool swap, + struct alginfo *authdata) +{ + struct program prg; + struct program *p = &prg; + uint32_t iv[3] = {0, 0, 0}; + LABEL(local_offset); + REFERENCE(move_cmd_read_descbuf); + REFERENCE(move_cmd_write_descbuf); + + PROGRAM_CNTXT_INIT(p, descbuf, 0); + if (swap) + PROGRAM_SET_BSWAP(p); + if (ps) + PROGRAM_SET_36BIT_ADDR(p); + + SHR_HDR(p, SHR_ALWAYS, 1, 0); + + if (rta_sec_era > RTA_SEC_ERA_2) { + MATHB(p, SEQINSZ, SUB, ZERO, VSEQINSZ, 4, 0); + MATHB(p, SEQINSZ, SUB, ZERO, MATH1, 4, 0); + } else { + MATHB(p, SEQINSZ, ADD, ONE, MATH1, 4, 0); + MATHB(p, MATH1, SUB, ONE, MATH1, 4, 0); + MATHB(p, ZERO, ADD, MATH1, VSEQINSZ, 4, 0); + MOVE(p, MATH1, 0, MATH0, 0, 8, IMMED); + + /* + * Since MOVELEN is available only starting with + * SEC ERA 3, use poor man's MOVELEN: create a MOVE + * command dynamically by writing the length from M1 by + * OR-ing the command in the M1 register and MOVE the + * result into the descriptor buffer. Care must be taken + * wrt. the location of the command because of SEC + * pipelining. The actual MOVEs are written at the end + * of the descriptor due to calculations needed on the + * offset in the descriptor for the MOVE command. + */ + move_cmd_read_descbuf = MOVE(p, DESCBUF, 0, MATH0, 0, 6, + IMMED); + move_cmd_write_descbuf = MOVE(p, MATH0, 0, DESCBUF, 0, 8, + WAITCOMP | IMMED); + } + MATHB(p, ZERO, ADD, MATH1, VSEQOUTSZ, 4, 0); + + switch (authdata->algtype) { + case PDCP_AUTH_TYPE_NULL: + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + if (rta_sec_era > RTA_SEC_ERA_2) { + MOVE(p, AB1, 0, OFIFO, 0, MATH1, 0); + } else { + SET_LABEL(p, local_offset); + + /* Shut off automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_DISABLE_AUTO_NFIFO, 0, IMMED); + + /* Placeholder for MOVE command with length from M1 + * register + */ + MOVE(p, IFIFOAB1, 0, OFIFO, 0, 0, IMMED); + + /* Enable automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_ENABLE_AUTO_NFIFO, 0, IMMED); + } + + LOAD(p, (uintptr_t)iv, MATH0, 0, 8, IMMED | COPY); + SEQFIFOLOAD(p, MSG1, 0, VLF | LAST1 | LAST2 | FLUSH1); + SEQSTORE(p, MATH0, 0, 4, 0); + + break; + + case PDCP_AUTH_TYPE_SNOW: + iv[0] = 0xFFFFFFFF; + iv[1] = swap ? swab32(0x04000000) : 0x04000000; + iv[2] = swap ? swab32(0xF8000000) : 0xF8000000; + + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + LOAD(p, (uintptr_t)&iv, CONTEXT2, 0, 12, IMMED | COPY); + ALG_OPERATION(p, OP_ALG_ALGSEL_SNOW_F9, + OP_ALG_AAI_F9, + OP_ALG_AS_INITFINAL, + ICV_CHECK_DISABLE, + DIR_ENC); + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + + if (rta_sec_era > RTA_SEC_ERA_2) { + MOVE(p, AB1, 0, OFIFO, 0, MATH1, 0); + } else { + SET_LABEL(p, local_offset); + + + /* Shut off automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_DISABLE_AUTO_NFIFO, 0, IMMED); + + /* Placeholder for MOVE command with length from M1 + * register + */ + MOVE(p, IFIFOAB1, 0, OFIFO, 0, 0, IMMED); + + /* Enable automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_ENABLE_AUTO_NFIFO, 0, IMMED); + } + SEQFIFOLOAD(p, MSGINSNOOP, 0, VLF | LAST1 | LAST2 | FLUSH1); + SEQSTORE(p, CONTEXT2, 0, 4, 0); + + break; + + case PDCP_AUTH_TYPE_AES: + iv[0] = 0xFFFFFFFF; + iv[1] = swap ? swab32(0xFC000000) : 0xFC000000; + iv[2] = 0x00000000; /* unused */ + + KEY(p, KEY1, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + LOAD(p, (uintptr_t)&iv, MATH0, 0, 8, IMMED | COPY); + MOVE(p, MATH0, 0, IFIFOAB1, 0, 8, IMMED); + ALG_OPERATION(p, OP_ALG_ALGSEL_AES, + OP_ALG_AAI_CMAC, + OP_ALG_AS_INITFINAL, + ICV_CHECK_DISABLE, + DIR_ENC); + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + + if (rta_sec_era > RTA_SEC_ERA_2) { + MOVE(p, AB2, 0, OFIFO, 0, MATH1, 0); + } else { + SET_LABEL(p, local_offset); + + /* Shut off automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_DISABLE_AUTO_NFIFO, 0, IMMED); + + /* Placeholder for MOVE command with length from M1 + * register + */ + MOVE(p, IFIFOAB2, 0, OFIFO, 0, 0, IMMED); + + /* Enable automatic Info FIFO entries */ + LOAD(p, 0, DCTRL, LDOFF_ENABLE_AUTO_NFIFO, 0, IMMED); + } + SEQFIFOLOAD(p, MSGINSNOOP, 0, VLF | LAST1 | LAST2 | FLUSH1); + SEQSTORE(p, CONTEXT1, 0, 4, 0); + + break; + + case PDCP_AUTH_TYPE_ZUC: + if (rta_sec_era < RTA_SEC_ERA_5) { + pr_err("Invalid era for selected algorithm\n"); + return -ENOTSUP; + } + iv[0] = 0xFFFFFFFF; + iv[1] = swap ? swab32(0xFC000000) : 0xFC000000; + iv[2] = 0x00000000; /* unused */ + + KEY(p, KEY2, authdata->key_enc_flags, authdata->key, + authdata->keylen, INLINE_KEY(authdata)); + LOAD(p, (uintptr_t)&iv, CONTEXT2, 0, 12, IMMED | COPY); + ALG_OPERATION(p, OP_ALG_ALGSEL_ZUCA, + OP_ALG_AAI_F9, + OP_ALG_AS_INITFINAL, + ICV_CHECK_DISABLE, + DIR_ENC); + SEQFIFOSTORE(p, MSG, 0, 0, VLF); + MOVE(p, AB1, 0, OFIFO, 0, MATH1, 0); + SEQFIFOLOAD(p, MSGINSNOOP, 0, VLF | LAST1 | LAST2 | FLUSH1); + SEQSTORE(p, CONTEXT2, 0, 4, 0); + + break; + + default: + pr_err("%s: Invalid integrity algorithm selected: %d\n", + "cnstr_shdsc_pdcp_short_mac", authdata->algtype); + return -EINVAL; + } + + + if (rta_sec_era < RTA_SEC_ERA_3) { + PATCH_MOVE(p, move_cmd_read_descbuf, local_offset); + PATCH_MOVE(p, move_cmd_write_descbuf, local_offset); + } + + return PROGRAM_FINALIZE(p); +} + +#endif /* __DESC_PDCP_H__ */