184615Snyan/*- 284615Snyan * Copyright (c) 2013 The FreeBSD Foundation 384615Snyan * Copyright (c) 2013 David E. O'Brien <obrien@NUXI.org> 484615Snyan * Copyright (c) 2012 Konstantin Belousov <kib@FreeBSD.org> 584615Snyan * All rights reserved. 684615Snyan * 784615Snyan * Portions of this software were developed by Konstantin Belousov 884615Snyan * under sponsorship from the FreeBSD Foundation. 984615Snyan * 1084615Snyan * Redistribution and use in source and binary forms, with or without 1184615Snyan * modification, are permitted provided that the following conditions 1284615Snyan * are met: 1384615Snyan * 1. Redistributions of source code must retain the above copyright 1484615Snyan * notice, this list of conditions and the following disclaimer 1584615Snyan * in this position and unchanged. 1684615Snyan * 2. Redistributions in binary form must reproduce the above copyright 1784615Snyan * notice, this list of conditions and the following disclaimer in the 1884615Snyan * documentation and/or other materials provided with the distribution. 1984615Snyan * 2084615Snyan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2184615Snyan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2284615Snyan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2384615Snyan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2484615Snyan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2584615Snyan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2684615Snyan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2784615Snyan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2884615Snyan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2984615Snyan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3084615Snyan * 3184615Snyan */ 3284615Snyan 3384615Snyan#include <sys/cdefs.h> 3484615Snyan__FBSDID("$FreeBSD$"); 3584615Snyan 3684615Snyan#include <sys/param.h> 3784615Snyan#include <sys/kernel.h> 3884615Snyan#include <sys/lock.h> 3984615Snyan#include <sys/malloc.h> 4084615Snyan#include <sys/module.h> 4184615Snyan#include <sys/random.h> 4284615Snyan#include <sys/selinfo.h> 4384615Snyan#include <sys/systm.h> 4484615Snyan 4584615Snyan#include <machine/md_var.h> 4690762Snyan#include <machine/specialreg.h> 4784615Snyan 4884615Snyan#include <dev/random/randomdev.h> 4990762Snyan#include <dev/random/randomdev_soft.h> 5090762Snyan#include <dev/random/random_harvestq.h> 5184615Snyan#include <dev/random/live_entropy_sources.h> 5284615Snyan#include <dev/random/random_adaptors.h> 5384615Snyan 5484615Snyan#define RETRY_COUNT 10 5584615Snyan 5684615Snyanstatic int random_ivy_read(void *, int); 5784615Snyan 5884615Snyanstatic struct random_hardware_source random_ivy = { 5984615Snyan .ident = "Hardware, Intel Secure Key RNG", 6084615Snyan .source = RANDOM_PURE_RDRAND, 6184615Snyan .read = random_ivy_read 6284615Snyan}; 6384615Snyan 6484615Snyanstatic inline int 6584615Snyanivy_rng_store(long *buf) 6684615Snyan{ 6784615Snyan#ifdef __GNUCLIKE_ASM 6884615Snyan long tmp; 6984615Snyan int retry; 7084615Snyan 7184615Snyan retry = RETRY_COUNT; 7284615Snyan __asm __volatile( 7384615Snyan "1:\n\t" 7484615Snyan "rdrand %2\n\t" /* read randomness into tmp */ 7584615Snyan "jb 2f\n\t" /* CF is set on success, exit retry loop */ 7684615Snyan "dec %0\n\t" /* otherwise, retry-- */ 7784615Snyan "jne 1b\n\t" /* and loop if retries are not exhausted */ 7884615Snyan "jmp 3f\n" /* failure, retry is 0, used as return value */ 7984615Snyan "2:\n\t" 8084615Snyan "mov %2,%1\n\t" /* *buf = tmp */ 8184615Snyan "3:" 8284615Snyan : "+q" (retry), "=m" (*buf), "=q" (tmp) : : "cc"); 8384615Snyan return (retry); 8484615Snyan#else /* __GNUCLIKE_ASM */ 8584615Snyan return (0); 8684615Snyan#endif 8784615Snyan} 8884615Snyan 8984615Snyanstatic int 9084615Snyanrandom_ivy_read(void *buf, int c) 9184615Snyan{ 9284615Snyan long *b; 9384615Snyan int count; 9484615Snyan 9584615Snyan KASSERT(c % sizeof(long) == 0, ("partial read %d", c)); 9684615Snyan for (b = buf, count = c; count > 0; count -= sizeof(long), b++) { 9784615Snyan if (ivy_rng_store(b) == 0) 9884615Snyan break; 9984615Snyan } 10084615Snyan return (c - count); 10184615Snyan} 10284615Snyan 10384615Snyanstatic int 10484615Snyanrdrand_modevent(module_t mod, int type, void *unused) 10584615Snyan{ 10684615Snyan int error = 0; 10784615Snyan 10884615Snyan switch (type) { 10984615Snyan case MOD_LOAD: 11084615Snyan if (cpu_feature2 & CPUID2_RDRAND) 11184615Snyan live_entropy_source_register(&random_ivy); 11284615Snyan else 11384615Snyan#ifndef KLD_MODULE 11484615Snyan if (bootverbose) 11584615Snyan#endif 11684615Snyan printf("%s: RDRAND is not present\n", 11784615Snyan random_ivy.ident); 11884615Snyan break; 11984615Snyan 12084615Snyan case MOD_UNLOAD: 12184615Snyan if (cpu_feature2 & CPUID2_RDRAND) 12284615Snyan live_entropy_source_deregister(&random_ivy); 12384615Snyan break; 12484615Snyan 12584615Snyan case MOD_SHUTDOWN: 12684615Snyan break; 12784615Snyan 12884615Snyan default: 12984615Snyan error = EOPNOTSUPP; 13084615Snyan break; 13184615Snyan 13284615Snyan } 13384615Snyan 13484615Snyan return (error); 13584615Snyan} 13684615Snyan 13784615SnyanLIVE_ENTROPY_SRC_MODULE(random_rdrand, rdrand_modevent, 1); 13884615Snyan