/*- * Copyright (C) 2001-2003 by NBMK Encryption Technologies. * All rights reserved. * * NBMK Encryption Technologies provides no support of any kind for * this software. Questions or concerns about it may be addressed to * the members of the relevant open-source community at * . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ static char const n8_id[] = "$Id: n8_SKSManager.c,v 1.2 2016/02/29 18:18:04 christos Exp $"; /*****************************************************************************/ /** @file n8_SKSManager.c * @brief NSP2000 SKS Manager * * This file is the portion of the SKS Management Interface that is always * kernel resident. * *****************************************************************************/ /***************************************************************************** * Revision history: * 01/21/04 jpw Change N8_NO_64_BURST macro to come from nsp2000_regs.h * 01/15/04 bac Bug #990: Added N8_NO_64_BURST definition and use to break up * writes to consecutive registers that are then optimized and * confusing to the NSP2000. * 10/25/02 brr Clean up function prototypes & include files. * 08/23/02 bac Fixed incorrect DBG messages that were generating compiler * warnings. * 05/02/02 brr Removed all references to queue structures. * 04/02/02 spm Changed %i escape sequence to %d, because at least BSD * kernel print doesn't understand %i. * 04/01/02 spm Moved deletion of key handle files from n8_SKSResetUnit * ioctl to N8_SKSReset API call. * 03/27/02 spm Changed all N8_HARDWARE_ERROR returns to N8_INVALID_KEY * (Bug 505) in n8_SKSWrite. Fixed return values for * n8_SKSResetUnit, so that N8_HARDWARE_ERROR is never used * (Bug 646). * 03/20/02 bac In n8_SKSWrite added a second delay loop to ensure the SKS * Go/Busy bit is really low. It has been observed to bounce * once after initially going low, which can then cause an * access error upon performing a write. * 03/14/02 bac Fixed n8_SKSResetUnit and n8_SKSWrite. A reset no longer * calls write with a single word when trying to zero the entire * SKS contents. Write grabs the lock once and does all of the * writing necessary. If the SKS is busy, we wait a few times * rather than just returning an error or waiting blindly whether * it is busy or not. * 03/12/02 brr Updated to use AtomicLocks. * 02/25/02 msz File created by moving functions from n8_sks.c ****************************************************************************/ /** @defgroup NSP2000Driver NSP2000 Device Driver Context Memory Manager. */ #include "helper.h" #include "n8_driver_main.h" #include "n8_enqueue_common.h" #include "n8_sks.h" #include "n8_daemon_sks.h" #include "n8_sks_util.h" #include "n8_SKSManager.h" #include "nsp2000_regs.h" #include "n8_time.h" #define MAX_FAILURES 6 extern int NSPcount_g; extern NspInstance_t NSPDeviceTable_g []; /***************************************************************************** * n8_SKSWrite *****************************************************************************/ /** @ingroup n8_sks * @brief Write data to the SKS PROM. * * More detailed description of the function including any unusual algorithms * or suprising details. * * @param targetSKS RO: A integer, the SKS PROM to write to. * @param data_p RO: A uint32_t pointer to the data to write. If data_p * is NULL, then the data_length of 0x0 will be written. * @param data_length RO: A int, the data_p buffer length in 32 bit words. * @param offset RO: A int, the SKS offset to begin the write. * * @par Externals: * None * * @return * N8_STATUS_OK indicates the write(s) successfully completed. * N8_UNEXPECTED_ERROR indicates an error writing to the SKS or that the * API was not or could not be initialized. * * @par Assumptions: * That the target SKS exists and that the queue control struct has a valid * pointer to the target SKS registers. *****************************************************************************/ N8_Status_t n8_SKSWrite(const unsigned int targetSKS, const uint32_t *data_p, const int data_length, const uint32_t offset_input, const int fromUser) { int i = 0; unsigned int failures; uint32_t word; N8_Status_t ret = N8_STATUS_OK; uint32_t offset = offset_input; uint32_t sks_status; NspInstance_t *NSPinstance_p; volatile NSP2000REGS_t *nsp; if (targetSKS >= NSPcount_g) { DBG(("Failed to get control structure: %d\n", ret)); return N8_UNEXPECTED_ERROR; } /* assign the right control struct for the target HW */ NSPinstance_p = &NSPDeviceTable_g[targetSKS]; /* Create a nsp pointer so the N8_NO_64_BURST macro will work */ /* N8_NO_64_BURST is used to interleave a dummy register access between * successive real 32 bit accesses that could be incorrectly "optimized" into a * 64 bit burst. */ nsp = ((NSP2000REGS_t *)(NSPinstance_p->NSPregs_p)); /* Entering critical section. */ N8_AtomicLock(NSPinstance_p->SKSSem); for (i = 0; i < data_length; i++) { /* Get the data. It either needs to be copied into kernel space */ /* or does not need the copy and can be read directly. */ if (data_p == NULL) { word = 0x0; } else if (fromUser == TRUE) { N8_FROM_USER(&word, &data_p[i], sizeof(word)); } else { word = data_p[i]; } /* * Cannot access data register while * PK_SKS_Go_Busy is on. */ failures = 0; if (SKS_READ_CONTROL(NSPinstance_p) & PK_SKS_Go_Busy) { if (++failures > MAX_FAILURES) { DBG(("Multiple failures waiting for SKS busy.\n")); ret = N8_INVALID_KEY; goto n8_SKSWrite_0; } /* go to sleep briefly */ n8_usleep(N8_MINIMUM_YIELD_USECS); } DBG(("Main wait for busy -- Iteration %d: Continuing " "after %d failures.\n", i, failures)); /* This second wait block is here due to occasional spiking behavior in * the SKS busy bit. It has been observed that the busy bit will go low, * and then briefly spike again before settling low. This secondary wait * look will ensure a single spike is detected and avoided. */ if (SKS_READ_CONTROL(NSPinstance_p) & PK_SKS_Go_Busy) { DBG(("Busy bit spike detected on iteration %d\n", i)); } failures = 0; if (SKS_READ_CONTROL(NSPinstance_p) & PK_SKS_Go_Busy) { if (++failures > MAX_FAILURES) { DBG(("Multiple failures waiting for SKS busy.\n")); ret = N8_INVALID_KEY; goto n8_SKSWrite_0; } /* go to sleep briefly */ n8_usleep(N8_MINIMUM_YIELD_USECS); } DBG(("2nd wait for busy -- Iteration %d: Continuing after %d failures.\n", i, failures)); /* Clear any residual errors */ SKS_WRITE_CONTROL(NSPinstance_p, PK_SKS_Access_Error | PK_SKS_PROM_Error); SKS_WRITE_DATA(NSPinstance_p, BE_to_uint32(&word)); /* Perform a dummy operation to thwart optimization that would lead to a * 64-bit burst output which confuses the NSP2000. * DO NOT REMOVE THIS CALL WITHOUT UNDERSTANDING THE IMPLICATIONS. */ N8_NO_64_BURST; /* Enable the SKS write. */ SKS_WRITE_CONTROL(NSPinstance_p, PK_SKS_Go_Busy | (offset++ & PK_Cmd_SKS_Offset_Mask)); /* Check for errors. */ sks_status = SKS_READ_CONTROL(NSPinstance_p); if ((sks_status & PK_SKS_Access_Error) | (sks_status & PK_SKS_PROM_Error)) { DBG(("Error writing to SKS PROM. SKS Control Register = %08x\n", sks_status)); /* Clear the error */ SKS_WRITE_CONTROL(NSPinstance_p, PK_SKS_Access_Error | PK_SKS_PROM_Error); ret = N8_INVALID_KEY; goto n8_SKSWrite_0; } } /* for loop */ /* * wait again so that no one tries to access * SKS before it is completely written. */ failures = 0; if (SKS_READ_CONTROL(NSPinstance_p) & PK_SKS_Go_Busy) { failures++; if (failures >= MAX_FAILURES) { ret = N8_INVALID_KEY; DBG(("Multiple failures waiting for SKS busy.\n")); goto n8_SKSWrite_0; } /* go to sleep briefly */ n8_usleep(N8_MINIMUM_YIELD_USECS); } n8_SKSWrite_0: /* Leaving critical section. */ N8_AtomicUnlock(NSPinstance_p->SKSSem); return ret; } /* n8_SKSWrite */ /***************************************************************************** * n8_SKSResetUnit *****************************************************************************/ /** @ingroup n8_sks * @brief Perform SKS reset for a specific unit. * * @param targetSKS RO: Unit number * * @par Externals * None * * @return * Status * * @par Errors * N8_STATUS_OK on success.
* N8_FILE_ERROR if errors occur while reading/writing files or * directories.
* * * @par Assumptions *
*****************************************************************************/ N8_Status_t n8_SKSResetUnit(const N8_Unit_t targetSKS) { int i; N8_Status_t ret = N8_STATUS_OK; NspInstance_t *NSPinstance_p; DBG(("Reset :\n")); /* Find out which, if any, key entries exist. Then blast 'em. */ DBG(("Resetting SKS %d.\n", targetSKS)); if ((targetSKS < 0) || (targetSKS >= NSPcount_g)) { DBG(("Failed to get control structure: %d\n", ret)); return N8_INVALID_VALUE; } /* assign the right control struct for the target HW */ NSPinstance_p = &NSPDeviceTable_g[targetSKS]; #ifndef SKIP_SKS_ZERO /* '0' out SKS. Wipe them out. All of them. Passing NULL as the data pointer * indicates to write 0x0 to entries. */ ret = n8_SKSWrite(targetSKS, NULL, SKS_PROM_MAX_OFFSET, 0, FALSE); if (ret != N8_STATUS_OK) { DBG(("Error zeroing SKS in N8_SKSReset: %d\n", ret)); return N8_INVALID_VALUE; } #endif /* Entering critical section. */ N8_AtomicLock(NSPinstance_p->SKSSem); for (i=0; i < SKS_ALLOC_UNITS_PER_PROM; i++) { /* Clear the SKS descriptor table. */ NSPinstance_p->SKS_map[i] = SKS_FREE; } /* Leaving critical section. */ N8_AtomicUnlock(NSPinstance_p->SKSSem); return N8_STATUS_OK; } /* n8_SKSResetUnit */ /***************************************************************************** * n8_SKSAllocate *****************************************************************************/ /** @ingroup n8_sks * @brief Allocate an entry for an SKS PROM. * * Attempts to find a best fit space in unallocated space, according to the * descriptor tables, for the given key handle. * * @param keyHandle_p RW: A N8_SKSKeyHandle_t. * * @par Externals: * None * @return * N8_STATUS_OK indicates the write(s) successfully completed. * N8_UNEXPECTED_ERROR indicates an error allocating the key handle or that * the API was not or could not be initialized. * * @par Assumptions: * That the key handle pointer is valid. *****************************************************************************/ N8_Status_t n8_SKSAllocate(N8_SKSKeyHandle_t* keyHandle_p) { unsigned int i; unsigned int free_blocks_start_index = 0; unsigned int free_blocks = 0; unsigned int best_fit_index = 0; unsigned int best_fit_blocks = 0; unsigned int SKS_words_needed = 0; unsigned int SKS_allocation_units_needed = 0; NspInstance_t *NSPinstance_p; N8_Boolean_t sks_hole_found = N8_FALSE; uint32_t keyLength = keyHandle_p->key_length; N8_SKSKeyType_t keyType = keyHandle_p->key_type; int targetSKS = (int) keyHandle_p->unitID; N8_Status_t ret = N8_STATUS_OK; DBG(("N8_Allocate: \n")); if ((targetSKS < 0) || (targetSKS >= NSPcount_g)) { DBG(("Invalid unit: %d\n", targetSKS)); return N8_INVALID_VALUE; } /* assign the right control struct for the target HW */ NSPinstance_p = &NSPDeviceTable_g[targetSKS]; DBG(("SKS WORDS %d\n", SKS_RSA_DATA_LENGTH(keyLength))); if ((ret = n8_ComputeKeyLength(keyType, keyLength, &SKS_words_needed)) != N8_STATUS_OK) { DBG(("Could not compute SKS key length: %d\n", ret)); return ret; } DBG(( "Total of %d words needed in the SKS (%d) PROM for key length %d.\n", SKS_words_needed, targetSKS, keyLength)); SKS_allocation_units_needed = CEIL(SKS_words_needed, SKS_WORDS_PER_ALLOC_UNIT); DBG(( "Total of %d allocation units needed in the SKS (%d) PROM.\n", SKS_allocation_units_needed, targetSKS)); DBG(( "Looking for free blocks in descriptor %d.\n", targetSKS)); best_fit_blocks = SKS_ALLOC_UNITS_PER_PROM; best_fit_index = 0; /* Entering critical section */ N8_AtomicLock(NSPinstance_p->SKSSem); /* Find the best fit for this block of words. */ sks_hole_found = N8_FALSE; i = SKS_PROM_MIN_OFFSET; while (i < SKS_ALLOC_UNITS_PER_PROM) { if (NSPinstance_p->SKS_map[i] == SKS_FREE) { DBG(("Found a free block at SKS allocation unit offset %d.\n", i)); free_blocks_start_index = i; i++; while ((i < SKS_ALLOC_UNITS_PER_PROM) && ((NSPinstance_p->SKS_map[i]) == SKS_FREE)) { i++; } free_blocks = i - free_blocks_start_index; DBG(("Number of free allocation blocks is %d.\n", free_blocks)); /* If the number of free blocks to allocate is larger than the * needed number of blocks (in groups of SKS_WORDS_PER_ALLOC_UNIT) * then we can allocate this block. */ if (free_blocks >= SKS_allocation_units_needed) { DBG(("Number of free blocks (%d) >= to needed blocks (%d).\n", free_blocks, SKS_allocation_units_needed)); sks_hole_found = N8_TRUE; /* See if this is the smallest fit. */ if (free_blocks <= best_fit_blocks) { best_fit_index = free_blocks_start_index; best_fit_blocks = free_blocks; } } else { /* block is too small */ DBG(("Number of free blocks (%d) < to needed blocks (%d).\n", free_blocks, SKS_allocation_units_needed)); } } i++; } /* while i < SKS_ALLOC_UNITS_PER_PROM */ if (sks_hole_found == N8_TRUE) { DBG(( "Allocating %d blocks out of %d free allocation blocks to key.\n", SKS_allocation_units_needed, free_blocks)); /* Mark the blocks, in alloc unit sizes, as in use. */ for (i = best_fit_index; i < best_fit_index + SKS_allocation_units_needed; i++) { DBG(("Allocating block %d.\n", i)); NSPinstance_p->SKS_map[i] = SKS_INUSE; } /* for */ /* Set the key offset. */ keyHandle_p->sks_offset = best_fit_index * SKS_WORDS_PER_ALLOC_UNIT; DBG(("New key handle offset will be :%d\n", keyHandle_p->sks_offset)); } else { /* No space found! */ DBG(( "Unable to find enough free space in SKS to allocate for key.\n")); ret = N8_NO_MORE_RESOURCE; } /* Leaving critical section. */ N8_AtomicUnlock(NSPinstance_p->SKSSem); return ret; } /* n8_SKSAllocate */ /***************************************************************************** * n8_SKSAllocate *****************************************************************************/ /** @ingroup n8_sks * @brief Set the status of an SKS entry. * * Sets status of SKS entry pointed to by key handle. * descriptor tables, for the given key handle. * * @param keyHandle_p RW: A N8_SKSKeyHandle_t. * @param status RO: The status value being set. * * @par Externals: * None * * @return * N8_STATUS_OK indicates the write(s) successfully completed. * N8_UNEXPECTED_ERROR indicates an error allocating the key handle or that * the API was not or could not be initialized. * * @par Assumptions: * That the key handle pointer is valid. And that the status is valid. * *****************************************************************************/ N8_Status_t n8_SKSsetStatus(N8_SKSKeyHandle_t *keyHandle_p, unsigned int status) { int i; unsigned int alloc_units_to_free = 0; unsigned int num_sks_words; unsigned int sks_alloc_unit_offset = 0; NspInstance_t *NSPinstance_p; N8_Status_t ret = N8_STATUS_OK; if ((keyHandle_p->unitID < 0) || (keyHandle_p->unitID >= NSPcount_g)) { DBG(("Invalid unit: %d\n", keyHandle_p->unitID)); return N8_INVALID_VALUE; } /* assign the right control struct for the target HW */ NSPinstance_p = &NSPDeviceTable_g[keyHandle_p->unitID]; ret = n8_ComputeKeyLength(keyHandle_p->key_type, keyHandle_p->key_length, &num_sks_words); if (ret != N8_STATUS_OK) { return ret; } /* given the number SKS words, compute the number of allocation units */ alloc_units_to_free = CEIL(num_sks_words, SKS_WORDS_PER_ALLOC_UNIT); /* given the offset in words, find the first allocation unit */ sks_alloc_unit_offset = keyHandle_p->sks_offset / SKS_WORDS_PER_ALLOC_UNIT; if (ret != N8_STATUS_OK) { return ret; } N8_AtomicLock(NSPinstance_p->SKSSem); for (i = 0; i < alloc_units_to_free; i++) { NSPinstance_p->SKS_map[sks_alloc_unit_offset + i] = status; } N8_AtomicUnlock(NSPinstance_p->SKSSem); return ret; } /* n8_SKSsetStatus */