1128059Smarkm/*-
2256381Smarkm * Copyright (c) 2013 Mark R V Murray
3128059Smarkm * All rights reserved.
4128059Smarkm *
5128059Smarkm * Redistribution and use in source and binary forms, with or without
6128059Smarkm * modification, are permitted provided that the following conditions
7128059Smarkm * are met:
8128059Smarkm * 1. Redistributions of source code must retain the above copyright
9128059Smarkm *    notice, this list of conditions and the following disclaimer
10128059Smarkm *    in this position and unchanged.
11128059Smarkm * 2. Redistributions in binary form must reproduce the above copyright
12128059Smarkm *    notice, this list of conditions and the following disclaimer in the
13128059Smarkm *    documentation and/or other materials provided with the distribution.
14128059Smarkm *
15128059Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16128059Smarkm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17128059Smarkm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18128059Smarkm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19128059Smarkm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20128059Smarkm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21128059Smarkm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22128059Smarkm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23128059Smarkm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24128059Smarkm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25128059Smarkm *
26128059Smarkm */
27128059Smarkm
28128059Smarkm#include <sys/cdefs.h>
29128059Smarkm__FBSDID("$FreeBSD$");
30128059Smarkm
31128368Smarkm#include <sys/param.h>
32256381Smarkm#include <sys/kernel.h>
33128059Smarkm#include <sys/lock.h>
34256381Smarkm#include <sys/malloc.h>
35254147Sobrien#include <sys/module.h>
36256381Smarkm#include <sys/random.h>
37128059Smarkm#include <sys/selinfo.h>
38128368Smarkm#include <sys/systm.h>
39128059Smarkm
40256381Smarkm#include <machine/segments.h>
41208834Skib#include <machine/pcb.h>
42254147Sobrien#include <machine/md_var.h>
43254147Sobrien#include <machine/specialreg.h>
44208834Skib
45256381Smarkm#include <dev/random/randomdev.h>
46256381Smarkm#include <dev/random/randomdev_soft.h>
47256381Smarkm#include <dev/random/random_harvestq.h>
48256381Smarkm#include <dev/random/live_entropy_sources.h>
49254147Sobrien#include <dev/random/random_adaptors.h>
50128059Smarkm
51128368Smarkmstatic void random_nehemiah_init(void);
52153575Spsstatic void random_nehemiah_deinit(void);
53128059Smarkmstatic int random_nehemiah_read(void *, int);
54128059Smarkm
55256381Smarkmstatic struct random_hardware_source random_nehemiah = {
56256381Smarkm	.ident = "Hardware, VIA Nehemiah Padlock RNG",
57256381Smarkm	.source = RANDOM_PURE_NEHEMIAH,
58256381Smarkm	.read = random_nehemiah_read
59128059Smarkm};
60128059Smarkm
61256381Smarkm/* TODO: now that the Davies-Meyer hash is gone and we only use
62256381Smarkm * the 'xstore' instruction, do we still need to preserve the
63256381Smarkm * FPU state with fpu_kern_(enter|leave)() ?
64128368Smarkm */
65230426Skibstatic struct fpu_kern_ctx *fpu_ctx_save;
66208834Skib
67256381Smarkm/* This H/W source never stores more than 8 bytes in one go */
68128059Smarkm/* ARGSUSED */
69128368Smarkmstatic __inline size_t
70128368SmarkmVIA_RNG_store(void *buf)
71128368Smarkm{
72128368Smarkm	uint32_t retval = 0;
73128368Smarkm	uint32_t rate = 0;
74128368Smarkm
75256381Smarkm#ifdef __GNUCLIKE_ASM
76128368Smarkm	__asm __volatile(
77256381Smarkm		"movl	$0,%%edx\n\t"
78256381Smarkm		".byte	0x0f, 0xa7, 0xc0" /* xstore */
79128368Smarkm			: "=a" (retval), "+d" (rate), "+D" (buf)
80128368Smarkm			:
81128368Smarkm			: "memory"
82128368Smarkm	);
83256381Smarkm#endif
84128368Smarkm	if (rate == 0)
85128368Smarkm		return (retval&0x1f);
86128368Smarkm	return (0);
87128368Smarkm}
88128368Smarkm
89128368Smarkmstatic void
90128368Smarkmrandom_nehemiah_init(void)
91128368Smarkm{
92192774Smarkm
93230426Skib	fpu_ctx_save = fpu_kern_alloc_ctx(FPU_KERN_NORMAL);
94128368Smarkm}
95128368Smarkm
96256381Smarkmstatic void
97153575Spsrandom_nehemiah_deinit(void)
98153575Sps{
99230426Skib
100230426Skib	fpu_kern_free_ctx(fpu_ctx_save);
101153575Sps}
102153575Sps
103128059Smarkmstatic int
104128059Smarkmrandom_nehemiah_read(void *buf, int c)
105128059Smarkm{
106256381Smarkm	uint8_t *b;
107128368Smarkm	size_t count, ret;
108256381Smarkm	uint64_t tmp;
109128059Smarkm
110256381Smarkm	if ((fpu_kern_enter(curthread, fpu_ctx_save, FPU_KERN_NORMAL) == 0)) {
111256381Smarkm		b = buf;
112256381Smarkm		for (count = c; count > 0; count -= ret) {
113256381Smarkm			ret = MIN(VIA_RNG_store(&tmp), count);
114256381Smarkm			memcpy(b, &tmp, ret);
115256381Smarkm			b += ret;
116256381Smarkm		}
117256381Smarkm		fpu_kern_leave(curthread, fpu_ctx_save);
118208834Skib	}
119256381Smarkm	else
120256381Smarkm		c = 0;
121153575Sps
122128059Smarkm	return (c);
123128059Smarkm}
124240135Skib
125254147Sobrienstatic int
126254147Sobriennehemiah_modevent(module_t mod, int type, void *unused)
127254147Sobrien{
128256381Smarkm	int error = 0;
129254147Sobrien
130254147Sobrien	switch (type) {
131254147Sobrien	case MOD_LOAD:
132254147Sobrien		if (via_feature_rng & VIA_HAS_RNG) {
133256381Smarkm			live_entropy_source_register(&random_nehemiah);
134256381Smarkm			random_nehemiah_init();
135256381Smarkm		} else
136254147Sobrien#ifndef KLD_MODULE
137254147Sobrien			if (bootverbose)
138240135Skib#endif
139256381Smarkm				printf("%s: VIA Padlock RNG not present\n",
140254147Sobrien				    random_nehemiah.ident);
141256381Smarkm		break;
142256381Smarkm
143256381Smarkm	case MOD_UNLOAD:
144256381Smarkm		if (via_feature_rng & VIA_HAS_RNG)
145256381Smarkm			random_nehemiah_deinit();
146256381Smarkm			live_entropy_source_deregister(&random_nehemiah);
147256381Smarkm		break;
148256381Smarkm
149256381Smarkm	case MOD_SHUTDOWN:
150256381Smarkm		break;
151256381Smarkm
152256381Smarkm	default:
153256381Smarkm		error = EOPNOTSUPP;
154256381Smarkm		break;
155256381Smarkm
156254147Sobrien	}
157254147Sobrien
158256381Smarkm	return (error);
159254147Sobrien}
160254147Sobrien
161256381SmarkmLIVE_ENTROPY_SRC_MODULE(nehemiah, nehemiah_modevent, 1);
162