Sync usage with man page.
[netbsd-mini2440.git] / sys / dev / pci / n8 / common / n8_SKSManager.c
blob49df74b9fe9f3878e879136429c8bd158deb13cd
1 /*-
2 * Copyright (C) 2001-2003 by NBMK Encryption Technologies.
3 * All rights reserved.
5 * NBMK Encryption Technologies provides no support of any kind for
6 * this software. Questions or concerns about it may be addressed to
7 * the members of the relevant open-source community at
8 * <tech-crypto@netbsd.org>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are
12 * met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above
18 * copyright notice, this list of conditions and the following
19 * disclaimer in the documentation and/or other materials provided
20 * with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
26 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
32 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 static char const n8_id[] = "$Id: n8_SKSManager.c,v 1.1 2008/10/30 12:02:14 darran Exp $";
36 /*****************************************************************************/
37 /** @file n8_SKSManager.c
38 * @brief NSP2000 SKS Manager
40 * This file is the portion of the SKS Management Interface that is always
41 * kernel resident.
43 *****************************************************************************/
45 /*****************************************************************************
46 * Revision history:
47 * 01/21/04 jpw Change N8_NO_64_BURST macro to come from nsp2000_regs.h
48 * 01/15/04 bac Bug #990: Added N8_NO_64_BURST definition and use to break up
49 * writes to consecutive registers that are then optimized and
50 * confusing to the NSP2000.
51 * 10/25/02 brr Clean up function prototypes & include files.
52 * 08/23/02 bac Fixed incorrect DBG messages that were generating compiler
53 * warnings.
54 * 05/02/02 brr Removed all references to queue structures.
55 * 04/02/02 spm Changed %i escape sequence to %d, because at least BSD
56 * kernel print doesn't understand %i.
57 * 04/01/02 spm Moved deletion of key handle files from n8_SKSResetUnit
58 * ioctl to N8_SKSReset API call.
59 * 03/27/02 spm Changed all N8_HARDWARE_ERROR returns to N8_INVALID_KEY
60 * (Bug 505) in n8_SKSWrite. Fixed return values for
61 * n8_SKSResetUnit, so that N8_HARDWARE_ERROR is never used
62 * (Bug 646).
63 * 03/20/02 bac In n8_SKSWrite added a second delay loop to ensure the SKS
64 * Go/Busy bit is really low. It has been observed to bounce
65 * once after initially going low, which can then cause an
66 * access error upon performing a write.
67 * 03/14/02 bac Fixed n8_SKSResetUnit and n8_SKSWrite. A reset no longer
68 * calls write with a single word when trying to zero the entire
69 * SKS contents. Write grabs the lock once and does all of the
70 * writing necessary. If the SKS is busy, we wait a few times
71 * rather than just returning an error or waiting blindly whether
72 * it is busy or not.
73 * 03/12/02 brr Updated to use AtomicLocks.
74 * 02/25/02 msz File created by moving functions from n8_sks.c
75 ****************************************************************************/
76 /** @defgroup NSP2000Driver NSP2000 Device Driver Context Memory Manager.
79 #include "helper.h"
80 #include "n8_driver_main.h"
81 #include "n8_enqueue_common.h"
82 #include "n8_sks.h"
83 #include "n8_daemon_sks.h"
84 #include "n8_sks_util.h"
85 #include "n8_SKSManager.h"
86 #include "nsp2000_regs.h"
87 #include "n8_time.h"
89 #define MAX_FAILURES 6
92 extern int NSPcount_g;
93 extern NspInstance_t NSPDeviceTable_g [];
95 /*****************************************************************************
96 * n8_SKSWrite
97 *****************************************************************************/
98 /** @ingroup n8_sks
99 * @brief Write data to the SKS PROM.
101 * More detailed description of the function including any unusual algorithms
102 * or suprising details.
104 * @param targetSKS RO: A integer, the SKS PROM to write to.
105 * @param data_p RO: A uint32_t pointer to the data to write. If data_p
106 * is NULL, then the data_length of 0x0 will be written.
107 * @param data_length RO: A int, the data_p buffer length in 32 bit words.
108 * @param offset RO: A int, the SKS offset to begin the write.
110 * @par Externals:
111 * None
113 * @return
114 * N8_STATUS_OK indicates the write(s) successfully completed.
115 * N8_UNEXPECTED_ERROR indicates an error writing to the SKS or that the
116 * API was not or could not be initialized.
118 * @par Assumptions:
119 * That the target SKS exists and that the queue control struct has a valid
120 * pointer to the target SKS registers.
121 *****************************************************************************/
123 N8_Status_t n8_SKSWrite(const unsigned int targetSKS,
124 const uint32_t *data_p,
125 const int data_length,
126 const uint32_t offset_input,
127 const int fromUser)
129 int i = 0;
130 unsigned int failures;
131 uint32_t word;
132 N8_Status_t ret = N8_STATUS_OK;
133 uint32_t offset = offset_input;
134 uint32_t sks_status;
136 NspInstance_t *NSPinstance_p;
137 volatile NSP2000REGS_t *nsp;
139 if ((targetSKS < 0) || (targetSKS >= NSPcount_g))
141 DBG(("Failed to get control structure: %d\n", ret));
142 return N8_UNEXPECTED_ERROR;
145 /* assign the right control struct for the target HW */
146 NSPinstance_p = &NSPDeviceTable_g[targetSKS];
148 /* Create a nsp pointer so the N8_NO_64_BURST macro will work */
149 /* N8_NO_64_BURST is used to interleave a dummy register access between
150 * successive real 32 bit accesses that could be incorrectly "optimized" into a
151 * 64 bit burst.
153 nsp = ((NSP2000REGS_t *)(NSPinstance_p->NSPregs_p));
155 /* Entering critical section. */
156 N8_AtomicLock(NSPinstance_p->SKSSem);
158 for (i = 0; i < data_length; i++)
160 /* Get the data. It either needs to be copied into kernel space */
161 /* or does not need the copy and can be read directly. */
162 if (data_p == NULL)
164 word = 0x0;
166 else if (fromUser == TRUE)
168 N8_FROM_USER(&word, &data_p[i], sizeof(word));
170 else
172 word = data_p[i];
176 * Cannot access data register while
177 * PK_SKS_Go_Busy is on.
179 failures = 0;
180 if (SKS_READ_CONTROL(NSPinstance_p) & PK_SKS_Go_Busy)
182 if (++failures > MAX_FAILURES)
184 DBG(("Multiple failures waiting for SKS busy.\n"));
185 ret = N8_INVALID_KEY;
186 goto n8_SKSWrite_0;
188 /* go to sleep briefly */
189 n8_usleep(N8_MINIMUM_YIELD_USECS);
191 DBG(("Main wait for busy -- Iteration %d: Continuing "
192 "after %d failures.\n", i, failures));
194 /* This second wait block is here due to occasional spiking behavior in
195 * the SKS busy bit. It has been observed that the busy bit will go low,
196 * and then briefly spike again before settling low. This secondary wait
197 * look will ensure a single spike is detected and avoided. */
198 if (SKS_READ_CONTROL(NSPinstance_p) & PK_SKS_Go_Busy)
200 DBG(("Busy bit spike detected on iteration %d\n", i));
202 failures = 0;
203 if (SKS_READ_CONTROL(NSPinstance_p) & PK_SKS_Go_Busy)
205 if (++failures > MAX_FAILURES)
207 DBG(("Multiple failures waiting for SKS busy.\n"));
208 ret = N8_INVALID_KEY;
209 goto n8_SKSWrite_0;
211 /* go to sleep briefly */
212 n8_usleep(N8_MINIMUM_YIELD_USECS);
214 DBG(("2nd wait for busy -- Iteration %d: Continuing after %d failures.\n",
215 i, failures));
217 /* Clear any residual errors */
218 SKS_WRITE_CONTROL(NSPinstance_p, PK_SKS_Access_Error | PK_SKS_PROM_Error);
219 SKS_WRITE_DATA(NSPinstance_p, BE_to_uint32(&word));
220 /* Perform a dummy operation to thwart optimization that would lead to a
221 * 64-bit burst output which confuses the NSP2000.
222 * DO NOT REMOVE THIS CALL WITHOUT UNDERSTANDING THE IMPLICATIONS.
224 N8_NO_64_BURST;
226 /* Enable the SKS write. */
227 SKS_WRITE_CONTROL(NSPinstance_p, PK_SKS_Go_Busy |
228 (offset++ & PK_Cmd_SKS_Offset_Mask));
229 /* Check for errors. */
230 sks_status = SKS_READ_CONTROL(NSPinstance_p);
231 if ((sks_status & PK_SKS_Access_Error) |
232 (sks_status & PK_SKS_PROM_Error))
234 DBG(("Error writing to SKS PROM. SKS Control Register = %08x\n",
235 sks_status));
236 /* Clear the error */
237 SKS_WRITE_CONTROL(NSPinstance_p,
238 PK_SKS_Access_Error | PK_SKS_PROM_Error);
239 ret = N8_INVALID_KEY;
240 goto n8_SKSWrite_0;
242 } /* for loop */
245 * wait again so that no one tries to access
246 * SKS before it is completely written.
249 failures = 0;
250 if (SKS_READ_CONTROL(NSPinstance_p) & PK_SKS_Go_Busy)
252 failures++;
253 if (failures >= MAX_FAILURES)
255 ret = N8_INVALID_KEY;
256 DBG(("Multiple failures waiting for SKS busy.\n"));
257 goto n8_SKSWrite_0;
259 /* go to sleep briefly */
260 n8_usleep(N8_MINIMUM_YIELD_USECS);
263 n8_SKSWrite_0:
264 /* Leaving critical section. */
265 N8_AtomicUnlock(NSPinstance_p->SKSSem);
266 return ret;
268 } /* n8_SKSWrite */
271 /*****************************************************************************
272 * n8_SKSResetUnit
273 *****************************************************************************/
274 /** @ingroup n8_sks
275 * @brief Perform SKS reset for a specific unit.
277 * @param targetSKS RO: Unit number
279 * @par Externals
280 * None
282 * @return
283 * Status
285 * @par Errors
286 * N8_STATUS_OK on success.<br>
287 * N8_FILE_ERROR if errors occur while reading/writing files or
288 * directories.<br>
291 * @par Assumptions
292 * <description of assumptions><br>
293 *****************************************************************************/
295 N8_Status_t n8_SKSResetUnit(const N8_Unit_t targetSKS)
297 int i;
298 N8_Status_t ret = N8_STATUS_OK;
299 NspInstance_t *NSPinstance_p;
301 DBG(("Reset :\n"));
303 /* Find out which, if any, key entries exist. Then blast 'em. */
304 DBG(("Resetting SKS %d.\n", targetSKS));
307 if ((targetSKS < 0) || (targetSKS >= NSPcount_g))
309 DBG(("Failed to get control structure: %d\n", ret));
310 return N8_INVALID_VALUE;
313 /* assign the right control struct for the target HW */
314 NSPinstance_p = &NSPDeviceTable_g[targetSKS];
317 #ifndef SKIP_SKS_ZERO
318 /* '0' out SKS. Wipe them out. All of them. Passing NULL as the data pointer
319 * indicates to write 0x0 to entries. */
320 ret = n8_SKSWrite(targetSKS, NULL, SKS_PROM_MAX_OFFSET, 0, FALSE);
321 if (ret != N8_STATUS_OK)
323 DBG(("Error zeroing SKS in N8_SKSReset: %d\n", ret));
324 return N8_INVALID_VALUE;
326 #endif
327 /* Entering critical section. */
328 N8_AtomicLock(NSPinstance_p->SKSSem);
330 for (i=0; i < SKS_ALLOC_UNITS_PER_PROM; i++)
332 /* Clear the SKS descriptor table. */
333 NSPinstance_p->SKS_map[i] = SKS_FREE;
336 /* Leaving critical section. */
337 N8_AtomicUnlock(NSPinstance_p->SKSSem);
339 return N8_STATUS_OK;
340 } /* n8_SKSResetUnit */
343 /*****************************************************************************
344 * n8_SKSAllocate
345 *****************************************************************************/
346 /** @ingroup n8_sks
347 * @brief Allocate an entry for an SKS PROM.
349 * Attempts to find a best fit space in unallocated space, according to the
350 * descriptor tables, for the given key handle.
352 * @param keyHandle_p RW: A N8_SKSKeyHandle_t.
354 * @par Externals:
355 * None
356 * @return
357 * N8_STATUS_OK indicates the write(s) successfully completed.
358 * N8_UNEXPECTED_ERROR indicates an error allocating the key handle or that
359 * the API was not or could not be initialized.
361 * @par Assumptions:
362 * That the key handle pointer is valid.
363 *****************************************************************************/
365 N8_Status_t n8_SKSAllocate(N8_SKSKeyHandle_t* keyHandle_p)
368 unsigned int i;
369 unsigned int free_blocks_start_index = 0;
370 unsigned int free_blocks = 0;
371 unsigned int best_fit_index = 0;
372 unsigned int best_fit_blocks = 0;
373 unsigned int SKS_words_needed = 0;
374 unsigned int SKS_allocation_units_needed = 0;
375 NspInstance_t *NSPinstance_p;
377 N8_Boolean_t sks_hole_found = N8_FALSE;
379 uint32_t keyLength = keyHandle_p->key_length;
380 N8_SKSKeyType_t keyType = keyHandle_p->key_type;
381 int targetSKS = (int) keyHandle_p->unitID;
382 N8_Status_t ret = N8_STATUS_OK;
384 DBG(("N8_Allocate: \n"));
386 if ((targetSKS < 0) || (targetSKS >= NSPcount_g))
388 DBG(("Invalid unit: %d\n", targetSKS));
389 return N8_INVALID_VALUE;
391 /* assign the right control struct for the target HW */
392 NSPinstance_p = &NSPDeviceTable_g[targetSKS];
394 DBG(("SKS WORDS %d\n", SKS_RSA_DATA_LENGTH(keyLength)));
396 if ((ret = n8_ComputeKeyLength(keyType, keyLength,
397 &SKS_words_needed)) != N8_STATUS_OK)
399 DBG(("Could not compute SKS key length: %d\n", ret));
400 return ret;
403 DBG((
404 "Total of %d words needed in the SKS (%d) PROM for key length %d.\n",
405 SKS_words_needed, targetSKS, keyLength));
407 SKS_allocation_units_needed =
408 CEIL(SKS_words_needed, SKS_WORDS_PER_ALLOC_UNIT);
410 DBG((
411 "Total of %d allocation units needed in the SKS (%d) PROM.\n",
412 SKS_allocation_units_needed, targetSKS));
413 DBG((
414 "Looking for free blocks in descriptor %d.\n", targetSKS));
416 best_fit_blocks = SKS_ALLOC_UNITS_PER_PROM;
417 best_fit_index = 0;
419 /* Entering critical section */
420 N8_AtomicLock(NSPinstance_p->SKSSem);
422 /* Find the best fit for this block of words. */
423 sks_hole_found = N8_FALSE;
424 i = SKS_PROM_MIN_OFFSET;
425 while (i < SKS_ALLOC_UNITS_PER_PROM)
427 if (NSPinstance_p->SKS_map[i] == SKS_FREE)
429 DBG(("Found a free block at SKS allocation unit offset %d.\n", i));
431 free_blocks_start_index = i;
432 i++;
433 while ((i < SKS_ALLOC_UNITS_PER_PROM) &&
434 ((NSPinstance_p->SKS_map[i]) == SKS_FREE))
436 i++;
439 free_blocks = i - free_blocks_start_index;
441 DBG(("Number of free allocation blocks is %d.\n", free_blocks));
443 /* If the number of free blocks to allocate is larger than the
444 * needed number of blocks (in groups of SKS_WORDS_PER_ALLOC_UNIT)
445 * then we can allocate this block.
448 if (free_blocks >= SKS_allocation_units_needed)
450 DBG(("Number of free blocks (%d) >= to needed blocks (%d).\n",
451 free_blocks, SKS_allocation_units_needed));
453 sks_hole_found = N8_TRUE;
455 /* See if this is the smallest fit. */
456 if (free_blocks <= best_fit_blocks)
458 best_fit_index = free_blocks_start_index;
459 best_fit_blocks = free_blocks;
462 else
464 /* block is too small */
465 DBG(("Number of free blocks (%d) < to needed blocks (%d).\n",
466 free_blocks, SKS_allocation_units_needed));
470 i++;
471 } /* while i < SKS_ALLOC_UNITS_PER_PROM */
473 if (sks_hole_found == N8_TRUE)
475 DBG((
476 "Allocating %d blocks out of %d free allocation blocks to key.\n",
477 SKS_allocation_units_needed, free_blocks));
479 /* Mark the blocks, in alloc unit sizes, as in use. */
480 for (i = best_fit_index;
481 i < best_fit_index + SKS_allocation_units_needed;
482 i++)
484 DBG(("Allocating block %d.\n", i));
485 NSPinstance_p->SKS_map[i] = SKS_INUSE;
486 } /* for */
488 /* Set the key offset. */
489 keyHandle_p->sks_offset =
490 best_fit_index * SKS_WORDS_PER_ALLOC_UNIT;
492 DBG(("New key handle offset will be :%d\n", keyHandle_p->sks_offset));
494 else
496 /* No space found! */
497 DBG((
498 "Unable to find enough free space in SKS to allocate for key.\n"));
499 ret = N8_NO_MORE_RESOURCE;
502 /* Leaving critical section. */
503 N8_AtomicUnlock(NSPinstance_p->SKSSem);
505 return ret;
506 } /* n8_SKSAllocate */
509 /*****************************************************************************
510 * n8_SKSAllocate
511 *****************************************************************************/
512 /** @ingroup n8_sks
513 * @brief Set the status of an SKS entry.
515 * Sets status of SKS entry pointed to by key handle.
516 * descriptor tables, for the given key handle.
518 * @param keyHandle_p RW: A N8_SKSKeyHandle_t.
519 * @param status RO: The status value being set.
521 * @par Externals:
522 * None
524 * @return
525 * N8_STATUS_OK indicates the write(s) successfully completed.
526 * N8_UNEXPECTED_ERROR indicates an error allocating the key handle or that
527 * the API was not or could not be initialized.
529 * @par Assumptions:
530 * That the key handle pointer is valid. And that the status is valid.
532 *****************************************************************************/
534 N8_Status_t n8_SKSsetStatus(N8_SKSKeyHandle_t *keyHandle_p,
535 unsigned int status)
537 int i;
538 unsigned int alloc_units_to_free = 0;
539 unsigned int num_sks_words;
540 unsigned int sks_alloc_unit_offset = 0;
541 NspInstance_t *NSPinstance_p;
542 N8_Status_t ret = N8_STATUS_OK;
544 if ((keyHandle_p->unitID < 0) || (keyHandle_p->unitID >= NSPcount_g))
546 DBG(("Invalid unit: %d\n", keyHandle_p->unitID));
547 return N8_INVALID_VALUE;
549 /* assign the right control struct for the target HW */
550 NSPinstance_p = &NSPDeviceTable_g[keyHandle_p->unitID];
552 ret = n8_ComputeKeyLength(keyHandle_p->key_type,
553 keyHandle_p->key_length,
554 &num_sks_words);
555 if (ret != N8_STATUS_OK)
557 return ret;
560 /* given the number SKS words, compute the number of allocation units */
561 alloc_units_to_free = CEIL(num_sks_words, SKS_WORDS_PER_ALLOC_UNIT);
563 /* given the offset in words, find the first allocation unit */
564 sks_alloc_unit_offset = keyHandle_p->sks_offset / SKS_WORDS_PER_ALLOC_UNIT;
565 if (ret != N8_STATUS_OK)
567 return ret;
570 N8_AtomicLock(NSPinstance_p->SKSSem);
571 for (i = 0; i < alloc_units_to_free; i++)
573 NSPinstance_p->SKS_map[sks_alloc_unit_offset + i] = status;
575 N8_AtomicUnlock(NSPinstance_p->SKSSem);
577 return ret;
579 } /* n8_SKSsetStatus */