diff options
Diffstat (limited to 'drivers/char/tpm')
-rw-r--r-- | drivers/char/tpm/tpm_bios.c | 556 |
1 files changed, 556 insertions, 0 deletions
diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c new file mode 100644 index 00000000000..0636520fa9b --- /dev/null +++ b/drivers/char/tpm/tpm_bios.c | |||
@@ -0,0 +1,556 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005 IBM Corporation | ||
3 | * | ||
4 | * Authors: | ||
5 | * Seiji Munetoh <munetoh@jp.ibm.com> | ||
6 | * Stefan Berger <stefanb@us.ibm.com> | ||
7 | * Reiner Sailer <sailer@watson.ibm.com> | ||
8 | * Kylene Hall <kjhall@us.ibm.com> | ||
9 | * | ||
10 | * Maintained by: <tpmdd-devel@lists.sourceforge.net> | ||
11 | * | ||
12 | * Access to the eventlog extended by the TCG BIOS of PC platform | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * as published by the Free Software Foundation; either version | ||
17 | * 2 of the License, or (at your option) any later version. | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <linux/seq_file.h> | ||
22 | #include <linux/fs.h> | ||
23 | #include <linux/security.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <acpi/acpi.h> | ||
27 | #include "tpm.h" | ||
28 | |||
29 | #define TCG_EVENT_NAME_LEN_MAX 255 | ||
30 | #define MAX_TEXT_EVENT 1000 /* Max event string length */ | ||
31 | #define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ | ||
32 | |||
33 | enum bios_platform_class { | ||
34 | BIOS_CLIENT = 0x00, | ||
35 | BIOS_SERVER = 0x01, | ||
36 | }; | ||
37 | |||
38 | struct tpm_bios_log { | ||
39 | void *bios_event_log; | ||
40 | void *bios_event_log_end; | ||
41 | }; | ||
42 | |||
43 | struct acpi_tcpa { | ||
44 | struct acpi_table_header hdr; | ||
45 | u16 platform_class; | ||
46 | union { | ||
47 | struct client_hdr { | ||
48 | u32 log_max_len __attribute__ ((packed)); | ||
49 | u64 log_start_addr __attribute__ ((packed)); | ||
50 | } client; | ||
51 | struct server_hdr { | ||
52 | u16 reserved; | ||
53 | u64 log_max_len __attribute__ ((packed)); | ||
54 | u64 log_start_addr __attribute__ ((packed)); | ||
55 | } server; | ||
56 | }; | ||
57 | }; | ||
58 | |||
59 | struct tcpa_event { | ||
60 | u32 pcr_index; | ||
61 | u32 event_type; | ||
62 | u8 pcr_value[20]; /* SHA1 */ | ||
63 | u32 event_size; | ||
64 | u8 event_data[0]; | ||
65 | }; | ||
66 | |||
67 | enum tcpa_event_types { | ||
68 | PREBOOT = 0, | ||
69 | POST_CODE, | ||
70 | UNUSED, | ||
71 | NO_ACTION, | ||
72 | SEPARATOR, | ||
73 | ACTION, | ||
74 | EVENT_TAG, | ||
75 | SCRTM_CONTENTS, | ||
76 | SCRTM_VERSION, | ||
77 | CPU_MICROCODE, | ||
78 | PLATFORM_CONFIG_FLAGS, | ||
79 | TABLE_OF_DEVICES, | ||
80 | COMPACT_HASH, | ||
81 | IPL, | ||
82 | IPL_PARTITION_DATA, | ||
83 | NONHOST_CODE, | ||
84 | NONHOST_CONFIG, | ||
85 | NONHOST_INFO, | ||
86 | }; | ||
87 | |||
88 | static const char* tcpa_event_type_strings[] = { | ||
89 | "PREBOOT", | ||
90 | "POST CODE", | ||
91 | "", | ||
92 | "NO ACTION", | ||
93 | "SEPARATOR", | ||
94 | "ACTION", | ||
95 | "EVENT TAG", | ||
96 | "S-CRTM Contents", | ||
97 | "S-CRTM Version", | ||
98 | "CPU Microcode", | ||
99 | "Platform Config Flags", | ||
100 | "Table of Devices", | ||
101 | "Compact Hash", | ||
102 | "IPL", | ||
103 | "IPL Partition Data", | ||
104 | "Non-Host Code", | ||
105 | "Non-Host Config", | ||
106 | "Non-Host Info" | ||
107 | }; | ||
108 | |||
109 | struct tcpa_pc_event { | ||
110 | u32 event_id; | ||
111 | u32 event_size; | ||
112 | u8 event_data[0]; | ||
113 | }; | ||
114 | |||
115 | enum tcpa_pc_event_ids { | ||
116 | SMBIOS = 1, | ||
117 | BIS_CERT, | ||
118 | POST_BIOS_ROM, | ||
119 | ESCD, | ||
120 | CMOS, | ||
121 | NVRAM, | ||
122 | OPTION_ROM_EXEC, | ||
123 | OPTION_ROM_CONFIG, | ||
124 | OPTION_ROM_MICROCODE = 10, | ||
125 | S_CRTM_VERSION, | ||
126 | S_CRTM_CONTENTS, | ||
127 | POST_CONTENTS, | ||
128 | HOST_TABLE_OF_DEVICES, | ||
129 | }; | ||
130 | |||
131 | static const char* tcpa_pc_event_id_strings[] = { | ||
132 | "", | ||
133 | "SMBIOS", | ||
134 | "BIS Certificate", | ||
135 | "POST BIOS ", | ||
136 | "ESCD ", | ||
137 | "CMOS", | ||
138 | "NVRAM", | ||
139 | "Option ROM", | ||
140 | "Option ROM config", | ||
141 | "", | ||
142 | "Option ROM microcode ", | ||
143 | "S-CRTM Version", | ||
144 | "S-CRTM Contents ", | ||
145 | "POST Contents ", | ||
146 | "Table of Devices", | ||
147 | }; | ||
148 | |||
149 | /* returns pointer to start of pos. entry of tcg log */ | ||
150 | static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) | ||
151 | { | ||
152 | loff_t i; | ||
153 | struct tpm_bios_log *log = m->private; | ||
154 | void *addr = log->bios_event_log; | ||
155 | void *limit = log->bios_event_log_end; | ||
156 | struct tcpa_event *event; | ||
157 | |||
158 | /* read over *pos measurements */ | ||
159 | for (i = 0; i < *pos; i++) { | ||
160 | event = addr; | ||
161 | |||
162 | if ((addr + sizeof(struct tcpa_event)) < limit) { | ||
163 | if (event->event_type == 0 && event->event_size == 0) | ||
164 | return NULL; | ||
165 | addr += sizeof(struct tcpa_event) + event->event_size; | ||
166 | } | ||
167 | } | ||
168 | |||
169 | /* now check if current entry is valid */ | ||
170 | if ((addr + sizeof(struct tcpa_event)) >= limit) | ||
171 | return NULL; | ||
172 | |||
173 | event = addr; | ||
174 | |||
175 | if ((event->event_type == 0 && event->event_size == 0) || | ||
176 | ((addr + sizeof(struct tcpa_event) + event->event_size) >= limit)) | ||
177 | return NULL; | ||
178 | |||
179 | return addr; | ||
180 | } | ||
181 | |||
182 | static void *tpm_bios_measurements_next(struct seq_file *m, void *v, | ||
183 | loff_t *pos) | ||
184 | { | ||
185 | struct tcpa_event *event = v; | ||
186 | struct tpm_bios_log *log = m->private; | ||
187 | void *limit = log->bios_event_log_end; | ||
188 | |||
189 | v += sizeof(struct tcpa_event) + event->event_size; | ||
190 | |||
191 | /* now check if current entry is valid */ | ||
192 | if ((v + sizeof(struct tcpa_event)) >= limit) | ||
193 | return NULL; | ||
194 | |||
195 | event = v; | ||
196 | |||
197 | if (event->event_type == 0 && event->event_size == 0) | ||
198 | return NULL; | ||
199 | |||
200 | if ((event->event_type == 0 && event->event_size == 0) || | ||
201 | ((v + sizeof(struct tcpa_event) + event->event_size) >= limit)) | ||
202 | return NULL; | ||
203 | |||
204 | (*pos)++; | ||
205 | return v; | ||
206 | } | ||
207 | |||
208 | static void tpm_bios_measurements_stop(struct seq_file *m, void *v) | ||
209 | { | ||
210 | } | ||
211 | |||
212 | static int get_event_name(char *dest, struct tcpa_event *event, | ||
213 | unsigned char * event_entry) | ||
214 | { | ||
215 | const char *name = ""; | ||
216 | /* 41 so there is room for 40 data and 1 nul */ | ||
217 | char data[41] = ""; | ||
218 | int i, n_len = 0, d_len = 0; | ||
219 | struct tcpa_pc_event *pc_event; | ||
220 | |||
221 | switch(event->event_type) { | ||
222 | case PREBOOT: | ||
223 | case POST_CODE: | ||
224 | case UNUSED: | ||
225 | case NO_ACTION: | ||
226 | case SCRTM_CONTENTS: | ||
227 | case SCRTM_VERSION: | ||
228 | case CPU_MICROCODE: | ||
229 | case PLATFORM_CONFIG_FLAGS: | ||
230 | case TABLE_OF_DEVICES: | ||
231 | case COMPACT_HASH: | ||
232 | case IPL: | ||
233 | case IPL_PARTITION_DATA: | ||
234 | case NONHOST_CODE: | ||
235 | case NONHOST_CONFIG: | ||
236 | case NONHOST_INFO: | ||
237 | name = tcpa_event_type_strings[event->event_type]; | ||
238 | n_len = strlen(name); | ||
239 | break; | ||
240 | case SEPARATOR: | ||
241 | case ACTION: | ||
242 | if (MAX_TEXT_EVENT > event->event_size) { | ||
243 | name = event_entry; | ||
244 | n_len = event->event_size; | ||
245 | } | ||
246 | break; | ||
247 | case EVENT_TAG: | ||
248 | pc_event = (struct tcpa_pc_event *)event_entry; | ||
249 | |||
250 | /* ToDo Row data -> Base64 */ | ||
251 | |||
252 | switch (pc_event->event_id) { | ||
253 | case SMBIOS: | ||
254 | case BIS_CERT: | ||
255 | case CMOS: | ||
256 | case NVRAM: | ||
257 | case OPTION_ROM_EXEC: | ||
258 | case OPTION_ROM_CONFIG: | ||
259 | case S_CRTM_VERSION: | ||
260 | name = tcpa_pc_event_id_strings[pc_event->event_id]; | ||
261 | n_len = strlen(name); | ||
262 | break; | ||
263 | /* hash data */ | ||
264 | case POST_BIOS_ROM: | ||
265 | case ESCD: | ||
266 | case OPTION_ROM_MICROCODE: | ||
267 | case S_CRTM_CONTENTS: | ||
268 | case POST_CONTENTS: | ||
269 | name = tcpa_pc_event_id_strings[pc_event->event_id]; | ||
270 | n_len = strlen(name); | ||
271 | for (i = 0; i < 20; i++) | ||
272 | d_len += sprintf(&data[2*i], "%02x", | ||
273 | pc_event->event_data[i]); | ||
274 | break; | ||
275 | default: | ||
276 | break; | ||
277 | } | ||
278 | default: | ||
279 | break; | ||
280 | } | ||
281 | |||
282 | return snprintf(dest, MAX_TEXT_EVENT, "[%.*s%.*s]", | ||
283 | n_len, name, d_len, data); | ||
284 | |||
285 | } | ||
286 | |||
287 | static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) | ||
288 | { | ||
289 | struct tcpa_event *event = v; | ||
290 | char *data = v; | ||
291 | int i; | ||
292 | |||
293 | for (i = 0; i < sizeof(struct tcpa_event) + event->event_size; i++) | ||
294 | seq_putc(m, data[i]); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static int tpm_bios_measurements_release(struct inode *inode, | ||
300 | struct file *file) | ||
301 | { | ||
302 | struct seq_file *seq = file->private_data; | ||
303 | struct tpm_bios_log *log = seq->private; | ||
304 | |||
305 | if (log) { | ||
306 | kfree(log->bios_event_log); | ||
307 | kfree(log); | ||
308 | } | ||
309 | |||
310 | return seq_release(inode, file); | ||
311 | } | ||
312 | |||
313 | static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) | ||
314 | { | ||
315 | int len = 0; | ||
316 | int i; | ||
317 | char *eventname; | ||
318 | struct tcpa_event *event = v; | ||
319 | unsigned char *event_entry = | ||
320 | (unsigned char *) (v + sizeof(struct tcpa_event)); | ||
321 | |||
322 | eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL); | ||
323 | if (!eventname) { | ||
324 | printk(KERN_ERR "%s: ERROR - No Memory for event name\n ", | ||
325 | __func__); | ||
326 | return -EFAULT; | ||
327 | } | ||
328 | |||
329 | seq_printf(m, "%2d ", event->pcr_index); | ||
330 | |||
331 | /* 2nd: SHA1 */ | ||
332 | for (i = 0; i < 20; i++) | ||
333 | seq_printf(m, "%02x", event->pcr_value[i]); | ||
334 | |||
335 | /* 3rd: event type identifier */ | ||
336 | seq_printf(m, " %02x", event->event_type); | ||
337 | |||
338 | len += get_event_name(eventname, event, event_entry); | ||
339 | |||
340 | /* 4th: eventname <= max + \'0' delimiter */ | ||
341 | seq_printf(m, " %s\n", eventname); | ||
342 | |||
343 | kfree(eventname); | ||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | static const struct seq_operations tpm_ascii_b_measurments_seqops = { | ||
348 | .start = tpm_bios_measurements_start, | ||
349 | .next = tpm_bios_measurements_next, | ||
350 | .stop = tpm_bios_measurements_stop, | ||
351 | .show = tpm_ascii_bios_measurements_show, | ||
352 | }; | ||
353 | |||
354 | static const struct seq_operations tpm_binary_b_measurments_seqops = { | ||
355 | .start = tpm_bios_measurements_start, | ||
356 | .next = tpm_bios_measurements_next, | ||
357 | .stop = tpm_bios_measurements_stop, | ||
358 | .show = tpm_binary_bios_measurements_show, | ||
359 | }; | ||
360 | |||
361 | /* read binary bios log */ | ||
362 | static int read_log(struct tpm_bios_log *log) | ||
363 | { | ||
364 | struct acpi_tcpa *buff; | ||
365 | acpi_status status; | ||
366 | struct acpi_table_header *virt; | ||
367 | u64 len, start; | ||
368 | |||
369 | if (log->bios_event_log != NULL) { | ||
370 | printk(KERN_ERR | ||
371 | "%s: ERROR - Eventlog already initialized\n", | ||
372 | __func__); | ||
373 | return -EFAULT; | ||
374 | } | ||
375 | |||
376 | /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ | ||
377 | status = acpi_get_table(ACPI_SIG_TCPA, 1, | ||
378 | (struct acpi_table_header **)&buff); | ||
379 | |||
380 | if (ACPI_FAILURE(status)) { | ||
381 | printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n", | ||
382 | __func__); | ||
383 | return -EIO; | ||
384 | } | ||
385 | |||
386 | switch(buff->platform_class) { | ||
387 | case BIOS_SERVER: | ||
388 | len = buff->server.log_max_len; | ||
389 | start = buff->server.log_start_addr; | ||
390 | break; | ||
391 | case BIOS_CLIENT: | ||
392 | default: | ||
393 | len = buff->client.log_max_len; | ||
394 | start = buff->client.log_start_addr; | ||
395 | break; | ||
396 | } | ||
397 | if (!len) { | ||
398 | printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); | ||
399 | return -EIO; | ||
400 | } | ||
401 | |||
402 | /* malloc EventLog space */ | ||
403 | log->bios_event_log = kmalloc(len, GFP_KERNEL); | ||
404 | if (!log->bios_event_log) { | ||
405 | printk("%s: ERROR - Not enough Memory for BIOS measurements\n", | ||
406 | __func__); | ||
407 | return -ENOMEM; | ||
408 | } | ||
409 | |||
410 | log->bios_event_log_end = log->bios_event_log + len; | ||
411 | |||
412 | virt = acpi_os_map_memory(start, len); | ||
413 | |||
414 | memcpy(log->bios_event_log, virt, len); | ||
415 | |||
416 | acpi_os_unmap_memory(virt, len); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static int tpm_ascii_bios_measurements_open(struct inode *inode, | ||
421 | struct file *file) | ||
422 | { | ||
423 | int err; | ||
424 | struct tpm_bios_log *log; | ||
425 | struct seq_file *seq; | ||
426 | |||
427 | log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL); | ||
428 | if (!log) | ||
429 | return -ENOMEM; | ||
430 | |||
431 | if ((err = read_log(log))) | ||
432 | goto out_free; | ||
433 | |||
434 | /* now register seq file */ | ||
435 | err = seq_open(file, &tpm_ascii_b_measurments_seqops); | ||
436 | if (!err) { | ||
437 | seq = file->private_data; | ||
438 | seq->private = log; | ||
439 | } else { | ||
440 | goto out_free; | ||
441 | } | ||
442 | |||
443 | out: | ||
444 | return err; | ||
445 | out_free: | ||
446 | kfree(log->bios_event_log); | ||
447 | kfree(log); | ||
448 | goto out; | ||
449 | } | ||
450 | |||
451 | static const struct file_operations tpm_ascii_bios_measurements_ops = { | ||
452 | .open = tpm_ascii_bios_measurements_open, | ||
453 | .read = seq_read, | ||
454 | .llseek = seq_lseek, | ||
455 | .release = tpm_bios_measurements_release, | ||
456 | }; | ||
457 | |||
458 | static int tpm_binary_bios_measurements_open(struct inode *inode, | ||
459 | struct file *file) | ||
460 | { | ||
461 | int err; | ||
462 | struct tpm_bios_log *log; | ||
463 | struct seq_file *seq; | ||
464 | |||
465 | log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL); | ||
466 | if (!log) | ||
467 | return -ENOMEM; | ||
468 | |||
469 | if ((err = read_log(log))) | ||
470 | goto out_free; | ||
471 | |||
472 | /* now register seq file */ | ||
473 | err = seq_open(file, &tpm_binary_b_measurments_seqops); | ||
474 | if (!err) { | ||
475 | seq = file->private_data; | ||
476 | seq->private = log; | ||
477 | } else { | ||
478 | goto out_free; | ||
479 | } | ||
480 | |||
481 | out: | ||
482 | return err; | ||
483 | out_free: | ||
484 | kfree(log->bios_event_log); | ||
485 | kfree(log); | ||
486 | goto out; | ||
487 | } | ||
488 | |||
489 | static const struct file_operations tpm_binary_bios_measurements_ops = { | ||
490 | .open = tpm_binary_bios_measurements_open, | ||
491 | .read = seq_read, | ||
492 | .llseek = seq_lseek, | ||
493 | .release = tpm_bios_measurements_release, | ||
494 | }; | ||
495 | |||
496 | static int is_bad(void *p) | ||
497 | { | ||
498 | if (!p) | ||
499 | return 1; | ||
500 | if (IS_ERR(p) && (PTR_ERR(p) != -ENODEV)) | ||
501 | return 1; | ||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | struct dentry **tpm_bios_log_setup(char *name) | ||
506 | { | ||
507 | struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file; | ||
508 | |||
509 | tpm_dir = securityfs_create_dir(name, NULL); | ||
510 | if (is_bad(tpm_dir)) | ||
511 | goto out; | ||
512 | |||
513 | bin_file = | ||
514 | securityfs_create_file("binary_bios_measurements", | ||
515 | S_IRUSR | S_IRGRP, tpm_dir, NULL, | ||
516 | &tpm_binary_bios_measurements_ops); | ||
517 | if (is_bad(bin_file)) | ||
518 | goto out_tpm; | ||
519 | |||
520 | ascii_file = | ||
521 | securityfs_create_file("ascii_bios_measurements", | ||
522 | S_IRUSR | S_IRGRP, tpm_dir, NULL, | ||
523 | &tpm_ascii_bios_measurements_ops); | ||
524 | if (is_bad(ascii_file)) | ||
525 | goto out_bin; | ||
526 | |||
527 | ret = kmalloc(3 * sizeof(struct dentry *), GFP_KERNEL); | ||
528 | if (!ret) | ||
529 | goto out_ascii; | ||
530 | |||
531 | ret[0] = ascii_file; | ||
532 | ret[1] = bin_file; | ||
533 | ret[2] = tpm_dir; | ||
534 | |||
535 | return ret; | ||
536 | |||
537 | out_ascii: | ||
538 | securityfs_remove(ascii_file); | ||
539 | out_bin: | ||
540 | securityfs_remove(bin_file); | ||
541 | out_tpm: | ||
542 | securityfs_remove(tpm_dir); | ||
543 | out: | ||
544 | return NULL; | ||
545 | } | ||
546 | EXPORT_SYMBOL_GPL(tpm_bios_log_setup); | ||
547 | |||
548 | void tpm_bios_log_teardown(struct dentry **lst) | ||
549 | { | ||
550 | int i; | ||
551 | |||
552 | for (i = 0; i < 3; i++) | ||
553 | securityfs_remove(lst[i]); | ||
554 | } | ||
555 | EXPORT_SYMBOL_GPL(tpm_bios_log_teardown); | ||
556 | MODULE_LICENSE("GPL"); | ||