mirror of https://github.com/F-Stack/f-stack.git
2797 lines
82 KiB
C
2797 lines
82 KiB
C
|
/*
|
||
|
* 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<<PDCP_U_PLANE_PDB_SHORT_SN_HFN_THR_SHIFT;
|
||
|
break;
|
||
|
|
||
|
case PDCP_SN_SIZE_12:
|
||
|
pdb.opt_res.opt &= (uint32_t)(~PDCP_U_PLANE_PDB_OPT_SHORT_SN);
|
||
|
pdb.hfn_res = hfn << PDCP_U_PLANE_PDB_LONG_SN_HFN_SHIFT;
|
||
|
pdb.hfn_thr_res =
|
||
|
hfn_threshold<<PDCP_U_PLANE_PDB_LONG_SN_HFN_THR_SHIFT;
|
||
|
break;
|
||
|
|
||
|
case PDCP_SN_SIZE_15:
|
||
|
pdb.opt_res.opt = (uint32_t)(PDCP_U_PLANE_PDB_OPT_15B_SN);
|
||
|
pdb.hfn_res = hfn << PDCP_U_PLANE_PDB_15BIT_SN_HFN_SHIFT;
|
||
|
pdb.hfn_thr_res =
|
||
|
hfn_threshold<<PDCP_U_PLANE_PDB_15BIT_SN_HFN_THR_SHIFT;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
pr_err("Invalid Sequence Number Size setting in PDB\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
pdb.bearer_dir_res = (uint32_t)
|
||
|
((bearer << PDCP_U_PLANE_PDB_BEARER_SHIFT) |
|
||
|
(direction << PDCP_U_PLANE_PDB_DIR_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 0;
|
||
|
}
|
||
|
/**
|
||
|
* cnstr_shdsc_pdcp_c_plane_encap - Function for creating a PDCP Control 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
|
||
|
* @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_encap(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 __maybe_unused) = {
|
||
|
{ /* NULL */
|
||
|
pdcp_insert_cplane_null_op, /* NULL */
|
||
|
pdcp_insert_cplane_int_only_op, /* SNOW f9 */
|
||
|
pdcp_insert_cplane_int_only_op, /* AES CMAC */
|
||
|
pdcp_insert_cplane_int_only_op /* ZUC-I */
|
||
|
},
|
||
|
{ /* SNOW f8 */
|
||
|
pdcp_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_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__ */
|