From 7163a993840f0906d4ce1e3f193575c99dac21e1 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Thu, 3 Jan 2013 14:19:09 -0500 Subject: ima: re-initialize IMA policy LSM info Although the IMA policy does not change, the LSM policy can be reloaded, leaving the IMA LSM based rules referring to the old, stale LSM policy. This patch updates the IMA LSM based rules to reflect the reloaded LSM policy. Reported-by: Sven Vermeulen tested-by: Sven Vermeulen Signed-off-by: Mimi Zohar Cc: Eric Paris Cc: Casey Schaufler --- security/integrity/ima/ima_policy.c | 68 +++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index af7d182d5a46..70f888de880d 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -49,6 +49,7 @@ struct ima_rule_entry { kuid_t fowner; struct { void *rule; /* LSM file metadata specific */ + void *args_p; /* audit value */ int type; /* audit type */ } lsm[MAX_LSM_RULES]; }; @@ -119,6 +120,35 @@ static int __init default_appraise_policy_setup(char *str) } __setup("ima_appraise_tcb", default_appraise_policy_setup); +/* + * Although the IMA policy does not change, the LSM policy can be + * reloaded, leaving the IMA LSM based rules referring to the old, + * stale LSM policy. + * + * Update the IMA LSM based rules to reflect the reloaded LSM policy. + * We assume the rules still exist; and BUG_ON() if they don't. + */ +static void ima_lsm_update_rules(void) +{ + struct ima_rule_entry *entry, *tmp; + int result; + int i; + + mutex_lock(&ima_rules_mutex); + list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) { + for (i = 0; i < MAX_LSM_RULES; i++) { + if (!entry->lsm[i].rule) + continue; + result = security_filter_rule_init(entry->lsm[i].type, + Audit_equal, + entry->lsm[i].args_p, + &entry->lsm[i].rule); + BUG_ON(!entry->lsm[i].rule); + } + } + mutex_unlock(&ima_rules_mutex); +} + /** * ima_match_rules - determine whether an inode matches the measure rule. * @rule: a pointer to a rule @@ -149,10 +179,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule, for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; u32 osid, sid; + int retried = 0; if (!rule->lsm[i].rule) continue; - +retry: switch (i) { case LSM_OBJ_USER: case LSM_OBJ_ROLE: @@ -176,6 +207,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule, default: break; } + if ((rc < 0) && (!retried)) { + retried = 1; + ima_lsm_update_rules(); + goto retry; + } if (!rc) return false; } @@ -306,19 +342,27 @@ static match_table_t policy_tokens = { }; static int ima_lsm_rule_init(struct ima_rule_entry *entry, - char *args, int lsm_rule, int audit_type) + substring_t *args, int lsm_rule, int audit_type) { int result; if (entry->lsm[lsm_rule].rule) return -EINVAL; + entry->lsm[lsm_rule].args_p = match_strdup(args); + if (!entry->lsm[lsm_rule].args_p) + return -ENOMEM; + entry->lsm[lsm_rule].type = audit_type; result = security_filter_rule_init(entry->lsm[lsm_rule].type, - Audit_equal, args, + Audit_equal, + entry->lsm[lsm_rule].args_p, &entry->lsm[lsm_rule].rule); - if (!entry->lsm[lsm_rule].rule) + if (!entry->lsm[lsm_rule].rule) { + kfree(entry->lsm[lsm_rule].args_p); return -EINVAL; + } + return result; } @@ -481,37 +525,37 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) break; case Opt_obj_user: ima_log_string(ab, "obj_user", args[0].from); - result = ima_lsm_rule_init(entry, args[0].from, + result = ima_lsm_rule_init(entry, args, LSM_OBJ_USER, AUDIT_OBJ_USER); break; case Opt_obj_role: ima_log_string(ab, "obj_role", args[0].from); - result = ima_lsm_rule_init(entry, args[0].from, + result = ima_lsm_rule_init(entry, args, LSM_OBJ_ROLE, AUDIT_OBJ_ROLE); break; case Opt_obj_type: ima_log_string(ab, "obj_type", args[0].from); - result = ima_lsm_rule_init(entry, args[0].from, + result = ima_lsm_rule_init(entry, args, LSM_OBJ_TYPE, AUDIT_OBJ_TYPE); break; case Opt_subj_user: ima_log_string(ab, "subj_user", args[0].from); - result = ima_lsm_rule_init(entry, args[0].from, + result = ima_lsm_rule_init(entry, args, LSM_SUBJ_USER, AUDIT_SUBJ_USER); break; case Opt_subj_role: ima_log_string(ab, "subj_role", args[0].from); - result = ima_lsm_rule_init(entry, args[0].from, + result = ima_lsm_rule_init(entry, args, LSM_SUBJ_ROLE, AUDIT_SUBJ_ROLE); break; case Opt_subj_type: ima_log_string(ab, "subj_type", args[0].from); - result = ima_lsm_rule_init(entry, args[0].from, + result = ima_lsm_rule_init(entry, args, LSM_SUBJ_TYPE, AUDIT_SUBJ_TYPE); break; @@ -589,9 +633,13 @@ ssize_t ima_parse_add_rule(char *rule) void ima_delete_rules(void) { struct ima_rule_entry *entry, *tmp; + int i; mutex_lock(&ima_rules_mutex); list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) { + for (i = 0; i < MAX_LSM_RULES; i++) + kfree(entry->lsm[i].args_p); + list_del(&entry->list); kfree(entry); } -- cgit v1.2.1 From e90805656d4683f84d360276102ae63adc777a38 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 3 Sep 2012 17:11:56 +0300 Subject: evm: remove unused cleanup functions EVM cannot be built as a kernel module. Remove the unncessary __exit functions. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/evm/evm.h | 1 - security/integrity/evm/evm_main.c | 9 --------- security/integrity/evm/evm_secfs.c | 6 ------ 3 files changed, 16 deletions(-) diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index c885247ebcf7..3eb30c6db419 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -45,6 +45,5 @@ extern int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name, extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr, char *hmac_val); extern int evm_init_secfs(void); -extern void evm_cleanup_secfs(void); #endif diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index eb5484504f50..a78a5e21ef70 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -427,15 +427,6 @@ err: return error; } -static void __exit cleanup_evm(void) -{ - evm_cleanup_secfs(); - if (hmac_tfm) - crypto_free_shash(hmac_tfm); - if (hash_tfm) - crypto_free_shash(hash_tfm); -} - /* * evm_display_config - list the EVM protected security extended attributes */ diff --git a/security/integrity/evm/evm_secfs.c b/security/integrity/evm/evm_secfs.c index ac7629950578..30f670ad6ac3 100644 --- a/security/integrity/evm/evm_secfs.c +++ b/security/integrity/evm/evm_secfs.c @@ -100,9 +100,3 @@ int __init evm_init_secfs(void) error = -EFAULT; return error; } - -void __exit evm_cleanup_secfs(void) -{ - if (evm_init_tpm) - securityfs_remove(evm_init_tpm); -} -- cgit v1.2.1 From def3e8b9ee23cb69036910e48ec4e3eff40e04cb Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 20 Sep 2012 22:38:53 +0300 Subject: ima: set appraise status in fix mode only when xattr is fixed When a file system is mounted read-only, setting the xattr value in fix mode fails with an error code -EROFS. The xattr should be fixed after the file system is remounted read-write. This patch verifies that the set xattr succeeds, before setting the appraise status value to INTEGRITY_PASS. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index bdc8ba1d1d27..b240c58403e2 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -42,12 +42,13 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func) return ima_match_policy(inode, func, mask, IMA_APPRAISE); } -static void ima_fix_xattr(struct dentry *dentry, +static int ima_fix_xattr(struct dentry *dentry, struct integrity_iint_cache *iint) { iint->ima_xattr.type = IMA_XATTR_DIGEST; - __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr, - sizeof iint->ima_xattr, 0); + return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, + (u8 *)&iint->ima_xattr, + sizeof(iint->ima_xattr), 0); } /* @@ -141,8 +142,8 @@ out: if ((ima_appraise & IMA_APPRAISE_FIX) && (!xattr_value || xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { - ima_fix_xattr(dentry, iint); - status = INTEGRITY_PASS; + if (!ima_fix_xattr(dentry, iint)) + status = INTEGRITY_PASS; } integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, op, cause, rc, 0); -- cgit v1.2.1 From 750943a30714b7e9a5a2b0e08eeef7a808b5a869 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 27 Sep 2012 15:57:10 +0300 Subject: ima: remove enforce checking duplication Based on the IMA appraisal policy, files are appraised. For those files appraised, the IMA hooks return the integrity appraisal result, assuming IMA-appraisal is in enforcing mode. This patch combines both of these criteria (in policy and enforcing file integrity), removing the checking duplication. Changelog v1: - Update hook comments Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_main.c | 52 +++++++++++++++------------------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 45de18e9a6f2..1cd4eb2c3b90 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -208,7 +208,9 @@ static int process_measurement(struct file *file, const unsigned char *filename, kfree(pathbuf); out: mutex_unlock(&inode->i_mutex); - return (rc && must_appraise) ? -EACCES : 0; + if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) + return -EACCES; + return 0; } /** @@ -219,19 +221,15 @@ out: * Measure files being mmapped executable based on the ima_must_measure() * policy decision. * - * Return 0 on success, an error code on failure. - * (Based on the results of appraise_measurement().) + * On success return 0. On integrity appraisal error, assuming the file + * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. */ int ima_file_mmap(struct file *file, unsigned long prot) { - int rc = 0; - - if (!file) - return 0; - if (prot & PROT_EXEC) - rc = process_measurement(file, file->f_dentry->d_name.name, - MAY_EXEC, FILE_MMAP); - return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; + if (file && (prot & PROT_EXEC)) + return process_measurement(file, file->f_dentry->d_name.name, + MAY_EXEC, FILE_MMAP); + return 0; } /** @@ -244,18 +242,15 @@ int ima_file_mmap(struct file *file, unsigned long prot) * So we can be certain that what we verify and measure here is actually * what is being executed. * - * Return 0 on success, an error code on failure. - * (Based on the results of appraise_measurement().) + * On success return 0. On integrity appraisal error, assuming the file + * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. */ int ima_bprm_check(struct linux_binprm *bprm) { - int rc; - - rc = process_measurement(bprm->file, + return process_measurement(bprm->file, (strcmp(bprm->filename, bprm->interp) == 0) ? bprm->filename : bprm->interp, MAY_EXEC, BPRM_CHECK); - return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; } /** @@ -265,18 +260,15 @@ int ima_bprm_check(struct linux_binprm *bprm) * * Measure files based on the ima_must_measure() policy decision. * - * Always return 0 and audit dentry_open failures. - * (Return code will be based upon measurement appraisal.) + * On success return 0. On integrity appraisal error, assuming the file + * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. */ int ima_file_check(struct file *file, int mask) { - int rc; - ima_rdwr_violation_check(file); - rc = process_measurement(file, file->f_dentry->d_name.name, + return process_measurement(file, file->f_dentry->d_name.name, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), FILE_CHECK); - return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; } EXPORT_SYMBOL_GPL(ima_file_check); @@ -286,19 +278,15 @@ EXPORT_SYMBOL_GPL(ima_file_check); * * Measure/appraise kernel modules based on policy. * - * Always return 0 and audit dentry_open failures. - * Return code is based upon measurement appraisal. + * On success return 0. On integrity appraisal error, assuming the file + * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. */ int ima_module_check(struct file *file) { - int rc; - if (!file) - rc = INTEGRITY_UNKNOWN; - else - rc = process_measurement(file, file->f_dentry->d_name.name, - MAY_EXEC, MODULE_CHECK); - return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; + return -EACCES; /* INTEGRITY_UNKNOWN */ + return process_measurement(file, file->f_dentry->d_name.name, + MAY_EXEC, MODULE_CHECK); } static int __init init_ima(void) -- cgit v1.2.1 From b51524635b73cfa27cc393859b277cee9c042820 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 21 Sep 2012 01:01:29 +0300 Subject: ima: remove security.ima hexdump Hexdump is not really helping. Audit messages prints error messages. Remove it. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index b240c58403e2..fa675c907e0f 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -107,11 +107,6 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, if (rc) { cause = "invalid-hash"; status = INTEGRITY_FAIL; - print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE, - xattr_value, sizeof(*xattr_value)); - print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE, - (u8 *)&iint->ima_xattr, - sizeof iint->ima_xattr); break; } status = INTEGRITY_PASS; -- cgit v1.2.1 From 16cac49f727621c6b0467ffe15ed72c2febb1296 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Thu, 13 Dec 2012 11:15:04 -0500 Subject: ima: rename FILE_MMAP to MMAP_CHECK Rename FILE_MMAP hook to MMAP_CHECK to be consistent with the other hook names. Signed-off-by: Mimi Zohar --- Documentation/ABI/testing/ima_policy | 2 +- security/integrity/ima/ima.h | 2 +- security/integrity/ima/ima_api.c | 4 ++-- security/integrity/ima/ima_main.c | 2 +- security/integrity/ima/ima_policy.c | 7 ++++--- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index ec0a38ef3145..6a0fc808fb6d 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -23,7 +23,7 @@ Description: lsm: [[subj_user=] [subj_role=] [subj_type=] [obj_user=] [obj_role=] [obj_type=]] - base: func:= [BPRM_CHECK][FILE_MMAP][FILE_CHECK][MODULE_CHECK] + base: func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK] mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC] fsmagic:= hex value uid:= decimal value diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 3b2adb794f15..1385c5c172f7 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -127,7 +127,7 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode); /* IMA policy related functions */ -enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, MODULE_CHECK, POST_SETATTR }; +enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR }; int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 0cea3db21657..fc722b44c416 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -100,12 +100,12 @@ err_out: * ima_get_action - appraise & measure decision based on policy. * @inode: pointer to inode to measure * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) - * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP, MODULE_CHECK) + * @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK) * * The policy is defined in terms of keypairs: * subj=, obj=, type=, func=, mask=, fsmagic= * subj,obj, and type: are LSM specific. - * func: FILE_CHECK | BPRM_CHECK | FILE_MMAP | MODULE_CHECK + * func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK * mask: contains the permission mask * fsmagic: hex value * diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 1cd4eb2c3b90..970693d1a320 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -228,7 +228,7 @@ int ima_file_mmap(struct file *file, unsigned long prot) { if (file && (prot & PROT_EXEC)) return process_measurement(file, file->f_dentry->d_name.name, - MAY_EXEC, FILE_MMAP); + MAY_EXEC, MMAP_CHECK); return 0; } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 70f888de880d..95194539d75e 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -75,7 +75,7 @@ static struct ima_rule_entry default_rules[] = { {.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC}, - {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC, + {.action = MEASURE,.func = MMAP_CHECK,.mask = MAY_EXEC, .flags = IMA_FUNC | IMA_MASK}, {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC, .flags = IMA_FUNC | IMA_MASK}, @@ -448,8 +448,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->func = FILE_CHECK; else if (strcmp(args[0].from, "MODULE_CHECK") == 0) entry->func = MODULE_CHECK; - else if (strcmp(args[0].from, "FILE_MMAP") == 0) - entry->func = FILE_MMAP; + else if ((strcmp(args[0].from, "FILE_MMAP") == 0) + || (strcmp(args[0].from, "MMAP_CHECK") == 0)) + entry->func = MMAP_CHECK; else if (strcmp(args[0].from, "BPRM_CHECK") == 0) entry->func = BPRM_CHECK; else -- cgit v1.2.1 From ee866331749b07373743ce18ceaffb1dd841d855 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 21 Sep 2012 17:00:43 +0300 Subject: integrity: reduce storage size for ima_status and evm_status This patch reduces size of the iint structure by 8 bytes. It saves about 15% of iint cache memory. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/integrity.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index e9db763a875e..0a298def5036 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -50,8 +50,8 @@ struct integrity_iint_cache { u64 version; /* track inode changes */ unsigned short flags; struct evm_ima_xattr_data ima_xattr; - enum integrity_status ima_status; - enum integrity_status evm_status; + enum integrity_status ima_status:4; + enum integrity_status evm_status:4; }; /* rbtree tree calls to lookup, insert, delete -- cgit v1.2.1 From ea1046d4c57ee6e3d5f68f19dd9a45bbab0b71a0 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 4 Sep 2012 00:40:17 +0300 Subject: ima: move full pathname resolution to separate function Define a new function ima_d_path(), which returns the full pathname. This function will be used further, for example, by the directory verification code. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 1 + security/integrity/ima/ima_api.c | 17 +++++++++++++ security/integrity/ima/ima_main.c | 51 +++++++++++++++------------------------ 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 1385c5c172f7..991844db98d9 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -119,6 +119,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint, int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode); void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show); +const char *ima_d_path(struct path *path, char **pathbuf); /* rbtree tree calls to lookup, insert, delete * integrity data associated with an inode. diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index fc722b44c416..9382a4c568b2 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -237,3 +237,20 @@ void ima_audit_measurement(struct integrity_iint_cache *iint, iint->flags |= IMA_AUDITED; } + +const char *ima_d_path(struct path *path, char **pathbuf) +{ + char *pathname = NULL; + + /* We will allow 11 spaces for ' (deleted)' to be appended */ + *pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL); + if (*pathbuf) { + pathname = d_path(path, *pathbuf, PATH_MAX + 11); + if (IS_ERR(pathname)) { + kfree(*pathbuf); + *pathbuf = NULL; + pathname = NULL; + } + } + return pathname; +} diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 970693d1a320..d743c9a0a4b4 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -61,7 +61,8 @@ static void ima_rdwr_violation_check(struct file *file) fmode_t mode = file->f_mode; int must_measure; bool send_tomtou = false, send_writers = false; - unsigned char *pathname = NULL, *pathbuf = NULL; + char *pathbuf = NULL; + const char *pathname; if (!S_ISREG(inode->i_mode) || !ima_initialized) return; @@ -86,22 +87,15 @@ out: if (!send_tomtou && !send_writers) return; - /* We will allow 11 spaces for ' (deleted)' to be appended */ - pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL); - if (pathbuf) { - pathname = d_path(&file->f_path, pathbuf, PATH_MAX + 11); - if (IS_ERR(pathname)) - pathname = NULL; - else if (strlen(pathname) > IMA_EVENT_NAME_LEN_MAX) - pathname = NULL; - } + pathname = ima_d_path(&file->f_path, &pathbuf); + if (!pathname || strlen(pathname) > IMA_EVENT_NAME_LEN_MAX) + pathname = dentry->d_name.name; + if (send_tomtou) - ima_add_violation(inode, - !pathname ? dentry->d_name.name : pathname, + ima_add_violation(inode, pathname, "invalid_pcr", "ToMToU"); if (send_writers) - ima_add_violation(inode, - !pathname ? dentry->d_name.name : pathname, + ima_add_violation(inode, pathname, "invalid_pcr", "open_writers"); kfree(pathbuf); } @@ -145,12 +139,13 @@ void ima_file_free(struct file *file) ima_check_last_writer(iint, inode, file); } -static int process_measurement(struct file *file, const unsigned char *filename, +static int process_measurement(struct file *file, const char *filename, int mask, int function) { struct inode *inode = file->f_dentry->d_inode; struct integrity_iint_cache *iint; - unsigned char *pathname = NULL, *pathbuf = NULL; + char *pathbuf = NULL; + const char *pathname = NULL; int rc = -ENOMEM, action, must_appraise; if (!ima_initialized || !S_ISREG(inode->i_mode)) @@ -187,24 +182,18 @@ static int process_measurement(struct file *file, const unsigned char *filename, if (rc != 0) goto out; - if (function != BPRM_CHECK) { - /* We will allow 11 spaces for ' (deleted)' to be appended */ - pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL); - if (pathbuf) { - pathname = - d_path(&file->f_path, pathbuf, PATH_MAX + 11); - if (IS_ERR(pathname)) - pathname = NULL; - } - } + if (function != BPRM_CHECK) + pathname = ima_d_path(&file->f_path, &pathbuf); + + if (!pathname) + pathname = filename; + if (action & IMA_MEASURE) - ima_store_measurement(iint, file, - !pathname ? filename : pathname); + ima_store_measurement(iint, file, pathname); if (action & IMA_APPRAISE) - rc = ima_appraise_measurement(iint, file, - !pathname ? filename : pathname); + rc = ima_appraise_measurement(iint, file, pathname); if (action & IMA_AUDIT) - ima_audit_measurement(iint, !pathname ? filename : pathname); + ima_audit_measurement(iint, pathname); kfree(pathbuf); out: mutex_unlock(&inode->i_mutex); -- cgit v1.2.1 From a175b8bb29ebbad380ab4788f307fbfc47997b19 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 27 Sep 2012 15:06:28 +0300 Subject: ima: forbid write access to files with digital signatures This patch forbids write access to files with digital signatures, as they are considered immutable. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_main.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index d743c9a0a4b4..cd00ba39e8e0 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -175,12 +175,12 @@ static int process_measurement(struct file *file, const char *filename, if (!action) { if (iint->flags & IMA_APPRAISED) rc = iint->ima_status; - goto out; + goto out_digsig; } rc = ima_collect_measurement(iint, file); if (rc != 0) - goto out; + goto out_digsig; if (function != BPRM_CHECK) pathname = ima_d_path(&file->f_path, &pathbuf); @@ -195,6 +195,9 @@ static int process_measurement(struct file *file, const char *filename, if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); kfree(pathbuf); +out_digsig: + if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG)) + rc = -EACCES; out: mutex_unlock(&inode->i_mutex); if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE)) -- cgit v1.2.1 From 0e5a247cb37a97d843ef76d09d5f80deb7893ba3 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 8 Jun 2012 13:58:49 +0300 Subject: ima: added policy support for 'security.ima' type The 'security.ima' extended attribute may contain either the file data's hash or a digital signature. This patch adds support for requiring a specific extended attribute type. It extends the IMA policy with a new keyword 'appraise_type=imasig'. (Default is hash.) Changelog v2: - Fixed Documentation/ABI/testing/ima_policy option syntax Changelog v1: - Differentiate between 'required' vs. 'actual' extended attribute Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- Documentation/ABI/testing/ima_policy | 4 +++- security/integrity/ima/ima_appraise.c | 5 +++++ security/integrity/ima/ima_main.c | 1 + security/integrity/ima/ima_policy.c | 18 +++++++++++++++++- security/integrity/integrity.h | 2 ++ 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 6a0fc808fb6d..de16de3f148d 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -18,10 +18,11 @@ Description: rule format: action [condition ...] action: measure | dont_measure | appraise | dont_appraise | audit - condition:= base | lsm + condition:= base | lsm [option] base: [[func=] [mask=] [fsmagic=] [uid=] [fowner]] lsm: [[subj_user=] [subj_role=] [subj_type=] [obj_user=] [obj_role=] [obj_type=]] + option: [[appraise_type=]] base: func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK] mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC] @@ -29,6 +30,7 @@ Description: uid:= decimal value fowner:=decimal value lsm: are LSM specific + option: appraise_type:= [imasig] default policy: # PROC_SUPER_MAGIC diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index fa675c907e0f..8004332ccb8f 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -102,6 +102,11 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, switch (xattr_value->type) { case IMA_XATTR_DIGEST: + if (iint->flags & IMA_DIGSIG_REQUIRED) { + cause = "IMA signature required"; + status = INTEGRITY_FAIL; + break; + } rc = memcmp(xattr_value->digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE); if (rc) { diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index cd00ba39e8e0..3cdd78768c29 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -169,6 +169,7 @@ static int process_measurement(struct file *file, const char *filename, * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED, * IMA_AUDIT, IMA_AUDITED) */ iint->flags |= action; + action &= IMA_DO_MASK; action &= ~((iint->flags & IMA_DONE_MASK) >> 1); /* Nothing to do, just return existing appraised status */ diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 95194539d75e..1a2543a8ee53 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -245,6 +245,8 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, if (!ima_match_rules(entry, inode, func, mask)) continue; + action |= entry->flags & IMA_ACTION_FLAGS; + action |= entry->action & IMA_DO_MASK; if (entry->action & IMA_DO_MASK) actmask &= ~(entry->action | entry->action << 1); @@ -318,7 +320,8 @@ enum { Opt_audit, Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, - Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner + Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner, + Opt_appraise_type }; static match_table_t policy_tokens = { @@ -338,6 +341,7 @@ static match_table_t policy_tokens = { {Opt_fsmagic, "fsmagic=%s"}, {Opt_uid, "uid=%s"}, {Opt_fowner, "fowner=%s"}, + {Opt_appraise_type, "appraise_type=%s"}, {Opt_err, NULL} }; @@ -560,6 +564,18 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) LSM_SUBJ_TYPE, AUDIT_SUBJ_TYPE); break; + case Opt_appraise_type: + if (entry->action != APPRAISE) { + result = -EINVAL; + break; + } + + ima_log_string(ab, "appraise_type", args[0].from); + if ((strcmp(args[0].from, "imasig")) == 0) + entry->flags |= IMA_DIGSIG_REQUIRED; + else + result = -EINVAL; + break; case Opt_err: ima_log_string(ab, "UNKNOWN", p); result = -EINVAL; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 0a298def5036..9334691b2b75 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -26,7 +26,9 @@ #define IMA_AUDITED 0x0080 /* iint cache flags */ +#define IMA_ACTION_FLAGS 0xff00 #define IMA_DIGSIG 0x0100 +#define IMA_DIGSIG_REQUIRED 0x0200 #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT) #define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \ -- cgit v1.2.1 From f578c08ec959cb0cdadf02bdc9689a4df3e9b9d4 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 5 Dec 2012 09:29:09 -0500 Subject: ima: increase iint flag size In preparation for hook specific appraise status results, increase the iint flags size. Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin --- security/integrity/integrity.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 9334691b2b75..329ad263e130 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -16,19 +16,19 @@ #include /* iint action cache flags */ -#define IMA_MEASURE 0x0001 -#define IMA_MEASURED 0x0002 -#define IMA_APPRAISE 0x0004 -#define IMA_APPRAISED 0x0008 -/*#define IMA_COLLECT 0x0010 do not use this flag */ -#define IMA_COLLECTED 0x0020 -#define IMA_AUDIT 0x0040 -#define IMA_AUDITED 0x0080 +#define IMA_MEASURE 0x00000001 +#define IMA_MEASURED 0x00000002 +#define IMA_APPRAISE 0x00000004 +#define IMA_APPRAISED 0x00000008 +/*#define IMA_COLLECT 0x00000010 do not use this flag */ +#define IMA_COLLECTED 0x00000020 +#define IMA_AUDIT 0x00000040 +#define IMA_AUDITED 0x00000080 /* iint cache flags */ -#define IMA_ACTION_FLAGS 0xff00 -#define IMA_DIGSIG 0x0100 -#define IMA_DIGSIG_REQUIRED 0x0200 +#define IMA_ACTION_FLAGS 0xff000000 +#define IMA_DIGSIG 0x01000000 +#define IMA_DIGSIG_REQUIRED 0x02000000 #define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT) #define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \ @@ -50,7 +50,7 @@ struct integrity_iint_cache { struct rb_node rb_node; /* rooted in integrity_iint_tree */ struct inode *inode; /* back pointer to inode in question */ u64 version; /* track inode changes */ - unsigned short flags; + unsigned long flags; struct evm_ima_xattr_data ima_xattr; enum integrity_status ima_status:4; enum integrity_status evm_status:4; -- cgit v1.2.1 From d79d72e02485c00b886179538dc8deaffa3be507 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 3 Dec 2012 17:08:11 -0500 Subject: ima: per hook cache integrity appraisal status With the new IMA policy 'appraise_type=' option, different hooks can require different methods for appraising a file's integrity. For example, the existing 'ima_appraise_tcb' policy defines a generic rule, requiring all root files to be appraised, without specfying the appraisal method. A more specific rule could require all kernel modules, for example, to be signed. appraise fowner=0 func=MODULE_CHECK appraise_type=imasig appraise fowner=0 As a result, the integrity appraisal results for the same inode, but for different hooks, could differ. This patch caches the integrity appraisal results on a per hook basis. Changelog v2: - Rename ima_cache_status() to ima_set_cache_status() - Rename and move get_appraise_status() to ima_get_cache_status() Changelog v0: - include IMA_APPRAISE/APPRAISED_SUBMASK in IMA_DO/DONE_MASK (Dmitry) - Support independent MODULE_CHECK appraise status. - fixed IMA_XXXX_APPRAISE/APPRAISED flags Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin --- security/integrity/iint.c | 10 ++++- security/integrity/ima/ima.h | 13 ++++++- security/integrity/ima/ima_appraise.c | 71 ++++++++++++++++++++++++++++++----- security/integrity/ima/ima_main.c | 19 ++++++---- security/integrity/ima/ima_policy.c | 22 +++++++++++ security/integrity/integrity.h | 26 +++++++++++-- 6 files changed, 136 insertions(+), 25 deletions(-) diff --git a/security/integrity/iint.c b/security/integrity/iint.c index d82a5a13d855..74522dbd10a6 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -72,7 +72,10 @@ static void iint_free(struct integrity_iint_cache *iint) { iint->version = 0; iint->flags = 0UL; - iint->ima_status = INTEGRITY_UNKNOWN; + iint->ima_file_status = INTEGRITY_UNKNOWN; + iint->ima_mmap_status = INTEGRITY_UNKNOWN; + iint->ima_bprm_status = INTEGRITY_UNKNOWN; + iint->ima_module_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; kmem_cache_free(iint_cache, iint); } @@ -149,7 +152,10 @@ static void init_once(void *foo) memset(iint, 0, sizeof *iint); iint->version = 0; iint->flags = 0UL; - iint->ima_status = INTEGRITY_UNKNOWN; + iint->ima_file_status = INTEGRITY_UNKNOWN; + iint->ima_mmap_status = INTEGRITY_UNKNOWN; + iint->ima_bprm_status = INTEGRITY_UNKNOWN; + iint->ima_module_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; } diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 991844db98d9..ab68bed8ac36 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -142,13 +142,16 @@ void ima_delete_rules(void); #define IMA_APPRAISE_FIX 0x02 #ifdef CONFIG_IMA_APPRAISE -int ima_appraise_measurement(struct integrity_iint_cache *iint, +int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename); int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); +enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, + int func); #else -static inline int ima_appraise_measurement(struct integrity_iint_cache *iint, +static inline int ima_appraise_measurement(int func, + struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename) { @@ -165,6 +168,12 @@ static inline void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) { } + +static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache + *iint, int func) +{ + return INTEGRITY_UNKNOWN; +} #endif /* LSM based policy rules require audit */ diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 8004332ccb8f..2d4becab8918 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -51,6 +51,62 @@ static int ima_fix_xattr(struct dentry *dentry, sizeof(iint->ima_xattr), 0); } +/* Return specific func appraised cached result */ +enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, + int func) +{ + switch(func) { + case MMAP_CHECK: + return iint->ima_mmap_status; + case BPRM_CHECK: + return iint->ima_bprm_status; + case MODULE_CHECK: + return iint->ima_module_status; + case FILE_CHECK: + default: + return iint->ima_file_status; + } +} + +static void ima_set_cache_status(struct integrity_iint_cache *iint, + int func, enum integrity_status status) +{ + switch(func) { + case MMAP_CHECK: + iint->ima_mmap_status = status; + break; + case BPRM_CHECK: + iint->ima_bprm_status = status; + break; + case MODULE_CHECK: + iint->ima_module_status = status; + break; + case FILE_CHECK: + default: + iint->ima_file_status = status; + break; + } +} + +static void ima_cache_flags(struct integrity_iint_cache *iint, int func) +{ + switch(func) { + case MMAP_CHECK: + iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED); + break; + case BPRM_CHECK: + iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED); + break; + case MODULE_CHECK: + iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED); + break; + case FILE_CHECK: + default: + iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED); + break; + } +} + /* * ima_appraise_measurement - appraise file measurement * @@ -59,7 +115,7 @@ static int ima_fix_xattr(struct dentry *dentry, * * Return 0 on success, error code otherwise */ -int ima_appraise_measurement(struct integrity_iint_cache *iint, +int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename) { struct dentry *dentry = file->f_dentry; @@ -75,9 +131,6 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, if (!inode->i_op->getxattr) return INTEGRITY_UNKNOWN; - if (iint->flags & IMA_APPRAISED) - return iint->ima_status; - rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value, 0, GFP_NOFS); if (rc <= 0) { @@ -99,7 +152,6 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, cause = "invalid-HMAC"; goto out; } - switch (xattr_value->type) { case IMA_XATTR_DIGEST: if (iint->flags & IMA_DIGSIG_REQUIRED) { @@ -148,9 +200,9 @@ out: integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, op, cause, rc, 0); } else { - iint->flags |= IMA_APPRAISED; + ima_cache_flags(iint, func); } - iint->ima_status = status; + ima_set_cache_status(iint, func, status); kfree(xattr_value); return status; } @@ -196,10 +248,11 @@ void ima_inode_post_setattr(struct dentry *dentry) must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR); iint = integrity_iint_find(inode); if (iint) { + iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED | + IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK | + IMA_ACTION_FLAGS); if (must_appraise) iint->flags |= IMA_APPRAISE; - else - iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED); } if (!must_appraise) rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 3cdd78768c29..66b7f408eff2 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -151,8 +151,10 @@ static int process_measurement(struct file *file, const char *filename, if (!ima_initialized || !S_ISREG(inode->i_mode)) return 0; - /* Determine if in appraise/audit/measurement policy, - * returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask. */ + /* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action + * bitmask based on the appraise/audit/measurement policy. + * Included is the appraise submask. + */ action = ima_get_action(inode, mask, function); if (!action) return 0; @@ -166,16 +168,17 @@ static int process_measurement(struct file *file, const char *filename, goto out; /* Determine if already appraised/measured based on bitmask - * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED, - * IMA_AUDIT, IMA_AUDITED) */ + * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED, + * IMA_AUDIT, IMA_AUDITED) + */ iint->flags |= action; action &= IMA_DO_MASK; action &= ~((iint->flags & IMA_DONE_MASK) >> 1); /* Nothing to do, just return existing appraised status */ if (!action) { - if (iint->flags & IMA_APPRAISED) - rc = iint->ima_status; + if (must_appraise) + rc = ima_get_cache_status(iint, function); goto out_digsig; } @@ -191,8 +194,8 @@ static int process_measurement(struct file *file, const char *filename, if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname); - if (action & IMA_APPRAISE) - rc = ima_appraise_measurement(iint, file, pathname); + if (action & IMA_APPRAISE_SUBMASK) + rc = ima_appraise_measurement(function, iint, file, pathname); if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); kfree(pathbuf); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 1a2543a8ee53..4d7c0ae656d3 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -218,6 +218,25 @@ retry: return true; } +/* + * In addition to knowing that we need to appraise the file in general, + * we need to differentiate between calling hooks. + */ +static int get_subaction(int func) +{ + switch(func) { + case MMAP_CHECK: + return IMA_MMAP_APPRAISE; + case BPRM_CHECK: + return IMA_BPRM_APPRAISE; + case MODULE_CHECK: + return IMA_MODULE_APPRAISE; + case FILE_CHECK: + default: + return IMA_FILE_APPRAISE; + } +} + /** * ima_match_policy - decision based on LSM and other conditions * @inode: pointer to an inode for which the policy decision is being made @@ -248,6 +267,9 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, action |= entry->flags & IMA_ACTION_FLAGS; action |= entry->action & IMA_DO_MASK; + if (entry->action & IMA_APPRAISE) + action |= get_subaction(func); + if (entry->action & IMA_DO_MASK) actmask &= ~(entry->action | entry->action << 1); else diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 329ad263e130..0ae08fc88585 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -30,9 +30,24 @@ #define IMA_DIGSIG 0x01000000 #define IMA_DIGSIG_REQUIRED 0x02000000 -#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT) -#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \ - | IMA_COLLECTED) +#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ + IMA_APPRAISE_SUBMASK) +#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \ + IMA_COLLECTED | IMA_APPRAISED_SUBMASK) + +/* iint subaction appraise cache flags */ +#define IMA_FILE_APPRAISE 0x00000100 +#define IMA_FILE_APPRAISED 0x00000200 +#define IMA_MMAP_APPRAISE 0x00000400 +#define IMA_MMAP_APPRAISED 0x00000800 +#define IMA_BPRM_APPRAISE 0x00001000 +#define IMA_BPRM_APPRAISED 0x00002000 +#define IMA_MODULE_APPRAISE 0x00004000 +#define IMA_MODULE_APPRAISED 0x00008000 +#define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \ + IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE) +#define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \ + IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED) enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, @@ -52,7 +67,10 @@ struct integrity_iint_cache { u64 version; /* track inode changes */ unsigned long flags; struct evm_ima_xattr_data ima_xattr; - enum integrity_status ima_status:4; + enum integrity_status ima_file_status:4; + enum integrity_status ima_mmap_status:4; + enum integrity_status ima_bprm_status:4; + enum integrity_status ima_module_status:4; enum integrity_status evm_status:4; }; -- cgit v1.2.1 From 5a73fcfa8875a94c2956e7ff8fba54d31a3e2854 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 5 Dec 2012 15:14:38 -0500 Subject: ima: differentiate appraise status only for hook specific rules Different hooks can require different methods for appraising a file's integrity. As a result, an integrity appraisal status is cached on a per hook basis. Only a hook specific rule, requires the inode to be re-appraised. This patch eliminates unnecessary appraisals. Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin --- security/integrity/ima/ima_main.c | 9 ++++++--- security/integrity/ima/ima_policy.c | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 66b7f408eff2..3e751a9743a1 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -146,7 +146,7 @@ static int process_measurement(struct file *file, const char *filename, struct integrity_iint_cache *iint; char *pathbuf = NULL; const char *pathname = NULL; - int rc = -ENOMEM, action, must_appraise; + int rc = -ENOMEM, action, must_appraise, _func; if (!ima_initialized || !S_ISREG(inode->i_mode)) return 0; @@ -161,6 +161,9 @@ static int process_measurement(struct file *file, const char *filename, must_appraise = action & IMA_APPRAISE; + /* Is the appraise rule hook specific? */ + _func = (action & IMA_FILE_APPRAISE) ? FILE_CHECK : function; + mutex_lock(&inode->i_mutex); iint = integrity_inode_get(inode); @@ -178,7 +181,7 @@ static int process_measurement(struct file *file, const char *filename, /* Nothing to do, just return existing appraised status */ if (!action) { if (must_appraise) - rc = ima_get_cache_status(iint, function); + rc = ima_get_cache_status(iint, _func); goto out_digsig; } @@ -195,7 +198,7 @@ static int process_measurement(struct file *file, const char *filename, if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname); if (action & IMA_APPRAISE_SUBMASK) - rc = ima_appraise_measurement(function, iint, file, pathname); + rc = ima_appraise_measurement(_func, iint, file, pathname); if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); kfree(pathbuf); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 4d7c0ae656d3..4adcd0f8c1dd 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -220,10 +220,13 @@ retry: /* * In addition to knowing that we need to appraise the file in general, - * we need to differentiate between calling hooks. + * we need to differentiate between calling hooks, for hook specific rules. */ -static int get_subaction(int func) +static int get_subaction(struct ima_rule_entry *rule, int func) { + if (!(rule->flags & IMA_FUNC)) + return IMA_FILE_APPRAISE; + switch(func) { case MMAP_CHECK: return IMA_MMAP_APPRAISE; @@ -268,7 +271,7 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, action |= entry->action & IMA_DO_MASK; if (entry->action & IMA_APPRAISE) - action |= get_subaction(func); + action |= get_subaction(entry, func); if (entry->action & IMA_DO_MASK) actmask &= ~(entry->action | entry->action << 1); -- cgit v1.2.1 From 26d438457ed1b99b6cb26d8f694e8d3de336f9d8 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 30 Jan 2013 11:30:05 +0200 Subject: digsig: remove unnecessary memory allocation and copying In existing use case, copying of the decoded data is unnecessary in pkcs_1_v1_5_decode_emsa. It is just enough to get pointer to the message. Removing copying and extra buffer allocation. Signed-off-by: Dmitry Kasatkin Signed-off-by: James Morris --- lib/digsig.c | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/lib/digsig.c b/lib/digsig.c index 8c0e62975c88..0103c5b9b802 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -30,11 +30,10 @@ static struct crypto_shash *shash; -static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg, - unsigned long msglen, - unsigned long modulus_bitlen, - unsigned char *out, - unsigned long *outlen) +static const char *pkcs_1_v1_5_decode_emsa(const unsigned char *msg, + unsigned long msglen, + unsigned long modulus_bitlen, + unsigned long *outlen) { unsigned long modulus_len, ps_len, i; @@ -42,11 +41,11 @@ static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg, /* test message size */ if ((msglen > modulus_len) || (modulus_len < 11)) - return -EINVAL; + return NULL; /* separate encoded message */ - if ((msg[0] != 0x00) || (msg[1] != (unsigned char)1)) - return -EINVAL; + if (msg[0] != 0x00 || msg[1] != 0x01) + return NULL; for (i = 2; i < modulus_len - 1; i++) if (msg[i] != 0xFF) @@ -56,19 +55,13 @@ static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg, if (msg[i] != 0) /* There was no octet with hexadecimal value 0x00 to separate ps from m. */ - return -EINVAL; + return NULL; ps_len = i - 2; - if (*outlen < (msglen - (2 + ps_len + 1))) { - *outlen = msglen - (2 + ps_len + 1); - return -EOVERFLOW; - } - *outlen = (msglen - (2 + ps_len + 1)); - memcpy(out, &msg[2 + ps_len + 1], *outlen); - return 0; + return msg + 2 + ps_len + 1; } /* @@ -83,7 +76,8 @@ static int digsig_verify_rsa(struct key *key, unsigned long mlen, mblen; unsigned nret, l; int head, i; - unsigned char *out1 = NULL, *out2 = NULL; + unsigned char *out1 = NULL; + const char *m; MPI in = NULL, res = NULL, pkey[2]; uint8_t *p, *datap, *endp; struct user_key_payload *ukp; @@ -120,7 +114,7 @@ static int digsig_verify_rsa(struct key *key, } mblen = mpi_get_nbits(pkey[0]); - mlen = (mblen + 7)/8; + mlen = DIV_ROUND_UP(mblen, 8); if (mlen == 0) goto err; @@ -129,10 +123,6 @@ static int digsig_verify_rsa(struct key *key, if (!out1) goto err; - out2 = kzalloc(mlen, GFP_KERNEL); - if (!out2) - goto err; - nret = siglen; in = mpi_read_from_buffer(sig, &nret); if (!in) @@ -162,18 +152,15 @@ static int digsig_verify_rsa(struct key *key, memset(out1, 0, head); memcpy(out1 + head, p, l); - err = pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len); - if (err) - goto err; + m = pkcs_1_v1_5_decode_emsa(out1, len, mblen, &len); - if (len != hlen || memcmp(out2, h, hlen)) + if (!m || len != hlen || memcmp(m, h, hlen)) err = -EINVAL; err: mpi_free(in); mpi_free(res); kfree(out1); - kfree(out2); while (--i >= 0) mpi_free(pkey[i]); err1: -- cgit v1.2.1 From 0d2a1b2d03dfd5ee79e7ebc59635690c8f08810f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 30 Jan 2013 11:30:06 +0200 Subject: mpilib: use DIV_ROUND_UP and remove unused macros Remove MIN, MAX and ABS macros that are duplicates kernel's native implementation. Signed-off-by: Andy Shevchenko Signed-off-by: James Morris --- lib/mpi/mpi-internal.h | 4 ---- lib/mpi/mpicoder.c | 8 ++++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/lib/mpi/mpi-internal.h b/lib/mpi/mpi-internal.h index 77adcf6bc257..60cf765628e9 100644 --- a/lib/mpi/mpi-internal.h +++ b/lib/mpi/mpi-internal.h @@ -65,10 +65,6 @@ typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */ typedef int mpi_size_t; /* (must be a signed type) */ -#define ABS(x) (x >= 0 ? x : -x) -#define MIN(l, o) ((l) < (o) ? (l) : (o)) -#define MAX(h, i) ((h) > (i) ? (h) : (i)) - static inline int RESIZE_IF_NEEDED(MPI a, unsigned b) { if (a->alloced < b) diff --git a/lib/mpi/mpicoder.c b/lib/mpi/mpicoder.c index 3962b7f7fe3f..5f9c44cdf1f5 100644 --- a/lib/mpi/mpicoder.c +++ b/lib/mpi/mpicoder.c @@ -52,7 +52,7 @@ MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes) else nbits = 0; - nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB; + nlimbs = DIV_ROUND_UP(nbytes, BYTES_PER_MPI_LIMB); val = mpi_alloc(nlimbs); if (!val) return NULL; @@ -96,8 +96,8 @@ MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread) buffer += 2; nread = 2; - nbytes = (nbits + 7) / 8; - nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB; + nbytes = DIV_ROUND_UP(nbits, 8); + nlimbs = DIV_ROUND_UP(nbytes, BYTES_PER_MPI_LIMB); val = mpi_alloc(nlimbs); if (!val) return NULL; @@ -193,7 +193,7 @@ int mpi_set_buffer(MPI a, const void *xbuffer, unsigned nbytes, int sign) int nlimbs; int i; - nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB; + nlimbs = DIV_ROUND_UP(nbytes, BYTES_PER_MPI_LIMB); if (RESIZE_IF_NEEDED(a, nlimbs) < 0) return -ENOMEM; a->sign = sign; -- cgit v1.2.1 From feab398e580b1847b729203573a638cf90d5e7fc Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 7 Nov 2012 17:00:45 -0600 Subject: tpm: add documentation for sysfs interfaces Reviewed-by: Peter Huewe Signed-off-by: Kent Yoder --- Documentation/ABI/stable/sysfs-class-tpm | 185 +++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 Documentation/ABI/stable/sysfs-class-tpm diff --git a/Documentation/ABI/stable/sysfs-class-tpm b/Documentation/ABI/stable/sysfs-class-tpm new file mode 100644 index 000000000000..a60b45e2493b --- /dev/null +++ b/Documentation/ABI/stable/sysfs-class-tpm @@ -0,0 +1,185 @@ +What: /sys/class/misc/tpmX/device/ +Date: April 2005 +KernelVersion: 2.6.12 +Contact: tpmdd-devel@lists.sf.net +Description: The device/ directory under a specific TPM instance exposes + the properties of that TPM chip + + +What: /sys/class/misc/tpmX/device/active +Date: April 2006 +KernelVersion: 2.6.17 +Contact: tpmdd-devel@lists.sf.net +Description: The "active" property prints a '1' if the TPM chip is accepting + commands. An inactive TPM chip still contains all the state of + an active chip (Storage Root Key, NVRAM, etc), and can be + visible to the OS, but will only accept a restricted set of + commands. See the TPM Main Specification part 2, Structures, + section 17 for more information on which commands are + available. + +What: /sys/class/misc/tpmX/device/cancel +Date: June 2005 +KernelVersion: 2.6.13 +Contact: tpmdd-devel@lists.sf.net +Description: The "cancel" property allows you to cancel the currently + pending TPM command. Writing any value to cancel will call the + TPM vendor specific cancel operation. + +What: /sys/class/misc/tpmX/device/caps +Date: April 2005 +KernelVersion: 2.6.12 +Contact: tpmdd-devel@lists.sf.net +Description: The "caps" property contains TPM manufacturer and version info. + + Example output: + + Manufacturer: 0x53544d20 + TCG version: 1.2 + Firmware version: 8.16 + + Manufacturer is a hex dump of the 4 byte manufacturer info + space in a TPM. TCG version shows the TCG TPM spec level that + the chip supports. Firmware version is that of the chip and + is manufacturer specific. + +What: /sys/class/misc/tpmX/device/durations +Date: March 2011 +KernelVersion: 3.1 +Contact: tpmdd-devel@lists.sf.net +Description: The "durations" property shows the 3 vendor-specific values + used to wait for a short, medium and long TPM command. All + TPM commands are categorized as short, medium or long in + execution time, so that the driver doesn't have to wait + any longer than necessary before starting to poll for a + result. + + Example output: + + 3015000 4508000 180995000 [original] + + Here the short, medium and long durations are displayed in + usecs. "[original]" indicates that the values are displayed + unmodified from when they were queried from the chip. + Durations can be modified in the case where a buggy chip + reports them in msec instead of usec and they need to be + scaled to be displayed in usecs. In this case "[adjusted]" + will be displayed in place of "[original]". + +What: /sys/class/misc/tpmX/device/enabled +Date: April 2006 +KernelVersion: 2.6.17 +Contact: tpmdd-devel@lists.sf.net +Description: The "enabled" property prints a '1' if the TPM chip is enabled, + meaning that it should be visible to the OS. This property + may be visible but produce a '0' after some operation that + disables the TPM. + +What: /sys/class/misc/tpmX/device/owned +Date: April 2006 +KernelVersion: 2.6.17 +Contact: tpmdd-devel@lists.sf.net +Description: The "owned" property produces a '1' if the TPM_TakeOwnership + ordinal has been executed successfully in the chip. A '0' + indicates that ownership hasn't been taken. + +What: /sys/class/misc/tpmX/device/pcrs +Date: April 2005 +KernelVersion: 2.6.12 +Contact: tpmdd-devel@lists.sf.net +Description: The "pcrs" property will dump the current value of all Platform + Configuration Registers in the TPM. Note that since these + values may be constantly changing, the output is only valid + for a snapshot in time. + + Example output: + + PCR-00: 3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75 + PCR-01: 3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75 + PCR-02: 3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75 + PCR-03: 3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75 + PCR-04: 3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75 + ... + + The number of PCRs and hex bytes needed to represent a PCR + value will vary depending on TPM chip version. For TPM 1.1 and + 1.2 chips, PCRs represent SHA-1 hashes, which are 20 bytes + long. Use the "caps" property to determine TPM version. + +What: /sys/class/misc/tpmX/device/pubek +Date: April 2005 +KernelVersion: 2.6.12 +Contact: tpmdd-devel@lists.sf.net +Description: The "pubek" property will return the TPM's public endorsement + key if possible. If the TPM has had ownership established and + is version 1.2, the pubek will not be available without the + owner's authorization. Since the TPM driver doesn't store any + secrets, it can't authorize its own request for the pubek, + making it unaccessible. The public endorsement key is gener- + ated at TPM menufacture time and exists for the life of the + chip. + + Example output: + + Algorithm: 00 00 00 01 + Encscheme: 00 03 + Sigscheme: 00 01 + Parameters: 00 00 08 00 00 00 00 02 00 00 00 00 + Modulus length: 256 + Modulus: + B4 76 41 82 C9 20 2C 10 18 40 BC 8B E5 44 4C 6C + 3A B2 92 0C A4 9B 2A 83 EB 5C 12 85 04 48 A0 B6 + 1E E4 81 84 CE B2 F2 45 1C F0 85 99 61 02 4D EB + 86 C4 F7 F3 29 60 52 93 6B B2 E5 AB 8B A9 09 E3 + D7 0E 7D CA 41 BF 43 07 65 86 3C 8C 13 7A D0 8B + 82 5E 96 0B F8 1F 5F 34 06 DA A2 52 C1 A9 D5 26 + 0F F4 04 4B D9 3F 2D F2 AC 2F 74 64 1F 8B CD 3E + 1E 30 38 6C 70 63 69 AB E2 50 DF 49 05 2E E1 8D + 6F 78 44 DA 57 43 69 EE 76 6C 38 8A E9 8E A3 F0 + A7 1F 3C A8 D0 12 15 3E CA 0E BD FA 24 CD 33 C6 + 47 AE A4 18 83 8E 22 39 75 93 86 E6 FD 66 48 B6 + 10 AD 94 14 65 F9 6A 17 78 BD 16 53 84 30 BF 70 + E0 DC 65 FD 3C C6 B0 1E BF B9 C1 B5 6C EF B1 3A + F8 28 05 83 62 26 11 DC B4 6B 5A 97 FF 32 26 B6 + F7 02 71 CF 15 AE 16 DD D1 C1 8E A8 CF 9B 50 7B + C3 91 FF 44 1E CF 7C 39 FE 17 77 21 20 BD CE 9B + + Possible values: + + Algorithm: TPM_ALG_RSA (1) + Encscheme: TPM_ES_RSAESPKCSv15 (2) + TPM_ES_RSAESOAEP_SHA1_MGF1 (3) + Sigscheme: TPM_SS_NONE (1) + Parameters, a byte string of 3 u32 values: + Key Length (bits): 00 00 08 00 (2048) + Num primes: 00 00 00 02 (2) + Exponent Size: 00 00 00 00 (0 means the + default exp) + Modulus Length: 256 (bytes) + Modulus: The 256 byte Endorsement Key modulus + +What: /sys/class/misc/tpmX/device/temp_deactivated +Date: April 2006 +KernelVersion: 2.6.17 +Contact: tpmdd-devel@lists.sf.net +Description: The "temp_deactivated" property returns a '1' if the chip has + been temporarily dectivated, usually until the next power + cycle. Whether a warm boot (reboot) will clear a TPM chip + from a temp_deactivated state is platform specific. + +What: /sys/class/misc/tpmX/device/timeouts +Date: March 2011 +KernelVersion: 3.1 +Contact: tpmdd-devel@lists.sf.net +Description: The "timeouts" property shows the 4 vendor-specific values + for the TPM's interface spec timeouts. The use of these + timeouts is defined by the TPM interface spec that the chip + conforms to. + + Example output: + + 750000 750000 750000 750000 [original] + + The four timeout values are shown in usecs, with a trailing + "[original]" or "[adjusted]" depending on whether the values + were scaled by the driver to be reported in usec from msecs. -- cgit v1.2.1 From 720ca4a9af1f1ce1d4d95ec4d6166ce84ede227f Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Mon, 12 Nov 2012 23:37:17 +0100 Subject: char/tpm: Remove duplicated lookup table The entries in tpm_protected_ordinal_duration are exactly the same as the first 12 in tpm_ordinal_duration, so we can simply remove this one, and save some bytes. This does not change the behavior of the driver. Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 93211df52aab..9e3c529cac80 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -65,21 +65,6 @@ static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES); * values of the SHORT, MEDIUM, and LONG durations are retrieved * from the chip during initialization with a call to tpm_get_timeouts. */ -static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = { - TPM_UNDEFINED, /* 0 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, /* 5 */ - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_UNDEFINED, - TPM_SHORT, /* 10 */ - TPM_SHORT, -}; - static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = { TPM_UNDEFINED, /* 0 */ TPM_UNDEFINED, @@ -357,8 +342,7 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < TPM_MAX_PROTECTED_ORDINAL) duration_idx = - tpm_protected_ordinal_duration[ordinal & - TPM_PROTECTED_ORDINAL_MASK]; + tpm_ordinal_duration[ordinal & TPM_PROTECTED_ORDINAL_MASK]; if (duration_idx != TPM_UNDEFINED) duration = chip->vendor.duration[duration_idx]; -- cgit v1.2.1 From 07b133e6060ba9de6cf6265fb23f0de8ec78e51c Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Fri, 16 Nov 2012 00:31:29 +0100 Subject: char/tpm: simplify duration calculation and eliminate smatch warning. This patch changes the semantics of the duration calculation for an ordinal, by masking out the higher bits of a tpm command, which specify whether it's an TPM_PROTECTED_COMMAND, TPM_UNPROTECTED_COMMAND, TPM_CONNECTION_COMMAND, TPM_CONNECTION_COMMAND, TPM_VENDOR_COMMAND. (See TPM Main Spec Part 2 Section 17 for details). For all TPM_PROTECTED and TPM_CONNECTION commands the results are unchanged. The TPM_UNPROTECTED commands are TSS commands and thus irrelevant as they are not sent to the tpm. For vendor commands the semantics change for ordinals 10 and 11 but they were probably wrong anyway. For everything else which has the ordinal set to 10 or 11 the semantics change as it now uses TPM_UNDEFINED instead of TPM_SHORT which was probably wrong anyway (but irrelevant as not defined by the standard). This patch also gets rid of the (false positive) smatch warning: drivers/char/tpm/tpm.c:360 tpm_calc_ordinal_duration() error: buffer overflow 'tpm_protected_ordinal_duration' 12 <= 243 Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 9e3c529cac80..0a08af031a57 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -40,8 +40,9 @@ enum tpm_duration { }; #define TPM_MAX_ORDINAL 243 -#define TPM_MAX_PROTECTED_ORDINAL 12 -#define TPM_PROTECTED_ORDINAL_MASK 0xFF +#define TSC_MAX_ORDINAL 12 +#define TPM_PROTECTED_COMMAND 0x00 +#define TPM_CONNECTION_COMMAND 0x40 /* * Bug workaround - some TPM's don't flush the most @@ -336,13 +337,11 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, { int duration_idx = TPM_UNDEFINED; int duration = 0; + u8 category = (ordinal >> 24) & 0xFF; - if (ordinal < TPM_MAX_ORDINAL) + if ((category == TPM_PROTECTED_COMMAND && ordinal < TPM_MAX_ORDINAL) || + (category == TPM_CONNECTION_COMMAND && ordinal < TSC_MAX_ORDINAL)) duration_idx = tpm_ordinal_duration[ordinal]; - else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) < - TPM_MAX_PROTECTED_ORDINAL) - duration_idx = - tpm_ordinal_duration[ordinal & TPM_PROTECTED_ORDINAL_MASK]; if (duration_idx != TPM_UNDEFINED) duration = chip->vendor.duration[duration_idx]; -- cgit v1.2.1 From c584af1926b7626ef3bdab3354382cb2bd434d36 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 21 Nov 2012 13:54:33 -0700 Subject: TPM: Issue TPM_STARTUP at driver load if the TPM has not been started The TPM will respond to TPM_GET_CAP with TPM_ERR_INVALID_POSTINIT if TPM_STARTUP has not been issued. Detect this and automatically issue TPM_STARTUP. This is for embedded applications where the kernel is the first thing to touch the TPM. Signed-off-by: Jason Gunthorpe Tested-by: Peter Huewe Reviewed-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 44 ++++++++++++++++++++++++++++++++++++++++---- drivers/char/tpm/tpm.h | 6 ++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 0a08af031a57..023dfbaffe73 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -451,7 +451,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, return -EFAULT; err = be32_to_cpu(cmd->header.out.return_code); - if (err != 0) + if (err != 0 && desc) dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); return err; @@ -511,6 +511,25 @@ void tpm_gen_interrupt(struct tpm_chip *chip) } EXPORT_SYMBOL_GPL(tpm_gen_interrupt); +#define TPM_ORD_STARTUP cpu_to_be32(153) +#define TPM_ST_CLEAR cpu_to_be16(1) +#define TPM_ST_STATE cpu_to_be16(2) +#define TPM_ST_DEACTIVATED cpu_to_be16(3) +static const struct tpm_input_header tpm_startup_header = { + .tag = TPM_TAG_RQU_COMMAND, + .length = cpu_to_be32(12), + .ordinal = TPM_ORD_STARTUP +}; + +static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) +{ + struct tpm_cmd_t start_cmd; + start_cmd.header.in = tpm_startup_header; + start_cmd.params.startup_in.startup_type = startup_type; + return transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE, + "attempting to start the TPM"); +} + int tpm_get_timeouts(struct tpm_chip *chip) { struct tpm_cmd_t tpm_cmd; @@ -524,11 +543,28 @@ int tpm_get_timeouts(struct tpm_chip *chip) tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; + rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL); - rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, - "attempting to determine the timeouts"); - if (rc) + if (rc == TPM_ERR_INVALID_POSTINIT) { + /* The TPM is not started, we are the first to talk to it. + Execute a startup command. */ + dev_info(chip->dev, "Issuing TPM_STARTUP"); + if (tpm_startup(chip, TPM_ST_CLEAR)) + return rc; + + tpm_cmd.header.in = tpm_getcap_header; + tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; + tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); + tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT; + rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, + NULL); + } + if (rc) { + dev_err(chip->dev, + "A TPM error (%zd) occurred attempting to determine the timeouts\n", + rc); goto duration; + } if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || be32_to_cpu(tpm_cmd.header.out.length) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 8ef7649a50aa..8971b128500b 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -47,6 +47,7 @@ enum tpm_addr { #define TPM_WARN_DOING_SELFTEST 0x802 #define TPM_ERR_DEACTIVATED 0x6 #define TPM_ERR_DISABLED 0x7 +#define TPM_ERR_INVALID_POSTINIT 38 #define TPM_HEADER_SIZE 10 extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr, @@ -291,6 +292,10 @@ struct tpm_getrandom_in { __be32 num_bytes; }__attribute__((packed)); +struct tpm_startup_in { + __be16 startup_type; +} __packed; + typedef union { struct tpm_getcap_params_out getcap_out; struct tpm_readpubek_params_out readpubek_out; @@ -301,6 +306,7 @@ typedef union { struct tpm_pcrextend_in pcrextend_in; struct tpm_getrandom_in getrandom_in; struct tpm_getrandom_out getrandom_out; + struct tpm_startup_in startup_in; } tpm_cmd_params; struct tpm_cmd_t { -- cgit v1.2.1 From 348df8db301d9fa01fb51e5eaca1e9f3b27bd4c8 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 21 Nov 2012 13:56:45 -0700 Subject: TPM: Switch to __packed instead of __attribute__((packed)) This seems to be preferred these days. Signed-off-by: Jason Gunthorpe Reviewed-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.h | 34 +++++++++++++++++----------------- drivers/char/tpm/tpm_acpi.c | 8 ++++---- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 8971b128500b..04b4547162c8 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -155,13 +155,13 @@ struct tpm_input_header { __be16 tag; __be32 length; __be32 ordinal; -}__attribute__((packed)); +} __packed; struct tpm_output_header { __be16 tag; __be32 length; __be32 return_code; -}__attribute__((packed)); +} __packed; struct stclear_flags_t { __be16 tag; @@ -170,14 +170,14 @@ struct stclear_flags_t { u8 physicalPresence; u8 physicalPresenceLock; u8 bGlobalLock; -}__attribute__((packed)); +} __packed; struct tpm_version_t { u8 Major; u8 Minor; u8 revMajor; u8 revMinor; -}__attribute__((packed)); +} __packed; struct tpm_version_1_2_t { __be16 tag; @@ -185,20 +185,20 @@ struct tpm_version_1_2_t { u8 Minor; u8 revMajor; u8 revMinor; -}__attribute__((packed)); +} __packed; struct timeout_t { __be32 a; __be32 b; __be32 c; __be32 d; -}__attribute__((packed)); +} __packed; struct duration_t { __be32 tpm_short; __be32 tpm_medium; __be32 tpm_long; -}__attribute__((packed)); +} __packed; struct permanent_flags_t { __be16 tag; @@ -222,7 +222,7 @@ struct permanent_flags_t { u8 tpmEstablished; u8 maintenanceDone; u8 disableFullDALogicInfo; -}__attribute__((packed)); +} __packed; typedef union { struct permanent_flags_t perm_flags; @@ -240,12 +240,12 @@ struct tpm_getcap_params_in { __be32 cap; __be32 subcap_size; __be32 subcap; -}__attribute__((packed)); +} __packed; struct tpm_getcap_params_out { __be32 cap_size; cap_t cap; -}__attribute__((packed)); +} __packed; struct tpm_readpubek_params_out { u8 algorithm[4]; @@ -256,7 +256,7 @@ struct tpm_readpubek_params_out { __be32 keysize; u8 modulus[256]; u8 checksum[20]; -}__attribute__((packed)); +} __packed; typedef union { struct tpm_input_header in; @@ -266,16 +266,16 @@ typedef union { #define TPM_DIGEST_SIZE 20 struct tpm_pcrread_out { u8 pcr_result[TPM_DIGEST_SIZE]; -}__attribute__((packed)); +} __packed; struct tpm_pcrread_in { __be32 pcr_idx; -}__attribute__((packed)); +} __packed; struct tpm_pcrextend_in { __be32 pcr_idx; u8 hash[TPM_DIGEST_SIZE]; -}__attribute__((packed)); +} __packed; /* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18 * bytes, but 128 is still a relatively large number of random bytes and @@ -286,11 +286,11 @@ struct tpm_pcrextend_in { struct tpm_getrandom_out { __be32 rng_data_len; u8 rng_data[TPM_MAX_RNG_DATA]; -}__attribute__((packed)); +} __packed; struct tpm_getrandom_in { __be32 num_bytes; -}__attribute__((packed)); +} __packed; struct tpm_startup_in { __be16 startup_type; @@ -312,7 +312,7 @@ typedef union { struct tpm_cmd_t { tpm_cmd_header header; tpm_cmd_params params; -}__attribute__((packed)); +} __packed; ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *); diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c index 56051d0c97a2..64420b3396a2 100644 --- a/drivers/char/tpm/tpm_acpi.c +++ b/drivers/char/tpm/tpm_acpi.c @@ -33,13 +33,13 @@ struct acpi_tcpa { u16 platform_class; union { struct client_hdr { - u32 log_max_len __attribute__ ((packed)); - u64 log_start_addr __attribute__ ((packed)); + u32 log_max_len __packed; + u64 log_start_addr __packed; } client; struct server_hdr { u16 reserved; - u64 log_max_len __attribute__ ((packed)); - u64 log_start_addr __attribute__ ((packed)); + u64 log_max_len __packed; + u64 log_start_addr __packed; } server; }; }; -- cgit v1.2.1 From 73249695f0cb10bb9fc8b294f3f8c4b001889e42 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Thu, 29 Nov 2012 22:08:41 +0100 Subject: char/tpm: Use true and false for bools Bool initializations should use true and false. Bool tests don't need comparisons. Based on contributions from Joe Perches, Rusty Russell and Bruce W Allan. The semantic patch that makes this output is available in scripts/coccinelle/misc/boolinit.cocci. More information about semantic patching is available at http://coccinelle.lip6.fr/ Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_tis.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index ea31dafbcac2..74ce2d1f08b4 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -374,7 +374,7 @@ static int probe_itpm(struct tpm_chip *chip) if (vendor != TPM_VID_INTEL) return 0; - itpm = 0; + itpm = false; rc = tpm_tis_send_data(chip, cmd_getticks, len); if (rc == 0) @@ -383,7 +383,7 @@ static int probe_itpm(struct tpm_chip *chip) tpm_tis_ready(chip); release_locality(chip, chip->vendor.locality, 0); - itpm = 1; + itpm = true; rc = tpm_tis_send_data(chip, cmd_getticks, len); if (rc == 0) { @@ -502,7 +502,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) return IRQ_HANDLED; } -static bool interrupts = 1; +static bool interrupts = true; module_param(interrupts, bool, 0444); MODULE_PARM_DESC(interrupts, "Enable interrupts"); @@ -545,7 +545,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, rc = -ENODEV; goto out_err; } - itpm = (probe == 0) ? 0 : 1; + itpm = !!probe; } if (itpm) @@ -741,10 +741,10 @@ static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev, if (pnp_irq_valid(pnp_dev, 0)) irq = pnp_irq(pnp_dev, 0); else - interrupts = 0; + interrupts = false; if (is_itpm(pnp_dev)) - itpm = 1; + itpm = true; return tpm_tis_init(&pnp_dev->dev, start, len, irq); } -- cgit v1.2.1 From 251a7b08213af82e40e4a70cac056e245853c410 Mon Sep 17 00:00:00 2001 From: Mathias Leblanc Date: Wed, 28 Nov 2012 18:22:24 +0100 Subject: TPM: STMicroelectronics ST33 I2C KERNEL 3.x * STMicroelectronics version 1.2.0, Copyright (C) 2010 * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. * This is free software, and you are welcome to redistribute it * under certain conditions. This is the driver for TPM chip from ST Microelectronics. If you have a TPM security chip from STMicroelectronics working with an I2C, in menuconfig or .config choose the tpm driver on device --> tpm and activate the protocol of your choice before compiling the kernel. The driver will be accessible from within Linux. Tested on linux x86/x64 on kernel 3.x Signed-off-by: Mathias Leblanc Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_stm_st33_i2c.c | 903 +++++++++++++++++++++++++++++++++++ drivers/char/tpm/tpm_stm_st33_i2c.h | 81 ++++ include/linux/i2c/tpm_stm_st33_i2c.h | 47 ++ 3 files changed, 1031 insertions(+) create mode 100644 drivers/char/tpm/tpm_stm_st33_i2c.c create mode 100644 drivers/char/tpm/tpm_stm_st33_i2c.h create mode 100644 include/linux/i2c/tpm_stm_st33_i2c.h diff --git a/drivers/char/tpm/tpm_stm_st33_i2c.c b/drivers/char/tpm/tpm_stm_st33_i2c.c new file mode 100644 index 000000000000..32cdd5e92141 --- /dev/null +++ b/drivers/char/tpm/tpm_stm_st33_i2c.c @@ -0,0 +1,903 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 + * Copyright (C) 2009, 2010 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * STMicroelectronics version 1.2.0, Copyright (C) 2010 + * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. + * This is free software, and you are welcome to redistribute it + * under certain conditions. + * + * @Author: Christophe RICARD tpmsupport@st.com + * + * @File: tpm_stm_st33_i2c.c + * + * @Synopsis: + * 09/15/2010: First shot driver tpm_tis driver for + lpc is used as model. + */ + +#include + +#include "tpm_stm_st33_i2c.h" + +enum stm33zp24_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum stm33zp24_status { + TPM_STS_VALID = 0x80, + TPM_STS_COMMAND_READY = 0x40, + TPM_STS_GO = 0x20, + TPM_STS_DATA_AVAIL = 0x10, + TPM_STS_DATA_EXPECT = 0x08, +}; + +enum stm33zp24_int_flags { + TPM_GLOBAL_INT_ENABLE = 0x80, + TPM_INTF_CMD_READY_INT = 0x080, + TPM_INTF_FIFO_AVALAIBLE_INT = 0x040, + TPM_INTF_WAKE_UP_READY_INT = 0x020, + TPM_INTF_LOCALITY_CHANGE_INT = 0x004, + TPM_INTF_STS_VALID_INT = 0x002, + TPM_INTF_DATA_AVAIL_INT = 0x001, +}; + +enum tis_defaults { + TIS_SHORT_TIMEOUT = 750, + TIS_LONG_TIMEOUT = 2000, +}; + +/* + * write8_reg + * Send byte to the TIS register according to the ST33ZP24 I2C protocol. + * @param: tpm_register, the tpm tis register where the data should be written + * @param: tpm_data, the tpm_data to write inside the tpm_register + * @param: tpm_size, The length of the data + * @return: Returns negative errno, or else the number of bytes written. + */ +static int write8_reg(struct i2c_client *client, u8 tpm_register, + u8 *tpm_data, u16 tpm_size) +{ + u8 data; + int value = 0; + struct st33zp24_platform_data *pin_infos; + + pin_infos = client->dev.platform_data; + + data = tpm_register; + memcpy(pin_infos->tpm_i2c_buffer[0], &data, sizeof(data)); + memcpy(pin_infos->tpm_i2c_buffer[0] + 1, tpm_data, tpm_size); + value = i2c_master_send(client, pin_infos->tpm_i2c_buffer[0], + tpm_size + 1); + return value; +} /* write8_reg() */ + +/* + * read8_reg + * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. + * @param: tpm_register, the tpm tis register where the data should be read + * @param: tpm_data, the TPM response + * @param: tpm_size, tpm TPM response size to read. + * @return: number of byte read successfully: should be one if success. + */ +static int read8_reg(struct i2c_client *client, u8 tpm_register, + u8 *tpm_data, int tpm_size) +{ + u8 status = 0; + u8 data; + struct st33zp24_platform_data *pin_infos; + + pin_infos = client->dev.platform_data; + + data = TPM_DUMMY_BYTE; + status = write8_reg(client, tpm_register, &data, 1); + if (status == 2) + status = i2c_master_recv(client, tpm_data, tpm_size); + return status; +} /* read8_reg() */ + +/* + * I2C_WRITE_DATA + * Send byte to the TIS register according to the ST33ZP24 I2C protocol. + * @param: client, the chip description + * @param: tpm_register, the tpm tis register where the data should be written + * @param: tpm_data, the tpm_data to write inside the tpm_register + * @param: tpm_size, The length of the data + * @return: number of byte written successfully: should be one if success. + */ +#define I2C_WRITE_DATA(client, tpm_register, tpm_data, tpm_size) \ + (write8_reg(client, tpm_register | \ + TPM_WRITE_DIRECTION, tpm_data, tpm_size)) + +/* + * I2C_READ_DATA + * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. + * @param: tpm, the chip description + * @param: tpm_register, the tpm tis register where the data should be read + * @param: tpm_data, the TPM response + * @param: tpm_size, tpm TPM response size to read. + * @return: number of byte read successfully: should be one if success. + */ +#define I2C_READ_DATA(client, tpm_register, tpm_data, tpm_size) \ + (read8_reg(client, tpm_register, tpm_data, tpm_size)) + +/* + * clear_interruption + * clear the TPM interrupt register. + * @param: tpm, the chip description + */ +static void clear_interruption(struct i2c_client *client) +{ + u8 interrupt; + I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1); + I2C_WRITE_DATA(client, TPM_INT_STATUS, &interrupt, 1); + I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1); +} /* clear_interruption() */ + +/* + * _wait_for_interrupt_serirq_timeout + * @param: tpm, the chip description + * @param: timeout, the timeout of the interrupt + * @return: the status of the interruption. + */ +static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip, + unsigned long timeout) +{ + long status; + struct i2c_client *client; + struct st33zp24_platform_data *pin_infos; + + client = (struct i2c_client *) chip->vendor.iobase; + pin_infos = client->dev.platform_data; + + status = wait_for_completion_interruptible_timeout( + &pin_infos->irq_detection, + timeout); + if (status > 0) + enable_irq(gpio_to_irq(pin_infos->io_serirq)); + gpio_direction_input(pin_infos->io_serirq); + + return status; +} /* wait_for_interrupt_serirq_timeout() */ + +int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition, + unsigned long timeout) +{ + int status = 2; + struct i2c_client *client; + struct st33zp24_platform_data *pin_infos; + + client = (struct i2c_client *) chip->vendor.iobase; + pin_infos = client->dev.platform_data; + + status = _wait_for_interrupt_serirq_timeout(chip, timeout); + if (!status) { + status = -EBUSY; + } else{ + clear_interruption(client); + if (condition) + status = 1; + } + return status; +} + +/* + * tpm_stm_i2c_cancel, cancel is not implemented. + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h + */ +static void tpm_stm_i2c_cancel(struct tpm_chip *chip) +{ + struct i2c_client *client; + u8 data; + + client = (struct i2c_client *) chip->vendor.iobase; + + data = TPM_STS_COMMAND_READY; + I2C_WRITE_DATA(client, TPM_STS, &data, 1); + if (chip->vendor.irq) + wait_for_serirq_timeout(chip, 1, chip->vendor.timeout_a); +} /* tpm_stm_i2c_cancel() */ + +/* + * tpm_stm_spi_status return the TPM_STS register + * @param: chip, the tpm chip description + * @return: the TPM_STS register value. + */ +static u8 tpm_stm_i2c_status(struct tpm_chip *chip) +{ + struct i2c_client *client; + u8 data; + client = (struct i2c_client *) chip->vendor.iobase; + + I2C_READ_DATA(client, TPM_STS, &data, 1); + return data; +} /* tpm_stm_i2c_status() */ + + +/* + * check_locality if the locality is active + * @param: chip, the tpm chip description + * @return: the active locality or -EACCESS. + */ +static int check_locality(struct tpm_chip *chip) +{ + struct i2c_client *client; + u8 data; + u8 status; + + client = (struct i2c_client *) chip->vendor.iobase; + + status = I2C_READ_DATA(client, TPM_ACCESS, &data, 1); + if (status && (data & + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) + return chip->vendor.locality; + + return -EACCES; + +} /* check_locality() */ + +/* + * request_locality request the TPM locality + * @param: chip, the chip description + * @return: the active locality or EACCESS. + */ +static int request_locality(struct tpm_chip *chip) +{ + unsigned long stop; + long rc; + struct i2c_client *client; + u8 data; + + client = (struct i2c_client *) chip->vendor.iobase; + + if (check_locality(chip) == chip->vendor.locality) + return chip->vendor.locality; + + data = TPM_ACCESS_REQUEST_USE; + rc = I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); + if (rc < 0) + goto end; + + if (chip->vendor.irq) { + rc = wait_for_serirq_timeout(chip, (check_locality + (chip) >= 0), + chip->vendor.timeout_a); + if (rc > 0) + return chip->vendor.locality; + } else{ + stop = jiffies + chip->vendor.timeout_a; + do { + if (check_locality(chip) >= 0) + return chip->vendor.locality; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + } + rc = -EACCES; +end: + return rc; +} /* request_locality() */ + +/* + * release_locality release the active locality + * @param: chip, the tpm chip description. + */ +static void release_locality(struct tpm_chip *chip) +{ + struct i2c_client *client; + u8 data; + + client = (struct i2c_client *) chip->vendor.iobase; + data = TPM_ACCESS_ACTIVE_LOCALITY; + + I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); +} + +/* + * get_burstcount return the burstcount address 0x19 0x1A + * @param: chip, the chip description + * return: the burstcount. + */ +static int get_burstcount(struct tpm_chip *chip) +{ + unsigned long stop; + int burstcnt, status; + u8 tpm_reg, temp; + + struct i2c_client *client = (struct i2c_client *) chip->vendor.iobase; + + stop = jiffies + chip->vendor.timeout_d; + do { + tpm_reg = TPM_STS + 1; + status = I2C_READ_DATA(client, tpm_reg, &temp, 1); + if (status < 0) + goto end; + + tpm_reg = tpm_reg + 1; + burstcnt = temp; + status = I2C_READ_DATA(client, tpm_reg, &temp, 1); + if (status < 0) + goto end; + + burstcnt |= temp << 8; + if (burstcnt) + return burstcnt; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + +end: + return -EBUSY; +} /* get_burstcount() */ + +/* + * wait_for_stat wait for a TPM_STS value + * @param: chip, the tpm chip description + * @param: mask, the value mask to wait + * @param: timeout, the timeout + * @param: queue, the wait queue. + * @return: the tpm status, 0 if success, -ETIME if timeout is reached. + */ +static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, + wait_queue_head_t *queue) +{ + unsigned long stop; + long rc; + u8 status; + + if (chip->vendor.irq) { + rc = wait_for_serirq_timeout(chip, ((tpm_stm_i2c_status + (chip) & mask) == + mask), timeout); + if (rc > 0) + return 0; + } else{ + stop = jiffies + timeout; + do { + msleep(TPM_TIMEOUT); + status = tpm_stm_i2c_status(chip); + if ((status & mask) == mask) + return 0; + } while (time_before(jiffies, stop)); + } + return -ETIME; +} /* wait_for_stat() */ + +/* + * recv_data receive data + * @param: chip, the tpm chip description + * @param: buf, the buffer where the data are received + * @param: count, the number of data to receive + * @return: the number of bytes read from TPM FIFO. + */ +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) +{ + int size = 0, burstcnt, len; + struct i2c_client *client; + + client = (struct i2c_client *) chip->vendor.iobase; + + while (size < count && + wait_for_stat(chip, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, + chip->vendor.timeout_c, + &chip->vendor.read_queue) + == 0) { + burstcnt = get_burstcount(chip); + len = min_t(int, burstcnt, count - size); + I2C_READ_DATA(client, TPM_DATA_FIFO, buf + size, len); + size += len; + } + return size; +} + +/* + * tpm_ioserirq_handler the serirq irq handler + * @param: irq, the tpm chip description + * @param: dev_id, the description of the chip + * @return: the status of the handler. + */ +static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) +{ + struct tpm_chip *chip = dev_id; + struct i2c_client *client; + struct st33zp24_platform_data *pin_infos; + + disable_irq_nosync(irq); + + client = (struct i2c_client *) chip->vendor.iobase; + pin_infos = client->dev.platform_data; + + complete(&pin_infos->irq_detection); + return IRQ_HANDLED; +} /* tpm_ioserirq_handler() */ + + +/* + * tpm_stm_i2c_send send TPM commands through the I2C bus. + * + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h + * @param: buf, the buffer to send. + * @param: count, the number of bytes to send. + * @return: In case of success the number of bytes sent. + * In other case, a < 0 value describing the issue. + */ +static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, + size_t len) +{ + u32 ret = 0, ordinal, + status, + burstcnt = 0, i, size; + u8 data; + struct i2c_client *client; + struct st33zp24_platform_data *pin_infos; + + if (chip == NULL) + return -EBUSY; + if (len < TPM_HEADER_SIZE) + return -EBUSY; + + client = (struct i2c_client *)chip->vendor.iobase; + pin_infos = client->dev.platform_data; + + ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); + + client->flags = 0; + + ret = request_locality(chip); + if (ret < 0) + return ret; + + status = tpm_stm_i2c_status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + tpm_stm_i2c_cancel(chip); + if (wait_for_stat + (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, + &chip->vendor.int_queue) < 0) { + ret = -ETIME; + goto out_err; + } + } + + for (i = 0 ; i < len - 1 ;) { + burstcnt = get_burstcount(chip); + size = min_t(int, len - i - 1, burstcnt); + ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf, size); + if (ret < 0) + goto out_err; + + i += size; + } + + status = tpm_stm_i2c_status(chip); + if ((status & TPM_STS_DATA_EXPECT) == 0) { + ret = -EIO; + goto out_err; + } + + ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf + len - 1, 1); + if (ret < 0) + goto out_err; + + status = tpm_stm_i2c_status(chip); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + ret = -EIO; + goto out_err; + } + + data = TPM_STS_GO; + I2C_WRITE_DATA(client, TPM_STS, &data, 1); + + return len; +out_err: + tpm_stm_i2c_cancel(chip); + release_locality(chip); + return ret; +} + +/* + * tpm_stm_i2c_recv received TPM response through the I2C bus. + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h. + * @param: buf, the buffer to store datas. + * @param: count, the number of bytes to send. + * @return: In case of success the number of bytes received. + * In other case, a < 0 value describing the issue. + */ +static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf, + size_t count) +{ + int size = 0; + int expected; + + struct i2c_client *client; + struct st33zp24_platform_data *pin_infos; + + client = (struct i2c_client *)chip->vendor.iobase; + pin_infos = client->dev.platform_data; + + + if (chip == NULL) + return -EBUSY; + + if (count < TPM_HEADER_SIZE) { + size = -EIO; + goto out; + } + + size = recv_data(chip, buf, TPM_HEADER_SIZE); + if (size < TPM_HEADER_SIZE) { + dev_err(chip->dev, "Unable to read header\n"); + goto out; + } + + expected = be32_to_cpu(*(__be32 *) (buf + 2)); + if (expected > count) { + size = -EIO; + goto out; + } + + size += recv_data(chip, &buf[TPM_HEADER_SIZE], + expected - TPM_HEADER_SIZE); + if (size < expected) { + dev_err(chip->dev, "Unable to read remainder of result\n"); + size = -ETIME; + goto out; + } + +out: + chip->vendor.cancel(chip); + release_locality(chip); + return size; +} + +static const struct file_operations tpm_st33_i2c_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = tpm_read, + .write = tpm_write, + .open = tpm_open, + .release = tpm_release, +}; + +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); +static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); +static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); +static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL); +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); +static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); + +static struct attribute *stm_tpm_attrs[] = { + &dev_attr_pubek.attr, + &dev_attr_pcrs.attr, + &dev_attr_enabled.attr, + &dev_attr_active.attr, + &dev_attr_owned.attr, + &dev_attr_temp_deactivated.attr, + &dev_attr_caps.attr, + &dev_attr_cancel.attr, NULL, +}; + +static struct attribute_group stm_tpm_attr_grp = { + .attrs = stm_tpm_attrs +}; + +static struct tpm_vendor_specific st_i2c_tpm = { + .send = tpm_stm_i2c_send, + .recv = tpm_stm_i2c_recv, + .cancel = tpm_stm_i2c_cancel, + .status = tpm_stm_i2c_status, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = TPM_STS_COMMAND_READY, + .attr_group = &stm_tpm_attr_grp, + .miscdev = {.fops = &tpm_st33_i2c_fops,}, +}; + +static int interrupts ; +module_param(interrupts, int, 0444); +MODULE_PARM_DESC(interrupts, "Enable interrupts"); + +static int power_mgt = 1; +module_param(power_mgt, int, 0444); +MODULE_PARM_DESC(power_mgt, "Power Management"); + +/* + * tpm_st33_i2c_probe initialize the TPM device + * @param: client, the i2c_client drescription (TPM I2C description). + * @param: id, the i2c_device_id struct. + * @return: 0 in case of success. + * -1 in other case. + */ +static int +tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + u32 err; + u8 intmask; + struct tpm_chip *chip; + struct st33zp24_platform_data *platform_data; + + err = 0; + + if (client == NULL) { + dev_info(&client->dev, "client is NULL. exiting.\n"); + err = -ENODEV; + goto end; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_info(&client->dev, "client not i2c capable\n"); + err = -ENODEV; + goto end; + } + + chip = tpm_register_hardware(&client->dev, &st_i2c_tpm); + if (!chip) { + dev_info(&client->dev, "fail chip\n"); + err = -ENODEV; + goto end; + } + + platform_data = client->dev.platform_data; + platform_data->tpm_i2c_buffer[0] = + kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); + if (platform_data->tpm_i2c_buffer[0] == NULL) { + err = -ENOMEM; + goto _tpm_clean_answer; + } + platform_data->tpm_i2c_buffer[1] = + kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); + if (platform_data->tpm_i2c_buffer[1] == NULL) { + err = -ENOMEM; + goto _tpm_clean_response; + } + + chip->vendor.iobase = client; + + chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + + chip->vendor.locality = LOCALITY0; + + if (power_mgt) { + err = gpio_request(platform_data->io_lpcpd, "TPM IO_LPCPD"); + if (err) + goto _gpio_init1; + gpio_set_value(platform_data->io_lpcpd, 1); + } + + if (interrupts) { + init_completion(&platform_data->irq_detection); + if (request_locality(chip) != LOCALITY0) { + err = -ENODEV; + goto _tpm_clean_response; + } + err = gpio_request(platform_data->io_serirq, "TPM IO_SERIRQ"); + if (err) + goto _gpio_init2; + + clear_interruption(client); + err = request_irq(gpio_to_irq(platform_data->io_serirq), + &tpm_ioserirq_handler, + IRQF_TRIGGER_HIGH, + "TPM SERIRQ management", chip); + if (err < 0) { + dev_err(chip->dev , "TPM SERIRQ signals %d not available\n", + gpio_to_irq(platform_data->io_serirq)); + goto _irq_set; + } + + err = I2C_READ_DATA(client, TPM_INT_ENABLE, &intmask, 1); + if (err < 0) + goto _irq_set; + + intmask |= TPM_INTF_CMD_READY_INT + | TPM_INTF_FIFO_AVALAIBLE_INT + | TPM_INTF_WAKE_UP_READY_INT + | TPM_INTF_LOCALITY_CHANGE_INT + | TPM_INTF_STS_VALID_INT + | TPM_INTF_DATA_AVAIL_INT; + + err = I2C_WRITE_DATA(client, TPM_INT_ENABLE, &intmask, 1); + if (err < 0) + goto _irq_set; + + intmask = TPM_GLOBAL_INT_ENABLE; + err = I2C_WRITE_DATA(client, (TPM_INT_ENABLE + 3), &intmask, 1); + if (err < 0) + goto _irq_set; + + err = I2C_READ_DATA(client, TPM_INT_STATUS, &intmask, 1); + if (err < 0) + goto _irq_set; + + chip->vendor.irq = interrupts; + + tpm_gen_interrupt(chip); + } + + tpm_get_timeouts(chip); + + i2c_set_clientdata(client, chip); + platform_data->bChipF = false; + + dev_info(chip->dev, "TPM I2C Initialized\n"); + return 0; +_irq_set: + free_irq(gpio_to_irq(platform_data->io_serirq), (void *) chip); +_gpio_init2: + if (platform_data && interrupts) + gpio_free(platform_data->io_serirq); +_gpio_init1: + if (platform_data && power_mgt) + gpio_free(platform_data->io_lpcpd); +_tpm_clean_response: + tpm_remove_hardware(chip->dev); + if (platform_data->tpm_i2c_buffer[1] != NULL) { + kzfree(platform_data->tpm_i2c_buffer[1]); + platform_data->tpm_i2c_buffer[1] = NULL; + } +_tpm_clean_answer: + if (platform_data->tpm_i2c_buffer[0] != NULL) { + kzfree(platform_data->tpm_i2c_buffer[0]); + platform_data->tpm_i2c_buffer[0] = NULL; + } + + platform_data->bChipF = true; +end: + pr_info("TPM I2C initialisation fail\n"); + return err; +} + +/* + * tpm_st33_i2c_remove remove the TPM device + * @param: client, the i2c_client drescription (TPM I2C description). + clear_bit(0, &chip->is_open); + * @return: 0 in case of success. + */ +static __devexit int tpm_st33_i2c_remove(struct i2c_client *client) +{ + struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client); + struct st33zp24_platform_data *pin_infos = + ((struct i2c_client *) chip->vendor.iobase)->dev.platform_data; + + if (pin_infos != NULL) { + free_irq(pin_infos->io_serirq, chip); + + gpio_free(pin_infos->io_serirq); + gpio_free(pin_infos->io_lpcpd); + + if (pin_infos->bChipF != true) + tpm_remove_hardware(chip->dev); + if (pin_infos->tpm_i2c_buffer[1] != NULL) { + kzfree(pin_infos->tpm_i2c_buffer[1]); + pin_infos->tpm_i2c_buffer[1] = NULL; + } + if (pin_infos->tpm_i2c_buffer[0] != NULL) { + kzfree(pin_infos->tpm_i2c_buffer[0]); + pin_infos->tpm_i2c_buffer[0] = NULL; + } + } + + return 0; +} + +/* + * tpm_st33_i2c_pm_suspend suspend the TPM device + * Added: Work around when suspend and no tpm application is running, suspend + * may fail because chip->data_buffer is not set (only set in tpm_open in Linux + * TPM core) + * @param: client, the i2c_client drescription (TPM I2C description). + * @param: mesg, the power management message. + * @return: 0 in case of success. + */ +static int tpm_st33_i2c_pm_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct tpm_chip *chip = + (struct tpm_chip *)i2c_get_clientdata(client); + struct st33zp24_platform_data *pin_infos = + ((struct i2c_client *)chip->vendor.iobase)->dev.platform_data; + int ret = 0; + + if (power_mgt) + gpio_set_value(pin_infos->io_lpcpd, 0); + else{ + if (chip->data_buffer == NULL) + chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; + ret = tpm_pm_suspend(&client->dev); + } + return ret; +} /* tpm_st33_i2c_suspend() */ + +/* + * tpm_st33_i2c_pm_resume resume the TPM device + * @param: client, the i2c_client drescription (TPM I2C description). + * @return: 0 in case of success. + */ +static int tpm_st33_i2c_pm_resume(struct i2c_client *client) +{ + struct tpm_chip *chip = + (struct tpm_chip *)i2c_get_clientdata(client); + struct st33zp24_platform_data *pin_infos = + ((struct i2c_client *)chip->vendor.iobase)->dev.platform_data; + + int ret = 0; + + if (power_mgt) { + gpio_set_value(pin_infos->io_lpcpd, 1); + ret = wait_for_serirq_timeout(chip, + (chip->vendor.status(chip) && + TPM_STS_VALID) == TPM_STS_VALID, + chip->vendor.timeout_b); + } else{ + if (chip->data_buffer == NULL) + chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; + ret = tpm_pm_resume(&client->dev); + if (!ret) + tpm_do_selftest(chip); + } + return ret; +} /* tpm_st33_i2c_pm_resume() */ + +static const struct i2c_device_id tpm_st33_i2c_id[] = { + {TPM_ST33_I2C, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tpm_st33_i2c_id); + +static struct i2c_driver tpm_st33_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TPM_ST33_I2C, + }, + .probe = tpm_st33_i2c_probe, + .remove = tpm_st33_i2c_remove, + .resume = tpm_st33_i2c_pm_resume, + .suspend = tpm_st33_i2c_pm_suspend, + .id_table = tpm_st33_i2c_id +}; + +/* + * tpm_st33_i2c_init initialize driver + * @return: 0 if successful, else non zero value. + */ +static int __init tpm_st33_i2c_init(void) +{ + return i2c_add_driver(&tpm_st33_i2c_driver); +} + +/* + * tpm_st33_i2c_exit The kernel calls this function during unloading the + * module or during shut down process + */ +static void __exit tpm_st33_i2c_exit(void) +{ + i2c_del_driver(&tpm_st33_i2c_driver); +} + +module_init(tpm_st33_i2c_init); +module_exit(tpm_st33_i2c_exit); + +MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)"); +MODULE_DESCRIPTION("STM TPM I2C ST33 Driver"); +MODULE_VERSION("1.2.0"); diff --git a/drivers/char/tpm/tpm_stm_st33_i2c.h b/drivers/char/tpm/tpm_stm_st33_i2c.h new file mode 100644 index 000000000000..d390542db869 --- /dev/null +++ b/drivers/char/tpm/tpm_stm_st33_i2c.h @@ -0,0 +1,81 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 + * Copyright (C) 2009, 2010 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * STMicroelectronics version 1.2.0, Copyright (C) 2010 + * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. + * This is free software, and you are welcome to redistribute it + * under certain conditions. + * + * @Author: Christophe RICARD tpmsupport@st.com + * + * @File: stm_st33_tpm_i2c.h + * + * @Date: 09/15/2010 + */ +#ifndef __STM_ST33_TPM_I2C_MAIN_H__ +#define __STM_ST33_TPM_I2C_MAIN_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpm.h" + +#define MINOR_NUM_I2C 224 + +#define TPM_ACCESS (0x0) +#define TPM_STS (0x18) +#define TPM_HASH_END (0x20) +#define TPM_DATA_FIFO (0x24) +#define TPM_HASH_DATA (0x24) +#define TPM_HASH_START (0x28) +#define TPM_INTF_CAPABILITY (0x14) +#define TPM_INT_STATUS (0x10) +#define TPM_INT_ENABLE (0x08) + +#define TPM_DUMMY_BYTE 0xAA +#define TPM_WRITE_DIRECTION 0x80 +#define TPM_HEADER_SIZE 10 +#define TPM_BUFSIZE 2048 + +#define LOCALITY0 0 + +struct st_tpm_hash { + int size; + u8 *data; +}; + +#endif /* __STM_ST33_TPM_I2C_MAIN_H__ */ diff --git a/include/linux/i2c/tpm_stm_st33_i2c.h b/include/linux/i2c/tpm_stm_st33_i2c.h new file mode 100644 index 000000000000..e3ce42d49422 --- /dev/null +++ b/include/linux/i2c/tpm_stm_st33_i2c.h @@ -0,0 +1,47 @@ +/* +* STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 +* Copyright (C) 2009, 2010 STMicroelectronics +* Christophe RICARD tpmsupport@st.com +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +* +* STMicroelectronics version 1.2.0, Copyright (C) 2010 +* STMicroelectronics comes with ABSOLUTELY NO WARRANTY. +* This is free software, and you are welcome to redistribute it +* under certain conditions. +* +* @File: stm_st33_tpm_i2c.h +* +* @Date: 06/15/2008 +*/ +#ifndef __STM_ST33_TPM_I2C_H__ +#define __STM_ST33_TPM_I2C_H__ + +#include + +#define TPM_ST33_I2C "st33zp24_i2c" +#define TPM_I2C_ST33_ADDR_WR_LOC0 (0x26 >> 1) + + +struct st33zp24_platform_data { + int io_serirq; + int io_lpcpd; + struct i2c_client *client; + bool bChipF; + u8 *tpm_i2c_buffer[2]; /* 0 Request 1 Response */ + struct completion irq_detection; + struct mutex lock; +}; + +#endif /* __STM_ST33_TPM_I2C_H__ */ -- cgit v1.2.1 From 775585e47cd6b5eddf814d53b117b56a1a171553 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 5 Dec 2012 11:36:20 -0600 Subject: tpm: rename vendor data to priv and provide an accessor "data" was too generic a name for what's being used as a generic private pointer by vendor-specific code. Rename it to "priv" and provide a #define for users. Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.h | 4 +++- drivers/char/tpm/tpm_ibmvtpm.c | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 04b4547162c8..cbee6319be75 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -101,12 +101,14 @@ struct tpm_vendor_specific { bool timeout_adjusted; unsigned long duration[3]; /* jiffies */ bool duration_adjusted; - void *data; + void *priv; wait_queue_head_t read_queue; wait_queue_head_t int_queue; }; +#define TPM_VPRIV(c) (c)->vendor.priv + #define TPM_VID_INTEL 0x8086 struct tpm_chip { diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 9978609d93b2..6cd99bc1c5d4 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -64,7 +64,7 @@ static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); if (chip) - return (struct ibmvtpm_dev *)chip->vendor.data; + return (struct ibmvtpm_dev *)TPM_VPRIV(chip); return NULL; } @@ -83,7 +83,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) u16 len; int sig; - ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data; + ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip); if (!ibmvtpm->rtce_buf) { dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n"); @@ -127,7 +127,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) u64 *word = (u64 *) &crq; int rc; - ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data; + ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip); if (!ibmvtpm->rtce_buf) { dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n"); @@ -647,7 +647,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev, ibmvtpm->dev = dev; ibmvtpm->vdev = vio_dev; - chip->vendor.data = (void *)ibmvtpm; + TPM_VPRIV(chip) = (void *)ibmvtpm; spin_lock_init(&ibmvtpm->rtce_lock); -- cgit v1.2.1 From 3d7a7bd75d446bd38367c77a723d7a634aba0261 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 5 Dec 2012 16:52:43 -0600 Subject: tpm: STM i2c driver fixes Store the i2c_client struct in the vendor private pointer. Get rid of the unnecessary include/linux/i2c/ header. Moved include files into the driver c file. Fix smatch warnings. Make use of module_i2c_driver(). Removed unused code from the tpm_stm_st33_i2c.h file. Fix return variable signedness in tpm_stm_i2c_send() and tpm_st33_i2c_probe(). Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_stm_st33_i2c.c | 119 +++++++++++++++++------------------ drivers/char/tpm/tpm_stm_st33_i2c.h | 39 +++--------- include/linux/i2c/tpm_stm_st33_i2c.h | 47 -------------- 3 files changed, 69 insertions(+), 136 deletions(-) delete mode 100644 include/linux/i2c/tpm_stm_st33_i2c.h diff --git a/drivers/char/tpm/tpm_stm_st33_i2c.c b/drivers/char/tpm/tpm_stm_st33_i2c.c index 32cdd5e92141..f78e532acec5 100644 --- a/drivers/char/tpm/tpm_stm_st33_i2c.c +++ b/drivers/char/tpm/tpm_stm_st33_i2c.c @@ -30,8 +30,29 @@ lpc is used as model. */ -#include - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpm.h" #include "tpm_stm_st33_i2c.h" enum stm33zp24_access { @@ -164,7 +185,7 @@ static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip, struct i2c_client *client; struct st33zp24_platform_data *pin_infos; - client = (struct i2c_client *) chip->vendor.iobase; + client = (struct i2c_client *) TPM_VPRIV(chip); pin_infos = client->dev.platform_data; status = wait_for_completion_interruptible_timeout( @@ -177,14 +198,14 @@ static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip, return status; } /* wait_for_interrupt_serirq_timeout() */ -int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition, +static int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition, unsigned long timeout) { int status = 2; struct i2c_client *client; struct st33zp24_platform_data *pin_infos; - client = (struct i2c_client *) chip->vendor.iobase; + client = (struct i2c_client *) TPM_VPRIV(chip); pin_infos = client->dev.platform_data; status = _wait_for_interrupt_serirq_timeout(chip, timeout); @@ -207,7 +228,7 @@ static void tpm_stm_i2c_cancel(struct tpm_chip *chip) struct i2c_client *client; u8 data; - client = (struct i2c_client *) chip->vendor.iobase; + client = (struct i2c_client *) TPM_VPRIV(chip); data = TPM_STS_COMMAND_READY; I2C_WRITE_DATA(client, TPM_STS, &data, 1); @@ -224,7 +245,7 @@ static u8 tpm_stm_i2c_status(struct tpm_chip *chip) { struct i2c_client *client; u8 data; - client = (struct i2c_client *) chip->vendor.iobase; + client = (struct i2c_client *) TPM_VPRIV(chip); I2C_READ_DATA(client, TPM_STS, &data, 1); return data; @@ -242,7 +263,7 @@ static int check_locality(struct tpm_chip *chip) u8 data; u8 status; - client = (struct i2c_client *) chip->vendor.iobase; + client = (struct i2c_client *) TPM_VPRIV(chip); status = I2C_READ_DATA(client, TPM_ACCESS, &data, 1); if (status && (data & @@ -266,7 +287,7 @@ static int request_locality(struct tpm_chip *chip) struct i2c_client *client; u8 data; - client = (struct i2c_client *) chip->vendor.iobase; + client = (struct i2c_client *) TPM_VPRIV(chip); if (check_locality(chip) == chip->vendor.locality) return chip->vendor.locality; @@ -304,7 +325,7 @@ static void release_locality(struct tpm_chip *chip) struct i2c_client *client; u8 data; - client = (struct i2c_client *) chip->vendor.iobase; + client = (struct i2c_client *) TPM_VPRIV(chip); data = TPM_ACCESS_ACTIVE_LOCALITY; I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); @@ -321,7 +342,7 @@ static int get_burstcount(struct tpm_chip *chip) int burstcnt, status; u8 tpm_reg, temp; - struct i2c_client *client = (struct i2c_client *) chip->vendor.iobase; + struct i2c_client *client = (struct i2c_client *) TPM_VPRIV(chip); stop = jiffies + chip->vendor.timeout_d; do { @@ -391,7 +412,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) int size = 0, burstcnt, len; struct i2c_client *client; - client = (struct i2c_client *) chip->vendor.iobase; + client = (struct i2c_client *) TPM_VPRIV(chip); while (size < count && wait_for_stat(chip, @@ -421,7 +442,7 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) disable_irq_nosync(irq); - client = (struct i2c_client *) chip->vendor.iobase; + client = (struct i2c_client *) TPM_VPRIV(chip); pin_infos = client->dev.platform_data; complete(&pin_infos->irq_detection); @@ -441,9 +462,10 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, size_t len) { - u32 ret = 0, ordinal, + u32 ordinal, status, burstcnt = 0, i, size; + int ret; u8 data; struct i2c_client *client; struct st33zp24_platform_data *pin_infos; @@ -453,7 +475,7 @@ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, if (len < TPM_HEADER_SIZE) return -EBUSY; - client = (struct i2c_client *)chip->vendor.iobase; + client = (struct i2c_client *)TPM_VPRIV(chip); pin_infos = client->dev.platform_data; ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); @@ -528,13 +550,12 @@ static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf, struct i2c_client *client; struct st33zp24_platform_data *pin_infos; - client = (struct i2c_client *)chip->vendor.iobase; - pin_infos = client->dev.platform_data; - - if (chip == NULL) return -EBUSY; + client = (struct i2c_client *)TPM_VPRIV(chip); + pin_infos = client->dev.platform_data; + if (count < TPM_HEADER_SIZE) { size = -EIO; goto out; @@ -629,7 +650,7 @@ MODULE_PARM_DESC(power_mgt, "Power Management"); static int tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { - u32 err; + int err; u8 intmask; struct tpm_chip *chip; struct st33zp24_platform_data *platform_data; @@ -666,10 +687,10 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); if (platform_data->tpm_i2c_buffer[1] == NULL) { err = -ENOMEM; - goto _tpm_clean_response; + goto _tpm_clean_response1; } - chip->vendor.iobase = client; + TPM_VPRIV(chip) = client; chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); @@ -689,7 +710,7 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) init_completion(&platform_data->irq_detection); if (request_locality(chip) != LOCALITY0) { err = -ENODEV; - goto _tpm_clean_response; + goto _tpm_clean_response2; } err = gpio_request(platform_data->io_serirq, "TPM IO_SERIRQ"); if (err) @@ -745,23 +766,19 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) _irq_set: free_irq(gpio_to_irq(platform_data->io_serirq), (void *) chip); _gpio_init2: - if (platform_data && interrupts) + if (interrupts) gpio_free(platform_data->io_serirq); _gpio_init1: - if (platform_data && power_mgt) + if (power_mgt) gpio_free(platform_data->io_lpcpd); -_tpm_clean_response: - tpm_remove_hardware(chip->dev); - if (platform_data->tpm_i2c_buffer[1] != NULL) { - kzfree(platform_data->tpm_i2c_buffer[1]); - platform_data->tpm_i2c_buffer[1] = NULL; - } +_tpm_clean_response2: + kzfree(platform_data->tpm_i2c_buffer[1]); + platform_data->tpm_i2c_buffer[1] = NULL; +_tpm_clean_response1: + kzfree(platform_data->tpm_i2c_buffer[0]); + platform_data->tpm_i2c_buffer[0] = NULL; _tpm_clean_answer: - if (platform_data->tpm_i2c_buffer[0] != NULL) { - kzfree(platform_data->tpm_i2c_buffer[0]); - platform_data->tpm_i2c_buffer[0] = NULL; - } - + tpm_remove_hardware(chip->dev); platform_data->bChipF = true; end: pr_info("TPM I2C initialisation fail\n"); @@ -778,7 +795,7 @@ static __devexit int tpm_st33_i2c_remove(struct i2c_client *client) { struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client); struct st33zp24_platform_data *pin_infos = - ((struct i2c_client *) chip->vendor.iobase)->dev.platform_data; + ((struct i2c_client *) TPM_VPRIV(chip))->dev.platform_data; if (pin_infos != NULL) { free_irq(pin_infos->io_serirq, chip); @@ -815,7 +832,7 @@ static int tpm_st33_i2c_pm_suspend(struct i2c_client *client, pm_message_t mesg) struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client); struct st33zp24_platform_data *pin_infos = - ((struct i2c_client *)chip->vendor.iobase)->dev.platform_data; + ((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data; int ret = 0; if (power_mgt) @@ -838,14 +855,14 @@ static int tpm_st33_i2c_pm_resume(struct i2c_client *client) struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client); struct st33zp24_platform_data *pin_infos = - ((struct i2c_client *)chip->vendor.iobase)->dev.platform_data; + ((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data; int ret = 0; if (power_mgt) { gpio_set_value(pin_infos->io_lpcpd, 1); ret = wait_for_serirq_timeout(chip, - (chip->vendor.status(chip) && + (chip->vendor.status(chip) & TPM_STS_VALID) == TPM_STS_VALID, chip->vendor.timeout_b); } else{ @@ -877,27 +894,9 @@ static struct i2c_driver tpm_st33_i2c_driver = { .id_table = tpm_st33_i2c_id }; -/* - * tpm_st33_i2c_init initialize driver - * @return: 0 if successful, else non zero value. - */ -static int __init tpm_st33_i2c_init(void) -{ - return i2c_add_driver(&tpm_st33_i2c_driver); -} - -/* - * tpm_st33_i2c_exit The kernel calls this function during unloading the - * module or during shut down process - */ -static void __exit tpm_st33_i2c_exit(void) -{ - i2c_del_driver(&tpm_st33_i2c_driver); -} - -module_init(tpm_st33_i2c_init); -module_exit(tpm_st33_i2c_exit); +module_i2c_driver(tpm_st33_i2c_driver); MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)"); MODULE_DESCRIPTION("STM TPM I2C ST33 Driver"); MODULE_VERSION("1.2.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_stm_st33_i2c.h b/drivers/char/tpm/tpm_stm_st33_i2c.h index d390542db869..5862d0376bd8 100644 --- a/drivers/char/tpm/tpm_stm_st33_i2c.h +++ b/drivers/char/tpm/tpm_stm_st33_i2c.h @@ -30,32 +30,6 @@ #ifndef __STM_ST33_TPM_I2C_MAIN_H__ #define __STM_ST33_TPM_I2C_MAIN_H__ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tpm.h" - -#define MINOR_NUM_I2C 224 - #define TPM_ACCESS (0x0) #define TPM_STS (0x18) #define TPM_HASH_END (0x20) @@ -73,9 +47,16 @@ #define LOCALITY0 0 -struct st_tpm_hash { - int size; - u8 *data; +#define TPM_ST33_I2C "st33zp24_i2c" + +struct st33zp24_platform_data { + int io_serirq; + int io_lpcpd; + struct i2c_client *client; + bool bChipF; + u8 *tpm_i2c_buffer[2]; /* 0 Request 1 Response */ + struct completion irq_detection; + struct mutex lock; }; #endif /* __STM_ST33_TPM_I2C_MAIN_H__ */ diff --git a/include/linux/i2c/tpm_stm_st33_i2c.h b/include/linux/i2c/tpm_stm_st33_i2c.h deleted file mode 100644 index e3ce42d49422..000000000000 --- a/include/linux/i2c/tpm_stm_st33_i2c.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -* STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 -* Copyright (C) 2009, 2010 STMicroelectronics -* Christophe RICARD tpmsupport@st.com -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License along -* with this program; if not, write to the Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -* -* STMicroelectronics version 1.2.0, Copyright (C) 2010 -* STMicroelectronics comes with ABSOLUTELY NO WARRANTY. -* This is free software, and you are welcome to redistribute it -* under certain conditions. -* -* @File: stm_st33_tpm_i2c.h -* -* @Date: 06/15/2008 -*/ -#ifndef __STM_ST33_TPM_I2C_H__ -#define __STM_ST33_TPM_I2C_H__ - -#include - -#define TPM_ST33_I2C "st33zp24_i2c" -#define TPM_I2C_ST33_ADDR_WR_LOC0 (0x26 >> 1) - - -struct st33zp24_platform_data { - int io_serirq; - int io_lpcpd; - struct i2c_client *client; - bool bChipF; - u8 *tpm_i2c_buffer[2]; /* 0 Request 1 Response */ - struct completion irq_detection; - struct mutex lock; -}; - -#endif /* __STM_ST33_TPM_I2C_H__ */ -- cgit v1.2.1 From 9da228ea0f5ab50032f77eaa0b2a02c04ceede1d Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Mon, 28 Jan 2013 07:52:44 -0600 Subject: tpm: rename STM driver to match other i2c drivers Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_i2c_stm_st33.c | 902 ++++++++++++++++++++++++++++++++++++ drivers/char/tpm/tpm_i2c_stm_st33.h | 62 +++ drivers/char/tpm/tpm_stm_st33_i2c.c | 902 ------------------------------------ drivers/char/tpm/tpm_stm_st33_i2c.h | 62 --- 4 files changed, 964 insertions(+), 964 deletions(-) create mode 100644 drivers/char/tpm/tpm_i2c_stm_st33.c create mode 100644 drivers/char/tpm/tpm_i2c_stm_st33.h delete mode 100644 drivers/char/tpm/tpm_stm_st33_i2c.c delete mode 100644 drivers/char/tpm/tpm_stm_st33_i2c.h diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c new file mode 100644 index 000000000000..cf4f42e3e83d --- /dev/null +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -0,0 +1,902 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 + * Copyright (C) 2009, 2010 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * STMicroelectronics version 1.2.0, Copyright (C) 2010 + * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. + * This is free software, and you are welcome to redistribute it + * under certain conditions. + * + * @Author: Christophe RICARD tpmsupport@st.com + * + * @File: tpm_stm_st33_i2c.c + * + * @Synopsis: + * 09/15/2010: First shot driver tpm_tis driver for + lpc is used as model. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpm.h" +#include "tpm_i2c_stm_st33.h" + +enum stm33zp24_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum stm33zp24_status { + TPM_STS_VALID = 0x80, + TPM_STS_COMMAND_READY = 0x40, + TPM_STS_GO = 0x20, + TPM_STS_DATA_AVAIL = 0x10, + TPM_STS_DATA_EXPECT = 0x08, +}; + +enum stm33zp24_int_flags { + TPM_GLOBAL_INT_ENABLE = 0x80, + TPM_INTF_CMD_READY_INT = 0x080, + TPM_INTF_FIFO_AVALAIBLE_INT = 0x040, + TPM_INTF_WAKE_UP_READY_INT = 0x020, + TPM_INTF_LOCALITY_CHANGE_INT = 0x004, + TPM_INTF_STS_VALID_INT = 0x002, + TPM_INTF_DATA_AVAIL_INT = 0x001, +}; + +enum tis_defaults { + TIS_SHORT_TIMEOUT = 750, + TIS_LONG_TIMEOUT = 2000, +}; + +/* + * write8_reg + * Send byte to the TIS register according to the ST33ZP24 I2C protocol. + * @param: tpm_register, the tpm tis register where the data should be written + * @param: tpm_data, the tpm_data to write inside the tpm_register + * @param: tpm_size, The length of the data + * @return: Returns negative errno, or else the number of bytes written. + */ +static int write8_reg(struct i2c_client *client, u8 tpm_register, + u8 *tpm_data, u16 tpm_size) +{ + u8 data; + int value = 0; + struct st33zp24_platform_data *pin_infos; + + pin_infos = client->dev.platform_data; + + data = tpm_register; + memcpy(pin_infos->tpm_i2c_buffer[0], &data, sizeof(data)); + memcpy(pin_infos->tpm_i2c_buffer[0] + 1, tpm_data, tpm_size); + value = i2c_master_send(client, pin_infos->tpm_i2c_buffer[0], + tpm_size + 1); + return value; +} /* write8_reg() */ + +/* + * read8_reg + * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. + * @param: tpm_register, the tpm tis register where the data should be read + * @param: tpm_data, the TPM response + * @param: tpm_size, tpm TPM response size to read. + * @return: number of byte read successfully: should be one if success. + */ +static int read8_reg(struct i2c_client *client, u8 tpm_register, + u8 *tpm_data, int tpm_size) +{ + u8 status = 0; + u8 data; + struct st33zp24_platform_data *pin_infos; + + pin_infos = client->dev.platform_data; + + data = TPM_DUMMY_BYTE; + status = write8_reg(client, tpm_register, &data, 1); + if (status == 2) + status = i2c_master_recv(client, tpm_data, tpm_size); + return status; +} /* read8_reg() */ + +/* + * I2C_WRITE_DATA + * Send byte to the TIS register according to the ST33ZP24 I2C protocol. + * @param: client, the chip description + * @param: tpm_register, the tpm tis register where the data should be written + * @param: tpm_data, the tpm_data to write inside the tpm_register + * @param: tpm_size, The length of the data + * @return: number of byte written successfully: should be one if success. + */ +#define I2C_WRITE_DATA(client, tpm_register, tpm_data, tpm_size) \ + (write8_reg(client, tpm_register | \ + TPM_WRITE_DIRECTION, tpm_data, tpm_size)) + +/* + * I2C_READ_DATA + * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. + * @param: tpm, the chip description + * @param: tpm_register, the tpm tis register where the data should be read + * @param: tpm_data, the TPM response + * @param: tpm_size, tpm TPM response size to read. + * @return: number of byte read successfully: should be one if success. + */ +#define I2C_READ_DATA(client, tpm_register, tpm_data, tpm_size) \ + (read8_reg(client, tpm_register, tpm_data, tpm_size)) + +/* + * clear_interruption + * clear the TPM interrupt register. + * @param: tpm, the chip description + */ +static void clear_interruption(struct i2c_client *client) +{ + u8 interrupt; + I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1); + I2C_WRITE_DATA(client, TPM_INT_STATUS, &interrupt, 1); + I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1); +} /* clear_interruption() */ + +/* + * _wait_for_interrupt_serirq_timeout + * @param: tpm, the chip description + * @param: timeout, the timeout of the interrupt + * @return: the status of the interruption. + */ +static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip, + unsigned long timeout) +{ + long status; + struct i2c_client *client; + struct st33zp24_platform_data *pin_infos; + + client = (struct i2c_client *) TPM_VPRIV(chip); + pin_infos = client->dev.platform_data; + + status = wait_for_completion_interruptible_timeout( + &pin_infos->irq_detection, + timeout); + if (status > 0) + enable_irq(gpio_to_irq(pin_infos->io_serirq)); + gpio_direction_input(pin_infos->io_serirq); + + return status; +} /* wait_for_interrupt_serirq_timeout() */ + +static int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition, + unsigned long timeout) +{ + int status = 2; + struct i2c_client *client; + struct st33zp24_platform_data *pin_infos; + + client = (struct i2c_client *) TPM_VPRIV(chip); + pin_infos = client->dev.platform_data; + + status = _wait_for_interrupt_serirq_timeout(chip, timeout); + if (!status) { + status = -EBUSY; + } else{ + clear_interruption(client); + if (condition) + status = 1; + } + return status; +} + +/* + * tpm_stm_i2c_cancel, cancel is not implemented. + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h + */ +static void tpm_stm_i2c_cancel(struct tpm_chip *chip) +{ + struct i2c_client *client; + u8 data; + + client = (struct i2c_client *) TPM_VPRIV(chip); + + data = TPM_STS_COMMAND_READY; + I2C_WRITE_DATA(client, TPM_STS, &data, 1); + if (chip->vendor.irq) + wait_for_serirq_timeout(chip, 1, chip->vendor.timeout_a); +} /* tpm_stm_i2c_cancel() */ + +/* + * tpm_stm_spi_status return the TPM_STS register + * @param: chip, the tpm chip description + * @return: the TPM_STS register value. + */ +static u8 tpm_stm_i2c_status(struct tpm_chip *chip) +{ + struct i2c_client *client; + u8 data; + client = (struct i2c_client *) TPM_VPRIV(chip); + + I2C_READ_DATA(client, TPM_STS, &data, 1); + return data; +} /* tpm_stm_i2c_status() */ + + +/* + * check_locality if the locality is active + * @param: chip, the tpm chip description + * @return: the active locality or -EACCESS. + */ +static int check_locality(struct tpm_chip *chip) +{ + struct i2c_client *client; + u8 data; + u8 status; + + client = (struct i2c_client *) TPM_VPRIV(chip); + + status = I2C_READ_DATA(client, TPM_ACCESS, &data, 1); + if (status && (data & + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) + return chip->vendor.locality; + + return -EACCES; + +} /* check_locality() */ + +/* + * request_locality request the TPM locality + * @param: chip, the chip description + * @return: the active locality or EACCESS. + */ +static int request_locality(struct tpm_chip *chip) +{ + unsigned long stop; + long rc; + struct i2c_client *client; + u8 data; + + client = (struct i2c_client *) TPM_VPRIV(chip); + + if (check_locality(chip) == chip->vendor.locality) + return chip->vendor.locality; + + data = TPM_ACCESS_REQUEST_USE; + rc = I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); + if (rc < 0) + goto end; + + if (chip->vendor.irq) { + rc = wait_for_serirq_timeout(chip, (check_locality + (chip) >= 0), + chip->vendor.timeout_a); + if (rc > 0) + return chip->vendor.locality; + } else{ + stop = jiffies + chip->vendor.timeout_a; + do { + if (check_locality(chip) >= 0) + return chip->vendor.locality; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + } + rc = -EACCES; +end: + return rc; +} /* request_locality() */ + +/* + * release_locality release the active locality + * @param: chip, the tpm chip description. + */ +static void release_locality(struct tpm_chip *chip) +{ + struct i2c_client *client; + u8 data; + + client = (struct i2c_client *) TPM_VPRIV(chip); + data = TPM_ACCESS_ACTIVE_LOCALITY; + + I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); +} + +/* + * get_burstcount return the burstcount address 0x19 0x1A + * @param: chip, the chip description + * return: the burstcount. + */ +static int get_burstcount(struct tpm_chip *chip) +{ + unsigned long stop; + int burstcnt, status; + u8 tpm_reg, temp; + + struct i2c_client *client = (struct i2c_client *) TPM_VPRIV(chip); + + stop = jiffies + chip->vendor.timeout_d; + do { + tpm_reg = TPM_STS + 1; + status = I2C_READ_DATA(client, tpm_reg, &temp, 1); + if (status < 0) + goto end; + + tpm_reg = tpm_reg + 1; + burstcnt = temp; + status = I2C_READ_DATA(client, tpm_reg, &temp, 1); + if (status < 0) + goto end; + + burstcnt |= temp << 8; + if (burstcnt) + return burstcnt; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + +end: + return -EBUSY; +} /* get_burstcount() */ + +/* + * wait_for_stat wait for a TPM_STS value + * @param: chip, the tpm chip description + * @param: mask, the value mask to wait + * @param: timeout, the timeout + * @param: queue, the wait queue. + * @return: the tpm status, 0 if success, -ETIME if timeout is reached. + */ +static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, + wait_queue_head_t *queue) +{ + unsigned long stop; + long rc; + u8 status; + + if (chip->vendor.irq) { + rc = wait_for_serirq_timeout(chip, ((tpm_stm_i2c_status + (chip) & mask) == + mask), timeout); + if (rc > 0) + return 0; + } else{ + stop = jiffies + timeout; + do { + msleep(TPM_TIMEOUT); + status = tpm_stm_i2c_status(chip); + if ((status & mask) == mask) + return 0; + } while (time_before(jiffies, stop)); + } + return -ETIME; +} /* wait_for_stat() */ + +/* + * recv_data receive data + * @param: chip, the tpm chip description + * @param: buf, the buffer where the data are received + * @param: count, the number of data to receive + * @return: the number of bytes read from TPM FIFO. + */ +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) +{ + int size = 0, burstcnt, len; + struct i2c_client *client; + + client = (struct i2c_client *) TPM_VPRIV(chip); + + while (size < count && + wait_for_stat(chip, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, + chip->vendor.timeout_c, + &chip->vendor.read_queue) + == 0) { + burstcnt = get_burstcount(chip); + len = min_t(int, burstcnt, count - size); + I2C_READ_DATA(client, TPM_DATA_FIFO, buf + size, len); + size += len; + } + return size; +} + +/* + * tpm_ioserirq_handler the serirq irq handler + * @param: irq, the tpm chip description + * @param: dev_id, the description of the chip + * @return: the status of the handler. + */ +static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) +{ + struct tpm_chip *chip = dev_id; + struct i2c_client *client; + struct st33zp24_platform_data *pin_infos; + + disable_irq_nosync(irq); + + client = (struct i2c_client *) TPM_VPRIV(chip); + pin_infos = client->dev.platform_data; + + complete(&pin_infos->irq_detection); + return IRQ_HANDLED; +} /* tpm_ioserirq_handler() */ + + +/* + * tpm_stm_i2c_send send TPM commands through the I2C bus. + * + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h + * @param: buf, the buffer to send. + * @param: count, the number of bytes to send. + * @return: In case of success the number of bytes sent. + * In other case, a < 0 value describing the issue. + */ +static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, + size_t len) +{ + u32 ordinal, + status, + burstcnt = 0, i, size; + int ret; + u8 data; + struct i2c_client *client; + struct st33zp24_platform_data *pin_infos; + + if (chip == NULL) + return -EBUSY; + if (len < TPM_HEADER_SIZE) + return -EBUSY; + + client = (struct i2c_client *)TPM_VPRIV(chip); + pin_infos = client->dev.platform_data; + + ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); + + client->flags = 0; + + ret = request_locality(chip); + if (ret < 0) + return ret; + + status = tpm_stm_i2c_status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + tpm_stm_i2c_cancel(chip); + if (wait_for_stat + (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, + &chip->vendor.int_queue) < 0) { + ret = -ETIME; + goto out_err; + } + } + + for (i = 0 ; i < len - 1 ;) { + burstcnt = get_burstcount(chip); + size = min_t(int, len - i - 1, burstcnt); + ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf, size); + if (ret < 0) + goto out_err; + + i += size; + } + + status = tpm_stm_i2c_status(chip); + if ((status & TPM_STS_DATA_EXPECT) == 0) { + ret = -EIO; + goto out_err; + } + + ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf + len - 1, 1); + if (ret < 0) + goto out_err; + + status = tpm_stm_i2c_status(chip); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + ret = -EIO; + goto out_err; + } + + data = TPM_STS_GO; + I2C_WRITE_DATA(client, TPM_STS, &data, 1); + + return len; +out_err: + tpm_stm_i2c_cancel(chip); + release_locality(chip); + return ret; +} + +/* + * tpm_stm_i2c_recv received TPM response through the I2C bus. + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h. + * @param: buf, the buffer to store datas. + * @param: count, the number of bytes to send. + * @return: In case of success the number of bytes received. + * In other case, a < 0 value describing the issue. + */ +static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf, + size_t count) +{ + int size = 0; + int expected; + + struct i2c_client *client; + struct st33zp24_platform_data *pin_infos; + + if (chip == NULL) + return -EBUSY; + + client = (struct i2c_client *)TPM_VPRIV(chip); + pin_infos = client->dev.platform_data; + + if (count < TPM_HEADER_SIZE) { + size = -EIO; + goto out; + } + + size = recv_data(chip, buf, TPM_HEADER_SIZE); + if (size < TPM_HEADER_SIZE) { + dev_err(chip->dev, "Unable to read header\n"); + goto out; + } + + expected = be32_to_cpu(*(__be32 *) (buf + 2)); + if (expected > count) { + size = -EIO; + goto out; + } + + size += recv_data(chip, &buf[TPM_HEADER_SIZE], + expected - TPM_HEADER_SIZE); + if (size < expected) { + dev_err(chip->dev, "Unable to read remainder of result\n"); + size = -ETIME; + goto out; + } + +out: + chip->vendor.cancel(chip); + release_locality(chip); + return size; +} + +static const struct file_operations tpm_st33_i2c_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = tpm_read, + .write = tpm_write, + .open = tpm_open, + .release = tpm_release, +}; + +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); +static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); +static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); +static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL); +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); +static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); + +static struct attribute *stm_tpm_attrs[] = { + &dev_attr_pubek.attr, + &dev_attr_pcrs.attr, + &dev_attr_enabled.attr, + &dev_attr_active.attr, + &dev_attr_owned.attr, + &dev_attr_temp_deactivated.attr, + &dev_attr_caps.attr, + &dev_attr_cancel.attr, NULL, +}; + +static struct attribute_group stm_tpm_attr_grp = { + .attrs = stm_tpm_attrs +}; + +static struct tpm_vendor_specific st_i2c_tpm = { + .send = tpm_stm_i2c_send, + .recv = tpm_stm_i2c_recv, + .cancel = tpm_stm_i2c_cancel, + .status = tpm_stm_i2c_status, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = TPM_STS_COMMAND_READY, + .attr_group = &stm_tpm_attr_grp, + .miscdev = {.fops = &tpm_st33_i2c_fops,}, +}; + +static int interrupts ; +module_param(interrupts, int, 0444); +MODULE_PARM_DESC(interrupts, "Enable interrupts"); + +static int power_mgt = 1; +module_param(power_mgt, int, 0444); +MODULE_PARM_DESC(power_mgt, "Power Management"); + +/* + * tpm_st33_i2c_probe initialize the TPM device + * @param: client, the i2c_client drescription (TPM I2C description). + * @param: id, the i2c_device_id struct. + * @return: 0 in case of success. + * -1 in other case. + */ +static int +tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int err; + u8 intmask; + struct tpm_chip *chip; + struct st33zp24_platform_data *platform_data; + + err = 0; + + if (client == NULL) { + dev_info(&client->dev, "client is NULL. exiting.\n"); + err = -ENODEV; + goto end; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_info(&client->dev, "client not i2c capable\n"); + err = -ENODEV; + goto end; + } + + chip = tpm_register_hardware(&client->dev, &st_i2c_tpm); + if (!chip) { + dev_info(&client->dev, "fail chip\n"); + err = -ENODEV; + goto end; + } + + platform_data = client->dev.platform_data; + platform_data->tpm_i2c_buffer[0] = + kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); + if (platform_data->tpm_i2c_buffer[0] == NULL) { + err = -ENOMEM; + goto _tpm_clean_answer; + } + platform_data->tpm_i2c_buffer[1] = + kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); + if (platform_data->tpm_i2c_buffer[1] == NULL) { + err = -ENOMEM; + goto _tpm_clean_response1; + } + + TPM_VPRIV(chip) = client; + + chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + + chip->vendor.locality = LOCALITY0; + + if (power_mgt) { + err = gpio_request(platform_data->io_lpcpd, "TPM IO_LPCPD"); + if (err) + goto _gpio_init1; + gpio_set_value(platform_data->io_lpcpd, 1); + } + + if (interrupts) { + init_completion(&platform_data->irq_detection); + if (request_locality(chip) != LOCALITY0) { + err = -ENODEV; + goto _tpm_clean_response2; + } + err = gpio_request(platform_data->io_serirq, "TPM IO_SERIRQ"); + if (err) + goto _gpio_init2; + + clear_interruption(client); + err = request_irq(gpio_to_irq(platform_data->io_serirq), + &tpm_ioserirq_handler, + IRQF_TRIGGER_HIGH, + "TPM SERIRQ management", chip); + if (err < 0) { + dev_err(chip->dev , "TPM SERIRQ signals %d not available\n", + gpio_to_irq(platform_data->io_serirq)); + goto _irq_set; + } + + err = I2C_READ_DATA(client, TPM_INT_ENABLE, &intmask, 1); + if (err < 0) + goto _irq_set; + + intmask |= TPM_INTF_CMD_READY_INT + | TPM_INTF_FIFO_AVALAIBLE_INT + | TPM_INTF_WAKE_UP_READY_INT + | TPM_INTF_LOCALITY_CHANGE_INT + | TPM_INTF_STS_VALID_INT + | TPM_INTF_DATA_AVAIL_INT; + + err = I2C_WRITE_DATA(client, TPM_INT_ENABLE, &intmask, 1); + if (err < 0) + goto _irq_set; + + intmask = TPM_GLOBAL_INT_ENABLE; + err = I2C_WRITE_DATA(client, (TPM_INT_ENABLE + 3), &intmask, 1); + if (err < 0) + goto _irq_set; + + err = I2C_READ_DATA(client, TPM_INT_STATUS, &intmask, 1); + if (err < 0) + goto _irq_set; + + chip->vendor.irq = interrupts; + + tpm_gen_interrupt(chip); + } + + tpm_get_timeouts(chip); + + i2c_set_clientdata(client, chip); + platform_data->bChipF = false; + + dev_info(chip->dev, "TPM I2C Initialized\n"); + return 0; +_irq_set: + free_irq(gpio_to_irq(platform_data->io_serirq), (void *) chip); +_gpio_init2: + if (interrupts) + gpio_free(platform_data->io_serirq); +_gpio_init1: + if (power_mgt) + gpio_free(platform_data->io_lpcpd); +_tpm_clean_response2: + kzfree(platform_data->tpm_i2c_buffer[1]); + platform_data->tpm_i2c_buffer[1] = NULL; +_tpm_clean_response1: + kzfree(platform_data->tpm_i2c_buffer[0]); + platform_data->tpm_i2c_buffer[0] = NULL; +_tpm_clean_answer: + tpm_remove_hardware(chip->dev); + platform_data->bChipF = true; +end: + pr_info("TPM I2C initialisation fail\n"); + return err; +} + +/* + * tpm_st33_i2c_remove remove the TPM device + * @param: client, the i2c_client drescription (TPM I2C description). + clear_bit(0, &chip->is_open); + * @return: 0 in case of success. + */ +static __devexit int tpm_st33_i2c_remove(struct i2c_client *client) +{ + struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client); + struct st33zp24_platform_data *pin_infos = + ((struct i2c_client *) TPM_VPRIV(chip))->dev.platform_data; + + if (pin_infos != NULL) { + free_irq(pin_infos->io_serirq, chip); + + gpio_free(pin_infos->io_serirq); + gpio_free(pin_infos->io_lpcpd); + + if (pin_infos->bChipF != true) + tpm_remove_hardware(chip->dev); + if (pin_infos->tpm_i2c_buffer[1] != NULL) { + kzfree(pin_infos->tpm_i2c_buffer[1]); + pin_infos->tpm_i2c_buffer[1] = NULL; + } + if (pin_infos->tpm_i2c_buffer[0] != NULL) { + kzfree(pin_infos->tpm_i2c_buffer[0]); + pin_infos->tpm_i2c_buffer[0] = NULL; + } + } + + return 0; +} + +/* + * tpm_st33_i2c_pm_suspend suspend the TPM device + * Added: Work around when suspend and no tpm application is running, suspend + * may fail because chip->data_buffer is not set (only set in tpm_open in Linux + * TPM core) + * @param: client, the i2c_client drescription (TPM I2C description). + * @param: mesg, the power management message. + * @return: 0 in case of success. + */ +static int tpm_st33_i2c_pm_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct tpm_chip *chip = + (struct tpm_chip *)i2c_get_clientdata(client); + struct st33zp24_platform_data *pin_infos = + ((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data; + int ret = 0; + + if (power_mgt) + gpio_set_value(pin_infos->io_lpcpd, 0); + else{ + if (chip->data_buffer == NULL) + chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; + ret = tpm_pm_suspend(&client->dev); + } + return ret; +} /* tpm_st33_i2c_suspend() */ + +/* + * tpm_st33_i2c_pm_resume resume the TPM device + * @param: client, the i2c_client drescription (TPM I2C description). + * @return: 0 in case of success. + */ +static int tpm_st33_i2c_pm_resume(struct i2c_client *client) +{ + struct tpm_chip *chip = + (struct tpm_chip *)i2c_get_clientdata(client); + struct st33zp24_platform_data *pin_infos = + ((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data; + + int ret = 0; + + if (power_mgt) { + gpio_set_value(pin_infos->io_lpcpd, 1); + ret = wait_for_serirq_timeout(chip, + (chip->vendor.status(chip) & + TPM_STS_VALID) == TPM_STS_VALID, + chip->vendor.timeout_b); + } else{ + if (chip->data_buffer == NULL) + chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; + ret = tpm_pm_resume(&client->dev); + if (!ret) + tpm_do_selftest(chip); + } + return ret; +} /* tpm_st33_i2c_pm_resume() */ + +static const struct i2c_device_id tpm_st33_i2c_id[] = { + {TPM_ST33_I2C, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tpm_st33_i2c_id); + +static struct i2c_driver tpm_st33_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TPM_ST33_I2C, + }, + .probe = tpm_st33_i2c_probe, + .remove = tpm_st33_i2c_remove, + .resume = tpm_st33_i2c_pm_resume, + .suspend = tpm_st33_i2c_pm_suspend, + .id_table = tpm_st33_i2c_id +}; + +module_i2c_driver(tpm_st33_i2c_driver); + +MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)"); +MODULE_DESCRIPTION("STM TPM I2C ST33 Driver"); +MODULE_VERSION("1.2.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.h b/drivers/char/tpm/tpm_i2c_stm_st33.h new file mode 100644 index 000000000000..5862d0376bd8 --- /dev/null +++ b/drivers/char/tpm/tpm_i2c_stm_st33.h @@ -0,0 +1,62 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 + * Copyright (C) 2009, 2010 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * STMicroelectronics version 1.2.0, Copyright (C) 2010 + * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. + * This is free software, and you are welcome to redistribute it + * under certain conditions. + * + * @Author: Christophe RICARD tpmsupport@st.com + * + * @File: stm_st33_tpm_i2c.h + * + * @Date: 09/15/2010 + */ +#ifndef __STM_ST33_TPM_I2C_MAIN_H__ +#define __STM_ST33_TPM_I2C_MAIN_H__ + +#define TPM_ACCESS (0x0) +#define TPM_STS (0x18) +#define TPM_HASH_END (0x20) +#define TPM_DATA_FIFO (0x24) +#define TPM_HASH_DATA (0x24) +#define TPM_HASH_START (0x28) +#define TPM_INTF_CAPABILITY (0x14) +#define TPM_INT_STATUS (0x10) +#define TPM_INT_ENABLE (0x08) + +#define TPM_DUMMY_BYTE 0xAA +#define TPM_WRITE_DIRECTION 0x80 +#define TPM_HEADER_SIZE 10 +#define TPM_BUFSIZE 2048 + +#define LOCALITY0 0 + +#define TPM_ST33_I2C "st33zp24_i2c" + +struct st33zp24_platform_data { + int io_serirq; + int io_lpcpd; + struct i2c_client *client; + bool bChipF; + u8 *tpm_i2c_buffer[2]; /* 0 Request 1 Response */ + struct completion irq_detection; + struct mutex lock; +}; + +#endif /* __STM_ST33_TPM_I2C_MAIN_H__ */ diff --git a/drivers/char/tpm/tpm_stm_st33_i2c.c b/drivers/char/tpm/tpm_stm_st33_i2c.c deleted file mode 100644 index f78e532acec5..000000000000 --- a/drivers/char/tpm/tpm_stm_st33_i2c.c +++ /dev/null @@ -1,902 +0,0 @@ -/* - * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 - * Copyright (C) 2009, 2010 STMicroelectronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * STMicroelectronics version 1.2.0, Copyright (C) 2010 - * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. - * This is free software, and you are welcome to redistribute it - * under certain conditions. - * - * @Author: Christophe RICARD tpmsupport@st.com - * - * @File: tpm_stm_st33_i2c.c - * - * @Synopsis: - * 09/15/2010: First shot driver tpm_tis driver for - lpc is used as model. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tpm.h" -#include "tpm_stm_st33_i2c.h" - -enum stm33zp24_access { - TPM_ACCESS_VALID = 0x80, - TPM_ACCESS_ACTIVE_LOCALITY = 0x20, - TPM_ACCESS_REQUEST_PENDING = 0x04, - TPM_ACCESS_REQUEST_USE = 0x02, -}; - -enum stm33zp24_status { - TPM_STS_VALID = 0x80, - TPM_STS_COMMAND_READY = 0x40, - TPM_STS_GO = 0x20, - TPM_STS_DATA_AVAIL = 0x10, - TPM_STS_DATA_EXPECT = 0x08, -}; - -enum stm33zp24_int_flags { - TPM_GLOBAL_INT_ENABLE = 0x80, - TPM_INTF_CMD_READY_INT = 0x080, - TPM_INTF_FIFO_AVALAIBLE_INT = 0x040, - TPM_INTF_WAKE_UP_READY_INT = 0x020, - TPM_INTF_LOCALITY_CHANGE_INT = 0x004, - TPM_INTF_STS_VALID_INT = 0x002, - TPM_INTF_DATA_AVAIL_INT = 0x001, -}; - -enum tis_defaults { - TIS_SHORT_TIMEOUT = 750, - TIS_LONG_TIMEOUT = 2000, -}; - -/* - * write8_reg - * Send byte to the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_register, the tpm tis register where the data should be written - * @param: tpm_data, the tpm_data to write inside the tpm_register - * @param: tpm_size, The length of the data - * @return: Returns negative errno, or else the number of bytes written. - */ -static int write8_reg(struct i2c_client *client, u8 tpm_register, - u8 *tpm_data, u16 tpm_size) -{ - u8 data; - int value = 0; - struct st33zp24_platform_data *pin_infos; - - pin_infos = client->dev.platform_data; - - data = tpm_register; - memcpy(pin_infos->tpm_i2c_buffer[0], &data, sizeof(data)); - memcpy(pin_infos->tpm_i2c_buffer[0] + 1, tpm_data, tpm_size); - value = i2c_master_send(client, pin_infos->tpm_i2c_buffer[0], - tpm_size + 1); - return value; -} /* write8_reg() */ - -/* - * read8_reg - * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_register, the tpm tis register where the data should be read - * @param: tpm_data, the TPM response - * @param: tpm_size, tpm TPM response size to read. - * @return: number of byte read successfully: should be one if success. - */ -static int read8_reg(struct i2c_client *client, u8 tpm_register, - u8 *tpm_data, int tpm_size) -{ - u8 status = 0; - u8 data; - struct st33zp24_platform_data *pin_infos; - - pin_infos = client->dev.platform_data; - - data = TPM_DUMMY_BYTE; - status = write8_reg(client, tpm_register, &data, 1); - if (status == 2) - status = i2c_master_recv(client, tpm_data, tpm_size); - return status; -} /* read8_reg() */ - -/* - * I2C_WRITE_DATA - * Send byte to the TIS register according to the ST33ZP24 I2C protocol. - * @param: client, the chip description - * @param: tpm_register, the tpm tis register where the data should be written - * @param: tpm_data, the tpm_data to write inside the tpm_register - * @param: tpm_size, The length of the data - * @return: number of byte written successfully: should be one if success. - */ -#define I2C_WRITE_DATA(client, tpm_register, tpm_data, tpm_size) \ - (write8_reg(client, tpm_register | \ - TPM_WRITE_DIRECTION, tpm_data, tpm_size)) - -/* - * I2C_READ_DATA - * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm, the chip description - * @param: tpm_register, the tpm tis register where the data should be read - * @param: tpm_data, the TPM response - * @param: tpm_size, tpm TPM response size to read. - * @return: number of byte read successfully: should be one if success. - */ -#define I2C_READ_DATA(client, tpm_register, tpm_data, tpm_size) \ - (read8_reg(client, tpm_register, tpm_data, tpm_size)) - -/* - * clear_interruption - * clear the TPM interrupt register. - * @param: tpm, the chip description - */ -static void clear_interruption(struct i2c_client *client) -{ - u8 interrupt; - I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1); - I2C_WRITE_DATA(client, TPM_INT_STATUS, &interrupt, 1); - I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1); -} /* clear_interruption() */ - -/* - * _wait_for_interrupt_serirq_timeout - * @param: tpm, the chip description - * @param: timeout, the timeout of the interrupt - * @return: the status of the interruption. - */ -static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip, - unsigned long timeout) -{ - long status; - struct i2c_client *client; - struct st33zp24_platform_data *pin_infos; - - client = (struct i2c_client *) TPM_VPRIV(chip); - pin_infos = client->dev.platform_data; - - status = wait_for_completion_interruptible_timeout( - &pin_infos->irq_detection, - timeout); - if (status > 0) - enable_irq(gpio_to_irq(pin_infos->io_serirq)); - gpio_direction_input(pin_infos->io_serirq); - - return status; -} /* wait_for_interrupt_serirq_timeout() */ - -static int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition, - unsigned long timeout) -{ - int status = 2; - struct i2c_client *client; - struct st33zp24_platform_data *pin_infos; - - client = (struct i2c_client *) TPM_VPRIV(chip); - pin_infos = client->dev.platform_data; - - status = _wait_for_interrupt_serirq_timeout(chip, timeout); - if (!status) { - status = -EBUSY; - } else{ - clear_interruption(client); - if (condition) - status = 1; - } - return status; -} - -/* - * tpm_stm_i2c_cancel, cancel is not implemented. - * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h - */ -static void tpm_stm_i2c_cancel(struct tpm_chip *chip) -{ - struct i2c_client *client; - u8 data; - - client = (struct i2c_client *) TPM_VPRIV(chip); - - data = TPM_STS_COMMAND_READY; - I2C_WRITE_DATA(client, TPM_STS, &data, 1); - if (chip->vendor.irq) - wait_for_serirq_timeout(chip, 1, chip->vendor.timeout_a); -} /* tpm_stm_i2c_cancel() */ - -/* - * tpm_stm_spi_status return the TPM_STS register - * @param: chip, the tpm chip description - * @return: the TPM_STS register value. - */ -static u8 tpm_stm_i2c_status(struct tpm_chip *chip) -{ - struct i2c_client *client; - u8 data; - client = (struct i2c_client *) TPM_VPRIV(chip); - - I2C_READ_DATA(client, TPM_STS, &data, 1); - return data; -} /* tpm_stm_i2c_status() */ - - -/* - * check_locality if the locality is active - * @param: chip, the tpm chip description - * @return: the active locality or -EACCESS. - */ -static int check_locality(struct tpm_chip *chip) -{ - struct i2c_client *client; - u8 data; - u8 status; - - client = (struct i2c_client *) TPM_VPRIV(chip); - - status = I2C_READ_DATA(client, TPM_ACCESS, &data, 1); - if (status && (data & - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) - return chip->vendor.locality; - - return -EACCES; - -} /* check_locality() */ - -/* - * request_locality request the TPM locality - * @param: chip, the chip description - * @return: the active locality or EACCESS. - */ -static int request_locality(struct tpm_chip *chip) -{ - unsigned long stop; - long rc; - struct i2c_client *client; - u8 data; - - client = (struct i2c_client *) TPM_VPRIV(chip); - - if (check_locality(chip) == chip->vendor.locality) - return chip->vendor.locality; - - data = TPM_ACCESS_REQUEST_USE; - rc = I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); - if (rc < 0) - goto end; - - if (chip->vendor.irq) { - rc = wait_for_serirq_timeout(chip, (check_locality - (chip) >= 0), - chip->vendor.timeout_a); - if (rc > 0) - return chip->vendor.locality; - } else{ - stop = jiffies + chip->vendor.timeout_a; - do { - if (check_locality(chip) >= 0) - return chip->vendor.locality; - msleep(TPM_TIMEOUT); - } while (time_before(jiffies, stop)); - } - rc = -EACCES; -end: - return rc; -} /* request_locality() */ - -/* - * release_locality release the active locality - * @param: chip, the tpm chip description. - */ -static void release_locality(struct tpm_chip *chip) -{ - struct i2c_client *client; - u8 data; - - client = (struct i2c_client *) TPM_VPRIV(chip); - data = TPM_ACCESS_ACTIVE_LOCALITY; - - I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1); -} - -/* - * get_burstcount return the burstcount address 0x19 0x1A - * @param: chip, the chip description - * return: the burstcount. - */ -static int get_burstcount(struct tpm_chip *chip) -{ - unsigned long stop; - int burstcnt, status; - u8 tpm_reg, temp; - - struct i2c_client *client = (struct i2c_client *) TPM_VPRIV(chip); - - stop = jiffies + chip->vendor.timeout_d; - do { - tpm_reg = TPM_STS + 1; - status = I2C_READ_DATA(client, tpm_reg, &temp, 1); - if (status < 0) - goto end; - - tpm_reg = tpm_reg + 1; - burstcnt = temp; - status = I2C_READ_DATA(client, tpm_reg, &temp, 1); - if (status < 0) - goto end; - - burstcnt |= temp << 8; - if (burstcnt) - return burstcnt; - msleep(TPM_TIMEOUT); - } while (time_before(jiffies, stop)); - -end: - return -EBUSY; -} /* get_burstcount() */ - -/* - * wait_for_stat wait for a TPM_STS value - * @param: chip, the tpm chip description - * @param: mask, the value mask to wait - * @param: timeout, the timeout - * @param: queue, the wait queue. - * @return: the tpm status, 0 if success, -ETIME if timeout is reached. - */ -static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, - wait_queue_head_t *queue) -{ - unsigned long stop; - long rc; - u8 status; - - if (chip->vendor.irq) { - rc = wait_for_serirq_timeout(chip, ((tpm_stm_i2c_status - (chip) & mask) == - mask), timeout); - if (rc > 0) - return 0; - } else{ - stop = jiffies + timeout; - do { - msleep(TPM_TIMEOUT); - status = tpm_stm_i2c_status(chip); - if ((status & mask) == mask) - return 0; - } while (time_before(jiffies, stop)); - } - return -ETIME; -} /* wait_for_stat() */ - -/* - * recv_data receive data - * @param: chip, the tpm chip description - * @param: buf, the buffer where the data are received - * @param: count, the number of data to receive - * @return: the number of bytes read from TPM FIFO. - */ -static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) -{ - int size = 0, burstcnt, len; - struct i2c_client *client; - - client = (struct i2c_client *) TPM_VPRIV(chip); - - while (size < count && - wait_for_stat(chip, - TPM_STS_DATA_AVAIL | TPM_STS_VALID, - chip->vendor.timeout_c, - &chip->vendor.read_queue) - == 0) { - burstcnt = get_burstcount(chip); - len = min_t(int, burstcnt, count - size); - I2C_READ_DATA(client, TPM_DATA_FIFO, buf + size, len); - size += len; - } - return size; -} - -/* - * tpm_ioserirq_handler the serirq irq handler - * @param: irq, the tpm chip description - * @param: dev_id, the description of the chip - * @return: the status of the handler. - */ -static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) -{ - struct tpm_chip *chip = dev_id; - struct i2c_client *client; - struct st33zp24_platform_data *pin_infos; - - disable_irq_nosync(irq); - - client = (struct i2c_client *) TPM_VPRIV(chip); - pin_infos = client->dev.platform_data; - - complete(&pin_infos->irq_detection); - return IRQ_HANDLED; -} /* tpm_ioserirq_handler() */ - - -/* - * tpm_stm_i2c_send send TPM commands through the I2C bus. - * - * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h - * @param: buf, the buffer to send. - * @param: count, the number of bytes to send. - * @return: In case of success the number of bytes sent. - * In other case, a < 0 value describing the issue. - */ -static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, - size_t len) -{ - u32 ordinal, - status, - burstcnt = 0, i, size; - int ret; - u8 data; - struct i2c_client *client; - struct st33zp24_platform_data *pin_infos; - - if (chip == NULL) - return -EBUSY; - if (len < TPM_HEADER_SIZE) - return -EBUSY; - - client = (struct i2c_client *)TPM_VPRIV(chip); - pin_infos = client->dev.platform_data; - - ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); - - client->flags = 0; - - ret = request_locality(chip); - if (ret < 0) - return ret; - - status = tpm_stm_i2c_status(chip); - if ((status & TPM_STS_COMMAND_READY) == 0) { - tpm_stm_i2c_cancel(chip); - if (wait_for_stat - (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, - &chip->vendor.int_queue) < 0) { - ret = -ETIME; - goto out_err; - } - } - - for (i = 0 ; i < len - 1 ;) { - burstcnt = get_burstcount(chip); - size = min_t(int, len - i - 1, burstcnt); - ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf, size); - if (ret < 0) - goto out_err; - - i += size; - } - - status = tpm_stm_i2c_status(chip); - if ((status & TPM_STS_DATA_EXPECT) == 0) { - ret = -EIO; - goto out_err; - } - - ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf + len - 1, 1); - if (ret < 0) - goto out_err; - - status = tpm_stm_i2c_status(chip); - if ((status & TPM_STS_DATA_EXPECT) != 0) { - ret = -EIO; - goto out_err; - } - - data = TPM_STS_GO; - I2C_WRITE_DATA(client, TPM_STS, &data, 1); - - return len; -out_err: - tpm_stm_i2c_cancel(chip); - release_locality(chip); - return ret; -} - -/* - * tpm_stm_i2c_recv received TPM response through the I2C bus. - * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h. - * @param: buf, the buffer to store datas. - * @param: count, the number of bytes to send. - * @return: In case of success the number of bytes received. - * In other case, a < 0 value describing the issue. - */ -static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf, - size_t count) -{ - int size = 0; - int expected; - - struct i2c_client *client; - struct st33zp24_platform_data *pin_infos; - - if (chip == NULL) - return -EBUSY; - - client = (struct i2c_client *)TPM_VPRIV(chip); - pin_infos = client->dev.platform_data; - - if (count < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } - - size = recv_data(chip, buf, TPM_HEADER_SIZE); - if (size < TPM_HEADER_SIZE) { - dev_err(chip->dev, "Unable to read header\n"); - goto out; - } - - expected = be32_to_cpu(*(__be32 *) (buf + 2)); - if (expected > count) { - size = -EIO; - goto out; - } - - size += recv_data(chip, &buf[TPM_HEADER_SIZE], - expected - TPM_HEADER_SIZE); - if (size < expected) { - dev_err(chip->dev, "Unable to read remainder of result\n"); - size = -ETIME; - goto out; - } - -out: - chip->vendor.cancel(chip); - release_locality(chip); - return size; -} - -static const struct file_operations tpm_st33_i2c_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = tpm_read, - .write = tpm_write, - .open = tpm_open, - .release = tpm_release, -}; - -static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); -static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); -static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); -static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); -static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); -static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL); -static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); -static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); - -static struct attribute *stm_tpm_attrs[] = { - &dev_attr_pubek.attr, - &dev_attr_pcrs.attr, - &dev_attr_enabled.attr, - &dev_attr_active.attr, - &dev_attr_owned.attr, - &dev_attr_temp_deactivated.attr, - &dev_attr_caps.attr, - &dev_attr_cancel.attr, NULL, -}; - -static struct attribute_group stm_tpm_attr_grp = { - .attrs = stm_tpm_attrs -}; - -static struct tpm_vendor_specific st_i2c_tpm = { - .send = tpm_stm_i2c_send, - .recv = tpm_stm_i2c_recv, - .cancel = tpm_stm_i2c_cancel, - .status = tpm_stm_i2c_status, - .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_canceled = TPM_STS_COMMAND_READY, - .attr_group = &stm_tpm_attr_grp, - .miscdev = {.fops = &tpm_st33_i2c_fops,}, -}; - -static int interrupts ; -module_param(interrupts, int, 0444); -MODULE_PARM_DESC(interrupts, "Enable interrupts"); - -static int power_mgt = 1; -module_param(power_mgt, int, 0444); -MODULE_PARM_DESC(power_mgt, "Power Management"); - -/* - * tpm_st33_i2c_probe initialize the TPM device - * @param: client, the i2c_client drescription (TPM I2C description). - * @param: id, the i2c_device_id struct. - * @return: 0 in case of success. - * -1 in other case. - */ -static int -tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - int err; - u8 intmask; - struct tpm_chip *chip; - struct st33zp24_platform_data *platform_data; - - err = 0; - - if (client == NULL) { - dev_info(&client->dev, "client is NULL. exiting.\n"); - err = -ENODEV; - goto end; - } - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_info(&client->dev, "client not i2c capable\n"); - err = -ENODEV; - goto end; - } - - chip = tpm_register_hardware(&client->dev, &st_i2c_tpm); - if (!chip) { - dev_info(&client->dev, "fail chip\n"); - err = -ENODEV; - goto end; - } - - platform_data = client->dev.platform_data; - platform_data->tpm_i2c_buffer[0] = - kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); - if (platform_data->tpm_i2c_buffer[0] == NULL) { - err = -ENOMEM; - goto _tpm_clean_answer; - } - platform_data->tpm_i2c_buffer[1] = - kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); - if (platform_data->tpm_i2c_buffer[1] == NULL) { - err = -ENOMEM; - goto _tpm_clean_response1; - } - - TPM_VPRIV(chip) = client; - - chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); - chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - - chip->vendor.locality = LOCALITY0; - - if (power_mgt) { - err = gpio_request(platform_data->io_lpcpd, "TPM IO_LPCPD"); - if (err) - goto _gpio_init1; - gpio_set_value(platform_data->io_lpcpd, 1); - } - - if (interrupts) { - init_completion(&platform_data->irq_detection); - if (request_locality(chip) != LOCALITY0) { - err = -ENODEV; - goto _tpm_clean_response2; - } - err = gpio_request(platform_data->io_serirq, "TPM IO_SERIRQ"); - if (err) - goto _gpio_init2; - - clear_interruption(client); - err = request_irq(gpio_to_irq(platform_data->io_serirq), - &tpm_ioserirq_handler, - IRQF_TRIGGER_HIGH, - "TPM SERIRQ management", chip); - if (err < 0) { - dev_err(chip->dev , "TPM SERIRQ signals %d not available\n", - gpio_to_irq(platform_data->io_serirq)); - goto _irq_set; - } - - err = I2C_READ_DATA(client, TPM_INT_ENABLE, &intmask, 1); - if (err < 0) - goto _irq_set; - - intmask |= TPM_INTF_CMD_READY_INT - | TPM_INTF_FIFO_AVALAIBLE_INT - | TPM_INTF_WAKE_UP_READY_INT - | TPM_INTF_LOCALITY_CHANGE_INT - | TPM_INTF_STS_VALID_INT - | TPM_INTF_DATA_AVAIL_INT; - - err = I2C_WRITE_DATA(client, TPM_INT_ENABLE, &intmask, 1); - if (err < 0) - goto _irq_set; - - intmask = TPM_GLOBAL_INT_ENABLE; - err = I2C_WRITE_DATA(client, (TPM_INT_ENABLE + 3), &intmask, 1); - if (err < 0) - goto _irq_set; - - err = I2C_READ_DATA(client, TPM_INT_STATUS, &intmask, 1); - if (err < 0) - goto _irq_set; - - chip->vendor.irq = interrupts; - - tpm_gen_interrupt(chip); - } - - tpm_get_timeouts(chip); - - i2c_set_clientdata(client, chip); - platform_data->bChipF = false; - - dev_info(chip->dev, "TPM I2C Initialized\n"); - return 0; -_irq_set: - free_irq(gpio_to_irq(platform_data->io_serirq), (void *) chip); -_gpio_init2: - if (interrupts) - gpio_free(platform_data->io_serirq); -_gpio_init1: - if (power_mgt) - gpio_free(platform_data->io_lpcpd); -_tpm_clean_response2: - kzfree(platform_data->tpm_i2c_buffer[1]); - platform_data->tpm_i2c_buffer[1] = NULL; -_tpm_clean_response1: - kzfree(platform_data->tpm_i2c_buffer[0]); - platform_data->tpm_i2c_buffer[0] = NULL; -_tpm_clean_answer: - tpm_remove_hardware(chip->dev); - platform_data->bChipF = true; -end: - pr_info("TPM I2C initialisation fail\n"); - return err; -} - -/* - * tpm_st33_i2c_remove remove the TPM device - * @param: client, the i2c_client drescription (TPM I2C description). - clear_bit(0, &chip->is_open); - * @return: 0 in case of success. - */ -static __devexit int tpm_st33_i2c_remove(struct i2c_client *client) -{ - struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client); - struct st33zp24_platform_data *pin_infos = - ((struct i2c_client *) TPM_VPRIV(chip))->dev.platform_data; - - if (pin_infos != NULL) { - free_irq(pin_infos->io_serirq, chip); - - gpio_free(pin_infos->io_serirq); - gpio_free(pin_infos->io_lpcpd); - - if (pin_infos->bChipF != true) - tpm_remove_hardware(chip->dev); - if (pin_infos->tpm_i2c_buffer[1] != NULL) { - kzfree(pin_infos->tpm_i2c_buffer[1]); - pin_infos->tpm_i2c_buffer[1] = NULL; - } - if (pin_infos->tpm_i2c_buffer[0] != NULL) { - kzfree(pin_infos->tpm_i2c_buffer[0]); - pin_infos->tpm_i2c_buffer[0] = NULL; - } - } - - return 0; -} - -/* - * tpm_st33_i2c_pm_suspend suspend the TPM device - * Added: Work around when suspend and no tpm application is running, suspend - * may fail because chip->data_buffer is not set (only set in tpm_open in Linux - * TPM core) - * @param: client, the i2c_client drescription (TPM I2C description). - * @param: mesg, the power management message. - * @return: 0 in case of success. - */ -static int tpm_st33_i2c_pm_suspend(struct i2c_client *client, pm_message_t mesg) -{ - struct tpm_chip *chip = - (struct tpm_chip *)i2c_get_clientdata(client); - struct st33zp24_platform_data *pin_infos = - ((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data; - int ret = 0; - - if (power_mgt) - gpio_set_value(pin_infos->io_lpcpd, 0); - else{ - if (chip->data_buffer == NULL) - chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; - ret = tpm_pm_suspend(&client->dev); - } - return ret; -} /* tpm_st33_i2c_suspend() */ - -/* - * tpm_st33_i2c_pm_resume resume the TPM device - * @param: client, the i2c_client drescription (TPM I2C description). - * @return: 0 in case of success. - */ -static int tpm_st33_i2c_pm_resume(struct i2c_client *client) -{ - struct tpm_chip *chip = - (struct tpm_chip *)i2c_get_clientdata(client); - struct st33zp24_platform_data *pin_infos = - ((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data; - - int ret = 0; - - if (power_mgt) { - gpio_set_value(pin_infos->io_lpcpd, 1); - ret = wait_for_serirq_timeout(chip, - (chip->vendor.status(chip) & - TPM_STS_VALID) == TPM_STS_VALID, - chip->vendor.timeout_b); - } else{ - if (chip->data_buffer == NULL) - chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; - ret = tpm_pm_resume(&client->dev); - if (!ret) - tpm_do_selftest(chip); - } - return ret; -} /* tpm_st33_i2c_pm_resume() */ - -static const struct i2c_device_id tpm_st33_i2c_id[] = { - {TPM_ST33_I2C, 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, tpm_st33_i2c_id); - -static struct i2c_driver tpm_st33_i2c_driver = { - .driver = { - .owner = THIS_MODULE, - .name = TPM_ST33_I2C, - }, - .probe = tpm_st33_i2c_probe, - .remove = tpm_st33_i2c_remove, - .resume = tpm_st33_i2c_pm_resume, - .suspend = tpm_st33_i2c_pm_suspend, - .id_table = tpm_st33_i2c_id -}; - -module_i2c_driver(tpm_st33_i2c_driver); - -MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)"); -MODULE_DESCRIPTION("STM TPM I2C ST33 Driver"); -MODULE_VERSION("1.2.0"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_stm_st33_i2c.h b/drivers/char/tpm/tpm_stm_st33_i2c.h deleted file mode 100644 index 5862d0376bd8..000000000000 --- a/drivers/char/tpm/tpm_stm_st33_i2c.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 - * Copyright (C) 2009, 2010 STMicroelectronics - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * STMicroelectronics version 1.2.0, Copyright (C) 2010 - * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. - * This is free software, and you are welcome to redistribute it - * under certain conditions. - * - * @Author: Christophe RICARD tpmsupport@st.com - * - * @File: stm_st33_tpm_i2c.h - * - * @Date: 09/15/2010 - */ -#ifndef __STM_ST33_TPM_I2C_MAIN_H__ -#define __STM_ST33_TPM_I2C_MAIN_H__ - -#define TPM_ACCESS (0x0) -#define TPM_STS (0x18) -#define TPM_HASH_END (0x20) -#define TPM_DATA_FIFO (0x24) -#define TPM_HASH_DATA (0x24) -#define TPM_HASH_START (0x28) -#define TPM_INTF_CAPABILITY (0x14) -#define TPM_INT_STATUS (0x10) -#define TPM_INT_ENABLE (0x08) - -#define TPM_DUMMY_BYTE 0xAA -#define TPM_WRITE_DIRECTION 0x80 -#define TPM_HEADER_SIZE 10 -#define TPM_BUFSIZE 2048 - -#define LOCALITY0 0 - -#define TPM_ST33_I2C "st33zp24_i2c" - -struct st33zp24_platform_data { - int io_serirq; - int io_lpcpd; - struct i2c_client *client; - bool bChipF; - u8 *tpm_i2c_buffer[2]; /* 0 Request 1 Response */ - struct completion irq_detection; - struct mutex lock; -}; - -#endif /* __STM_ST33_TPM_I2C_MAIN_H__ */ -- cgit v1.2.1 From 2bfee22f6c60e046f6b04f2d4855fddac1f67290 Mon Sep 17 00:00:00 2001 From: Mathias Leblanc Date: Wed, 14 Nov 2012 15:31:44 +0100 Subject: TPM: STMicroelectronics ST33 I2C BUILD STUFF * STMicroelectronics version 1.2.0, Copyright (C) 2010 * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. * This is free software, and you are welcome to redistribute it * under certain conditions. This is the driver for TPM chip from ST Microelectronics. If you have a TPM security chip from STMicroelectronics working with an I2C, in menuconfig or .config choose the tpm driver on device --> tpm and activate the protocol of your choice before compiling the kernel. The driver will be accessible from within Linux. Tested on linux x86/x64, beagleboard REV B & XM REV C and CHROMIUM OS Signed-off-by: Mathias Leblanc Signed-off-by: Kent Yoder --- drivers/char/tpm/Kconfig | 10 ++++++++++ drivers/char/tpm/Makefile | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 915875e431d2..765d1c7e92b9 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -81,4 +81,14 @@ config TCG_IBMVTPM will be accessible from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_ibmvtpm. +config TCG_ST33_I2C + tristate "STMicroelectronics ST33 I2C TPM" + depends on I2C + depends on GPIOLIB + ---help--- + If you have a TPM security chip from STMicroelectronics working with + an I2C bus say Yes and it will be accessible from within Linux. + To compile this driver as a module, choose M here; the module will be + called tpm_stm_st33_i2c. + endif # TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 5b3fc8bc6c13..a3736c97c65a 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o +obj-$(CONFIG_TCG_ST33_I2C) += tpm_i2c_stm_st33.o -- cgit v1.2.1 From d459335381eca1cb91fefb87021d3d172342e55a Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Thu, 6 Dec 2012 01:20:51 +0100 Subject: char/tpm: Use struct dev_pm_ops for power management This patch converts the suspend and resume functions for tpm_i2c_stm_st33 to the new dev_pm_ops. Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_i2c_stm_st33.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index cf4f42e3e83d..24a33e08d0c6 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -818,6 +818,7 @@ static __devexit int tpm_st33_i2c_remove(struct i2c_client *client) return 0; } +#ifdef CONFIG_PM_SLEEP /* * tpm_st33_i2c_pm_suspend suspend the TPM device * Added: Work around when suspend and no tpm application is running, suspend @@ -827,12 +828,10 @@ static __devexit int tpm_st33_i2c_remove(struct i2c_client *client) * @param: mesg, the power management message. * @return: 0 in case of success. */ -static int tpm_st33_i2c_pm_suspend(struct i2c_client *client, pm_message_t mesg) +static int tpm_st33_i2c_pm_suspend(struct device *dev) { - struct tpm_chip *chip = - (struct tpm_chip *)i2c_get_clientdata(client); - struct st33zp24_platform_data *pin_infos = - ((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data; + struct tpm_chip *chip = dev_get_drvdata(dev); + struct st33zp24_platform_data *pin_infos = dev->platform_data; int ret = 0; if (power_mgt) @@ -840,7 +839,7 @@ static int tpm_st33_i2c_pm_suspend(struct i2c_client *client, pm_message_t mesg) else{ if (chip->data_buffer == NULL) chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; - ret = tpm_pm_suspend(&client->dev); + ret = tpm_pm_suspend(dev); } return ret; } /* tpm_st33_i2c_suspend() */ @@ -850,12 +849,10 @@ static int tpm_st33_i2c_pm_suspend(struct i2c_client *client, pm_message_t mesg) * @param: client, the i2c_client drescription (TPM I2C description). * @return: 0 in case of success. */ -static int tpm_st33_i2c_pm_resume(struct i2c_client *client) +static int tpm_st33_i2c_pm_resume(struct device *dev) { - struct tpm_chip *chip = - (struct tpm_chip *)i2c_get_clientdata(client); - struct st33zp24_platform_data *pin_infos = - ((struct i2c_client *)TPM_VPRIV(chip))->dev.platform_data; + struct tpm_chip *chip = dev_get_drvdata(dev); + struct st33zp24_platform_data *pin_infos = dev->platform_data; int ret = 0; @@ -868,29 +865,28 @@ static int tpm_st33_i2c_pm_resume(struct i2c_client *client) } else{ if (chip->data_buffer == NULL) chip->data_buffer = pin_infos->tpm_i2c_buffer[0]; - ret = tpm_pm_resume(&client->dev); + ret = tpm_pm_resume(dev); if (!ret) tpm_do_selftest(chip); } return ret; } /* tpm_st33_i2c_pm_resume() */ +#endif static const struct i2c_device_id tpm_st33_i2c_id[] = { {TPM_ST33_I2C, 0}, {} }; - MODULE_DEVICE_TABLE(i2c, tpm_st33_i2c_id); - +static SIMPLE_DEV_PM_OPS(tpm_st33_i2c_ops, tpm_st33_i2c_pm_suspend, tpm_st33_i2c_pm_resume); static struct i2c_driver tpm_st33_i2c_driver = { .driver = { .owner = THIS_MODULE, .name = TPM_ST33_I2C, + .pm = &tpm_st33_i2c_ops, }, .probe = tpm_st33_i2c_probe, .remove = tpm_st33_i2c_remove, - .resume = tpm_st33_i2c_pm_resume, - .suspend = tpm_st33_i2c_pm_suspend, .id_table = tpm_st33_i2c_id }; -- cgit v1.2.1 From 1fbc5e95356a4600fab3a04a82dc8bb49591aedd Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Fri, 18 Jan 2013 17:42:25 -0600 Subject: tpm_i2c_stm_st33: fix oops when i2c client is unavailable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When no i2c bus exists, user-space can cause an oops by triggering a device probe through a message sent to an i2c "new_device" sysfs entry. Adding a check for a NULL i2c client structure in the probe function closes the hole. This patch also fixes accessing the NULL client struct in the print function call reporting the error. Reported-by: Peter Hüwe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_i2c_stm_st33.c | 16 +++++++++++----- drivers/char/tpm/tpm_i2c_stm_st33.h | 1 - 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index 24a33e08d0c6..36524ed8ada9 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -658,7 +658,8 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) err = 0; if (client == NULL) { - dev_info(&client->dev, "client is NULL. exiting.\n"); + pr_info("%s: i2c client is NULL. Device not accessible.\n", + __func__); err = -ENODEV; goto end; } @@ -677,6 +678,13 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) } platform_data = client->dev.platform_data; + + if (!platform_data) { + dev_info(&client->dev, "chip not available\n"); + err = -ENODEV; + goto _tpm_clean_answer; + } + platform_data->tpm_i2c_buffer[0] = kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL); if (platform_data->tpm_i2c_buffer[0] == NULL) { @@ -759,7 +767,6 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) tpm_get_timeouts(chip); i2c_set_clientdata(client, chip); - platform_data->bChipF = false; dev_info(chip->dev, "TPM I2C Initialized\n"); return 0; @@ -779,7 +786,6 @@ _tpm_clean_response1: platform_data->tpm_i2c_buffer[0] = NULL; _tpm_clean_answer: tpm_remove_hardware(chip->dev); - platform_data->bChipF = true; end: pr_info("TPM I2C initialisation fail\n"); return err; @@ -803,8 +809,8 @@ static __devexit int tpm_st33_i2c_remove(struct i2c_client *client) gpio_free(pin_infos->io_serirq); gpio_free(pin_infos->io_lpcpd); - if (pin_infos->bChipF != true) - tpm_remove_hardware(chip->dev); + tpm_remove_hardware(chip->dev); + if (pin_infos->tpm_i2c_buffer[1] != NULL) { kzfree(pin_infos->tpm_i2c_buffer[1]); pin_infos->tpm_i2c_buffer[1] = NULL; diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.h b/drivers/char/tpm/tpm_i2c_stm_st33.h index 5862d0376bd8..439a43249aa6 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.h +++ b/drivers/char/tpm/tpm_i2c_stm_st33.h @@ -53,7 +53,6 @@ struct st33zp24_platform_data { int io_serirq; int io_lpcpd; struct i2c_client *client; - bool bChipF; u8 *tpm_i2c_buffer[2]; /* 0 Request 1 Response */ struct completion irq_detection; struct mutex lock; -- cgit v1.2.1 From 4643826a3da17767494b85c956c73c138c96a7ea Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 21 Nov 2012 13:15:54 -0800 Subject: TPM: Work around buggy TPMs that block during continue self test We've been testing an alternative TPM for our embedded products and found random kernel boot failures due to time outs after the continue self test command. This was happening randomly, and has been *very* hard to track down, but it looks like with this chip there is some kind of race with the tpm_tis_status() check of TPM_STS_COMMAND_READY. If things get there 'too fast' then it sees the chip is ready, or tpm_tis_ready() works. Otherwise it takes somewhere over 400ms before the chip will return TPM_STS_COMMAND_READY. Adding some delay after tpm_continue_selftest() makes things reliably hit the failure path, otherwise it is a crapshot. The spec says it should be returning TPM_WARN_DOING_SELFTEST, not holding off on ready.. Boot log during this event looks like this: tpm_tis 70030000.tpm_tis: 1.2 TPM (device-id 0x3204, rev-id 64) tpm_tis 70030000.tpm_tis: Issuing TPM_STARTUP tpm_tis 70030000.tpm_tis: tpm_transmit: tpm_send: error -62 tpm_tis 70030000.tpm_tis: [Hardware Error]: TPM command timed out during continue self test tpm_tis 70030000.tpm_tis: tpm_transmit: tpm_send: error -62 tpm_tis 70030000.tpm_tis: [Hardware Error]: TPM command timed out during continue self test tpm_tis 70030000.tpm_tis: tpm_transmit: tpm_send: error -62 tpm_tis 70030000.tpm_tis: [Hardware Error]: TPM command timed out during continue self test tpm_tis 70030000.tpm_tis: tpm_transmit: tpm_send: error -62 tpm_tis 70030000.tpm_tis: [Hardware Error]: TPM command timed out during continue self test The other TPM vendor we use doesn't show this wonky behaviour: tpm_tis 70030000.tpm_tis: 1.2 TPM (device-id 0xFE, rev-id 70) Signed-off-by: Jason Gunthorpe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 023dfbaffe73..e38054b33bb4 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -843,7 +843,7 @@ int tpm_do_selftest(struct tpm_chip *chip) { int rc; unsigned int loops; - unsigned int delay_msec = 1000; + unsigned int delay_msec = 100; unsigned long duration; struct tpm_cmd_t cmd; @@ -864,6 +864,14 @@ int tpm_do_selftest(struct tpm_chip *chip) cmd.header.in = pcrread_header; cmd.params.pcrread_in.pcr_idx = cpu_to_be32(0); rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE); + /* Some buggy TPMs will not respond to tpm_tis_ready() for + * around 300ms while the self test is ongoing, keep trying + * until the self test duration expires. */ + if (rc == -ETIME) { + dev_info(chip->dev, HW_ERR "TPM command timed out during continue self test"); + msleep(delay_msec); + continue; + } if (rc < TPM_HEADER_SIZE) return -EFAULT; -- cgit v1.2.1 From 3e3a5e906998b090ad929010a5f5082ac0dbbdfb Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 22 Jan 2013 13:50:07 -0600 Subject: tpm: Store TPM vendor ID Store the TPM vendor ID for later use. Signed-off-by: Stefan Berger Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.h | 2 ++ drivers/char/tpm/tpm_tis.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index cbee6319be75..ef4ba5fcf7d9 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -105,6 +105,8 @@ struct tpm_vendor_specific { wait_queue_head_t read_queue; wait_queue_head_t int_queue; + + u16 manufacturer_id; }; #define TPM_VPRIV(c) (c)->vendor.priv diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 74ce2d1f08b4..3615d210e603 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -534,6 +534,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, } vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); + chip->vendor.manufacturer_id = vendor; dev_info(dev, "1.2 TPM (device-id 0x%X, rev-id %d)\n", -- cgit v1.2.1 From 1f866057291fc00f14e4962473bd7724ffa8f578 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 22 Jan 2013 13:52:35 -0600 Subject: tpm: Fix cancellation of TPM commands (polling mode) On one of my machines the cancellation of TPM commands does not work. The reason is that by writing into sysfs 'cancel' the tpm_tis_ready call causes the status flag TPM_STS_VALID to be set in the statusregister. However, the TIS driver seems to wait for TPM_STS_COMMAND_READY. Once a 2nd time sysfs 'cancel' is written to, the TPM_STS_COMMAND_READY flag also gets set, resulting in TPM_STS_VALID|TPM_STS_COMMAND_READY to be read from the status register. This patch now converts req_canceled into a function to enable more complex comparisons against possible cancellation status codes. Signed-off-by: Stefan Berger Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 2 +- drivers/char/tpm/tpm.h | 4 +++- drivers/char/tpm/tpm_atmel.c | 7 ++++++- drivers/char/tpm/tpm_i2c_infineon.c | 7 ++++++- drivers/char/tpm/tpm_i2c_stm_st33.c | 7 ++++++- drivers/char/tpm/tpm_ibmvtpm.c | 7 ++++++- drivers/char/tpm/tpm_nsc.c | 7 ++++++- drivers/char/tpm/tpm_tis.c | 15 ++++++++++++++- 8 files changed, 48 insertions(+), 8 deletions(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index e38054b33bb4..a244cd3e7dbc 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -393,7 +393,7 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, chip->vendor.req_complete_val) goto out_recv; - if ((status == chip->vendor.req_canceled)) { + if (chip->vendor.req_canceled(chip, status)) { dev_err(chip->dev, "Operation Canceled\n"); rc = -ECANCELED; goto out; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index ef4ba5fcf7d9..725cb9b657dd 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -78,7 +78,7 @@ struct tpm_chip; struct tpm_vendor_specific { const u8 req_complete_mask; const u8 req_complete_val; - const u8 req_canceled; + bool (*req_canceled)(struct tpm_chip *chip, u8 status); void __iomem *iobase; /* ioremapped address */ unsigned long base; /* TPM base address */ @@ -112,6 +112,8 @@ struct tpm_vendor_specific { #define TPM_VPRIV(c) (c)->vendor.priv #define TPM_VID_INTEL 0x8086 +#define TPM_VID_WINBOND 0x1050 +#define TPM_VID_STM 0x104A struct tpm_chip { struct device *dev; /* Device stuff */ diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index 678d57019dc4..99d6820c611d 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -116,6 +116,11 @@ static u8 tpm_atml_status(struct tpm_chip *chip) return ioread8(chip->vendor.iobase + 1); } +static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status) +{ + return (status == ATML_STATUS_READY); +} + static const struct file_operations atmel_ops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -147,7 +152,7 @@ static const struct tpm_vendor_specific tpm_atmel = { .status = tpm_atml_status, .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL, .req_complete_val = ATML_STATUS_DATA_AVAIL, - .req_canceled = ATML_STATUS_READY, + .req_canceled = tpm_atml_req_canceled, .attr_group = &atmel_attr_grp, .miscdev = { .fops = &atmel_ops, }, }; diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index fb447bd0cb61..8fe7ac3d095b 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -505,6 +505,11 @@ out_err: return rc; } +static bool tpm_tis_i2c_req_canceled(struct tpm_chip *chip, u8 status) +{ + return (status == TPM_STS_COMMAND_READY); +} + static const struct file_operations tis_ops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -550,7 +555,7 @@ static struct tpm_vendor_specific tpm_tis_i2c = { .cancel = tpm_tis_i2c_ready, .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_canceled = TPM_STS_COMMAND_READY, + .req_canceled = tpm_tis_i2c_req_canceled, .attr_group = &tis_attr_grp, .miscdev.fops = &tis_ops, }; diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index 36524ed8ada9..e4154bfc8be7 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -587,6 +587,11 @@ out: return size; } +static bool tpm_st33_i2c_req_canceled(struct tpm_chip *chip, u8 status) +{ + return (status == TPM_STS_COMMAND_READY); +} + static const struct file_operations tpm_st33_i2c_fops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -627,7 +632,7 @@ static struct tpm_vendor_specific st_i2c_tpm = { .status = tpm_stm_i2c_status, .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_canceled = TPM_STS_COMMAND_READY, + .req_canceled = tpm_st33_i2c_req_canceled, .attr_group = &stm_tpm_attr_grp, .miscdev = {.fops = &tpm_st33_i2c_fops,}, }; diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 6cd99bc1c5d4..56b07c35a13e 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -398,6 +398,11 @@ static int tpm_ibmvtpm_resume(struct device *dev) return rc; } +static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status) +{ + return (status == 0); +} + static const struct file_operations ibmvtpm_ops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -441,7 +446,7 @@ static const struct tpm_vendor_specific tpm_ibmvtpm = { .status = tpm_ibmvtpm_status, .req_complete_mask = 0, .req_complete_val = 0, - .req_canceled = 0, + .req_canceled = tpm_ibmvtpm_req_canceled, .attr_group = &ibmvtpm_attr_grp, .miscdev = { .fops = &ibmvtpm_ops, }, }; diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 640c9a427b59..770c46f8eb30 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -227,6 +227,11 @@ static u8 tpm_nsc_status(struct tpm_chip *chip) return inb(chip->vendor.base + NSC_STATUS); } +static bool tpm_nsc_req_canceled(struct tpm_chip *chip, u8 status) +{ + return (status == NSC_STATUS_RDY); +} + static const struct file_operations nsc_ops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -258,7 +263,7 @@ static const struct tpm_vendor_specific tpm_nsc = { .status = tpm_nsc_status, .req_complete_mask = NSC_STATUS_OBF, .req_complete_val = NSC_STATUS_OBF, - .req_canceled = NSC_STATUS_RDY, + .req_canceled = tpm_nsc_req_canceled, .attr_group = &nsc_attr_grp, .miscdev = { .fops = &nsc_ops, }, }; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 3615d210e603..e4e0c65df768 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -400,6 +400,19 @@ out: return rc; } +static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status) +{ + switch (chip->vendor.manufacturer_id) { + case TPM_VID_WINBOND: + return ((status == TPM_STS_VALID) || + (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY))); + case TPM_VID_STM: + return (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY)); + default: + return (status == TPM_STS_COMMAND_READY); + } +} + static const struct file_operations tis_ops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -445,7 +458,7 @@ static struct tpm_vendor_specific tpm_tis = { .cancel = tpm_tis_ready, .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_canceled = TPM_STS_COMMAND_READY, + .req_canceled = tpm_tis_req_canceled, .attr_group = &tis_attr_grp, .miscdev = { .fops = &tis_ops,}, -- cgit v1.2.1 From 78f09cc248731716633c0ef219613e762a9c4f2e Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Tue, 22 Jan 2013 13:57:53 -0600 Subject: tpm: Fix cancellation of TPM commands (interrupt mode) Support cancellation of TPM commands when driver is used in interrupt mode. Signed-off-by: Stefan Berger Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 29 ++++++++++++++++++++++++----- drivers/char/tpm/tpm.h | 2 +- drivers/char/tpm/tpm_tis.c | 12 ++++++------ 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index a244cd3e7dbc..0d2e82f95577 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1102,12 +1102,28 @@ ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr, } EXPORT_SYMBOL_GPL(tpm_store_cancel); +static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, bool check_cancel, + bool *canceled) +{ + u8 status = chip->vendor.status(chip); + + *canceled = false; + if ((status & mask) == mask) + return true; + if (check_cancel && chip->vendor.req_canceled(chip, status)) { + *canceled = true; + return true; + } + return false; +} + int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, - wait_queue_head_t *queue) + wait_queue_head_t *queue, bool check_cancel) { unsigned long stop; long rc; u8 status; + bool canceled = false; /* check current status */ status = chip->vendor.status(chip); @@ -1122,11 +1138,14 @@ again: if ((long)timeout <= 0) return -ETIME; rc = wait_event_interruptible_timeout(*queue, - ((chip->vendor.status(chip) - & mask) == mask), - timeout); - if (rc > 0) + wait_for_tpm_stat_cond(chip, mask, check_cancel, + &canceled), + timeout); + if (rc > 0) { + if (canceled) + return -ECANCELED; return 0; + } if (rc == -ERESTARTSYS && freezing(current)) { clear_thread_flag(TIF_SIGPENDING); goto again; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 725cb9b657dd..81b52015f669 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -338,7 +338,7 @@ extern void tpm_remove_hardware(struct device *); extern int tpm_pm_suspend(struct device *); extern int tpm_pm_resume(struct device *); extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, - wait_queue_head_t *); + wait_queue_head_t *, bool); #ifdef CONFIG_ACPI extern int tpm_add_ppi(struct kobject *); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index e4e0c65df768..ae91c115c438 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -198,7 +198,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) wait_for_tpm_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, chip->vendor.timeout_c, - &chip->vendor.read_queue) + &chip->vendor.read_queue, true) == 0) { burstcnt = get_burstcount(chip); for (; burstcnt > 0 && size < count; burstcnt--) @@ -241,7 +241,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) } wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, - &chip->vendor.int_queue); + &chip->vendor.int_queue, false); status = tpm_tis_status(chip); if (status & TPM_STS_DATA_AVAIL) { /* retry? */ dev_err(chip->dev, "Error left over data\n"); @@ -277,7 +277,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) tpm_tis_ready(chip); if (wait_for_tpm_stat (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, - &chip->vendor.int_queue) < 0) { + &chip->vendor.int_queue, false) < 0) { rc = -ETIME; goto out_err; } @@ -292,7 +292,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) } wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, - &chip->vendor.int_queue); + &chip->vendor.int_queue, false); status = tpm_tis_status(chip); if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) { rc = -EIO; @@ -304,7 +304,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) iowrite8(buf[count], chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality)); wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, - &chip->vendor.int_queue); + &chip->vendor.int_queue, false); status = tpm_tis_status(chip); if ((status & TPM_STS_DATA_EXPECT) != 0) { rc = -EIO; @@ -342,7 +342,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) if (wait_for_tpm_stat (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, tpm_calc_ordinal_duration(chip, ordinal), - &chip->vendor.read_queue) < 0) { + &chip->vendor.read_queue, false) < 0) { rc = -ETIME; goto out_err; } -- cgit v1.2.1 From 7240b98320a28f1d41b9361dc1cc8244dfda0272 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Thu, 6 Dec 2012 15:44:54 -0700 Subject: TPM: Wait for TPM_ACCESS tpmRegValidSts to go high at startup The TIS specification (pg 47) says the valid bit must be set, but the TPM will not set it until it has completed its internal startup. The driver checks that the valid bit is set during request_locality, but it issues a TPM_ACCESS_REQUEST_USE without validating the valid bit is set. Some TPMs will ignore the TPM_ACCESS_REQUEST_USE, until valid is set which causes the request_locality to timeout, which breaks the driver attach. Wait one timeout unit for valid to assert. If valid does not assert then assume -ENODEV. Seen on embedded with a: 1.2 TPM (device-id 0x3204, rev-id 64) Signed-off-by: Jason Gunthorpe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_tis.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index ae91c115c438..6c69ad72f82f 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -98,6 +98,22 @@ static inline int is_itpm(struct pnp_dev *dev) } #endif +/* Before we attempt to access the TPM we must see that the valid bit is set. + * The specification says that this bit is 0 at reset and remains 0 until the + * 'TPM has gone through its self test and initialization and has established + * correct values in the other bits.' */ +static int wait_startup(struct tpm_chip *chip, int l) +{ + unsigned long stop = jiffies + chip->vendor.timeout_a; + do { + if (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & + TPM_ACCESS_VALID) + return 0; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + return -1; +} + static int check_locality(struct tpm_chip *chip, int l) { if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & @@ -541,6 +557,11 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + if (wait_startup(chip, 0) != 0) { + rc = -ENODEV; + goto out_err; + } + if (request_locality(chip, 0) != 0) { rc = -ENODEV; goto out_err; -- cgit v1.2.1 From d479042c82941772cb1c003b46707d55185a49f6 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Tue, 29 Jan 2013 09:43:54 -0600 Subject: tpm_i2c_stm_st33: removed unused variables/code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Peter Hüwe Signed-off-by: Peter Hüwe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_i2c_stm_st33.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index e4154bfc8be7..8c60d33d116c 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -123,9 +123,6 @@ static int read8_reg(struct i2c_client *client, u8 tpm_register, { u8 status = 0; u8 data; - struct st33zp24_platform_data *pin_infos; - - pin_infos = client->dev.platform_data; data = TPM_DUMMY_BYTE; status = write8_reg(client, tpm_register, &data, 1); @@ -203,10 +200,8 @@ static int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition, { int status = 2; struct i2c_client *client; - struct st33zp24_platform_data *pin_infos; client = (struct i2c_client *) TPM_VPRIV(chip); - pin_infos = client->dev.platform_data; status = _wait_for_interrupt_serirq_timeout(chip, timeout); if (!status) { @@ -462,13 +457,11 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, size_t len) { - u32 ordinal, - status, + u32 status, burstcnt = 0, i, size; int ret; u8 data; struct i2c_client *client; - struct st33zp24_platform_data *pin_infos; if (chip == NULL) return -EBUSY; @@ -476,9 +469,6 @@ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, return -EBUSY; client = (struct i2c_client *)TPM_VPRIV(chip); - pin_infos = client->dev.platform_data; - - ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); client->flags = 0; @@ -547,15 +537,9 @@ static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf, int size = 0; int expected; - struct i2c_client *client; - struct st33zp24_platform_data *pin_infos; - if (chip == NULL) return -EBUSY; - client = (struct i2c_client *)TPM_VPRIV(chip); - pin_infos = client->dev.platform_data; - if (count < TPM_HEADER_SIZE) { size = -EIO; goto out; -- cgit v1.2.1 From 64298919ad034aa07a6ec85b82e5c8861e364ae5 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 29 Jan 2013 22:01:59 +0100 Subject: char/tpm/tpm_i2c_stm_st33: Don't use memcpy for one byte assignment We don't need to call memcpy for one byte, but assign it directly. And to make the offset clearer we use the array syntax on the subsequent call to memset to make the relationship clearer. Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_i2c_stm_st33.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index 8c60d33d116c..16f1f785c0b9 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -96,15 +96,13 @@ enum tis_defaults { static int write8_reg(struct i2c_client *client, u8 tpm_register, u8 *tpm_data, u16 tpm_size) { - u8 data; int value = 0; struct st33zp24_platform_data *pin_infos; pin_infos = client->dev.platform_data; - data = tpm_register; - memcpy(pin_infos->tpm_i2c_buffer[0], &data, sizeof(data)); - memcpy(pin_infos->tpm_i2c_buffer[0] + 1, tpm_data, tpm_size); + pin_infos->tpm_i2c_buffer[0][0] = tpm_register; + memcpy(&pin_infos->tpm_i2c_buffer[0][1], tpm_data, tpm_size); value = i2c_master_send(client, pin_infos->tpm_i2c_buffer[0], tpm_size + 1); return value; -- cgit v1.2.1 From e02983cffc5e65069edee9892672e0aef9d9e2cd Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 29 Jan 2013 22:02:00 +0100 Subject: char/tpm/tpm_i2c_stm_st33: Remove __devexit attribute With the HOTPLUG changes 3.8 this attribute is going away. Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_i2c_stm_st33.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index 16f1f785c0b9..e3d1887e5be4 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -784,7 +784,7 @@ end: clear_bit(0, &chip->is_open); * @return: 0 in case of success. */ -static __devexit int tpm_st33_i2c_remove(struct i2c_client *client) +static int tpm_st33_i2c_remove(struct i2c_client *client) { struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client); struct st33zp24_platform_data *pin_infos = -- cgit v1.2.1 From 07d721688b8464662393f3b3136defbbad52be75 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 29 Jan 2013 22:02:01 +0100 Subject: char/tpm/tpm_i2c_stm_st33: remove dead assignment in tpm_st33_i2c_probe Err is never read before it is assigned again -> remove the dead assigment. Found with clang static analyzer Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_i2c_stm_st33.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index e3d1887e5be4..b14c4b59958d 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -642,8 +642,6 @@ tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) struct tpm_chip *chip; struct st33zp24_platform_data *platform_data; - err = 0; - if (client == NULL) { pr_info("%s: i2c client is NULL. Device not accessible.\n", __func__); -- cgit v1.2.1 From 7333549bf4f345d338825dcd5b85400600e4cae7 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 29 Jan 2013 22:02:02 +0100 Subject: char/tpm/tpm_i2c_stm_st33: drop temporary variable for return value We don't need a temporary variable just to store the return value which gets return in the next statement. Signed-off-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_i2c_stm_st33.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index b14c4b59958d..1f5f71e14abe 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -96,16 +96,14 @@ enum tis_defaults { static int write8_reg(struct i2c_client *client, u8 tpm_register, u8 *tpm_data, u16 tpm_size) { - int value = 0; struct st33zp24_platform_data *pin_infos; pin_infos = client->dev.platform_data; pin_infos->tpm_i2c_buffer[0][0] = tpm_register; memcpy(&pin_infos->tpm_i2c_buffer[0][1], tpm_data, tpm_size); - value = i2c_master_send(client, pin_infos->tpm_i2c_buffer[0], + return i2c_master_send(client, pin_infos->tpm_i2c_buffer[0], tpm_size + 1); - return value; } /* write8_reg() */ /* -- cgit v1.2.1 From 6e38bfaad6c83bdd07eb659f9bfd50f8d71a5a46 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Thu, 31 Jan 2013 09:39:31 -0600 Subject: tpm_tis: check pnp_acpi_device return code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Peter Hüwe Reviewed-by: Peter Huewe Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_tis.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 6c69ad72f82f..8a41b6be23a0 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -84,6 +84,9 @@ static int is_itpm(struct pnp_dev *dev) struct acpi_device *acpi = pnp_acpi_device(dev); struct acpi_hardware_id *id; + if (!acpi) + return 0; + list_for_each_entry(id, &acpi->pnp.ids, list) { if (!strcmp("INTC0102", id->id)) return 1; -- cgit v1.2.1 From 74de66842473bdafa798010e58f1999ec70a8983 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 10 Sep 2012 10:37:20 +0300 Subject: evm: add file system uuid to EVM hmac EVM uses the same key for all file systems to calculate the HMAC, making it possible to paste inodes from one file system on to another one, without EVM being able to detect it. To prevent such an attack, it is necessary to make the EVM HMAC file system specific. This patch uses the file system UUID, a file system unique identifier, to bind the EVM HMAC to the file system. The value inode->i_sb->s_uuid is used for the HMAC hash calculation, instead of using it for deriving the file system specific key. Initializing the key for every inode HMAC calculation is a bit more expensive operation than adding the uuid to the HMAC hash. Changing the HMAC calculation method or adding additional info to the calculation, requires existing EVM labeled file systems to be relabeled. This patch adds a Kconfig HMAC version option for backwards compatability. Changelog v1: - squash "hmac version setting" Changelog v0: - add missing Kconfig depends (Mimi) Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/evm/Kconfig | 13 +++++++++++++ security/integrity/evm/evm.h | 1 + security/integrity/evm/evm_crypto.c | 3 +++ security/integrity/evm/evm_main.c | 1 + 4 files changed, 18 insertions(+) diff --git a/security/integrity/evm/Kconfig b/security/integrity/evm/Kconfig index afbb59dd262d..fea9749c3756 100644 --- a/security/integrity/evm/Kconfig +++ b/security/integrity/evm/Kconfig @@ -11,3 +11,16 @@ config EVM integrity attacks. If you are unsure how to answer this question, answer N. + +config EVM_HMAC_VERSION + int "EVM HMAC version" + depends on EVM + default 2 + help + This options adds EVM HMAC version support. + 1 - original version + 2 - add per filesystem unique identifier (UUID) (default) + + WARNING: changing the HMAC calculation method or adding + additional info to the calculation, requires existing EVM + labeled file systems to be relabeled. diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index 3eb30c6db419..30bd1ec0232e 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -24,6 +24,7 @@ extern int evm_initialized; extern char *evm_hmac; extern char *evm_hash; +extern int evm_hmac_version; extern struct crypto_shash *hmac_tfm; extern struct crypto_shash *hash_tfm; diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index dfb26918699c..ff8e2abf8f21 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -110,6 +110,9 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode, hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid); hmac_misc.mode = inode->i_mode; crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc); + if (evm_hmac_version > 1) + crypto_shash_update(desc, inode->i_sb->s_uuid, + sizeof(inode->i_sb->s_uuid)); crypto_shash_final(desc, digest); } diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index a78a5e21ef70..cdbde1762189 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -26,6 +26,7 @@ int evm_initialized; char *evm_hmac = "hmac(sha1)"; char *evm_hash = "sha1"; +int evm_hmac_version = CONFIG_EVM_HMAC_VERSION; char *evm_config_xattrnames[] = { #ifdef CONFIG_SECURITY_SELINUX -- cgit v1.2.1 From 85865c1fa189fcba49089e6254a0226f2269bebc Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 3 Sep 2012 23:23:13 +0300 Subject: ima: add policy support for file system uuid The IMA policy permits specifying rules to enable or disable measurement/appraisal/audit based on the file system magic number. If, for example, the policy contains an ext4 measurement rule, the rule is enabled for all ext4 partitions. Sometimes it might be necessary to enable measurement/appraisal/audit only for one partition and disable it for another partition of the same type. With the existing IMA policy syntax, this can not be done. This patch provides support for IMA policy rules to specify the file system by its UUID (eg. fsuuid=397449cd-687d-4145-8698-7fed4a3e0363). For partitions not being appraised, it might be a good idea to mount file systems with the 'noexec' option to prevent executing non-verified binaries. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- Documentation/ABI/testing/ima_policy | 4 +++- security/integrity/ima/ima_policy.c | 22 +++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index de16de3f148d..f1c5cc9d17a8 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -19,7 +19,8 @@ Description: action: measure | dont_measure | appraise | dont_appraise | audit condition:= base | lsm [option] - base: [[func=] [mask=] [fsmagic=] [uid=] [fowner]] + base: [[func=] [mask=] [fsmagic=] [fsuuid=] [uid=] + [fowner]] lsm: [[subj_user=] [subj_role=] [subj_type=] [obj_user=] [obj_role=] [obj_type=]] option: [[appraise_type=]] @@ -27,6 +28,7 @@ Description: base: func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK] mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC] fsmagic:= hex value + fsuuid:= file system UUID (e.g 8bcbe394-4f13-4144-be8e-5aa9ea2ce2f6) uid:= decimal value fowner:=decimal value lsm: are LSM specific diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 4adcd0f8c1dd..23f49e37a957 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "ima.h" @@ -25,6 +26,7 @@ #define IMA_FSMAGIC 0x0004 #define IMA_UID 0x0008 #define IMA_FOWNER 0x0010 +#define IMA_FSUUID 0x0020 #define UNKNOWN 0 #define MEASURE 0x0001 /* same as IMA_MEASURE */ @@ -45,6 +47,7 @@ struct ima_rule_entry { enum ima_hooks func; int mask; unsigned long fsmagic; + u8 fsuuid[16]; kuid_t uid; kuid_t fowner; struct { @@ -172,6 +175,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule, if ((rule->flags & IMA_FSMAGIC) && rule->fsmagic != inode->i_sb->s_magic) return false; + if ((rule->flags & IMA_FSUUID) && + memcmp(rule->fsuuid, inode->i_sb->s_uuid, sizeof(rule->fsuuid))) + return false; if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid)) return false; if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid)) @@ -346,7 +352,7 @@ enum { Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner, - Opt_appraise_type + Opt_appraise_type, Opt_fsuuid }; static match_table_t policy_tokens = { @@ -364,6 +370,7 @@ static match_table_t policy_tokens = { {Opt_func, "func=%s"}, {Opt_mask, "mask=%s"}, {Opt_fsmagic, "fsmagic=%s"}, + {Opt_fsuuid, "fsuuid=%s"}, {Opt_uid, "uid=%s"}, {Opt_fowner, "fowner=%s"}, {Opt_appraise_type, "appraise_type=%s"}, @@ -519,6 +526,19 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) if (!result) entry->flags |= IMA_FSMAGIC; break; + case Opt_fsuuid: + ima_log_string(ab, "fsuuid", args[0].from); + + if (memchr_inv(entry->fsuuid, 0x00, + sizeof(entry->fsuuid))) { + result = -EINVAL; + break; + } + + part_pack_uuid(args[0].from, entry->fsuuid); + entry->flags |= IMA_FSUUID; + result = 0; + break; case Opt_uid: ima_log_string(ab, "uid", args[0].from); -- cgit v1.2.1 From 76bb28f6126f20ee987b9d2570fa653d95d30ae9 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Fri, 8 Jun 2012 10:42:30 +0300 Subject: ima: use new crypto_shash API instead of old crypto_hash Old crypto hash API internally uses shash API. Using shash API directly is more efficient. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 1 + security/integrity/ima/ima_crypto.c | 75 ++++++++++++++++++------------------- security/integrity/ima/ima_init.c | 3 ++ 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index ab68bed8ac36..5a94f9c92605 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -89,6 +89,7 @@ int ima_calc_template_hash(int template_len, void *template, char *digest); int ima_calc_boot_aggregate(char *digest); void ima_add_violation(struct inode *inode, const unsigned char *filename, const char *op, const char *cause); +int ima_init_crypto(void); /* * used to protect h_table and sha_table diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index b21ee5b5495a..920f49cfbf13 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -19,24 +19,22 @@ #include #include #include +#include #include "ima.h" -static int init_desc(struct hash_desc *desc) +static struct crypto_shash *ima_shash_tfm; + +int ima_init_crypto(void) { - int rc; + long rc; - desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(desc->tfm)) { - pr_info("IMA: failed to load %s transform: %ld\n", - ima_hash, PTR_ERR(desc->tfm)); - rc = PTR_ERR(desc->tfm); + ima_shash_tfm = crypto_alloc_shash(ima_hash, 0, 0); + if (IS_ERR(ima_shash_tfm)) { + rc = PTR_ERR(ima_shash_tfm); + pr_err("Can not allocate %s (reason: %ld)\n", ima_hash, rc); return rc; } - desc->flags = 0; - rc = crypto_hash_init(desc); - if (rc) - crypto_free_hash(desc->tfm); - return rc; + return 0; } /* @@ -44,13 +42,18 @@ static int init_desc(struct hash_desc *desc) */ int ima_calc_hash(struct file *file, char *digest) { - struct hash_desc desc; - struct scatterlist sg[1]; loff_t i_size, offset = 0; char *rbuf; int rc, read = 0; + struct { + struct shash_desc shash; + char ctx[crypto_shash_descsize(ima_shash_tfm)]; + } desc; - rc = init_desc(&desc); + desc.shash.tfm = ima_shash_tfm; + desc.shash.flags = 0; + + rc = crypto_shash_init(&desc.shash); if (rc != 0) return rc; @@ -75,19 +78,17 @@ int ima_calc_hash(struct file *file, char *digest) if (rbuf_len == 0) break; offset += rbuf_len; - sg_init_one(sg, rbuf, rbuf_len); - rc = crypto_hash_update(&desc, sg, rbuf_len); + rc = crypto_shash_update(&desc.shash, rbuf, rbuf_len); if (rc) break; } kfree(rbuf); if (!rc) - rc = crypto_hash_final(&desc, digest); + rc = crypto_shash_final(&desc.shash, digest); if (read) file->f_mode &= ~FMODE_READ; out: - crypto_free_hash(desc.tfm); return rc; } @@ -96,20 +97,15 @@ out: */ int ima_calc_template_hash(int template_len, void *template, char *digest) { - struct hash_desc desc; - struct scatterlist sg[1]; - int rc; + struct { + struct shash_desc shash; + char ctx[crypto_shash_descsize(ima_shash_tfm)]; + } desc; - rc = init_desc(&desc); - if (rc != 0) - return rc; + desc.shash.tfm = ima_shash_tfm; + desc.shash.flags = 0; - sg_init_one(sg, template, template_len); - rc = crypto_hash_update(&desc, sg, template_len); - if (!rc) - rc = crypto_hash_final(&desc, digest); - crypto_free_hash(desc.tfm); - return rc; + return crypto_shash_digest(&desc.shash, template, template_len, digest); } static void __init ima_pcrread(int idx, u8 *pcr) @@ -126,12 +122,17 @@ static void __init ima_pcrread(int idx, u8 *pcr) */ int __init ima_calc_boot_aggregate(char *digest) { - struct hash_desc desc; - struct scatterlist sg; u8 pcr_i[IMA_DIGEST_SIZE]; int rc, i; + struct { + struct shash_desc shash; + char ctx[crypto_shash_descsize(ima_shash_tfm)]; + } desc; + + desc.shash.tfm = ima_shash_tfm; + desc.shash.flags = 0; - rc = init_desc(&desc); + rc = crypto_shash_init(&desc.shash); if (rc != 0) return rc; @@ -139,11 +140,9 @@ int __init ima_calc_boot_aggregate(char *digest) for (i = TPM_PCR0; i < TPM_PCR8; i++) { ima_pcrread(i, pcr_i); /* now accumulate with current aggregate */ - sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE); - rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE); + rc = crypto_shash_update(&desc.shash, pcr_i, IMA_DIGEST_SIZE); } if (!rc) - crypto_hash_final(&desc, digest); - crypto_free_hash(desc.tfm); + crypto_shash_final(&desc.shash, digest); return rc; } diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index b5dfd534f13d..162ea723db3d 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -85,6 +85,9 @@ int __init ima_init(void) if (!ima_used_chip) pr_info("IMA: No TPM chip found, activating TPM-bypass!\n"); + rc = ima_init_crypto(); + if (rc) + return rc; ima_add_boot_aggregate(); /* boot aggregate must be first entry */ ima_init_policy(); -- cgit v1.2.1 From 50af554466804bf51a52fa3d1d0a76f96bd33929 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Mon, 14 May 2012 14:13:56 +0300 Subject: ima: rename hash calculation functions Rename hash calculation functions to reflect meaning and change argument order in conventional way. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 4 ++-- security/integrity/ima/ima_api.c | 6 +++--- security/integrity/ima/ima_crypto.c | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 5a94f9c92605..6e69697fd530 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -84,8 +84,8 @@ void ima_fs_cleanup(void); int ima_inode_alloc(struct inode *inode); int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode); -int ima_calc_hash(struct file *file, char *digest); -int ima_calc_template_hash(int template_len, void *template, char *digest); +int ima_calc_file_hash(struct file *file, char *digest); +int ima_calc_buffer_hash(const void *data, int len, char *digest); int ima_calc_boot_aggregate(char *digest); void ima_add_violation(struct inode *inode, const unsigned char *filename, const char *op, const char *cause); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 9382a4c568b2..d9030b29d84d 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -50,8 +50,8 @@ int ima_store_template(struct ima_template_entry *entry, entry->template_len = sizeof(entry->template); if (!violation) { - result = ima_calc_template_hash(entry->template_len, - &entry->template, + result = ima_calc_buffer_hash(&entry->template, + entry->template_len, entry->digest); if (result < 0) { integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, @@ -148,7 +148,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, u64 i_version = file->f_dentry->d_inode->i_version; iint->ima_xattr.type = IMA_XATTR_DIGEST; - result = ima_calc_hash(file, iint->ima_xattr.digest); + result = ima_calc_file_hash(file, iint->ima_xattr.digest); if (!result) { iint->version = i_version; iint->flags |= IMA_COLLECTED; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 920f49cfbf13..b691e0f3830c 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -40,7 +40,7 @@ int ima_init_crypto(void) /* * Calculate the MD5/SHA1 file digest */ -int ima_calc_hash(struct file *file, char *digest) +int ima_calc_file_hash(struct file *file, char *digest) { loff_t i_size, offset = 0; char *rbuf; @@ -93,9 +93,9 @@ out: } /* - * Calculate the hash of a given template + * Calculate the hash of a given buffer */ -int ima_calc_template_hash(int template_len, void *template, char *digest) +int ima_calc_buffer_hash(const void *data, int len, char *digest) { struct { struct shash_desc shash; @@ -105,7 +105,7 @@ int ima_calc_template_hash(int template_len, void *template, char *digest) desc.shash.tfm = ima_shash_tfm; desc.shash.flags = 0; - return crypto_shash_digest(&desc.shash, template, template_len, digest); + return crypto_shash_digest(&desc.shash, data, len, digest); } static void __init ima_pcrread(int idx, u8 *pcr) -- cgit v1.2.1 From e0751257a64ea10cca96ccb06522bfb10e36cb5b Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 7 Feb 2013 00:12:08 +0200 Subject: ima: digital signature verification using asymmetric keys Asymmetric keys were introduced in linux-3.7 to verify the signature on signed kernel modules. The asymmetric keys infrastructure abstracts the signature verification from the crypto details. This patch adds IMA/EVM signature verification using asymmetric keys. Support for additional signature verification methods can now be delegated to the asymmetric key infrastructure. Although the module signature header and the IMA/EVM signature header could use the same format, to minimize the signature length and save space in the extended attribute, this patch defines a new IMA/EVM header format. The main difference is that the key identifier is a sha1[12 - 19] hash of the key modulus and exponent, similar to the current implementation. The only purpose of the key identifier is to identify the corresponding key in the kernel keyring. ima-evm-utils was updated to support the new signature format. While asymmetric signature verification functionality supports many different hash algorithms, the hash used in this patch is calculated during the IMA collection phase, based on the configured algorithm. The default algorithm is sha1, but for backwards compatibility md5 is supported. Due to this current limitation, signatures should be generated using a sha1 hash algorithm. Changes in this patch: - Functionality has been moved to separate source file in order to get rid of in source #ifdefs. - keyid is derived according to the RFC 3280. It does not require to assign IMA/EVM specific "description" when loading X509 certificate. Kernel asymmetric key subsystem automatically generate the description. Also loading a certificate does not require using of ima-evm-utils and can be done using keyctl only. - keyid size is reduced to 32 bits to save xattr space. Key search is done using partial match functionality of asymmetric_key_match(). - Kconfig option title was changed Signed-off-by: Dmitry Kasatkin Acked-by: David Howells Signed-off-by: Mimi Zohar --- security/integrity/Kconfig | 12 ++++ security/integrity/Makefile | 1 + security/integrity/digsig.c | 11 +++- security/integrity/digsig_asymmetric.c | 115 +++++++++++++++++++++++++++++++++ security/integrity/integrity.h | 12 ++++ 5 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 security/integrity/digsig_asymmetric.c diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index 5bd1cc1b4a54..4bb3a775a996 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -17,5 +17,17 @@ config INTEGRITY_SIGNATURE This is useful for evm and module keyrings, when keys are usually only added from initramfs. +config INTEGRITY_ASYMMETRIC_KEYS + boolean "Enable asymmetric keys support" + depends on INTEGRITY_SIGNATURE + default n + select ASYMMETRIC_KEY_TYPE + select ASYMMETRIC_PUBLIC_KEY_SUBTYPE + select PUBLIC_KEY_ALGO_RSA + select X509_CERTIFICATE_PARSER + help + This option enables digital signature verification using + asymmetric keys. + source security/integrity/ima/Kconfig source security/integrity/evm/Kconfig diff --git a/security/integrity/Makefile b/security/integrity/Makefile index d43799cc14f6..ebb6409b3fcb 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_INTEGRITY) += integrity.o obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o +obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o integrity-y := iint.o diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 2dc167d7cde9..0b759e17a131 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -44,5 +44,14 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, } } - return digsig_verify(keyring[id], sig, siglen, digest, digestlen); + switch (sig[0]) { + case 1: + return digsig_verify(keyring[id], sig, siglen, + digest, digestlen); + case 2: + return asymmetric_verify(keyring[id], sig, siglen, + digest, digestlen); + } + + return -EOPNOTSUPP; } diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c new file mode 100644 index 000000000000..b4754667659d --- /dev/null +++ b/security/integrity/digsig_asymmetric.c @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2013 Intel Corporation + * + * Author: + * Dmitry Kasatkin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include + +#include "integrity.h" + +/* + * signature format v2 - for using with asymmetric keys + */ +struct signature_v2_hdr { + uint8_t version; /* signature format version */ + uint8_t hash_algo; /* Digest algorithm [enum pkey_hash_algo] */ + uint32_t keyid; /* IMA key identifier - not X509/PGP specific*/ + uint16_t sig_size; /* signature size */ + uint8_t sig[0]; /* signature payload */ +} __packed; + +/* + * Request an asymmetric key. + */ +static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) +{ + struct key *key; + char name[12]; + + sprintf(name, "id:%x", keyid); + + pr_debug("key search: \"%s\"\n", name); + + if (keyring) { + /* search in specific keyring */ + key_ref_t kref; + kref = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, name); + if (IS_ERR(kref)) + key = ERR_CAST(kref); + else + key = key_ref_to_ptr(kref); + } else { + key = request_key(&key_type_asymmetric, name, NULL); + } + + if (IS_ERR(key)) { + pr_warn("Request for unknown key '%s' err %ld\n", + name, PTR_ERR(key)); + switch (PTR_ERR(key)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + return ERR_PTR(-ENOKEY); + default: + return key; + } + } + + pr_debug("%s() = 0 [%x]\n", __func__, key_serial(key)); + + return key; +} + +int asymmetric_verify(struct key *keyring, const char *sig, + int siglen, const char *data, int datalen) +{ + struct public_key_signature pks; + struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; + struct key *key; + int ret = -ENOMEM; + + if (siglen <= sizeof(*hdr)) + return -EBADMSG; + + siglen -= sizeof(*hdr); + + if (siglen != __be16_to_cpu(hdr->sig_size)) + return -EBADMSG; + + if (hdr->hash_algo >= PKEY_HASH__LAST) + return -ENOPKG; + + key = request_asymmetric_key(keyring, __be32_to_cpu(hdr->keyid)); + if (IS_ERR(key)) + return PTR_ERR(key); + + memset(&pks, 0, sizeof(pks)); + + pks.pkey_hash_algo = hdr->hash_algo; + pks.digest = (u8 *)data; + pks.digest_size = datalen; + pks.nr_mpi = 1; + pks.rsa.s = mpi_read_raw_data(hdr->sig, siglen); + + if (pks.rsa.s) + ret = verify_signature(key, &pks); + + mpi_free(pks.rsa.s); + key_put(key); + pr_debug("%s() = %d\n", __func__, ret); + return ret; +} diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 0ae08fc88585..84c37c4db914 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -14,6 +14,7 @@ #include #include #include +#include /* iint action cache flags */ #define IMA_MEASURE 0x00000001 @@ -101,5 +102,16 @@ static inline int integrity_digsig_verify(const unsigned int id, #endif /* CONFIG_INTEGRITY_SIGNATURE */ +#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS +int asymmetric_verify(struct key *keyring, const char *sig, + int siglen, const char *data, int datalen); +#else +static inline int asymmetric_verify(struct key *keyring, const char *sig, + int siglen, const char *data, int datalen) +{ + return -EOPNOTSUPP; +} +#endif + /* set during initialization */ extern int iint_initialized; -- cgit v1.2.1 From 5b2660326039a32b28766cb4c1a8b1bdcfadc375 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Mon, 11 Feb 2013 14:36:44 -0500 Subject: tpm/ibmvtpm: build only when IBM pseries is configured Signed-off-by: Kent Yoder --- drivers/char/tpm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 765d1c7e92b9..dbfd56446c31 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -75,7 +75,7 @@ config TCG_INFINEON config TCG_IBMVTPM tristate "IBM VTPM Interface" - depends on PPC64 + depends on PPC_PSERIES ---help--- If you have IBM virtual TPM (VTPM) support say Yes and it will be accessible from within Linux. To compile this driver -- cgit v1.2.1