2 * Copyright (C) 2001-2003 by NBMK Encryption Technologies.
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>.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions are
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
43 *****************************************************************************/
45 /*****************************************************************************
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
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
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
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.
80 #include "n8_driver_main.h"
81 #include "n8_enqueue_common.h"
83 #include "n8_daemon_sks.h"
84 #include "n8_sks_util.h"
85 #include "n8_SKSManager.h"
86 #include "nsp2000_regs.h"
89 #define MAX_FAILURES 6
92 extern int NSPcount_g
;
93 extern NspInstance_t NSPDeviceTable_g
[];
95 /*****************************************************************************
97 *****************************************************************************/
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.
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.
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
,
130 unsigned int failures
;
132 N8_Status_t ret
= N8_STATUS_OK
;
133 uint32_t offset
= offset_input
;
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
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. */
166 else if (fromUser
== TRUE
)
168 N8_FROM_USER(&word
, &data_p
[i
], sizeof(word
));
176 * Cannot access data register while
177 * PK_SKS_Go_Busy is on.
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
;
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
));
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
;
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",
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.
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",
236 /* Clear the error */
237 SKS_WRITE_CONTROL(NSPinstance_p
,
238 PK_SKS_Access_Error
| PK_SKS_PROM_Error
);
239 ret
= N8_INVALID_KEY
;
245 * wait again so that no one tries to access
246 * SKS before it is completely written.
250 if (SKS_READ_CONTROL(NSPinstance_p
) & PK_SKS_Go_Busy
)
253 if (failures
>= MAX_FAILURES
)
255 ret
= N8_INVALID_KEY
;
256 DBG(("Multiple failures waiting for SKS busy.\n"));
259 /* go to sleep briefly */
260 n8_usleep(N8_MINIMUM_YIELD_USECS
);
264 /* Leaving critical section. */
265 N8_AtomicUnlock(NSPinstance_p
->SKSSem
);
271 /*****************************************************************************
273 *****************************************************************************/
275 * @brief Perform SKS reset for a specific unit.
277 * @param targetSKS RO: Unit number
286 * N8_STATUS_OK on success.<br>
287 * N8_FILE_ERROR if errors occur while reading/writing files or
292 * <description of assumptions><br>
293 *****************************************************************************/
295 N8_Status_t
n8_SKSResetUnit(const N8_Unit_t targetSKS
)
298 N8_Status_t ret
= N8_STATUS_OK
;
299 NspInstance_t
*NSPinstance_p
;
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
;
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
);
340 } /* n8_SKSResetUnit */
343 /*****************************************************************************
345 *****************************************************************************/
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.
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.
362 * That the key handle pointer is valid.
363 *****************************************************************************/
365 N8_Status_t
n8_SKSAllocate(N8_SKSKeyHandle_t
* keyHandle_p
)
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
));
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
);
411 "Total of %d allocation units needed in the SKS (%d) PROM.\n",
412 SKS_allocation_units_needed
, targetSKS
));
414 "Looking for free blocks in descriptor %d.\n", targetSKS
));
416 best_fit_blocks
= SKS_ALLOC_UNITS_PER_PROM
;
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
;
433 while ((i
< SKS_ALLOC_UNITS_PER_PROM
) &&
434 ((NSPinstance_p
->SKS_map
[i
]) == SKS_FREE
))
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
;
464 /* block is too small */
465 DBG(("Number of free blocks (%d) < to needed blocks (%d).\n",
466 free_blocks
, SKS_allocation_units_needed
));
471 } /* while i < SKS_ALLOC_UNITS_PER_PROM */
473 if (sks_hole_found
== N8_TRUE
)
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
;
484 DBG(("Allocating block %d.\n", i
));
485 NSPinstance_p
->SKS_map
[i
] = SKS_INUSE
;
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
));
496 /* No space found! */
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
);
506 } /* n8_SKSAllocate */
509 /*****************************************************************************
511 *****************************************************************************/
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.
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.
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
,
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
,
555 if (ret
!= N8_STATUS_OK
)
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
)
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
);
579 } /* n8_SKSsetStatus */