NetBSD/sys/dev/pci/n8/common/n8_SKSManager.c
darran 0e11c6bfd5 NetOctave NSP2000 driver, ported from FreeBSD and integrated with
opencrypto by CoyotePoint Systems.  The FreeBSD driver source was recently
made available by NBMK Encryption Technologies.

The port includes some currently unused code which implements kernel and
user space interfaces for the driver in FreeBSD.  These are left in at this
time to facilitate the port of these interface to NetBSD if they are of
interest.
2008-10-30 12:02:14 +00: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 */