/**************************************************************************** * sec2_dpd.c -- Code to construct a packet descriptor for SEC2 Driver **************************************************************************** * Copyright (c) Certicom Corp. 1996-2002. All rights reserved * Copyright (c) 2003-2005 Freescale Semiconductor * All Rights Reserved. Proprietary and Confidential. * * NOTICE: The information contained in this file is proprietary * to Freescale Semiconductor, and is being made available to * Freescale's customers under strict license agreements. * Use or disclosure of this information is permissible only * under the terms of the existing license agreement. ***************************************************************************/ /* * Revision History: * 1.0 Aug 24,2003 dgs - adapted from the final version of mpc18x * May 26,2004 sec - add support for scatter pointers * 1.1.0 Nov 16,2004 sec - incorporate linux changes from prior versions * 1.1.1 Dec 16,2004 sec - clean up prototypes and warnings * 1.2 Jan 27,2005 sec - bugfix in DPD dumper */ #include "Sec2Driver.h" #include "Sec2Descriptors.h" #include "sec2_dpd_Table.h" static int VerifyRequest(void *request, DPD_DETAILS_ENTRY *pDesc); static DPD_DETAILS_ENTRY *GetRequestDescEntry(unsigned long opId); /* Build a SEC2-channel-compatible fragment map from a generic one */ /* inList: external linked list */ /* ptrEnt: will become head of compatible list we'll construct */ /* */ int MapScatterFragments(unsigned long inList, unsigned long *ptrEnt) { int i, listSiz; EXT_SCATTER_ELEMENT *inElem; SCATTER_ELEMENT *outList; inElem = (EXT_SCATTER_ELEMENT *)inList; /* How many elements to alloc? */ listSiz = 0; while (inElem != NULL) { listSiz++; inElem = inElem->next; } SEC2Dump(DBGTXT_SETRQ, ("MapScatterFragments(): list 0x%08lx has %d fragments\n", inList, listSiz)); /* Allocate a chunk off the driver's available heap */ outList = malloc(sizeof(SCATTER_ELEMENT) * listSiz); if (outList == NULL) return(SEC2_FRAGMENT_POOL_EXHAUSTED); /* Stuff allocated chunk with caller's info */ i = 0; inElem = (EXT_SCATTER_ELEMENT *)inList; while (inElem != NULL) { outList[i].segAddr = (void *)__vpa((unsigned long)inElem->fragment); outList[i].segLen = (unsigned short)inElem->size; outList[i].linkStat = 0; inElem = inElem->next; SEC2Dump(DBGTXT_SETRQ, ("MapScatterFragments(): new fragment 0x%08x, %d bytes\n", (unsigned)outList[i].segAddr, outList[i].segLen)); i++; } /* once done, set the "return" bit in the last element */ outList[i - 1].linkStat |= SEGLIST_RETURN; *ptrEnt = __vpa((unsigned long)outList); return(SEC2_SUCCESS); } /* Free a SEC2-hardware-compatible fragment chain. antithesis of MapScatterFragments */ /* Should be called upon as part of a channel interrupt cleanup process */ void UnmapScatterFragments(unsigned long *ptrEnt) { /* free((void *)ChannelAssignments[channel].Dpds[i]->fld[j].ptr); */ } /* * Convert driver-native request structure to DPD and insert */ int RequestToDpd(register GENERIC_REQ *pReq, register int channel) { #define DPDPTR (ChannelAssignments[channel].Dpds[dpdCount]) register DPD_DETAILS_ENTRY *pDesc; register unsigned long opId; register int i, status; register int dpdCount; SEC2Dump(DBGTXT_SETRQ, ("RequestToDpd(): req @0x%08x ch%d\n", (unsigned)pReq, channel+1)); if (pReq == NULL) return SEC2_NULL_REQUEST; dpdCount = 0; /* process request(s) */ /* do {stuff} while pReq points to real request */ do { SEC2Dump(DBGTXT_SETRQ, ("RequestToDpd(): get entry for opId 0x%08lx\n", pReq->opId)); pDesc = GetRequestDescEntry (pReq->opId); if (pDesc == NULL) return SEC2_INVALID_OPERATION_ID; /* Validate the contents of the request */ SEC2Dump(DBGTXT_SETRQ, ("RequestToDpd(): VerifyRequest(0x%08x, 0x%08x)\n", (unsigned)pReq, (unsigned)pDesc)); status = VerifyRequest(pReq, pDesc); if (status != SEC2_SUCCESS) return status; /* If there are insufficient DPDs save the current request list entry and offset. Indicate that the request is "chunked" */ if (dpdCount >= MAX_DPDS) return (SEC2_INSUFFICIENT_REQS); /* * We have sufficient resources (DPD slots) to process this request. * Initialize a DPD and link the prior DPD to this one (if it is not the first) */ SEC2Dump(DBGTXT_SETRQ, ("RequestToDpd(): clear DPD 0x%08x\n", (unsigned)DPDPTR)); memset(DPDPTR, 0x00, sizeof(DPD)); /* fill in the dpd header */ opId = pReq->opId; DPDPTR->header = pDesc->hdrDesc[1*(opId & DESC_NUM_MASK)]; /* For each field in a request: */ for (i = 0; i < NUM_DPD_FLDS && pDesc->fld[i].txt != NULL; i++) { if (*(long *)pDesc->fld[i].txt == *(long *)"NIL") { DPDPTR->fld[i].ptr = 0; DPDPTR->fld[i].len = 0; } else { /* deal with basic pointer */ SEC2Dump(DBGTXT_SETRQ, ("RequestToDpd(): converting entry %d->0x%08x\n", i, *(unsigned int *)((unsigned int)pReq + pDesc->fld[i].ptrOffset1st))); /* if offset AND caller specfied non-NULL pointer, assign/convert that pointer */ if ((pDesc->fld[i].ptrOffset1st != 0) && *(unsigned int *)((unsigned int)pReq + pDesc->fld[i].ptrOffset1st)) DPDPTR->fld[i].ptr = __vpa(*(unsigned int *) ((unsigned int)pReq + pDesc->fld[i].ptrOffset1st)); /* if scatter pointer, deal with special case */ if (pReq->scatterBufs & (0x01 << i)) { SEC2Dump(DBGTXT_SETRQ, ("RequestToDpd(): pointer word %d is scattered\n", i+1)); /* this should be linked list to fragment map, do conversion */ status = MapScatterFragments(*(unsigned int *)((unsigned int)pReq + pDesc->fld[i].ptrOffset1st), &(DPDPTR->fld[i].ptr)); if (status != SEC2_SUCCESS) /* map failed? */ return(status); } /* deal with length */ SEC2Dump(DBGTXT_SETRQ, ("RequestToDpd(): set length = %d\n", *(unsigned int *)((unsigned int)pReq + pDesc->fld[i].lenOffset1st))); DPDPTR->fld[i].len = *(unsigned int *) ((unsigned int)pReq + pDesc->fld[i].lenOffset1st); DPDPTR->fld[i].len <<= 16; SEC2Dump(DBGTXT_SETRQ, ("RequestToDpd(): set offset= %d\n", *(unsigned int *)((unsigned int)pReq + pDesc->fld[i].extOffset))); if (pDesc->fld[i].extOffset != 0) DPDPTR->fld[i].len |= *(unsigned int *) ((unsigned int)pReq + pDesc->fld[i].extOffset) << 8; if (pReq->scatterBufs & (0x01 << i)) DPDPTR->fld[i].len |= PTRTYPE_JBIT; /* set scatter flag bit */ } } #ifdef DBG /* If DPD dump requested, show this packet descriptor content */ if (SEC2DebugLevel | DBGTXT_DPDSHOW) { SEC2Dump(DBGTXT_DPDSHOW, ("DPD header = 0x%08lx\n", DPDPTR->header)); SEC2Dump(DBGTXT_DPDSHOW, ("Primary EU/Mode 0x%02lx:%02lx, ", (DPDPTR->header & 0xf0000000) >> 28, (DPDPTR->header & 0x0ff00000) >> 20)); SEC2Dump(DBGTXT_DPDSHOW, ("Secondary EU/Mode 0x%02lx:%02lx, ", (DPDPTR->header & 0x000f0000) >> 16, (DPDPTR->header & 0x0000ff00) >> 8)); SEC2Dump(DBGTXT_DPDSHOW, ("Type 0x%02lx, ", (DPDPTR->header & 0x000000f8) >> 3)); if (DPDPTR->header & 0x00000002) SEC2Dump(DBGTXT_DPDSHOW, ("Inbound, ")); else SEC2Dump(DBGTXT_DPDSHOW, ("Outbound, ")); if (DPDPTR->header & 0x00000001) SEC2Dump(DBGTXT_DPDSHOW, ("Done\n")); else SEC2Dump(DBGTXT_DPDSHOW, ("\n")); for (i = 0; i < NUM_DPD_FLDS; i++) SEC2Dump(DBGTXT_DPDSHOW, ("DPD ptr:len/ext p%d = 0x%08lx:%08lx\n", i, DPDPTR->fld[i].ptr, DPDPTR->fld[i].len)); } /* if (DPD SHOW) */ #endif /* Move onto the next block */ pReq = (GENERIC_REQ *)pReq->nextReq; dpdCount++; } while (pReq != NULL); /* End while (request != NULL) */ return SEC2_SUCCESS; } /* end of RequestToDpd */ /* * Validate request content */ int VerifyRequest(register void *request, register DPD_DETAILS_ENTRY *pDesc) { register int i; register unsigned long len; /* register unsigned long addr; */ for (i = 0; i < NUM_DPD_FLDS && pDesc->fld[i].txt != NULL; i++) { /* A NULL pointer to a size checking function means "don't bother checking" */ if (pDesc->fld[i].pFncSize != NULL) { len = *(unsigned int *)((unsigned int)request + pDesc->fld[i].lenOffset1st); if (!pDesc->fld[i].pFncSize(len)) { SEC2Dump(DBGTXT_SETRQ, ("VerifyRequest->fld(%s) Invalid Length=%ld\n", pDesc->fld[i].txt, len)); return SEC2_INVALID_LENGTH; } } /* check Extent Length < 128 */ if (pDesc->fld[i].extOffset != 0) { len = *(unsigned int *)((unsigned int)request + pDesc->fld[i].extOffset); if (len >= 128) return SEC2_INVALID_LENGTH; } } return SEC2_SUCCESS; } /* Scan the DPDDetails table for a particular opId value. Return a pointer to the entry if it is found, NULL otherwise */ DPD_DETAILS_ENTRY *GetRequestDescEntry(unsigned long opId) { int i = 0; opId &= DESC_TYPE_MASK; while (DpdDetails[i].txt != NULL && DpdDetails[i].opId != opId) i++; return(DpdDetails[i].opId == 0 ? NULL : &DpdDetails[i]); } /* The following Chk???() routines and the FitInBlock() routine are used to verify that the length of a data area is consistent with a request type. They are supplied as pointers to functions in the DPDDetails table */ BOOLEAN ChkDesIvLen(unsigned long len) { return len != 0 && len != DES_BLOCK ? FALSE : TRUE; } BOOLEAN ChkDesKeyLen(unsigned long len) { return len != DES_BLOCK && len != (2*DES_BLOCK) && len != (3*DES_BLOCK) ? FALSE : TRUE; } BOOLEAN ChkDesStaticDataLen(unsigned long len) { return (len & 0x7) != 0 ? FALSE : TRUE; } BOOLEAN ChkDesDataLen(unsigned long len) { return (len & 0x7) != 0 ? FALSE : TRUE; } BOOLEAN ChkDesCtxLen(unsigned long len) { return len != 0 && len != DES_BLOCK != 0 ? FALSE : TRUE; } BOOLEAN ChkAesKeyLen(unsigned long len) { return len != 16 && len != 24 && len != 32 ? FALSE : TRUE; } BOOLEAN ChkAesIvLen(unsigned long len) { return len != 0 && len != 16 ? FALSE : TRUE; } BOOLEAN ChkArcKeyLen(unsigned long len) { return len < ARC4_MIN_KEYBYTES || len > ARC4_MAX_KEYBYTES ? FALSE : TRUE; } BOOLEAN ChkArcCtxLen(unsigned long len) { return len != ARC4_CONTEXTBYTES ? FALSE : TRUE; } BOOLEAN ChkOptionalArcCtxLen(unsigned long len) { return len != ARC4_CONTEXTBYTES && len != 0 ? FALSE : TRUE; } BOOLEAN ChkEccLen(unsigned long len) { return len > 64 ? FALSE : TRUE; } BOOLEAN FitInBlock(unsigned long len) { return len > BlockSize ? FALSE : TRUE; } BOOLEAN ChkCcmpKeyLen(unsigned long len) { return len != 0 && len != 16 ? FALSE : TRUE; }