diff options
Diffstat (limited to 'drivers/char/hw_random/intel-rng.c')
-rw-r--r-- | drivers/char/hw_random/intel-rng.c | 186 |
1 files changed, 177 insertions, 9 deletions
diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c index ccd7e7102234..8efbc9c0e545 100644 --- a/drivers/char/hw_random/intel-rng.c +++ b/drivers/char/hw_random/intel-rng.c | |||
@@ -50,6 +50,43 @@ | |||
50 | #define INTEL_RNG_ADDR_LEN 3 | 50 | #define INTEL_RNG_ADDR_LEN 3 |
51 | 51 | ||
52 | /* | 52 | /* |
53 | * LPC bridge PCI config space registers | ||
54 | */ | ||
55 | #define FWH_DEC_EN1_REG_OLD 0xe3 | ||
56 | #define FWH_DEC_EN1_REG_NEW 0xd9 /* high byte of 16-bit register */ | ||
57 | #define FWH_F8_EN_MASK 0x80 | ||
58 | |||
59 | #define BIOS_CNTL_REG_OLD 0x4e | ||
60 | #define BIOS_CNTL_REG_NEW 0xdc | ||
61 | #define BIOS_CNTL_WRITE_ENABLE_MASK 0x01 | ||
62 | #define BIOS_CNTL_LOCK_ENABLE_MASK 0x02 | ||
63 | |||
64 | /* | ||
65 | * Magic address at which Intel Firmware Hubs get accessed | ||
66 | */ | ||
67 | #define INTEL_FWH_ADDR 0xffff0000 | ||
68 | #define INTEL_FWH_ADDR_LEN 2 | ||
69 | |||
70 | /* | ||
71 | * Intel Firmware Hub command codes (write to any address inside the device) | ||
72 | */ | ||
73 | #define INTEL_FWH_RESET_CMD 0xff /* aka READ_ARRAY */ | ||
74 | #define INTEL_FWH_READ_ID_CMD 0x90 | ||
75 | |||
76 | /* | ||
77 | * Intel Firmware Hub Read ID command result addresses | ||
78 | */ | ||
79 | #define INTEL_FWH_MANUFACTURER_CODE_ADDRESS 0x000000 | ||
80 | #define INTEL_FWH_DEVICE_CODE_ADDRESS 0x000001 | ||
81 | |||
82 | /* | ||
83 | * Intel Firmware Hub Read ID command result values | ||
84 | */ | ||
85 | #define INTEL_FWH_MANUFACTURER_CODE 0x89 | ||
86 | #define INTEL_FWH_DEVICE_CODE_8M 0xac | ||
87 | #define INTEL_FWH_DEVICE_CODE_4M 0xad | ||
88 | |||
89 | /* | ||
53 | * Data for PCI driver interface | 90 | * Data for PCI driver interface |
54 | * | 91 | * |
55 | * This data only exists for exporting the supported | 92 | * This data only exists for exporting the supported |
@@ -58,12 +95,50 @@ | |||
58 | * want to register another driver on the same PCI id. | 95 | * want to register another driver on the same PCI id. |
59 | */ | 96 | */ |
60 | static const struct pci_device_id pci_tbl[] = { | 97 | static const struct pci_device_id pci_tbl[] = { |
61 | { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, | 98 | /* AA |
62 | { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, | 99 | { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ |
63 | { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, | 100 | { 0x8086, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* AA */ |
64 | { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, | 101 | /* AB |
65 | { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, | 102 | { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ |
66 | { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, | 103 | { 0x8086, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* AB */ |
104 | /* ?? | ||
105 | { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ | ||
106 | /* BAM, CAM, DBM, FBM, GxM | ||
107 | { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ | ||
108 | { 0x8086, 0x244c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* BAM */ | ||
109 | { 0x8086, 0x248c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CAM */ | ||
110 | { 0x8086, 0x24cc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* DBM */ | ||
111 | { 0x8086, 0x2641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* FBM */ | ||
112 | { 0x8086, 0x27b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* GxM */ | ||
113 | { 0x8086, 0x27bd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* GxM DH */ | ||
114 | /* BA, CA, DB, Ex, 6300, Fx, 631x/632x, Gx | ||
115 | { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ | ||
116 | { 0x8086, 0x2440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* BA */ | ||
117 | { 0x8086, 0x2480, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CA */ | ||
118 | { 0x8086, 0x24c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* DB */ | ||
119 | { 0x8086, 0x24d0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ex */ | ||
120 | { 0x8086, 0x25a1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 6300 */ | ||
121 | { 0x8086, 0x2640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Fx */ | ||
122 | { 0x8086, 0x2670, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
123 | { 0x8086, 0x2671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
124 | { 0x8086, 0x2672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
125 | { 0x8086, 0x2673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
126 | { 0x8086, 0x2674, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
127 | { 0x8086, 0x2675, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
128 | { 0x8086, 0x2676, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
129 | { 0x8086, 0x2677, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
130 | { 0x8086, 0x2678, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
131 | { 0x8086, 0x2679, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
132 | { 0x8086, 0x267a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
133 | { 0x8086, 0x267b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
134 | { 0x8086, 0x267c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
135 | { 0x8086, 0x267d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
136 | { 0x8086, 0x267e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
137 | { 0x8086, 0x267f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */ | ||
138 | { 0x8086, 0x27b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Gx */ | ||
139 | /* E | ||
140 | { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */ | ||
141 | { 0x8086, 0x2450, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* E */ | ||
67 | { 0, }, /* terminate list */ | 142 | { 0, }, /* terminate list */ |
68 | }; | 143 | }; |
69 | MODULE_DEVICE_TABLE(pci, pci_tbl); | 144 | MODULE_DEVICE_TABLE(pci, pci_tbl); |
@@ -138,22 +213,115 @@ static struct hwrng intel_rng = { | |||
138 | }; | 213 | }; |
139 | 214 | ||
140 | 215 | ||
216 | #ifdef CONFIG_SMP | ||
217 | static char __initdata waitflag; | ||
218 | |||
219 | static void __init intel_init_wait(void *unused) | ||
220 | { | ||
221 | while (waitflag) | ||
222 | cpu_relax(); | ||
223 | } | ||
224 | #endif | ||
225 | |||
141 | static int __init mod_init(void) | 226 | static int __init mod_init(void) |
142 | { | 227 | { |
143 | int err = -ENODEV; | 228 | int err = -ENODEV; |
229 | unsigned i; | ||
230 | struct pci_dev *dev = NULL; | ||
144 | void __iomem *mem; | 231 | void __iomem *mem; |
145 | u8 hw_status; | 232 | unsigned long flags; |
233 | u8 bios_cntl_off, fwh_dec_en1_off; | ||
234 | u8 bios_cntl_val = 0xff, fwh_dec_en1_val = 0xff; | ||
235 | u8 hw_status, mfc, dvc; | ||
146 | 236 | ||
147 | if (!pci_dev_present(pci_tbl)) | 237 | for (i = 0; !dev && pci_tbl[i].vendor; ++i) |
238 | dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device, NULL); | ||
239 | |||
240 | if (!dev) | ||
148 | goto out; /* Device not found. */ | 241 | goto out; /* Device not found. */ |
149 | 242 | ||
243 | /* Check for Intel 82802 */ | ||
244 | if (dev->device < 0x2640) { | ||
245 | fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD; | ||
246 | bios_cntl_off = BIOS_CNTL_REG_OLD; | ||
247 | } else { | ||
248 | fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW; | ||
249 | bios_cntl_off = BIOS_CNTL_REG_NEW; | ||
250 | } | ||
251 | |||
252 | pci_read_config_byte(dev, fwh_dec_en1_off, &fwh_dec_en1_val); | ||
253 | pci_read_config_byte(dev, bios_cntl_off, &bios_cntl_val); | ||
254 | |||
255 | mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN); | ||
256 | if (mem == NULL) { | ||
257 | pci_dev_put(dev); | ||
258 | err = -EBUSY; | ||
259 | goto out; | ||
260 | } | ||
261 | |||
262 | /* | ||
263 | * Since the BIOS code/data is going to disappear from its normal | ||
264 | * location with the Read ID command, all activity on the system | ||
265 | * must be stopped until the state is back to normal. | ||
266 | */ | ||
267 | #ifdef CONFIG_SMP | ||
268 | set_mb(waitflag, 1); | ||
269 | if (smp_call_function(intel_init_wait, NULL, 1, 0) != 0) { | ||
270 | set_mb(waitflag, 0); | ||
271 | pci_dev_put(dev); | ||
272 | printk(KERN_ERR PFX "cannot run on all processors\n"); | ||
273 | err = -EAGAIN; | ||
274 | goto err_unmap; | ||
275 | } | ||
276 | #endif | ||
277 | local_irq_save(flags); | ||
278 | |||
279 | if (!(fwh_dec_en1_val & FWH_F8_EN_MASK)) | ||
280 | pci_write_config_byte(dev, | ||
281 | fwh_dec_en1_off, | ||
282 | fwh_dec_en1_val | FWH_F8_EN_MASK); | ||
283 | if (!(bios_cntl_val & | ||
284 | (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))) | ||
285 | pci_write_config_byte(dev, | ||
286 | bios_cntl_off, | ||
287 | bios_cntl_val | BIOS_CNTL_WRITE_ENABLE_MASK); | ||
288 | |||
289 | writeb(INTEL_FWH_RESET_CMD, mem); | ||
290 | writeb(INTEL_FWH_READ_ID_CMD, mem); | ||
291 | mfc = readb(mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS); | ||
292 | dvc = readb(mem + INTEL_FWH_DEVICE_CODE_ADDRESS); | ||
293 | writeb(INTEL_FWH_RESET_CMD, mem); | ||
294 | |||
295 | if (!(bios_cntl_val & | ||
296 | (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK))) | ||
297 | pci_write_config_byte(dev, bios_cntl_off, bios_cntl_val); | ||
298 | if (!(fwh_dec_en1_val & FWH_F8_EN_MASK)) | ||
299 | pci_write_config_byte(dev, fwh_dec_en1_off, fwh_dec_en1_val); | ||
300 | |||
301 | local_irq_restore(flags); | ||
302 | #ifdef CONFIG_SMP | ||
303 | /* Tell other CPUs to resume. */ | ||
304 | set_mb(waitflag, 0); | ||
305 | #endif | ||
306 | |||
307 | iounmap(mem); | ||
308 | pci_dev_put(dev); | ||
309 | |||
310 | if (mfc != INTEL_FWH_MANUFACTURER_CODE || | ||
311 | (dvc != INTEL_FWH_DEVICE_CODE_8M && | ||
312 | dvc != INTEL_FWH_DEVICE_CODE_4M)) { | ||
313 | printk(KERN_ERR PFX "FWH not detected\n"); | ||
314 | err = -ENODEV; | ||
315 | goto out; | ||
316 | } | ||
317 | |||
150 | err = -ENOMEM; | 318 | err = -ENOMEM; |
151 | mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); | 319 | mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); |
152 | if (!mem) | 320 | if (!mem) |
153 | goto out; | 321 | goto out; |
154 | intel_rng.priv = (unsigned long)mem; | 322 | intel_rng.priv = (unsigned long)mem; |
155 | 323 | ||
156 | /* Check for Intel 82802 */ | 324 | /* Check for Random Number Generator */ |
157 | err = -ENODEV; | 325 | err = -ENODEV; |
158 | hw_status = hwstatus_get(mem); | 326 | hw_status = hwstatus_get(mem); |
159 | if ((hw_status & INTEL_RNG_PRESENT) == 0) | 327 | if ((hw_status & INTEL_RNG_PRESENT) == 0) |