/*! * Get random data. * * @param user_ctx A user context from #fsl_shw_register_user(). * @param length The number of octets of @a data being requested. * @param data A pointer to a location of @a length octets to where * random data will be returned. * * @return FSL_RETURN_NO_RESOURCE_S A return code of type #fsl_shw_return_t. * FSL_RETURN_OK_S */ fsl_shw_return_t fsl_shw_get_random(fsl_shw_uco_t * user_ctx, uint32_t length, uint8_t * data) { fsl_shw_return_t return_code = FSL_RETURN_NO_RESOURCE_S; /* Boost up length to cover any 'missing' bytes at end of a word */ uint32_t *buf = os_alloc_memory(length + 3, 0); volatile rng_work_entry_t *work = os_alloc_memory(sizeof(*work), 0); if ((rng_availability != RNG_STATUS_OK) || (buf == NULL) || (work == NULL)) { if (rng_availability != RNG_STATUS_OK) { LOG_KDIAG_ARGS("rng not available: %d\n", rng_availability); } else { LOG_KDIAG_ARGS ("Resource allocation failure: %d or %d bytes", length, sizeof(*work)); } /* Cannot perform function. Clean up and clear out. */ if (buf != NULL) { os_free_memory(buf); } if (work != NULL) { os_free_memory((void *)work); } } else { unsigned blocking = user_ctx->flags & FSL_UCO_BLOCKING_MODE; work->hdr.user_ctx = user_ctx; work->hdr.flags = user_ctx->flags; work->hdr.callback = user_ctx->callback; work->hdr.user_ref = user_ctx->user_ref; work->hdr.postprocess = finish_random; work->length = length; work->data_local = buf; work->data_user = data; RNG_ADD_WORK_ENTRY((rng_work_entry_t *) work); if (blocking) { os_sleep(rng_wait_queue, work->completed != FALSE, FALSE); finish_random((shw_queue_entry_t *) work); return_code = work->hdr.code; os_free_memory((void *)work); } else { return_code = FSL_RETURN_OK_S; } } return return_code; } /* fsl_shw_get_entropy */
fsl_shw_return_t do_scc_decrypt_region(fsl_shw_uco_t * user_ctx, void *partition_base, uint32_t offset_bytes, uint32_t byte_count, const uint8_t * black_data, uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode) { scc_return_t scc_ret; fsl_shw_return_t retval = FSL_RETURN_ERROR_S; #ifdef FSL_HAVE_SCC2 #ifdef DIAG_ADAPTOR uint32_t *owner_32 = (uint32_t *) & (owner_id); LOG_KDIAG_ARGS ("partition base: %p, offset: %i, count: %i, black data: %p\n", partition_base, offset_bytes, byte_count, (void *)black_data); #endif (void)user_ctx; /* The SCC2 DMA engine will be reading from the black ram, so we need to * make sure that the data is pushed out of the cache. Note that the red * ram is not an issue because it is mapped with the cache disabled. */ os_cache_flush_range(black_data, byte_count); scc_ret = scc_decrypt_region((uint32_t) partition_base, offset_bytes, byte_count, (uint8_t *) __virt_to_phys(black_data), IV, cypher_mode); if (scc_ret == SCC_RET_OK) { retval = FSL_RETURN_OK_S; } else { retval = FSL_RETURN_ERROR_S; } #else (void)scc_ret; #endif /* FSL_HAVE_SCC2 */ return retval; }
/* * partition_base - physical address of the partition * offset - offset, in blocks, of the data from the start of the partition * length - length, in bytes, of the data to be encrypted (multiple of 4) * black_data - virtual address that the encrypted data should be stored at * Note that this virtual address must be translatable using the __virt_to_phys * macro; ie, it can't be a specially mapped address. To do encryption with those * addresses, use the scc_encrypt_region function directly. This is to make * this function compatible with the user mode declaration, which does not know * the physical addresses of the data it is using. */ fsl_shw_return_t do_scc_encrypt_region(fsl_shw_uco_t * user_ctx, void *partition_base, uint32_t offset_bytes, uint32_t byte_count, uint8_t * black_data, uint32_t * IV, fsl_shw_cypher_mode_t cypher_mode) { scc_return_t scc_ret; fsl_shw_return_t retval = FSL_RETURN_ERROR_S; #ifdef FSL_HAVE_SCC2 #ifdef DIAG_ADAPTOR uint32_t *owner_32 = (uint32_t *) & (owner_id); LOG_KDIAG_ARGS ("partition base: %p, offset: %i, count: %i, black data: %p\n", partition_base, offset_bytes, byte_count, (void *)black_data); #endif (void)user_ctx; os_cache_flush_range(black_data, byte_count); scc_ret = scc_encrypt_region((uint32_t) partition_base, offset_bytes, byte_count, __virt_to_phys(black_data), IV, cypher_mode); if (scc_ret == SCC_RET_OK) { retval = FSL_RETURN_OK_S; } else { retval = FSL_RETURN_ERROR_S; } /* The SCC2 DMA engine should have written to the black ram, so we need to * invalidate that region of memory. Note that the red ram is not an * because it is mapped with the cache disabled. */ os_cache_inv_range(black_data, byte_count); #else (void)scc_ret; #endif /* FSL_HAVE_SCC2 */ return retval; }
/*! * Read configuration information from the RNG. * * Sets #rng_output_fifo_size. * * @return A error code indicating whether the part is the expected one. */ static os_error_code rng_grab_config_values(void) { enum rng_type type; os_error_code ret = OS_ERROR_FAIL_S; /* Go for type, versions... */ type = RNG_GET_RNG_TYPE(); /* Make sure type is the one this code has been compiled for. */ if (RNG_VERIFY_TYPE(type)) { rng_output_fifo_size = RNG_GET_FIFO_SIZE(); if (rng_output_fifo_size != 0) { ret = OS_ERROR_OK_S; } } if (ret != OS_ERROR_OK_S) { LOG_KDIAG_ARGS ("Unknown or unexpected RNG type %d (FIFO size %d)." " Failing driver initialization", type, rng_output_fifo_size); } return ret; }
/*! * 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 */
/*! * 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 */
/*! * Noisily write a 32-bit value to an RNG register. * @param offset The address of the register to written. * * @param value The new register value */ static void dbg_rng_write_register(uint32_t offset, uint32_t value) { LOG_KDIAG_ARGS("WR: 0x%4x : 0x%08x", offset, value); os_write32(value, rng_base + offset); return; }