| /* |
| * Copyright (c) 2009 Mindspeed Technologies, Inc. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation; either version 2 |
| * of the License, or (at your option) any later version. |
| |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| * |
| * |
| */ |
| |
| #include "module_ipsec.h" |
| #include "system.h" |
| #include <mach/reset.h> |
| |
| /* |
| * Allocation control mask for SAs. One word describes 32 SAs |
| */ |
| #if !defined(IPSEC_ARAM_MAX_BYTESIZE) |
| # define IPSEC_ARAM_MAX_BYTESIZE (1024 * 28) |
| #endif |
| |
| #define NUM_ARAM_SADSC (((((IPSEC_ARAM_MAX_BYTESIZE - ELP_SA_ARAM_OFFSET) + ELP_SA_ALIGN-1)>>ELP_SA_ALIGN_LOG)+31)>>5) |
| static unsigned int aram_sa_amask[NUM_ARAM_SADSC]; |
| |
| IPSec_hw_context UTIL_DMEM_SH2(g_ipsec_hw_ctx) __attribute__((aligned(8))); |
| |
| int hw_sa_set_digest_key(PSAEntry sa, U16 key_bits, U8* key) |
| { |
| struct _tElliptic_SA *elp_sa = sa->elp_sa; |
| U32 key_len = (key_bits + 7) >>3; |
| |
| *(U32*)&elp_sa->auth_key0 = ntohl(*(U32*)&key[0]); |
| *(U32*)&elp_sa->auth_key1 = ntohl(*(U32*)&key[4]); |
| *(U32*)&elp_sa->auth_key2 = ntohl(*(U32*)&key[8]); |
| *(U32*)&elp_sa->auth_key3 = ntohl(*(U32*)&key[12]); |
| *(U32*)&elp_sa->auth_key4 = ntohl(*(U32*)&key[16]); |
| |
| if (key_len > 20) { |
| *(U32*)&elp_sa->ext_auth_key0 = ntohl(*(U32*)&key[20]); |
| *(U32*)&elp_sa->ext_auth_key1 = ntohl(*(U32*)&key[24]); |
| *(U32*)&elp_sa->ext_auth_key2 = ntohl(*(U32*)&key[28]); |
| } |
| |
| return 0; |
| } |
| |
| int hw_sa_set_cipher_ALG_AESCTR(PSAEntry sa, U16 key_bits, U8* key , U8* algo) |
| { |
| struct _tElliptic_SA *elp_sa = sa->elp_sa; |
| if (key_bits == 128+32) { |
| *algo = ELP_CIPHER_AES128_CTR; |
| *(U32*)&elp_sa->CTR_Nonce = ntohl(*(U32*)&key[16]); |
| } |
| else if (key_bits == 192+32) { |
| *algo = ELP_CIPHER_AES192_CTR; |
| *(U32*)&elp_sa->CTR_Nonce = ntohl(*(U32*)&key[24]); |
| } |
| else if (key_bits == 256+32) { |
| /* mat-20090325 - key structure needs to be expanded to carry 288 bits (see IPSEC_MAX_KEY_SIZE) */ |
| *algo = ELP_CIPHER_AES256_CTR; |
| *(U32*)&elp_sa->CTR_Nonce = ntohl(*(U32*)&key[32]); |
| } |
| else |
| return -1; |
| |
| sa->blocksz = 4; |
| return 0; |
| } |
| int hw_sa_set_cipher_key(PSAEntry sa, U8* key) |
| { |
| |
| struct _tElliptic_SA *elp_sa = sa->elp_sa; |
| |
| *(U32*)&elp_sa->cipher0 = ntohl(*(U32*)&key[0]); |
| *(U32*)&elp_sa->cipher1 = ntohl(*(U32*)&key[4]); |
| *(U32*)&elp_sa->cipher2 = ntohl(*(U32*)&key[8]); |
| *(U32*)&elp_sa->cipher3 = ntohl(*(U32*)&key[12]); |
| *(U32*)&elp_sa->cipher4 = ntohl(*(U32*)&key[16]); |
| *(U32*)&elp_sa->cipher5 = ntohl(*(U32*)&key[20]); |
| *(U32*)&elp_sa->cipher6 = ntohl(*(U32*)&key[24]); |
| *(U32*)&elp_sa->cipher7 = ntohl(*(U32*)&key[28]); |
| |
| return 0; |
| |
| } |
| |
| int hw_sa_set_lifetime(CommandIPSecSetLifetime *cmd, PSAEntry sa) |
| { |
| if (sa->lft_conf.soft_byte_limit) { |
| sa->elp_sa->soft_ttl_hi = (cmd->soft_time.bytes[1]); |
| sa->elp_sa->soft_ttl_lo = (cmd->soft_time.bytes[0]); |
| } |
| if (sa->lft_conf.hard_byte_limit) { |
| sa->elp_sa->hard_ttl_hi = (cmd->hard_time.bytes[1]); |
| sa->elp_sa->hard_ttl_lo = (cmd->hard_time.bytes[0]); |
| } |
| if ((sa->lft_conf.soft_byte_limit) || (sa->lft_conf.hard_byte_limit)) |
| { |
| #ifdef CONTROL_IPSEC_DEBUG |
| printk (KERN_INFO "hw_set_lifetime:bytes:%llu - %llu\n",sa->lft_conf.soft_byte_limit, sa->lft_conf.hard_byte_limit); |
| #endif |
| sa->elp_sa->flags |= ESPAH_TTL_ENABLE; |
| } |
| |
| consistent_elpctl(sa->elp_sa,0); |
| return 0; |
| } |
| |
| static int isAramElpSA(void *p) { |
| // Checks if address of elliptic sa is within aram range |
| // Returns 1 if it is, 0 if it is not |
| IPSec_hw_context *pipsc = &gIpSecHWCtx; |
| if (pipsc->ARAM_sa_base) { |
| if ((((U32) p ) >= pipsc->ARAM_sa_base ) && |
| (((U32) p ) <= pipsc->ARAM_sa_base_end)); |
| return 1; |
| } |
| return 0; |
| } |
| |
| |
| static PElliptic_SA allocAramElpSA(void) { |
| // Attempt to allocate elliptic SA from aram |
| // returns NULL if no SA's are available |
| // and DRAM allocation is needed |
| U32 idx,widx; |
| IPSec_hw_context *pipsc = &gIpSecHWCtx; |
| |
| for(widx=0;widx<NUM_ARAM_SADSC;widx++) { |
| if (aram_sa_amask[widx]) { |
| __asm__ ( |
| //"clz idx, aram_sa_amask[widx]" |
| "clz %0, %1" : "=r"(idx) : "r"(aram_sa_amask[widx]) |
| ); |
| aram_sa_amask[widx] &= 0xffffffffUL ^ (0x80000000UL >> idx); |
| return (PElliptic_SA) (pipsc->ARAM_sa_base + ((idx + widx*32)* ELP_SA_ALIGN)); |
| } |
| } |
| |
| return NULL; |
| } |
| |
| |
| int freeAramElpSA(PElliptic_SA psa) |
| { |
| // Attempt to free elliptic SA to aram |
| // returns 1 if SA was freed and 0 if it is non-aram SA |
| // and freeing needs to be retried. |
| U32 idx,widx; |
| IPSec_hw_context *pipsc = &gIpSecHWCtx; |
| |
| if (isAramElpSA(psa)) { |
| idx = (((U32) psa) - pipsc->ARAM_sa_base) >>ELP_SA_ALIGN_LOG ; |
| // Split into bit index and wordindex |
| widx = idx >> 5; |
| idx &= 0x1f; |
| aram_sa_amask[widx] |= (0x80000000UL >> idx); |
| return 1; |
| } |
| return 0; |
| } |
| |
| |
| PElliptic_SA allocElpSA(dma_addr_t* dma_addr) { |
| // Allocate Hardware SA descriptor. |
| // Try private aram, then DRAM. |
| PElliptic_SA psa; |
| void *p; |
| struct pfe_ctrl *ctrl = &pfe->ctrl; |
| |
| if ((psa = allocAramElpSA()) != NULL ) |
| { |
| *dma_addr = virt_to_phys_iram((void*)psa); |
| return psa; |
| } |
| // No aram SAs - try dram |
| // We do not consider aram heap to avid wasting space |
| // Note that buddy allocator will return 256-byte aligned pointer |
| /* modify this to get it from DDR with alignment*/ |
| p = dma_pool_alloc (ctrl->dma_pool, GFP_ATOMIC , dma_addr); |
| return (PElliptic_SA) p; |
| } |
| |
| void disableElpSA(PElliptic_SA psa) |
| { |
| #ifdef CONTROL_IPSEC_DEBUG |
| printk(KERN_INFO "Disabling ELP SA \n"); |
| #endif |
| if (psa == NULL) |
| return; |
| psa->flags = 0; // Clear ESPAH_ENABLED |
| consistent_elpctl(psa,1); |
| } |
| |
| void freeElpSA(PElliptic_SA psa, dma_addr_t dma_addr) { |
| //U32 tmp; |
| //void *p; |
| struct pfe_ctrl *ctrl = &pfe->ctrl; |
| |
| #ifdef CONTROL_IPSEC_DEBUG |
| printk(KERN_INFO "Freeing ELP SA \n"); |
| #endif |
| if ( freeAramElpSA(psa)) |
| return; |
| |
| dma_pool_free(ctrl->dma_pool, psa, dma_addr); |
| #if 0 |
| tmp = (U32) psa; |
| |
| p = *(void **) ( tmp+ELP_SA_ALIGN); |
| Heap_Free(p); |
| #endif |
| } |
| |
| |
| static U32 ipsec_init_pe(U32 memaddr, struct elp_packet_engine *ppe) |
| { |
| |
| ppe->wq_avail = ELP_WQ_RING_SIZE; |
| ppe->pe_wq_ring = (struct elp_wqentry*) memaddr; |
| ppe->pe_wq_ringlast = ppe->pe_wq_ring + ELP_WQ_RING_SIZE-1; |
| ppe->pe_wq_free = ppe->pe_wq_ring; |
| |
| memset((void*) ppe->pe_wq_ring, 0, ELP_WQ_RING_SIZE * sizeof(struct elp_wqentry)); |
| memaddr += sizeof(struct elp_wqentry) * ELP_WQ_RING_SIZE; |
| |
| return memaddr; |
| } |
| |
| |
| static void elp_start_device(IPSec_hw_context *sc) |
| { |
| |
| #if 0 |
| // enable ipsec clocks in case ACP power management disabled them. |
| *(volatile U32 *)CLKCORE_CLK_PWR_DWN &= ~(IPSEC_AHBCLK_PD | IPSEC2_AHBCLK_PD | IPSEC_CORECLK_PD); |
| #endif |
| |
| |
| #if 0 |
| /* TODO Need to check if these are exactly needed */ |
| /* This is already done in barebox */ |
| writel(CLKCORE_IPSEC_CLK_CNTRL, 0x3); |
| writel(CLKCORE_IPSEC_CLK_DIV_CNTRL, 0x2); |
| #endif |
| /* Reset the IPSEC block at the time of initialization */ |
| /* This is required when we stop and start the driver */ |
| c2000_block_reset(COMPONENT_AXI_IPSEC_EAPE, 1); |
| mdelay(1); |
| c2000_block_reset(COMPONENT_AXI_IPSEC_EAPE, 0); |
| |
| #ifdef CONTROL_IPSEC_DEBUG |
| printk(KERN_INFO "%s --- Started \n", __func__); |
| #endif |
| |
| #if defined(ESPAH_USE_TRNG) |
| // de-assert ipsec reset |
| *(volatile U32 *)(CLKCORE_BLK_RESET) |= IPSEC_RST_N; |
| // Enable random number generator |
| while ( TRNG_CNTL & 0x80000000UL) |
| ; |
| TRNG_CNTL = 0x80000000UL; |
| while ( TRNG_CNTL & 0x80000000UL) |
| ; |
| // Make first request |
| TRNG_CNTL = 1; |
| int i; |
| for(i=0;i<48;) { |
| while ( TRNG_CNTL & 0x80000000UL) |
| ; |
| sc->ELP_REGS->ESPAH_IV_RND = TNG_DATA0; |
| sc->ELP_REGS->ESPAH_IV_RND = TNG_DATA1; |
| sc->ELP_REGS->ESPAH_IV_RND = TNG_DATA2; |
| sc->ELP_REGS->ESPAH_IV_RND = TNG_DATA3; |
| TRNG_CNTL = 1; |
| i+=4; |
| |
| } |
| #elif defined(ESPAH_USE_PRNG) |
| #if 0 |
| *(volatile U32 *)(CLKCORE_BLK_RESET) |= ( IPSEC_RST_N | PRNG_RST_N); |
| { |
| U32 i,j; |
| for(i=0;i<48;i++) { |
| j= *((V32*) CLKCORE_PRNG_STATUS); |
| j = j<<16; |
| j |= *((V32*) CLKCORE_PRNG_STATUS); |
| sc->ELP_REGS->espah_iv_rnd = j; |
| } |
| } |
| #endif |
| /* use kernel random number generator to get the random number */ |
| { |
| U32 i,j; |
| for(i=0;i<48;i++) { |
| get_random_bytes(&j, 4); |
| //sc->ELP_REGS->espah_iv_rnd = j; |
| #ifdef CONTROL_IPSEC_DEBUG |
| printk(KERN_INFO "addr: %x random: %x\n", &sc->ELP_REGS->espah_iv_rnd, j); |
| #endif |
| writel(j, &sc->ELP_REGS->espah_iv_rnd); |
| #ifdef CONTROL_IPSEC_DEBUG |
| printk(KERN_INFO "value :%x - %x\n", sc->ELP_REGS->espah_iv_rnd, readl(&sc->ELP_REGS->espah_iv_rnd)); |
| #endif |
| } |
| } |
| |
| #endif |
| // All the offsets are coded through ddt. |
| // Set hw offsets to 0 |
| //sc->ELP_REGS->espah_out_offset = 0; |
| writel(0, &sc->ELP_REGS->espah_out_offset); |
| //sc->ELP_REGS->espah_in_offset = 0; |
| writel(0, &sc->ELP_REGS->espah_in_offset); |
| // Acknowledge all interrupts |
| //sc->ELP_REGS->espah_irq_stat = ESPAH_STAT_OUTBND_CMD|ESPAH_STAT_OUTBND_STAT|ESPAH_STAT_INBND_CMD|ESPAH_STAT_INBND_STAT; |
| |
| writel(ESPAH_STAT_OUTBND_CMD|ESPAH_STAT_OUTBND_STAT|ESPAH_STAT_INBND_CMD|ESPAH_STAT_INBND_STAT, &sc->ELP_REGS->espah_irq_stat); |
| #if 0 |
| HAL_clear_interrupt(IRQM_IPSEC_EPEA); |
| #if !defined(IPSEC_POLL) || (!IPSEC_POLL) |
| HAL_arm1_fiq_enable(IRQM_IPSEC_EPEA); |
| // enable interrupts |
| sc->irq_en_flags = ESPAH_IRQ_OUTBND_STAT_EN | ESPAH_IRQ_INBND_STAT_EN|ESPAH_IRQ_GLBL_EN; |
| sc->ELP_REGS->espah_irq_en = sc->irq_en_flags; |
| #endif |
| #endif |
| } |
| |
| int ipsec_send_ctx_utilpe(IPSec_hw_context* sc); |
| |
| void ipsec_common_hard_init(IPSec_hw_context *sc) |
| { |
| // Hardware init - done once |
| // elliptic is known to be owned by this arm. |
| U32 aram_addr; |
| U32 i,j; |
| int m; |
| #if 0 |
| U32 IPSEC_ARAM_BYTESIZE = ((unsigned int) Image$$aram_ipsec$$Length + (unsigned int) Image$$aram_ipsec$$ZI$$Length); |
| #else |
| U32 IPSEC_ARAM_BYTESIZE = IPSEC_IRAM_SIZE; |
| #endif |
| |
| #ifdef CONTROL_IPSEC_DEBUG |
| printk(KERN_INFO "%s .. Started..\n", __func__); |
| #endif |
| sc->ELP_REGS = (clp30_espah_regs *)((u32)pfe->ipsec_baseaddr + ESPAH_BASE); |
| #ifdef CONTROL_IPSEC_DEBUG |
| printk(KERN_INFO "%s ipsec_baseaddr:%x - espah_base:%x\n", __func__, (u32)pfe->ipsec_baseaddr, (u32)((u32)pfe->ipsec_baseaddr + ESPAH_BASE)); |
| #endif |
| //sc->ARAM_base = (U32) ipsec_aram_storage; |
| sc->ARAM_base = (U32) pfe->iram_baseaddr + IPSEC_IRAM_BASEADDR; |
| #ifdef CONTROL_IPSEC_DEBUG |
| printk(KERN_INFO "%s iram_baseaddr:%x - aram_base:%x\n", __func__, (u32)pfe->iram_baseaddr, (u32)sc->ARAM_base); |
| #endif |
| #if 0 |
| /* TODO */ |
| sc->elp_irqm = IRQM_IPSEC_EPEA; |
| #endif |
| |
| aram_addr=ipsec_init_pe(sc->ARAM_base, &sc->in_pe); |
| aram_addr=ipsec_init_pe(aram_addr, &sc->out_pe); |
| sc->ARAM_sa_base = (aram_addr+ELP_SA_ALIGN-1) & ~(ELP_SA_ALIGN-1); |
| sc->ARAM_sa_base_end = sc->ARAM_sa_base + IPSEC_ARAM_BYTESIZE - 1; |
| // Use remaining space to allocate SA's in aram |
| m = IPSEC_ARAM_BYTESIZE - (sc->ARAM_sa_base - sc->ARAM_base); |
| for(j=0;j<NUM_ARAM_SADSC;j++) { |
| if (m >= 32 * ELP_SA_ALIGN) { |
| aram_sa_amask[j] = 0xffffffffUL; |
| m -= 32*ELP_SA_ALIGN; |
| } else if (m >= ELP_SA_ALIGN) { |
| aram_sa_amask[j] = 0; |
| i = 0x80000000UL; |
| do { |
| aram_sa_amask[j] |= i; |
| i = i>>1; |
| m -= ELP_SA_ALIGN; |
| } while (m>0) ; // && (i) not needed because m < 32 * SA_SIZE |
| } else { |
| aram_sa_amask[j] = 0; |
| } |
| } |
| elp_start_device(sc); |
| |
| /* Send the hardware context to utilpe */ |
| ipsec_send_ctx_utilpe(sc); |
| } |
| |
| |
| /** Send the ipsec context to utilpe. |
| * This function updates the ipsec context with allocated ARAM addresses to |
| * utilpe. |
| * @param sc pointer to the global context structure. |
| * |
| */ |
| |
| int ipsec_send_ctx_utilpe(IPSec_hw_context* sc) |
| { |
| |
| IPSec_hw_context* hw_ctx = &util_g_ipsec_hw_ctx; |
| //struct pfe_ctrl *ctrl = &pfe->ctrl; |
| |
| #ifdef CONTROL_IPSEC_DEBUG |
| printk(KERN_INFO "%s --- Started \n", __func__); |
| printk(KERN_INFO "ELP_REGS : %x\n", (u32)sc->ELP_REGS); |
| printk(KERN_INFO "Aram_base : %x\n", (u32)sc->ARAM_base); |
| printk(KERN_INFO "irq_en_flags : %x\n", sc->irq_en_flags); |
| printk(KERN_INFO "in_pe:wq_avail : %d- ring:%x ringlast:%x free:%x\n", sc->in_pe.wq_avail, (u32)sc->in_pe.pe_wq_ring, (u32)sc->in_pe.pe_wq_ringlast, (u32)sc->in_pe.pe_wq_free); |
| printk(KERN_INFO "out_pe:wq_avail : %d- ring:%x ringlast:%x free:%x\n", sc->out_pe.wq_avail, (u32)sc->out_pe.pe_wq_ring, (u32)sc->out_pe.pe_wq_ringlast, (u32)sc->out_pe.pe_wq_free); |
| printk(KERN_INFO "Aram_SA_base : %x - end:%x\n", (u32)sc->ARAM_sa_base, (u32)sc->ARAM_sa_base_end); |
| #endif |
| /* Convert the structure elements to big endian */ |
| |
| hw_ctx->ELP_REGS = (clp30_espah_regs *)cpu_to_be32((u32)virt_to_phys_ipsec_axi((void*)sc->ELP_REGS)); |
| hw_ctx->ARAM_base = cpu_to_be32(virt_to_phys_iram((void*)sc->ARAM_base)); |
| hw_ctx->irq_en_flags = cpu_to_be32(sc->irq_en_flags); |
| hw_ctx->in_pe.wq_avail = cpu_to_be32(sc->in_pe.wq_avail); |
| hw_ctx->in_pe.pe_wq_ring = (struct elp_wqentry*)cpu_to_be32(virt_to_phys_iram((void*)sc->in_pe.pe_wq_ring)); |
| hw_ctx->in_pe.pe_wq_ringlast = (struct elp_wqentry*)cpu_to_be32(virt_to_phys_iram((void*)sc->in_pe.pe_wq_ringlast)); |
| hw_ctx->in_pe.pe_wq_free = (struct elp_wqentry*)cpu_to_be32(virt_to_phys_iram((void*)sc->in_pe.pe_wq_free)); |
| hw_ctx->out_pe.wq_avail = cpu_to_be32(sc->out_pe.wq_avail); |
| hw_ctx->out_pe.pe_wq_ring = (struct elp_wqentry*)cpu_to_be32(virt_to_phys_iram((void*)sc->out_pe.pe_wq_ring)); |
| hw_ctx->out_pe.pe_wq_ringlast = (struct elp_wqentry*)cpu_to_be32(virt_to_phys_iram((void*)sc->out_pe.pe_wq_ringlast)); |
| hw_ctx->out_pe.pe_wq_free = (struct elp_wqentry*)cpu_to_be32(virt_to_phys_iram((void*)sc->out_pe.pe_wq_free)); |
| hw_ctx->ARAM_sa_base = cpu_to_be32(virt_to_phys_iram((void*)sc->ARAM_sa_base)); |
| hw_ctx->ARAM_sa_base_end = cpu_to_be32(virt_to_phys_iram((void*)sc->ARAM_sa_base_end)); |
| |
| |
| pe_dmem_memcpy_to32(UTIL_ID, virt_to_util_dmem(&util_g_ipsec_hw_ctx), &util_g_ipsec_hw_ctx, sizeof(IPSec_hw_context)); |
| |
| return NO_ERR; |
| } |
| |
| |