1 /* $NetBSD: fwhrng.c,v 1.2 2010/08/23 02:57:19 jakllsch Exp $ */ 2 3 /* 4 * Copyright (c) 2000 Michael Shalayeff 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 25 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 26 * THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * from OpenBSD: pchb.c,v 1.23 2000/10/23 20:07:30 deraadt Exp 29 */ 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: fwhrng.c,v 1.2 2010/08/23 02:57:19 jakllsch Exp $"); 33 34 #include "rnd.h" 35 36 #if NRND == 0 37 #error fwhrng requires rnd pseudo-device 38 #endif 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/device.h> 43 #include <sys/time.h> 44 #include <sys/rnd.h> 45 46 #include <machine/bus.h> 47 48 #include <arch/x86/pci/i82802reg.h> 49 50 struct fwhrng_softc { 51 device_t sc_dev; 52 53 bus_space_tag_t sc_st; 54 bus_space_handle_t sc_sh; 55 56 struct callout sc_rnd_ch; 57 rndsource_element_t sc_rnd_source; 58 59 int sc_rnd_i; 60 uint32_t sc_rnd_ax; 61 }; 62 63 static int fwhrng_match(device_t, cfdata_t, void *); 64 static void fwhrng_attach(device_t, device_t, void *); 65 static int fwhrng_detach(device_t, int); 66 67 static void fwhrng_callout(void *v); 68 69 #define FWHRNG_RETRIES 1000 70 #define FWHRNG_MIN_SAMPLES 10 71 72 CFATTACH_DECL_NEW(fwhrng, sizeof(struct fwhrng_softc), 73 fwhrng_match, fwhrng_attach, fwhrng_detach, NULL); 74 75 static int 76 fwhrng_match(device_t parent, cfdata_t match, void *aux) 77 { 78 bus_space_tag_t bst; 79 bus_space_handle_t bsh; 80 int ret; 81 uint8_t id0, id1, data0, data1; 82 83 ret = 0; 84 85 bst = x86_bus_space_mem; 86 87 /* read chip ID */ 88 if (bus_space_map(bst, I82802AB_MEMBASE, I82802AB_WINSIZE, 0, &bsh)) 89 return 0; 90 91 bus_space_write_1(bst, bsh, 0, 0xff); /* reset */ 92 data0 = bus_space_read_1(bst, bsh, 0); 93 data1 = bus_space_read_1(bst, bsh, 1); 94 bus_space_write_1(bst, bsh, 0, 0x90); /* enter read id */ 95 id0 = bus_space_read_1(bst, bsh, 0); 96 id1 = bus_space_read_1(bst, bsh, 1); 97 bus_space_write_1(bst, bsh, 0, 0xff); /* reset */ 98 99 bus_space_unmap(bst, bsh, I82802AB_WINSIZE); 100 101 aprint_debug_dev(parent, "fwh: data %02x,%02x, id %02x,%02x\n", 102 data0, data1, id0, id1); 103 104 /* unlikely to have these match if we actually read the ID */ 105 if ((id0 == data0) && (id1 == data1)) 106 return 0; 107 108 /* check for chips with RNG */ 109 if (!(id0 == I82802_MFG)) 110 return 0; 111 if (!((id1 == I82802AB_ID) || (id1 == I82802AC_ID))) 112 return 0; 113 114 /* check for RNG presence */ 115 if (bus_space_map(bst, I82802AC_REGBASE, I82802AC_WINSIZE, 0, &bsh)) 116 return 0; 117 data0 = bus_space_read_1(bst, bsh, I82802_RNG_HSR); 118 bus_space_unmap(bst, bsh, I82802AC_WINSIZE); 119 if ((data0 & I82802_RNG_HSR_PRESENT) == I82802_RNG_HSR_PRESENT) 120 return 1; 121 122 return 0; 123 } 124 125 static void 126 fwhrng_attach(device_t parent, device_t self, void *aux) 127 { 128 struct fwhrng_softc *sc; 129 int i, j, count_ff; 130 uint8_t reg8; 131 132 sc = device_private(self); 133 sc->sc_dev = self; 134 135 aprint_naive("\n"); 136 aprint_normal(": Intel Firmware Hub Random Number Generator\n"); 137 138 sc->sc_st = x86_bus_space_mem; 139 140 if (bus_space_map(sc->sc_st, I82802AC_REGBASE, I82802AC_WINSIZE, 0, 141 &sc->sc_sh) != 0) { 142 aprint_error_dev(self, "unable to map registers\n"); 143 return; 144 } 145 146 /* Enable the RNG. */ 147 reg8 = bus_space_read_1(sc->sc_st, sc->sc_sh, I82802_RNG_HSR); 148 bus_space_write_1(sc->sc_st, sc->sc_sh, I82802_RNG_HSR, 149 reg8 | I82802_RNG_HSR_ENABLE); 150 reg8 = bus_space_read_1(sc->sc_st, sc->sc_sh, I82802_RNG_HSR); 151 if ((reg8 & I82802_RNG_HSR_ENABLE) == 0) { 152 aprint_error_dev(self, "unable to enable\n"); 153 bus_space_unmap(sc->sc_st, sc->sc_sh, I82802AC_WINSIZE); 154 return; 155 } 156 157 /* Check to see if we can read data from the RNG. */ 158 count_ff = 0; 159 for (j = 0; j < FWHRNG_MIN_SAMPLES; ++j) { 160 for (i = 0; i < FWHRNG_RETRIES; i++) { 161 reg8 = bus_space_read_1(sc->sc_st, sc->sc_sh, 162 I82802_RNG_DSR); 163 if (!(reg8 & I82802_RNG_DSR_VALID)) { 164 delay(10); 165 continue; 166 } 167 reg8 = bus_space_read_1(sc->sc_st, sc->sc_sh, 168 I82802_RNG_DR); 169 break; 170 } 171 if (i == FWHRNG_RETRIES) { 172 bus_space_unmap(sc->sc_st, sc->sc_sh, I82802AC_WINSIZE); 173 aprint_verbose_dev(sc->sc_dev, 174 "timeout reading test samples, RNG disabled.\n"); 175 return; 176 } 177 if (reg8 == 0xff) 178 ++count_ff; 179 } 180 181 if (count_ff == FWHRNG_MIN_SAMPLES) { 182 /* Disable the RNG. */ 183 reg8 = bus_space_read_1(sc->sc_st, sc->sc_sh, I82802_RNG_HSR); 184 bus_space_write_1(sc->sc_st, sc->sc_sh, I82802_RNG_HSR, 185 reg8 & ~I82802_RNG_HSR_ENABLE); 186 bus_space_unmap(sc->sc_st, sc->sc_sh, I82802AC_WINSIZE); 187 aprint_error_dev(sc->sc_dev, 188 "returns constant 0xff stream, RNG disabled.\n"); 189 return; 190 } 191 192 /* 193 * Should test entropy source to ensure 194 * that it passes the Statistical Random 195 * Number Generator Tests in section 4.11.1, 196 * FIPS PUB 140-1. 197 * 198 * http://csrc.nist.gov/fips/fips1401.htm 199 */ 200 201 aprint_debug_dev(sc->sc_dev, "random number generator enabled\n"); 202 203 callout_init(&sc->sc_rnd_ch, 0); 204 /* FWH is polled for entropy, so no estimate is available. */ 205 rnd_attach_source(&sc->sc_rnd_source, device_xname(sc->sc_dev), 206 RND_TYPE_RNG, RND_FLAG_NO_ESTIMATE); 207 sc->sc_rnd_i = sizeof(sc->sc_rnd_ax); 208 fwhrng_callout(sc); 209 210 return; 211 } 212 213 static int 214 fwhrng_detach(device_t self, int flags) 215 { 216 struct fwhrng_softc *sc; 217 uint8_t reg8; 218 219 sc = device_private(self); 220 221 rnd_detach_source(&sc->sc_rnd_source); 222 223 callout_stop(&sc->sc_rnd_ch); 224 callout_destroy(&sc->sc_rnd_ch); 225 226 /* Disable the RNG. */ 227 reg8 = bus_space_read_1(sc->sc_st, sc->sc_sh, I82802_RNG_HSR); 228 bus_space_write_1(sc->sc_st, sc->sc_sh, I82802_RNG_HSR, 229 reg8 & ~I82802_RNG_HSR_ENABLE); 230 231 bus_space_unmap(sc->sc_st, sc->sc_sh, I82802AC_WINSIZE); 232 233 return 0; 234 } 235 236 static void 237 fwhrng_callout(void *v) 238 { 239 struct fwhrng_softc *sc = v; 240 241 if ((bus_space_read_1(sc->sc_st, sc->sc_sh, I82802_RNG_DSR) & 242 I82802_RNG_DSR_VALID) != 0) { 243 sc->sc_rnd_ax = (sc->sc_rnd_ax << NBBY) | 244 bus_space_read_1(sc->sc_st, sc->sc_sh, I82802_RNG_DR); 245 if (--sc->sc_rnd_i == 0) { 246 sc->sc_rnd_i = sizeof(sc->sc_rnd_ax); 247 rnd_add_data(&sc->sc_rnd_source, &sc->sc_rnd_ax, 248 sizeof(sc->sc_rnd_ax), 249 sizeof(sc->sc_rnd_ax) * NBBY); 250 } 251 } 252 callout_reset(&sc->sc_rnd_ch, 1, fwhrng_callout, sc); 253 } 254