/* * 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__ */