/*! * Undo everything done by rng_init() and place driver in fail mode. * * Deregister from SCC, stop tasklet, shutdown the RNG. Leave the register * map in place in case other drivers call rng_read/write_register() * * @return void */ static void rng_cleanup(void) { struct clk *clk; #ifdef FSL_HAVE_RNGA scc_stop_monitoring_security_failure(rng_sec_failure); #endif clk = clk_get(NULL, "rng_clk"); clk_disable(clk); if (task_started) { os_dev_stop_task(rng_entropy_task); } if (rng_base != NULL) { /* mask off RNG interrupts */ RNG_MASK_ALL_INTERRUPTS(); RNG_SLEEP(); if (rng_irq_set) { /* unmap the interrupts from the IRQ lines */ os_deregister_interrupt(INT_RNG); rng_irq_set = FALSE; } LOG_KDIAG("Leaving rng driver status as failed"); rng_availability = RNG_STATUS_FAILED; } else { LOG_KDIAG("Leaving rng driver status as unimplemented"); rng_availability = RNG_STATUS_UNIMPLEMENTED; } LOG_KDIAG("Cleaned up"); } /* rng_cleanup */
/****************************************************************************** * * CAUTION: NONE * * MODIFICATION HISTORY: * * Date Person Change * 30/07/2003 MW Initial Creation ******************************************************************************/ int sah_Intr_Init(wait_queue_head_t * wait_queue) { #ifdef DIAG_DRV_INTERRUPT char err_string[200]; #endif int result; #ifdef KERNEL_TEST SAHARA_INT_PTR = sah_Intr_Top_Half; #endif /* Set queue used by the interrupt handler to match the driver interface */ int_queue = wait_queue; /* Request use of the Interrupt line. */ result = request_irq(SAHARA_IRQ, sah_Intr_Top_Half, 0, SAHARA_NAME, NULL); #ifdef DIAG_DRV_INTERRUPT if (result != 0) { sprintf(err_string, "Cannot use SAHARA interrupt line %d. " "request_irq() return code is %i.", SAHARA_IRQ, result); LOG_KDIAG(err_string); } else { sprintf(err_string, "SAHARA driver registered for interrupt %d. ", SAHARA_IRQ); LOG_KDIAG(err_string); } #endif return result; }
/****************************************************************************** * * CAUTION: This does not kfree() the entry. Does not check to see if the entry * actually belongs to the queue. ******************************************************************************/ void sah_Queue_Remove_Any_Entry(sah_Queue * q, sah_Head_Desc * entry) { sah_Head_Desc *prev_entry = NULL; sah_Head_Desc *next_entry = NULL; if ((q == NULL) || (entry == NULL)) { #if defined DIAG_DRV_QUEUE && defined DIAG_DURING_INTERRUPT LOG_KDIAG("Null pointer input."); #endif return; } if (q->count == 1) { /* If q is the only entry in the queue. */ q->tail = NULL; q->head = NULL; q->count = 0; } else if (q->count > 1) { /* There are 2 or more entries in the queue. */ #if defined DIAG_DRV_QUEUE && defined DIAG_DURING_INTERRUPT if ((entry->next == NULL) && (entry->prev == NULL)) { LOG_KDIAG ("Queue is not empty yet both next and prev pointers" " are NULL"); } #endif if (entry->next == NULL) { /* If this is the end of the queue */ prev_entry = entry->prev; prev_entry->next = NULL; q->tail = prev_entry; } else if (entry->prev == NULL) { /* If this is the head of the queue */ next_entry = entry->next; next_entry->prev = NULL; q->head = next_entry; } else { /* If this is somewhere in the middle of the queue */ prev_entry = entry->prev; next_entry = entry->next; prev_entry->next = next_entry; next_entry->prev = prev_entry; } q->count--; } /* * Otherwise we are removing an entry from an empty queue. * Don't do anything in the product code */ #if defined DIAG_DRV_QUEUE && defined DIAG_DURING_INTERRUPT else { LOG_KDIAG("Trying to remove an entry from an empty queue."); } #endif entry->next = NULL; entry->prev = NULL; }
/****************************************************************************** * * CAUTION: NONE ******************************************************************************/ void sah_Queue_Append_Entry (sah_Queue *q, sah_Head_Desc *entry) { sah_Head_Desc *tail_entry = NULL; if ((q == NULL) || (entry == NULL)) { #ifdef DIAG_DRV_QUEUE LOG_KDIAG ("Null pointer input."); #endif return; } if (q->count == 0) { /* The queue is empty */ q->head = entry; q->tail = entry; entry->next = NULL; entry->prev = NULL; } else { /* The queue is not empty */ tail_entry = q->tail; tail_entry->next = entry; entry->next = NULL; entry->prev = tail_entry; q->tail = entry; } q->count++; }
/*! * Dump chain of links to the log. * * @brief Dump chain of links * * @param prefix Text to put in front of dumped data * @param link Kernel virtual address of start of chain of links * * @return void */ static void km_Dump_Link(const char *prefix, const sah_Link * link) { while (link != NULL) { km_Dump_Words(prefix, (unsigned *)link, 3 /* # words in h/w link */ ); if (link->flags & SAH_STORED_KEY_INFO) { #ifdef CAN_DUMP_SCC_DATA uint32_t len; #endif #ifdef CAN_DUMP_SCC_DATA { char buf[50]; scc_get_slot_info(link->ownerid, link->slot, (uint32_t *) & link->data, /* RED key address */ &len); /* key length */ sprintf(buf, " SCC slot %d: ", link->slot); km_Dump_Words(buf, (void *)IO_ADDRESS((uint32_t) link->data), link->len / 4); } #else sprintf(Diag_msg, " SCC slot %d", link->slot); LOG_KDIAG(Diag_msg); #endif } else if (link->data != NULL) { km_Dump_Region(" Data", link->data, link->len); } link = link->next; } }
/*! * Dump given region of data to the log. * * @brief Dump data * * @param prefix Text to put in front of dumped data * @param data Kernel virtual address of start of region to dump * @param length Amount of data to dump * * @return void */ void km_Dump_Region(const char *prefix, const unsigned char *data, unsigned length) { unsigned count; char *output; unsigned data_len; sprintf(Diag_msg, "%s (%08X,%u):", prefix, (uint32_t) data, length); /* Restrict amount of data to dump */ if (length > MAX_DUMP) { data_len = MAX_DUMP; } else { data_len = length; } /* We've already printed some text in output buffer, skip over it */ output = Diag_msg + strlen(Diag_msg); for (count = 0; count < data_len; count++) { if (count % 4 == 0) { *output++ = ' '; } sprintf(output, "%02X", *data++); output += 2; } LOG_KDIAG(Diag_msg); }
static void *my_malloc(void *ref, size_t n) { register void *mem; #ifndef DIAG_MEM_ERRORS mem = os_alloc_memory(n, GFP_KERNEL); #else { uint32_t rand; /* are we feeling lucky ? */ os_get_random_bytes(&rand, sizeof(rand)); if ((rand % DIAG_MEM_CONST) == 0) { mem = 0; } else { mem = os_alloc_memory(n, GFP_ATOMIC); } } #endif /* DIAG_MEM_ERRORS */ #ifdef DIAG_MEM sprintf(Diag_msg, "API kmalloc: %p for %d\n", mem, n); LOG_KDIAG(Diag_msg); #endif ref = 0; /* unused param warning */ return mem; }
static void my_free(void* ref, void* ptr) { ref = 0; /* unused param warning */ #ifdef DIAG_MEM sprintf(Diag_msg, "API kfree: %p\n", ptr); LOG_KDIAG(Diag_msg); #endif os_free_memory(ptr); }
/*! * This function writes the Descriptor Chain to the kernel driver. * * @brief Writes the Descriptor Chain to the kernel driver. * * @param dar A pointer to a Descriptor Chain of type sah_Head_Desc * @param uco The user context object * * @return A return code of type #fsl_shw_return_t. */ fsl_shw_return_t adaptor_Exec_Descriptor_Chain( sah_Head_Desc *dar, fsl_shw_uco_t *uco) { sah_Head_Desc *kernel_space_desc = NULL; fsl_shw_return_t code = FSL_RETURN_OK_S; int os_error_code = 0; unsigned blocking_mode = dar->uco_flags & FSL_UCO_BLOCKING_MODE; #ifdef DIAG_ADAPTOR km_Dump_Chain(&dar->desc); #endif dar->user_info = uco; dar->user_desc = dar; /* This code has been shamelessly copied from sah_driver_interface.c */ /* It needs to be moved somewhere common ... */ kernel_space_desc = sah_Physicalise_Descriptors(dar); if ( kernel_space_desc == NULL ) { /* We may have failed due to a -EFAULT as well, but we will return * -ENOMEM since either way it is a memory related failure. */ code = FSL_RETURN_NO_RESOURCE_S; #ifdef DIAG_DRV_IF LOG_KDIAG("sah_Physicalise_Descriptors() failed\n"); #endif } else { if (blocking_mode) { #ifdef SAHARA_POLL_MODE os_error_code = sah_Handle_Poll(dar); #else os_error_code = sah_blocking_mode(dar); #endif if (os_error_code != 0) { code = FSL_RETURN_ERROR_S; } else { /* status of actual operation */ code = dar->result; } /* free this chain */ if (!(uco->flags & FSL_UCO_SAVE_DESC_CHAIN)) { sah_Descriptor_Chain_Destroy(uco->mem_util, &dar); } } else { #ifdef SAHARA_POLL_MODE sah_Handle_Poll(dar); #else /* just put someting in the DAR */ sah_Queue_Manager_Append_Entry(dar); #endif /* SAHARA_POLL_MODE */ } } return code; }
/*! ******************************************************************************* * This function is the Bottom Half of the interrupt handler. It calls * #sah_postprocess_queue() to complete the processing of the Descriptor Chains * which were finished by the hardware. * * @brief SAHARA Interrupt Handler Bottom Half * * @param data Part of the kernel prototype. * * @return void */ static void sah_Intr_Bottom_Half(unsigned long reset_flag) { #if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT) LOG_KDIAG("Bottom half of Sahara's interrupt handler called."); #endif sah_postprocess_queue(*(unsigned long *)reset_flag); return; }
/****************************************************************************** * * CAUTION: This function does not free any queue entries. * ******************************************************************************/ void sah_Queue_Destroy (sah_Queue *q) { #ifdef DIAG_DRV_QUEUE if (q == NULL) { LOG_KDIAG ("Trying to kfree() a NULL pointer."); } else { if (q->count != 0) { LOG_KDIAG ("Trying to destroy a queue that is not empty."); } } #endif if (q != NULL) { os_free_memory(q); q = NULL; } }
/*! ******************************************************************************* * This function is the Top Half of the interrupt handler. It updates the * status of any finished descriptor chains and then tries to add any pending * requests into the hardware. It then queues the bottom half to complete * operations on the finished chains. * * @brief SAHARA Interrupt Handler Top Half * * @param irq Part of the kernel prototype. * @param dev_id Part of the kernel prototype. * * @return An IRQ_RETVAL() -- non-zero to that function means 'handled' */ static irqreturn_t sah_Intr_Top_Half(int irq, void *dev_id) { #if defined(DIAG_DRV_INTERRUPT) && defined(DIAG_DURING_INTERRUPT) LOG_KDIAG("Top half of Sahara's interrupt handler called."); #endif interrupt_count++; reset_flag = sah_Handle_Interrupt(sah_HW_Read_Status()); /* Schedule the Bottom Half of the Interrupt. */ tasklet_schedule(&BH_task); /* To get rid of the unused parameter warnings. */ irq = 0; dev_id = NULL; return IRQ_RETVAL(1); }
/*! * Dump given wors of data to the log. * * @brief Dump data * * @param prefix Text to put in front of dumped data * @param data Kernel virtual address of start of region to dump * @param word_count Amount of data to dump * * @return void */ void km_Dump_Words(const char *prefix, const unsigned *data, unsigned word_count) { char *output; sprintf(Diag_msg, "%s (%08X,%uw): ", prefix, (uint32_t) data, word_count); /* We've already printed some text in output buffer, skip over it */ output = Diag_msg + strlen(Diag_msg); while (word_count--) { sprintf(output, "%08X ", *data++); output += 9; } LOG_KDIAG(Diag_msg); }
/*! * Register #rng_irq() as the interrupt handler for #INT_RNG. * * @return OS_ERROR_OK_S on success, os_error_code on failure */ static os_error_code rng_setup_interrupt_handling(void) { os_error_code error_code; /* * Install interrupt service routine for the RNG. Ignore the * assigned IRQ number. */ error_code = os_register_interrupt(RNG_DRIVER_NAME, INT_RNG, OS_DEV_ISR_REF(rng_irq)); if (error_code != OS_ERROR_OK_S) { LOG_KDIAG("RNG Driver: Error installing Interrupt Handler"); } else { rng_irq_set = TRUE; RNG_UNMASK_ALL_INTERRUPTS(); } return error_code; } /* rng_setup_interrupt_handling */
/****************************************************************************** * * CAUTION: This function may sleep in low-memory situations, as it uses * kmalloc ( ..., GFP_KERNEL). ******************************************************************************/ sah_Queue *sah_Queue_Construct (void) { sah_Queue *q = (sah_Queue *)os_alloc_memory (sizeof(sah_Queue), GFP_KERNEL); if (q != NULL) { /* Initialise the queue to an empty state. */ q->head = NULL; q->tail = NULL; q->count = 0; } #ifdef DIAG_DRV_QUEUE else { LOG_KDIAG ("kmalloc() failed."); } #endif return q; }
/*! * Make sure that register access is legal. * * Verify that, if in secure mode, only safe registers are used. * For any register access, make sure that read-only registers are not written * and that write-only registers are not read. This check also disallows any * access to the RNG's Output FIFO, to prevent other drivers from draining the * FIFO and causing an underflow condition. * * This routine is only for checking accesses by other than this driver. * * @param offset The (byte) offset within the RNG block * of the register to be accessed. See * @ref rngregs for meanings. * @param access_write 0 for read, anything else for write * * @return 0 if invalid, 1 if OK. */ static int rng_check_register_accessible(uint32_t offset, int access_write) { int return_code = FALSE; /* invalid */ uint32_t secure = RNG_GET_HIGH_ASSURANCE(); /* First check for RNG in Secure Mode -- most registers inaccessible. * Also disallowing access to RNG_OUTPUT_FIFO except by the driver. */ if (! #ifdef FSL_HAVE_RNGA (secure && ((offset == RNGA_OUTPUT_FIFO) || (offset == RNGA_MODE) || (offset == RNGA_VERIFICATION_CONTROL) || (offset == RNGA_OSCILLATOR_CONTROL_COUNTER) || (offset == RNGA_OSCILLATOR1_COUNTER) || (offset == RNGA_OSCILLATOR2_COUNTER) || (offset == RNGA_OSCILLATOR_COUNTER_STATUS))) #else /* RNGB or RNGC */ (secure && ((offset == RNGC_FIFO) || (offset == RNGC_VERIFICATION_CONTROL) || (offset == RNGC_OSC_COUNTER_CONTROL) || (offset == RNGC_OSC_COUNTER) || (offset == RNGC_OSC_COUNTER_STATUS))) #endif ) { /* Passed that test. Either not in high assurance, and/or are checking register that is always available. Now check R/W permissions. */ if (access_write == RNG_CHECK_READ) { /* read request */ /* Only the entropy register is write-only */ #ifdef FSL_HAVE_RNGC /* No registers are write-only */ return_code = TRUE; #else /* else RNGA or RNGB */ #ifdef FSL_HAVE_RNGA if (1) { #else if (!(offset == RNGB_ENTROPY)) { #endif return_code = TRUE; /* Let all others be read */ } else { pr_debug ("RNG: Offset %04x denied read access\n", offset); } #endif /* RNGA or RNGB */ } /* read */ else { /* access_write means write */ /* Check against list of non-writable registers */ if (! #ifdef FSL_HAVE_RNGA ((offset == RNGA_STATUS) || (offset == RNGA_OUTPUT_FIFO) || (offset == RNGA_OSCILLATOR1_COUNTER) || (offset == RNGA_OSCILLATOR2_COUNTER) || (offset == RNGA_OSCILLATOR_COUNTER_STATUS)) #else /* FSL_HAVE_RNGB or FSL_HAVE_RNGC */ ((offset == RNGC_STATUS) || (offset == RNGC_FIFO) || (offset == RNGC_OSC_COUNTER) || (offset == RNGC_OSC_COUNTER_STATUS)) #endif ) { return_code = TRUE; /* can be written */ } else { LOG_KDIAG_ARGS ("Offset %04x denied write access", offset); } } /* write */ } /* not high assurance and inaccessible register... */ else { LOG_KDIAG_ARGS("Offset %04x denied high-assurance access", offset); } return return_code; } /* rng_check_register_accessible */ #endif /* RNG_REGISTER_PEEK_POKE */ /*****************************************************************************/ /* fn rng_irq() */ /*****************************************************************************/ /*! * This is the interrupt handler for the RNG. It is only ever invoked if the * RNG detects a FIFO Underflow error. * * If the error is a Security Violation, this routine will * set the #rng_availability to #RNG_STATUS_FAILED, as the entropy pool may * have been corrupted. The RNG will also be placed into low power mode. The * SCC will have noticed the problem as well. * * The other possibility, if the RNG is not in High Assurance mode, would be * simply a FIFO Underflow. No special action, other than to * clear the interrupt, is taken. */ OS_DEV_ISR(rng_irq) { int handled = FALSE; /* assume interrupt isn't from RNG */ LOG_KDIAG("rng irq!"); if (RNG_SEED_DONE()) { complete(&rng_seed_done); RNG_CLEAR_ALL_STATUS(); handled = TRUE; } if (RNG_SELF_TEST_DONE()) { complete(&rng_self_testing); RNG_CLEAR_ALL_STATUS(); handled = TRUE; } /* Look to see whether RNG needs attention */ if (RNG_HAS_ERROR()) { if (RNG_GET_HIGH_ASSURANCE()) { RNG_SLEEP(); rng_availability = RNG_STATUS_FAILED; RNG_MASK_ALL_INTERRUPTS(); } handled = TRUE; /* Clear the interrupt */ RNG_CLEAR_ALL_STATUS(); } os_dev_isr_return(handled); } /* rng_irq */ /*****************************************************************************/ /* fn map_RNG_memory() */ /*****************************************************************************/ /*! * Place the RNG's memory into kernel virtual space. * * @return OS_ERROR_OK_S on success, os_error_code on failure */ static os_error_code rng_map_RNG_memory(void) { os_error_code error_code = OS_ERROR_FAIL_S; rng_base = os_map_device(RNG_BASE_ADDR, RNG_ADDRESS_RANGE); if (rng_base == NULL) { /* failure ! */ LOG_KDIAG("RNG Driver: ioremap failed."); } else { error_code = OS_ERROR_OK_S; } return error_code; } /* rng_map_RNG_memory */
/*! * This function copies words from the RNG FIFO into the caller's buffer. * * * @param random_p Location to copy random data * @param count_words Number of words to copy * * @return An error code. */ static fsl_shw_return_t rng_drain_fifo(uint32_t * random_p, int count_words) { int words_in_rng; /* Number of words available now in RNG */ fsl_shw_return_t code = FSL_RETURN_ERROR_S; int sequential_count = 0; /* times through big while w/empty FIFO */ int fifo_empty_count = 0; /* number of times FIFO was empty */ int max_sequential = 0; /* max times 0 seen in a row */ #if !defined(FSL_HAVE_RNGA) int count_for_reseed = 0; INIT_COMPLETION(rng_seed_done); #endif #if !defined(FSL_HAVE_RNGA) if (RNG_RESEED()) { do { LOG_KDIAG("Reseeding RNG"); RNG_CLEAR_ERR(); RNG_SEED_GEN(); wait_for_completion(&rng_seed_done); if (count_for_reseed == 3) { os_printk(KERN_ALERT "Device was not able to enter RESEED Mode\n"); code = FSL_RETURN_INTERNAL_ERROR_S; } count_for_reseed++; } while (RNG_CHECK_SEED_ERR()); } #endif /* Copy all of them in. Stop if pool fills. */ while ((rng_availability == RNG_STATUS_OK) && (count_words > 0)) { /* Ask RNG how many words currently in FIFO */ words_in_rng = RNG_GET_WORDS_IN_FIFO(); if (words_in_rng == 0) { ++sequential_count; fifo_empty_count++; if (sequential_count > max_sequential) { max_sequential = sequential_count; } if (sequential_count >= RNG_MAX_TRIES) { LOG_KDIAG_ARGS("FIFO staying empty (%d)", words_in_rng); code = FSL_RETURN_NO_RESOURCE_S; break; } } else { /* Found at least one word */ sequential_count = 0; /* Now adjust: words_in_rng = MAX(count_words, words_in_rng) */ words_in_rng = (count_words < words_in_rng) ? count_words : words_in_rng; } /* else found words */ #ifdef RNG_FORCE_FIFO_UNDERFLOW /* * For unit test, force occasional extraction of more words than * available. This should cause FIFO Underflow, and IRQ invocation. */ words_in_rng = count_words; #endif /* Copy out all available & neeeded data */ while (words_in_rng-- > 0) { *random_p++ = RNG_READ_FIFO(); count_words--; } } /* while words still needed */ if (count_words == 0) { code = FSL_RETURN_OK_S; } if (fifo_empty_count != 0) { LOG_KDIAG_ARGS("FIFO empty %d times, max loop count %d", fifo_empty_count, max_sequential); } return code; } /* rng_drain_fifo */