netbsd/sys/dev/pci/n8/common/n8_SKSManager.c
2013-04-06 16:48:33 +02:00

581 lines
19 KiB
C

/*-
* 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
* <tech-crypto@netbsd.org>.
*
* 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.1 2008/10/30 12:02:14 darran 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 < 0) || (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.<br>
* N8_FILE_ERROR if errors occur while reading/writing files or
* directories.<br>
*
*
* @par Assumptions
* <description of assumptions><br>
*****************************************************************************/
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 */