예제 #1
0
/*!
 * 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;
}
예제 #4
0
/*!
 * 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;
}
예제 #5
0
/*!
 * 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 */
예제 #6
0
/*!
 * 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 */
예제 #7
0
/*!
 * 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;
}