From 7ea59202db8d20806d9ae552acd1875c3a978bcc Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 23 May 2016 10:54:11 -0400 Subject: selinux: Only apply bounds checking to source types The current bounds checking of both source and target types requires allowing any domain that has access to the child domain to also have the same permissions to the parent, which is undesirable. Drop the target bounds checking. KaiGai Kohei originally removed all use of target bounds in commit 7d52a155e38d ("selinux: remove dead code in type_attribute_bounds_av()") but this was reverted in commit 2ae3ba39389b ("selinux: libsepol: remove dead code in check_avtab_hierarchy_callback()") because it would have required explicitly allowing the parent any permissions to the child that the child is allowed to itself. This change in contrast retains the logic for the case where both source and target types are bounded, thereby allowing access if the parent of the source is allowed the corresponding permissions to the parent of the target. Further, this change reworks the logic such that we only perform a single computation for each case and there is no ambiguity as to how to resolve a bounds violation. Under the new logic, if the source type and target types are both bounded, then the parent of the source type must be allowed the same permissions to the parent of the target type. If only the source type is bounded, then the parent of the source type must be allowed the same permissions to the target type. Examples of the new logic and comparisons with the old logic: 1. If we have: typebounds A B; then: allow B self:process ; will satisfy the bounds constraint iff: allow A self:process ; is also allowed in policy. Under the old logic, the allow rule on B satisfies the bounds constraint if any of the following three are allowed: allow A B:process ; or allow B A:process ; or allow A self:process ; However, either of the first two ultimately require the third to satisfy the bounds constraint under the old logic, and therefore this degenerates to the same result (but is more efficient - we only need to perform one compute_av call). 2. If we have: typebounds A B; typebounds A_exec B_exec; then: allow B B_exec:file ; will satisfy the bounds constraint iff: allow A A_exec:file ; is also allowed in policy. This is essentially the same as #1; it is merely included as an example of dealing with object types related to a bounded domain in a manner that satisfies the bounds relationship. Note that this approach is preferable to leaving B_exec unbounded and having: allow A B_exec:file ; in policy because that would allow B's entrypoints to be used to enter A. Similarly for _tmp or other related types. 3. If we have: typebounds A B; and an unbounded type T, then: allow B T:file ; will satisfy the bounds constraint iff: allow A T:file ; is allowed in policy. The old logic would have been identical for this example. 4. If we have: typebounds A B; and an unbounded domain D, then: allow D B:unix_stream_socket ; is not subject to any bounds constraints under the new logic because D is not bounded. This is desirable so that we can allow a domain to e.g. connectto a child domain without having to allow it to do the same to its parent. The old logic would have required: allow D A:unix_stream_socket ; to also be allowed in policy. Signed-off-by: Stephen Smalley [PM: re-wrapped description to appease checkpatch.pl] Signed-off-by: Paul Moore diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 89df646..082b20c 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -543,7 +543,7 @@ static void type_attribute_bounds_av(struct context *scontext, struct av_decision *avd) { struct context lo_scontext; - struct context lo_tcontext; + struct context lo_tcontext, *tcontextp = tcontext; struct av_decision lo_avd; struct type_datum *source; struct type_datum *target; @@ -553,67 +553,41 @@ static void type_attribute_bounds_av(struct context *scontext, scontext->type - 1); BUG_ON(!source); + if (!source->bounds) + return; + target = flex_array_get_ptr(policydb.type_val_to_struct_array, tcontext->type - 1); BUG_ON(!target); - if (source->bounds) { - memset(&lo_avd, 0, sizeof(lo_avd)); - - memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); - lo_scontext.type = source->bounds; + memset(&lo_avd, 0, sizeof(lo_avd)); - context_struct_compute_av(&lo_scontext, - tcontext, - tclass, - &lo_avd, - NULL); - if ((lo_avd.allowed & avd->allowed) == avd->allowed) - return; /* no masked permission */ - masked = ~lo_avd.allowed & avd->allowed; - } + memcpy(&lo_scontext, scontext, sizeof(lo_scontext)); + lo_scontext.type = source->bounds; if (target->bounds) { - memset(&lo_avd, 0, sizeof(lo_avd)); - memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext)); lo_tcontext.type = target->bounds; - - context_struct_compute_av(scontext, - &lo_tcontext, - tclass, - &lo_avd, - NULL); - if ((lo_avd.allowed & avd->allowed) == avd->allowed) - return; /* no masked permission */ - masked = ~lo_avd.allowed & avd->allowed; + tcontextp = &lo_tcontext; } - if (source->bounds && target->bounds) { - memset(&lo_avd, 0, sizeof(lo_avd)); - /* - * lo_scontext and lo_tcontext are already - * set up. - */ + context_struct_compute_av(&lo_scontext, + tcontextp, + tclass, + &lo_avd, + NULL); - context_struct_compute_av(&lo_scontext, - &lo_tcontext, - tclass, - &lo_avd, - NULL); - if ((lo_avd.allowed & avd->allowed) == avd->allowed) - return; /* no masked permission */ - masked = ~lo_avd.allowed & avd->allowed; - } + masked = ~lo_avd.allowed & avd->allowed; - if (masked) { - /* mask violated permissions */ - avd->allowed &= ~masked; + if (likely(!masked)) + return; /* no masked permission */ - /* audit masked permissions */ - security_dump_masked_av(scontext, tcontext, - tclass, masked, "bounds"); - } + /* mask violated permissions */ + avd->allowed &= ~masked; + + /* audit masked permissions */ + security_dump_masked_av(scontext, tcontext, + tclass, masked, "bounds"); } /* -- cgit v0.10.2 From 39baa7e6add5062bebf7cf1bdf6ecb4f380386bc Mon Sep 17 00:00:00 2001 From: "Serge E. Hallyn" Date: Fri, 27 May 2016 14:23:39 -0500 Subject: MAINTAINERS: update Serge Hallyn's email address Author: Serge Hallyn Date: Thu May 19 23:37:05 2016 -0500 Update email address My canonical.com email address will soon stop working. Signed-off-by: Serge Hallyn Signed-off-by: James Morris diff --git a/MAINTAINERS b/MAINTAINERS index ed42cb6..7227c79 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2813,7 +2813,7 @@ F: include/uapi/linux/can/error.h F: include/uapi/linux/can/netlink.h CAPABILITIES -M: Serge Hallyn +M: Serge Hallyn L: linux-security-module@vger.kernel.org S: Supported F: include/linux/capability.h -- cgit v0.10.2 From 2885c1e3e0c29e4a1915214ddc9673925f538299 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Tue, 31 May 2016 17:24:15 -0700 Subject: LSM: Fix for security_inode_getsecurity and -EOPNOTSUPP Serge Hallyn pointed out that the current implementation of security_inode_getsecurity() works if there is only one hook provided for it, but will fail if there is more than one and the attribute requested isn't supplied by the first module. This isn't a problem today, since only SELinux and Smack provide this hook and there is (currently) no way to enable both of those modules at the same time. Serge, however, wants to introduce a capability attribute and an inode_getsecurity hook in the capability security module to handle it. This addresses that upcoming problem, will be required for "extreme stacking" and is just a better implementation. Signed-off-by: Casey Schaufler Acked-by: Serge Hallyn Signed-off-by: James Morris diff --git a/security/security.c b/security/security.c index 7095693..c4bb47d 100644 --- a/security/security.c +++ b/security/security.c @@ -700,18 +700,39 @@ int security_inode_killpriv(struct dentry *dentry) int security_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc) { + struct security_hook_list *hp; + int rc; + if (unlikely(IS_PRIVATE(inode))) return -EOPNOTSUPP; - return call_int_hook(inode_getsecurity, -EOPNOTSUPP, inode, name, - buffer, alloc); + /* + * Only one module will provide an attribute with a given name. + */ + list_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) { + rc = hp->hook.inode_getsecurity(inode, name, buffer, alloc); + if (rc != -EOPNOTSUPP) + return rc; + } + return -EOPNOTSUPP; } int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags) { + struct security_hook_list *hp; + int rc; + if (unlikely(IS_PRIVATE(inode))) return -EOPNOTSUPP; - return call_int_hook(inode_setsecurity, -EOPNOTSUPP, inode, name, - value, size, flags); + /* + * Only one module will provide an attribute with a given name. + */ + list_for_each_entry(hp, &security_hook_heads.inode_setsecurity, list) { + rc = hp->hook.inode_setsecurity(inode, name, value, size, + flags); + if (rc != -EOPNOTSUPP) + return rc; + } + return -EOPNOTSUPP; } int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size) -- cgit v0.10.2 From 98f368e9e2630a3ce3e80fb10fb2e02038cf9578 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 2 Jun 2016 23:43:21 -0500 Subject: kernel: Add noaudit variant of ns_capable() When checking the current cred for a capability in a specific user namespace, it isn't always desirable to have the LSMs audit the check. This patch adds a noaudit variant of ns_capable() for when those situations arise. The common logic between ns_capable() and the new ns_capable_noaudit() is moved into a single, shared function to keep duplicated code to a minimum and ease maintainability. Signed-off-by: Tyler Hicks Acked-by: Serge E. Hallyn Signed-off-by: James Morris diff --git a/include/linux/capability.h b/include/linux/capability.h index 00690ff..5f3c63d 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -206,6 +206,7 @@ extern bool has_ns_capability_noaudit(struct task_struct *t, struct user_namespace *ns, int cap); extern bool capable(int cap); extern bool ns_capable(struct user_namespace *ns, int cap); +extern bool ns_capable_noaudit(struct user_namespace *ns, int cap); #else static inline bool has_capability(struct task_struct *t, int cap) { @@ -233,6 +234,10 @@ static inline bool ns_capable(struct user_namespace *ns, int cap) { return true; } +static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap) +{ + return true; +} #endif /* CONFIG_MULTIUSER */ extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap); extern bool file_ns_capable(const struct file *file, struct user_namespace *ns, int cap); diff --git a/kernel/capability.c b/kernel/capability.c index 45432b5..00411c8 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -361,6 +361,24 @@ bool has_capability_noaudit(struct task_struct *t, int cap) return has_ns_capability_noaudit(t, &init_user_ns, cap); } +static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit) +{ + int capable; + + if (unlikely(!cap_valid(cap))) { + pr_crit("capable() called with invalid cap=%u\n", cap); + BUG(); + } + + capable = audit ? security_capable(current_cred(), ns, cap) : + security_capable_noaudit(current_cred(), ns, cap); + if (capable == 0) { + current->flags |= PF_SUPERPRIV; + return true; + } + return false; +} + /** * ns_capable - Determine if the current task has a superior capability in effect * @ns: The usernamespace we want the capability in @@ -374,19 +392,27 @@ bool has_capability_noaudit(struct task_struct *t, int cap) */ bool ns_capable(struct user_namespace *ns, int cap) { - if (unlikely(!cap_valid(cap))) { - pr_crit("capable() called with invalid cap=%u\n", cap); - BUG(); - } - - if (security_capable(current_cred(), ns, cap) == 0) { - current->flags |= PF_SUPERPRIV; - return true; - } - return false; + return ns_capable_common(ns, cap, true); } EXPORT_SYMBOL(ns_capable); +/** + * ns_capable_noaudit - Determine if the current task has a superior capability + * (unaudited) in effect + * @ns: The usernamespace we want the capability in + * @cap: The capability to be tested for + * + * Return true if the current task has the given superior capability currently + * available for use, false if not. + * + * This sets PF_SUPERPRIV on the task if the capability is available on the + * assumption that it's about to be used. + */ +bool ns_capable_noaudit(struct user_namespace *ns, int cap) +{ + return ns_capable_common(ns, cap, false); +} +EXPORT_SYMBOL(ns_capable_noaudit); /** * capable - Determine if the current task has a superior capability in effect -- cgit v0.10.2 From d6e0d306449bcb5fa3c80e7a3edf11d45abf9ae9 Mon Sep 17 00:00:00 2001 From: Tyler Hicks Date: Thu, 2 Jun 2016 23:43:22 -0500 Subject: net: Use ns_capable_noaudit() when determining net sysctl permissions The capability check should not be audited since it is only being used to determine the inode permissions. A failed check does not indicate a violation of security policy but, when an LSM is enabled, a denial audit message was being generated. The denial audit message caused confusion for some application authors because root-running Go applications always triggered the denial. To prevent this confusion, the capability check in net_ctl_permissions() is switched to the noaudit variant. BugLink: https://launchpad.net/bugs/1465724 Signed-off-by: Tyler Hicks Acked-by: Serge E. Hallyn Signed-off-by: James Morris diff --git a/net/sysctl_net.c b/net/sysctl_net.c index ed98c1f..46a71c7 100644 --- a/net/sysctl_net.c +++ b/net/sysctl_net.c @@ -46,7 +46,7 @@ static int net_ctl_permissions(struct ctl_table_header *head, kgid_t root_gid = make_kgid(net->user_ns, 0); /* Allow network administrator to have same access as root. */ - if (ns_capable(net->user_ns, CAP_NET_ADMIN) || + if (ns_capable_noaudit(net->user_ns, CAP_NET_ADMIN) || uid_eq(root_uid, current_euid())) { int mode = (table->mode >> 6) & 7; return (mode << 6) | (mode << 3) | mode; -- cgit v0.10.2 From 40d273782ff16fe1a7445cc05c66a447dfea3433 Mon Sep 17 00:00:00 2001 From: Mike Danese Date: Thu, 19 May 2016 21:37:53 -0700 Subject: security: tomoyo: simplify the gc kthread creation The code is doing the equivalent of the kthread_run macro. Signed-off-by: Mike Danese Acked-by: Tetsuo Handa Signed-off-by: James Morris diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 986a6a7..540bc29 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -645,11 +645,6 @@ void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register) } } spin_unlock(&tomoyo_io_buffer_list_lock); - if (is_write) { - struct task_struct *task = kthread_create(tomoyo_gc_thread, - NULL, - "GC for TOMOYO"); - if (!IS_ERR(task)) - wake_up_process(task); - } + if (is_write) + kthread_run(tomoyo_gc_thread, NULL, "GC for TOMOYO"); } -- cgit v0.10.2 From 0e0e36774081534783aa8eeb9f6fbddf98d3c061 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 6 Jun 2016 15:17:20 -0400 Subject: netlabel: add address family checks to netlbl_{sock,req}_delattr() It seems risky to always rely on the caller to ensure the socket's address family is correct before passing it to the NetLabel kAPI, especially since we see at least one LSM which didn't. Add address family checks to the *_delattr() functions to help prevent future problems. Cc: Reported-by: Maninder Singh Signed-off-by: Paul Moore diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 1325776..bd007a9 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -824,7 +824,11 @@ socket_setattr_return: */ void netlbl_sock_delattr(struct sock *sk) { - cipso_v4_sock_delattr(sk); + switch (sk->sk_family) { + case AF_INET: + cipso_v4_sock_delattr(sk); + break; + } } /** @@ -987,7 +991,11 @@ req_setattr_return: */ void netlbl_req_delattr(struct request_sock *req) { - cipso_v4_req_delattr(req); + switch (req->rsk_ops->family) { + case AF_INET: + cipso_v4_req_delattr(req); + break; + } } /** -- cgit v0.10.2 From 02f06918156b32a584ff2dd9ebfa653abf4d8d5d Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Tue, 7 Jun 2016 10:30:53 -0400 Subject: iucv: properly clone LSM attributes to newly created child sockets Much like we had to do for AF_BLUETOOTH and AF_ALG, make sure we properly clone the parent socket's LSM attributes to newly created child sockets. Signed-off-by: Paul Moore diff --git a/net/iucv/af_iucv.c b/net/iucv/af_iucv.c index fc3598a..a0d1e36 100644 --- a/net/iucv/af_iucv.c +++ b/net/iucv/af_iucv.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -530,8 +531,10 @@ static void iucv_sock_close(struct sock *sk) static void iucv_sock_init(struct sock *sk, struct sock *parent) { - if (parent) + if (parent) { sk->sk_type = parent->sk_type; + security_sk_clone(parent, sk); + } } static struct sock *iucv_sock_alloc(struct socket *sock, int proto, gfp_t prio, int kern) -- cgit v0.10.2 From 18d872f77cecec2677a394170f26aaeb08562cee Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Mon, 4 Apr 2016 11:14:53 +0200 Subject: Smack: ignore null signal in smack_task_kill Kill with signal number 0 is commonly used for checking PID existence. Smack treated such cases like any other kills, although no signal is actually delivered when sig == 0. Checking permissions when sig == 0 didn't prevent an unprivileged caller from learning whether PID exists or not. When it existed, kernel returned EPERM, when it didn't - ESRCH. The only effect of policy check in such case is noise in audit logs. This change lets Smack silently ignore kill() invocations with sig == 0. Signed-off-by: Rafal Krypa Acked-by: Casey Schaufler diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 6777295..e96080e 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2227,6 +2227,9 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, struct smack_known *tkp = smk_of_task_struct(p); int rc; + if (!sig) + return 0; /* null signal; existence test */ + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); smk_ad_setfield_u_tsk(&ad, p); /* -- cgit v0.10.2 From 8bebe88c0995f331b0614f413285ce2b1d6fe09c Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 9 Jun 2016 10:40:37 -0400 Subject: selinux: import NetLabel category bitmaps correctly The existing ebitmap_netlbl_import() code didn't correctly handle the case where the ebitmap_node was not aligned/sized to a power of two, this patch fixes this (on x86_64 ebitmap_node contains six bitmaps making a range of 0..383). Signed-off-by: Paul Moore diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 57644b1..894b6cd 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -165,7 +165,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap, e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); if (e_iter == NULL) goto netlbl_import_failure; - e_iter->startbit = offset & ~(EBITMAP_SIZE - 1); + e_iter->startbit = offset - (offset % EBITMAP_SIZE); if (e_prev == NULL) ebmap->node = e_iter; else -- cgit v0.10.2 From 50b8629a606b876e2df000699d68904f239dcfeb Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 9 Jun 2016 10:56:02 -0400 Subject: netlabel: handle sparse category maps in netlbl_catmap_getlong() In cases where the category bitmap is sparse enough that gaps exist between netlbl_lsm_catmap structs, callers to netlbl_catmap_getlong() could find themselves prematurely ending their search through the category bitmap. Further, the methods used to calculate the 'idx' and 'off' values were incorrect for bitmaps this large. This patch changes the netlbl_catmap_getlong() behavior so that it always skips over gaps and calculates the index and offset values correctly. Signed-off-by: Paul Moore diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index bd007a9..3c070f2 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -609,20 +609,19 @@ int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap, off = catmap->startbit; *offset = off; } - iter = _netlbl_catmap_getnode(&catmap, off, _CM_F_NONE, 0); + iter = _netlbl_catmap_getnode(&catmap, off, _CM_F_WALK, 0); if (iter == NULL) { *offset = (u32)-1; return 0; } if (off < iter->startbit) { - off = iter->startbit; - *offset = off; + *offset = iter->startbit; + off = 0; } else off -= iter->startbit; - idx = off / NETLBL_CATMAP_MAPSIZE; - *bitmap = iter->bitmap[idx] >> (off % NETLBL_CATMAP_SIZE); + *bitmap = iter->bitmap[idx] >> (off % NETLBL_CATMAP_MAPSIZE); return 0; } -- cgit v0.10.2 From 965475acca2cbcc1d748a8b6a05f8c7cf57d075a Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 14 Jun 2016 10:29:44 +0100 Subject: KEYS: Strip trailing spaces Strip some trailing spaces. Signed-off-by: David Howells diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h index fc48754..5de0673 100644 --- a/include/keys/rxrpc-type.h +++ b/include/keys/rxrpc-type.h @@ -51,7 +51,7 @@ struct krb5_principal { struct krb5_tagged_data { /* for tag value, see /usr/include/krb5/krb5.h * - KRB5_AUTHDATA_* for auth data - * - + * - */ s32 tag; u32 data_len; diff --git a/security/keys/persistent.c b/security/keys/persistent.c index 2ef45b3..1edc1f0 100644 --- a/security/keys/persistent.c +++ b/security/keys/persistent.c @@ -114,7 +114,7 @@ found: ret = key_link(key_ref_to_ptr(dest_ref), persistent); if (ret == 0) { key_set_timeout(persistent, persistent_keyring_expiry); - ret = persistent->serial; + ret = persistent->serial; } } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index a29e355..43affcf 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -442,7 +442,7 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx, if (ctx->index_key.type == &key_type_keyring) return ERR_PTR(-EPERM); - + user = key_user_lookup(current_fsuid()); if (!user) return ERR_PTR(-ENOMEM); -- cgit v0.10.2 From 9552c7aebb8c36912612fddad5b55267c671a303 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 14 Jun 2016 13:18:33 +0100 Subject: modsign: Make sign-file determine the format of the X.509 cert Make sign-file determine the format of the X.509 certificate by reading the first two bytes and seeing if the first byte is 0x30 and the second 0x81-0x84. If this is the case, assume it's DER encoded, otherwise assume it to be PEM encoded. Without this, it gets awkward to deal with the error messages from d2i_X509_bio() when we want to call BIO_reset() and then PEM_read_bio() in case the certificate was PEM encoded rather than X.509 encoded. Reported-by: Ben Hutchings Signed-off-by: David Howells Tested-by: Ben Hutchings cc: David Woodhouse cc: Juerg Haefliger cc: Ben Hutchings diff --git a/scripts/sign-file.c b/scripts/sign-file.c index d912d5a..53af6dc 100755 --- a/scripts/sign-file.c +++ b/scripts/sign-file.c @@ -1,6 +1,6 @@ /* Sign a module file using the given key. * - * Copyright © 2014-2015 Red Hat, Inc. All Rights Reserved. + * Copyright © 2014-2016 Red Hat, Inc. All Rights Reserved. * Copyright © 2015 Intel Corporation. * Copyright © 2016 Hewlett Packard Enterprise Development LP * @@ -167,19 +167,37 @@ static EVP_PKEY *read_private_key(const char *private_key_name) static X509 *read_x509(const char *x509_name) { + unsigned char buf[2]; X509 *x509; BIO *b; + int n; b = BIO_new_file(x509_name, "rb"); ERR(!b, "%s", x509_name); - x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */ - if (!x509) { - ERR(BIO_reset(b) != 1, "%s", x509_name); - x509 = PEM_read_bio_X509(b, NULL, NULL, - NULL); /* PEM encoded X.509 */ - if (x509) - drain_openssl_errors(); + + /* Look at the first two bytes of the file to determine the encoding */ + n = BIO_read(b, buf, 2); + if (n != 2) { + if (BIO_should_retry(b)) { + fprintf(stderr, "%s: Read wanted retry\n", x509_name); + exit(1); + } + if (n >= 0) { + fprintf(stderr, "%s: Short read\n", x509_name); + exit(1); + } + ERR(1, "%s", x509_name); } + + ERR(BIO_reset(b) != 0, "%s", x509_name); + + if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84) + /* Assume raw DER encoded X.509 */ + x509 = d2i_X509_bio(b, NULL); + else + /* Assume PEM encoded X.509 */ + x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); + BIO_free(b); ERR(!x509, "%s", x509_name); -- cgit v0.10.2 From 58d0a862f573c3354fa912603ef5a4db188774e7 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 26 May 2016 11:47:01 -0700 Subject: seccomp: add tests for ptrace hole One problem with seccomp was that ptrace could be used to change a syscall after seccomp filtering had completed. This was a well documented limitation, and it was recommended to block ptrace when defining a filter to avoid this problem. This can be quite a limitation for containers or other places where ptrace is desired even under seccomp filters. This adds tests for both SECCOMP_RET_TRACE and PTRACE_SYSCALL manipulations. Signed-off-by: Kees Cook Cc: Andy Lutomirski diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 2e58549..03f1fa4 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -1021,8 +1021,8 @@ void tracer_stop(int sig) typedef void tracer_func_t(struct __test_metadata *_metadata, pid_t tracee, int status, void *args); -void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee, - tracer_func_t tracer_func, void *args) +void start_tracer(struct __test_metadata *_metadata, int fd, pid_t tracee, + tracer_func_t tracer_func, void *args, bool ptrace_syscall) { int ret = -1; struct sigaction action = { @@ -1042,12 +1042,16 @@ void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee, /* Wait for attach stop */ wait(NULL); - ret = ptrace(PTRACE_SETOPTIONS, tracee, NULL, PTRACE_O_TRACESECCOMP); + ret = ptrace(PTRACE_SETOPTIONS, tracee, NULL, ptrace_syscall ? + PTRACE_O_TRACESYSGOOD : + PTRACE_O_TRACESECCOMP); ASSERT_EQ(0, ret) { TH_LOG("Failed to set PTRACE_O_TRACESECCOMP"); kill(tracee, SIGKILL); } - ptrace(PTRACE_CONT, tracee, NULL, 0); + ret = ptrace(ptrace_syscall ? PTRACE_SYSCALL : PTRACE_CONT, + tracee, NULL, 0); + ASSERT_EQ(0, ret); /* Unblock the tracee */ ASSERT_EQ(1, write(fd, "A", 1)); @@ -1063,12 +1067,13 @@ void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee, /* Child is dead. Time to go. */ return; - /* Make sure this is a seccomp event. */ - ASSERT_EQ(true, IS_SECCOMP_EVENT(status)); + /* Check if this is a seccomp event. */ + ASSERT_EQ(!ptrace_syscall, IS_SECCOMP_EVENT(status)); tracer_func(_metadata, tracee, status, args); - ret = ptrace(PTRACE_CONT, tracee, NULL, NULL); + ret = ptrace(ptrace_syscall ? PTRACE_SYSCALL : PTRACE_CONT, + tracee, NULL, 0); ASSERT_EQ(0, ret); } /* Directly report the status of our test harness results. */ @@ -1079,7 +1084,7 @@ void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee, void cont_handler(int num) { } pid_t setup_trace_fixture(struct __test_metadata *_metadata, - tracer_func_t func, void *args) + tracer_func_t func, void *args, bool ptrace_syscall) { char sync; int pipefd[2]; @@ -1095,7 +1100,8 @@ pid_t setup_trace_fixture(struct __test_metadata *_metadata, signal(SIGALRM, cont_handler); if (tracer_pid == 0) { close(pipefd[0]); - tracer(_metadata, pipefd[1], tracee, func, args); + start_tracer(_metadata, pipefd[1], tracee, func, args, + ptrace_syscall); syscall(__NR_exit, 0); } close(pipefd[1]); @@ -1177,7 +1183,7 @@ FIXTURE_SETUP(TRACE_poke) /* Launch tracer. */ self->tracer = setup_trace_fixture(_metadata, tracer_poke, - &self->tracer_args); + &self->tracer_args, false); } FIXTURE_TEARDOWN(TRACE_poke) @@ -1399,6 +1405,29 @@ void tracer_syscall(struct __test_metadata *_metadata, pid_t tracee, } +void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee, + int status, void *args) +{ + int ret, nr; + unsigned long msg; + static bool entry; + + /* Make sure we got an empty message. */ + ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg); + EXPECT_EQ(0, ret); + EXPECT_EQ(0, msg); + + /* The only way to tell PTRACE_SYSCALL entry/exit is by counting. */ + entry = !entry; + if (!entry) + return; + + nr = get_syscall(_metadata, tracee); + + if (nr == __NR_getpid) + change_syscall(_metadata, tracee, __NR_getppid); +} + FIXTURE_DATA(TRACE_syscall) { struct sock_fprog prog; pid_t tracer, mytid, mypid, parent; @@ -1440,7 +1469,8 @@ FIXTURE_SETUP(TRACE_syscall) ASSERT_NE(self->parent, self->mypid); /* Launch tracer. */ - self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL); + self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL, + false); } FIXTURE_TEARDOWN(TRACE_syscall) @@ -1500,6 +1530,130 @@ TEST_F(TRACE_syscall, syscall_dropped) EXPECT_NE(self->mytid, syscall(__NR_gettid)); } +TEST_F(TRACE_syscall, skip_after_RET_TRACE) +{ + struct sock_filter filter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | EPERM), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog = { + .len = (unsigned short)ARRAY_SIZE(filter), + .filter = filter, + }; + long ret; + + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret); + + /* Install fixture filter. */ + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0); + ASSERT_EQ(0, ret); + + /* Install "errno on getppid" filter. */ + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0); + ASSERT_EQ(0, ret); + + /* Tracer will redirect getpid to getppid, and we should see EPERM. */ + EXPECT_EQ(-1, syscall(__NR_getpid)); + EXPECT_EQ(EPERM, errno); +} + +TEST_F_SIGNAL(TRACE_syscall, kill_after_RET_TRACE, SIGSYS) +{ + struct sock_filter filter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog = { + .len = (unsigned short)ARRAY_SIZE(filter), + .filter = filter, + }; + long ret; + + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret); + + /* Install fixture filter. */ + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0); + ASSERT_EQ(0, ret); + + /* Install "death on getppid" filter. */ + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0); + ASSERT_EQ(0, ret); + + /* Tracer will redirect getpid to getppid, and we should die. */ + EXPECT_NE(self->mypid, syscall(__NR_getpid)); +} + +TEST_F(TRACE_syscall, skip_after_ptrace) +{ + struct sock_filter filter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | EPERM), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog = { + .len = (unsigned short)ARRAY_SIZE(filter), + .filter = filter, + }; + long ret; + + /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */ + teardown_trace_fixture(_metadata, self->tracer); + self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL, + true); + + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret); + + /* Install "errno on getppid" filter. */ + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0); + ASSERT_EQ(0, ret); + + /* Tracer will redirect getpid to getppid, and we should see EPERM. */ + EXPECT_EQ(-1, syscall(__NR_getpid)); + EXPECT_EQ(EPERM, errno); +} + +TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS) +{ + struct sock_filter filter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog = { + .len = (unsigned short)ARRAY_SIZE(filter), + .filter = filter, + }; + long ret; + + /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */ + teardown_trace_fixture(_metadata, self->tracer); + self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL, + true); + + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret); + + /* Install "death on getppid" filter. */ + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0); + ASSERT_EQ(0, ret); + + /* Tracer will redirect getpid to getppid, and we should die. */ + EXPECT_NE(self->mypid, syscall(__NR_getpid)); +} + #ifndef __NR_seccomp # if defined(__i386__) # define __NR_seccomp 354 -- cgit v0.10.2 From 2f275de5d1ed7269913ef9b4c64a13952c0a38e8 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Fri, 27 May 2016 12:57:02 -0700 Subject: seccomp: Add a seccomp_data parameter secure_computing() Currently, if arch code wants to supply seccomp_data directly to seccomp (which is generally much faster than having seccomp do it using the syscall_get_xyz() API), it has to use the two-phase seccomp hooks. Add it to the easy hooks, too. Cc: linux-arch@vger.kernel.org Signed-off-by: Andy Lutomirski Signed-off-by: Kees Cook diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 4d93758..1027d3b 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -934,7 +934,7 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno) /* Do the secure computing check first; failures should be fast. */ #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER - if (secure_computing() == -1) + if (secure_computing(NULL) == -1) return -1; #else /* XXX: remove this once OABI gets fixed */ diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 3f6cd5c..6e2cf04 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1247,7 +1247,7 @@ static void tracehook_report_syscall(struct pt_regs *regs, asmlinkage int syscall_trace_enter(struct pt_regs *regs) { /* Do the secure computing check first; failures should be fast. */ - if (secure_computing() == -1) + if (secure_computing(NULL) == -1) return -1; if (test_thread_flag(TIF_SYSCALL_TRACE)) diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 0dcf691..c50af84 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -893,7 +893,7 @@ asmlinkage long syscall_trace_enter(struct pt_regs *regs, long syscall) current_thread_info()->syscall = syscall; - if (secure_computing() == -1) + if (secure_computing(NULL) == -1) return -1; if (test_thread_flag(TIF_SYSCALL_TRACE) && diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c index b5458b3..8edc47c 100644 --- a/arch/parisc/kernel/ptrace.c +++ b/arch/parisc/kernel/ptrace.c @@ -312,7 +312,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, long do_syscall_trace_enter(struct pt_regs *regs) { /* Do the secure computing check first. */ - if (secure_computing() == -1) + if (secure_computing(NULL) == -1) return -1; if (test_thread_flag(TIF_SYSCALL_TRACE) && diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index 30a03c0..ed799e9 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -1783,7 +1783,7 @@ static int do_seccomp(struct pt_regs *regs) * have already loaded -ENOSYS into r3, or seccomp has put * something else in r3 (via SECCOMP_RET_ERRNO/TRACE). */ - if (__secure_computing()) + if (__secure_computing(NULL)) return -1; /* diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 49b1c13..c238e99 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -824,7 +824,7 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) long ret = 0; /* Do the secure computing check first. */ - if (secure_computing()) { + if (secure_computing(NULL)) { /* seccomp failures shouldn't expose any additional code. */ ret = -1; goto out; diff --git a/arch/tile/kernel/ptrace.c b/arch/tile/kernel/ptrace.c index 54e7b72..8c6d2f2 100644 --- a/arch/tile/kernel/ptrace.c +++ b/arch/tile/kernel/ptrace.c @@ -255,7 +255,7 @@ int do_syscall_trace_enter(struct pt_regs *regs) { u32 work = ACCESS_ONCE(current_thread_info()->flags); - if (secure_computing() == -1) + if (secure_computing(NULL) == -1) return -1; if (work & _TIF_SYSCALL_TRACE) { diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c index 48b0dcb..9c5570f 100644 --- a/arch/um/kernel/skas/syscall.c +++ b/arch/um/kernel/skas/syscall.c @@ -21,7 +21,7 @@ void handle_syscall(struct uml_pt_regs *r) PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS); /* Do the secure computing check first; failures should be fast. */ - if (secure_computing() == -1) + if (secure_computing(NULL) == -1) return; if (syscall_trace_enter(regs)) diff --git a/arch/x86/entry/vsyscall/vsyscall_64.c b/arch/x86/entry/vsyscall/vsyscall_64.c index 174c254..85acde5 100644 --- a/arch/x86/entry/vsyscall/vsyscall_64.c +++ b/arch/x86/entry/vsyscall/vsyscall_64.c @@ -207,7 +207,7 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) */ regs->orig_ax = syscall_nr; regs->ax = -ENOSYS; - tmp = secure_computing(); + tmp = secure_computing(NULL); if ((!tmp && regs->orig_ax != syscall_nr) || regs->ip != address) { warn_bad_vsyscall(KERN_DEBUG, regs, "seccomp tried to change syscall nr or ip"); diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 2296e6b..9eaa7b3 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -28,11 +28,11 @@ struct seccomp { }; #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER -extern int __secure_computing(void); -static inline int secure_computing(void) +extern int __secure_computing(const struct seccomp_data *sd); +static inline int secure_computing(const struct seccomp_data *sd) { if (unlikely(test_thread_flag(TIF_SECCOMP))) - return __secure_computing(); + return __secure_computing(sd); return 0; } @@ -61,7 +61,7 @@ struct seccomp { }; struct seccomp_filter { }; #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER -static inline int secure_computing(void) { return 0; } +static inline int secure_computing(struct seccomp_data *sd) { return 0; } #else static inline void secure_computing_strict(int this_syscall) { return; } #endif diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 7002796..0681629 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -554,9 +554,9 @@ void secure_computing_strict(int this_syscall) BUG(); } #else -int __secure_computing(void) +int __secure_computing(const struct seccomp_data *sd) { - u32 phase1_result = seccomp_phase1(NULL); + u32 phase1_result = seccomp_phase1(sd); if (likely(phase1_result == SECCOMP_PHASE1_OK)) return 0; -- cgit v0.10.2 From c87a85177e7a7f9a9ee32893fb99a928e02fe23a Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Fri, 27 May 2016 13:08:59 -0700 Subject: x86/entry: Get rid of two-phase syscall entry work I added two-phase syscall entry work back when the entry slow path was very slow. Nowadays, the entry slow path is fast and two-phase entry work serves no purpose. Remove it. Signed-off-by: Andy Lutomirski Signed-off-by: Kees Cook diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index ec138e5..df56ca3 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -64,20 +64,13 @@ static void do_audit_syscall_entry(struct pt_regs *regs, u32 arch) } /* - * We can return 0 to resume the syscall or anything else to go to phase - * 2. If we resume the syscall, we need to put something appropriate in - * regs->orig_ax. - * - * NB: We don't have full pt_regs here, but regs->orig_ax and regs->ax - * are fully functional. - * - * For phase 2's benefit, our return value is: - * 0: resume the syscall - * 1: go to phase 2; no seccomp phase 2 needed - * anything else: go to phase 2; pass return value to seccomp + * Returns the syscall nr to run (which should match regs->orig_ax) or -1 + * to skip the syscall. */ -unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch) +static long syscall_trace_enter(struct pt_regs *regs) { + u32 arch = in_ia32_syscall() ? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64; + struct thread_info *ti = pt_regs_to_thread_info(regs); unsigned long ret = 0; u32 work; @@ -118,59 +111,9 @@ unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch) sd.args[5] = regs->bp; } - BUILD_BUG_ON(SECCOMP_PHASE1_OK != 0); - BUILD_BUG_ON(SECCOMP_PHASE1_SKIP != 1); - - ret = seccomp_phase1(&sd); - if (ret == SECCOMP_PHASE1_SKIP) { - regs->orig_ax = -1; - ret = 0; - } else if (ret != SECCOMP_PHASE1_OK) { - return ret; /* Go directly to phase 2 */ - } - - work &= ~_TIF_SECCOMP; - } -#endif - - /* Do our best to finish without phase 2. */ - if (work == 0) - return ret; /* seccomp and/or nohz only (ret == 0 here) */ - -#ifdef CONFIG_AUDITSYSCALL - if (work == _TIF_SYSCALL_AUDIT) { - /* - * If there is no more work to be done except auditing, - * then audit in phase 1. Phase 2 always audits, so, if - * we audit here, then we can't go on to phase 2. - */ - do_audit_syscall_entry(regs, arch); - return 0; - } -#endif - - return 1; /* Something is enabled that we can't handle in phase 1 */ -} - -/* Returns the syscall nr to run (which should match regs->orig_ax). */ -long syscall_trace_enter_phase2(struct pt_regs *regs, u32 arch, - unsigned long phase1_result) -{ - struct thread_info *ti = pt_regs_to_thread_info(regs); - long ret = 0; - u32 work = ACCESS_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY; - - if (IS_ENABLED(CONFIG_DEBUG_ENTRY)) - BUG_ON(regs != task_pt_regs(current)); - -#ifdef CONFIG_SECCOMP - /* - * Call seccomp_phase2 before running the other hooks so that - * they can see any changes made by a seccomp tracer. - */ - if (phase1_result > 1 && seccomp_phase2(phase1_result)) { - /* seccomp failures shouldn't expose any additional code. */ - return -1; + ret = __secure_computing(&sd); + if (ret == -1) + return ret; } #endif @@ -189,17 +132,6 @@ long syscall_trace_enter_phase2(struct pt_regs *regs, u32 arch, return ret ?: regs->orig_ax; } -long syscall_trace_enter(struct pt_regs *regs) -{ - u32 arch = in_ia32_syscall() ? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64; - unsigned long phase1_result = syscall_trace_enter_phase1(regs, arch); - - if (phase1_result == 0) - return regs->orig_ax; - else - return syscall_trace_enter_phase2(regs, arch, phase1_result); -} - #define EXIT_TO_USERMODE_LOOP_FLAGS \ (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE | \ _TIF_NEED_RESCHED | _TIF_USER_RETURN_NOTIFY) diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h index 6271281..2b5d686 100644 --- a/arch/x86/include/asm/ptrace.h +++ b/arch/x86/include/asm/ptrace.h @@ -83,12 +83,6 @@ extern void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs, int error_code, int si_code); -extern unsigned long syscall_trace_enter_phase1(struct pt_regs *, u32 arch); -extern long syscall_trace_enter_phase2(struct pt_regs *, u32 arch, - unsigned long phase1_result); - -extern long syscall_trace_enter(struct pt_regs *); - static inline unsigned long regs_return_value(struct pt_regs *regs) { return regs->ax; -- cgit v0.10.2 From 8112c4f140fa03f9ee68aad2cc79afa7df5418d3 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 1 Jun 2016 16:02:17 -0700 Subject: seccomp: remove 2-phase API Since nothing is using the 2-phase API, and it adds more complexity than benefit, remove it. Signed-off-by: Kees Cook Cc: Andy Lutomirski diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 9eaa7b3..ecc296c 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -35,12 +35,6 @@ static inline int secure_computing(const struct seccomp_data *sd) return __secure_computing(sd); return 0; } - -#define SECCOMP_PHASE1_OK 0 -#define SECCOMP_PHASE1_SKIP 1 - -extern u32 seccomp_phase1(struct seccomp_data *sd); -int seccomp_phase2(u32 phase1_result); #else extern void secure_computing_strict(int this_syscall); #endif diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 0681629..14a37d7 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -173,7 +173,7 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen) * * Returns valid seccomp BPF response codes. */ -static u32 seccomp_run_filters(struct seccomp_data *sd) +static u32 seccomp_run_filters(const struct seccomp_data *sd) { struct seccomp_data sd_local; u32 ret = SECCOMP_RET_ALLOW; @@ -554,20 +554,9 @@ void secure_computing_strict(int this_syscall) BUG(); } #else -int __secure_computing(const struct seccomp_data *sd) -{ - u32 phase1_result = seccomp_phase1(sd); - - if (likely(phase1_result == SECCOMP_PHASE1_OK)) - return 0; - else if (likely(phase1_result == SECCOMP_PHASE1_SKIP)) - return -1; - else - return seccomp_phase2(phase1_result); -} #ifdef CONFIG_SECCOMP_FILTER -static u32 __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd) +static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd) { u32 filter_ret, action; int data; @@ -599,10 +588,33 @@ static u32 __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd) goto skip; case SECCOMP_RET_TRACE: - return filter_ret; /* Save the rest for phase 2. */ + /* ENOSYS these calls if there is no tracer attached. */ + if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) { + syscall_set_return_value(current, + task_pt_regs(current), + -ENOSYS, 0); + goto skip; + } + + /* Allow the BPF to provide the event message */ + ptrace_event(PTRACE_EVENT_SECCOMP, data); + /* + * The delivery of a fatal signal during event + * notification may silently skip tracer notification. + * Terminating the task now avoids executing a system + * call that may not be intended. + */ + if (fatal_signal_pending(current)) + do_exit(SIGSYS); + /* Check if the tracer forced the syscall to be skipped. */ + this_syscall = syscall_get_nr(current, task_pt_regs(current)); + if (this_syscall < 0) + goto skip; + + return 0; case SECCOMP_RET_ALLOW: - return SECCOMP_PHASE1_OK; + return 0; case SECCOMP_RET_KILL: default: @@ -614,96 +626,37 @@ static u32 __seccomp_phase1_filter(int this_syscall, struct seccomp_data *sd) skip: audit_seccomp(this_syscall, 0, action); - return SECCOMP_PHASE1_SKIP; + return -1; +} +#else +static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd) +{ + BUG(); } #endif -/** - * seccomp_phase1() - run fast path seccomp checks on the current syscall - * @arg sd: The seccomp_data or NULL - * - * This only reads pt_regs via the syscall_xyz helpers. The only change - * it will make to pt_regs is via syscall_set_return_value, and it will - * only do that if it returns SECCOMP_PHASE1_SKIP. - * - * If sd is provided, it will not read pt_regs at all. - * - * It may also call do_exit or force a signal; these actions must be - * safe. - * - * If it returns SECCOMP_PHASE1_OK, the syscall passes checks and should - * be processed normally. - * - * If it returns SECCOMP_PHASE1_SKIP, then the syscall should not be - * invoked. In this case, seccomp_phase1 will have set the return value - * using syscall_set_return_value. - * - * If it returns anything else, then the return value should be passed - * to seccomp_phase2 from a context in which ptrace hooks are safe. - */ -u32 seccomp_phase1(struct seccomp_data *sd) +int __secure_computing(const struct seccomp_data *sd) { int mode = current->seccomp.mode; - int this_syscall = sd ? sd->nr : - syscall_get_nr(current, task_pt_regs(current)); + int this_syscall; if (config_enabled(CONFIG_CHECKPOINT_RESTORE) && unlikely(current->ptrace & PT_SUSPEND_SECCOMP)) - return SECCOMP_PHASE1_OK; + return 0; + + this_syscall = sd ? sd->nr : + syscall_get_nr(current, task_pt_regs(current)); switch (mode) { case SECCOMP_MODE_STRICT: __secure_computing_strict(this_syscall); /* may call do_exit */ - return SECCOMP_PHASE1_OK; -#ifdef CONFIG_SECCOMP_FILTER + return 0; case SECCOMP_MODE_FILTER: - return __seccomp_phase1_filter(this_syscall, sd); -#endif + return __seccomp_filter(this_syscall, sd); default: BUG(); } } - -/** - * seccomp_phase2() - finish slow path seccomp work for the current syscall - * @phase1_result: The return value from seccomp_phase1() - * - * This must be called from a context in which ptrace hooks can be used. - * - * Returns 0 if the syscall should be processed or -1 to skip the syscall. - */ -int seccomp_phase2(u32 phase1_result) -{ - struct pt_regs *regs = task_pt_regs(current); - u32 action = phase1_result & SECCOMP_RET_ACTION; - int data = phase1_result & SECCOMP_RET_DATA; - - BUG_ON(action != SECCOMP_RET_TRACE); - - audit_seccomp(syscall_get_nr(current, regs), 0, action); - - /* Skip these calls if there is no tracer. */ - if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) { - syscall_set_return_value(current, regs, - -ENOSYS, 0); - return -1; - } - - /* Allow the BPF to provide the event message */ - ptrace_event(PTRACE_EVENT_SECCOMP, data); - /* - * The delivery of a fatal signal during event - * notification may silently skip tracer notification. - * Terminating the task now avoids executing a system - * call that may not be intended. - */ - if (fatal_signal_pending(current)) - do_exit(SIGSYS); - if (syscall_get_nr(current, regs) < 0) - return -1; /* Explicit request to skip. */ - - return 0; -} #endif /* CONFIG_HAVE_ARCH_SECCOMP_FILTER */ long prctl_get_seccomp(void) -- cgit v0.10.2 From ce6526e8afa4b6ad0ab134a4cc50c9c863319637 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 1 Jun 2016 19:29:15 -0700 Subject: seccomp: recheck the syscall after RET_TRACE When RET_TRACE triggers, a tracer may change a syscall into something that should be filtered by seccomp. This re-runs seccomp after a trace event to make sure things continue to pass. Signed-off-by: Kees Cook Cc: Andy Lutomirski diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 14a37d7..54d15eb 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -556,7 +556,8 @@ void secure_computing_strict(int this_syscall) #else #ifdef CONFIG_SECCOMP_FILTER -static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd) +static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, + const bool recheck_after_trace) { u32 filter_ret, action; int data; @@ -588,6 +589,10 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd) goto skip; case SECCOMP_RET_TRACE: + /* We've been put in this state by the ptracer already. */ + if (recheck_after_trace) + return 0; + /* ENOSYS these calls if there is no tracer attached. */ if (!ptrace_event_enabled(current, PTRACE_EVENT_SECCOMP)) { syscall_set_return_value(current, @@ -611,6 +616,15 @@ static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd) if (this_syscall < 0) goto skip; + /* + * Recheck the syscall, since it may have changed. This + * intentionally uses a NULL struct seccomp_data to force + * a reload of all registers. This does not goto skip since + * a skip would have already been reported. + */ + if (__seccomp_filter(this_syscall, NULL, true)) + return -1; + return 0; case SECCOMP_RET_ALLOW: @@ -629,7 +643,8 @@ skip: return -1; } #else -static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd) +static int __seccomp_filter(int this_syscall, const struct seccomp_data *sd, + const bool recheck_after_trace) { BUG(); } @@ -652,7 +667,7 @@ int __secure_computing(const struct seccomp_data *sd) __secure_computing_strict(this_syscall); /* may call do_exit */ return 0; case SECCOMP_MODE_FILTER: - return __seccomp_filter(this_syscall, sd); + return __seccomp_filter(this_syscall, sd, false); default: BUG(); } -- cgit v0.10.2 From 93e35efb8de45393cf61ed07f7b407629bf698ea Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 9 Jun 2016 12:36:50 -0700 Subject: x86/ptrace: run seccomp after ptrace This moves seccomp after ptrace on x86 to that seccomp can catch changes made by ptrace. Emulation should skip the rest of processing too. We can get rid of test_thread_flag because there's no longer any opportunity for seccomp to mess with ptrace state before invoking ptrace. Suggested-by: Andy Lutomirski Signed-off-by: Kees Cook Cc: x86@kernel.org Cc: Andy Lutomirski diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index df56ca3..81c0e12 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c @@ -73,6 +73,7 @@ static long syscall_trace_enter(struct pt_regs *regs) struct thread_info *ti = pt_regs_to_thread_info(regs); unsigned long ret = 0; + bool emulated = false; u32 work; if (IS_ENABLED(CONFIG_DEBUG_ENTRY)) @@ -80,11 +81,19 @@ static long syscall_trace_enter(struct pt_regs *regs) work = ACCESS_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY; + if (unlikely(work & _TIF_SYSCALL_EMU)) + emulated = true; + + if ((emulated || (work & _TIF_SYSCALL_TRACE)) && + tracehook_report_syscall_entry(regs)) + return -1L; + + if (emulated) + return -1L; + #ifdef CONFIG_SECCOMP /* - * Do seccomp first -- it should minimize exposure of other - * code, and keeping seccomp fast is probably more valuable - * than the rest of this. + * Do seccomp after ptrace, to catch any tracer changes. */ if (work & _TIF_SECCOMP) { struct seccomp_data sd; @@ -117,13 +126,6 @@ static long syscall_trace_enter(struct pt_regs *regs) } #endif - if (unlikely(work & _TIF_SYSCALL_EMU)) - ret = -1L; - - if ((ret || test_thread_flag(TIF_SYSCALL_TRACE)) && - tracehook_report_syscall_entry(regs)) - ret = -1L; - if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->orig_ax); -- cgit v0.10.2 From 0f3912fd934cdfd03d93f2dc6f064099795bf638 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 2 Jun 2016 12:16:31 -0700 Subject: arm/ptrace: run seccomp after ptrace Close the hole where ptrace can change a syscall out from under seccomp. Signed-off-by: Kees Cook Cc: Russell King Cc: linux-arm-kernel@lists.infradead.org diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c index 1027d3b..ce131ed 100644 --- a/arch/arm/kernel/ptrace.c +++ b/arch/arm/kernel/ptrace.c @@ -932,18 +932,19 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs, int scno) { current_thread_info()->syscall = scno; - /* Do the secure computing check first; failures should be fast. */ + if (test_thread_flag(TIF_SYSCALL_TRACE)) + tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER); + + /* Do seccomp after ptrace; syscall may have changed. */ #ifdef CONFIG_HAVE_ARCH_SECCOMP_FILTER if (secure_computing(NULL) == -1) return -1; #else /* XXX: remove this once OABI gets fixed */ - secure_computing_strict(scno); + secure_computing_strict(current_thread_info()->syscall); #endif - if (test_thread_flag(TIF_SYSCALL_TRACE)) - tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER); - + /* Tracer or seccomp may have changed syscall. */ scno = current_thread_info()->syscall; if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) -- cgit v0.10.2 From a5cd110cb8369d6b37ef5ccfe56b3fa1338c9615 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 2 Jun 2016 12:28:52 -0700 Subject: arm64/ptrace: run seccomp after ptrace Close the hole where ptrace can change a syscall out from under seccomp. Signed-off-by: Kees Cook Cc: Catalin Marinas Cc: Will Deacon Cc: Mark Rutland Cc: linux-arm-kernel@lists.infradead.org diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 6e2cf04..602316c 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -1246,13 +1246,13 @@ static void tracehook_report_syscall(struct pt_regs *regs, asmlinkage int syscall_trace_enter(struct pt_regs *regs) { - /* Do the secure computing check first; failures should be fast. */ - if (secure_computing(NULL) == -1) - return -1; - if (test_thread_flag(TIF_SYSCALL_TRACE)) tracehook_report_syscall(regs, PTRACE_SYSCALL_ENTER); + /* Do the secure computing after ptrace; failures should be fast. */ + if (secure_computing(NULL) == -1) + return -1; + if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) trace_sys_enter(regs, regs->syscallno); -- cgit v0.10.2 From 2ac3c8d18ed136827ad41786f30c6ed396429660 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 2 Jun 2016 12:33:44 -0700 Subject: MIPS/ptrace: run seccomp after ptrace Close the hole where ptrace can change a syscall out from under seccomp. Signed-off-by: Kees Cook Cc: Ralf Baechle Cc: "Maciej W. Rozycki" Cc: James Hogan Cc: linux-mips@linux-mips.org diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index c50af84..6103b24 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c @@ -888,17 +888,16 @@ long arch_ptrace(struct task_struct *child, long request, */ asmlinkage long syscall_trace_enter(struct pt_regs *regs, long syscall) { - long ret = 0; user_exit(); current_thread_info()->syscall = syscall; - if (secure_computing(NULL) == -1) - return -1; - if (test_thread_flag(TIF_SYSCALL_TRACE) && tracehook_report_syscall_entry(regs)) - ret = -1; + return -1; + + if (secure_computing(NULL) == -1) + return -1; if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->regs[2]); -- cgit v0.10.2 From 375f0183047109b98658d539db6ff22a6ac24abc Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 2 Jun 2016 13:15:52 -0700 Subject: parisc/ptrace: run seccomp after ptrace Close the hole where ptrace can change a syscall out from under seccomp. Signed-off-by: Kees Cook Cc: "James E.J. Bottomley" Cc: Helge Deller Cc: linux-parisc@vger.kernel.org diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c index 8edc47c..e02d7b4 100644 --- a/arch/parisc/kernel/ptrace.c +++ b/arch/parisc/kernel/ptrace.c @@ -311,10 +311,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, long do_syscall_trace_enter(struct pt_regs *regs) { - /* Do the secure computing check first. */ - if (secure_computing(NULL) == -1) - return -1; - if (test_thread_flag(TIF_SYSCALL_TRACE) && tracehook_report_syscall_entry(regs)) { /* @@ -325,6 +321,11 @@ long do_syscall_trace_enter(struct pt_regs *regs) regs->gr[20] = -1UL; goto out; } + + /* Do the secure computing check after ptrace. */ + if (secure_computing(NULL) == -1) + return -1; + #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->gr[20]); -- cgit v0.10.2 From 0208b9445bc031791e589c334a93365cbad008fe Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 2 Jun 2016 13:19:36 -0700 Subject: s390/ptrace: run seccomp after ptrace Close the hole where ptrace can change a syscall out from under seccomp. Signed-off-by: Kees Cook Cc: Heiko Carstens Cc: Martin Schwidefsky Cc: linux-s390@vger.kernel.org diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index c238e99..cea1701 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -821,15 +821,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) { - long ret = 0; - - /* Do the secure computing check first. */ - if (secure_computing(NULL)) { - /* seccomp failures shouldn't expose any additional code. */ - ret = -1; - goto out; - } - /* * The sysc_tracesys code in entry.S stored the system * call number to gprs[2]. @@ -843,7 +834,13 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) * the system call and the system call restart handling. */ clear_pt_regs_flag(regs, PIF_SYSCALL); - ret = -1; + return -1; + } + + /* Do the secure computing check after ptrace. */ + if (secure_computing(NULL)) { + /* seccomp failures shouldn't expose any additional code. */ + return -1; } if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) @@ -852,8 +849,8 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs) audit_syscall_entry(regs->gprs[2], regs->orig_gpr2, regs->gprs[3], regs->gprs[4], regs->gprs[5]); -out: - return ret ?: regs->gprs[2]; + + return regs->gprs[2]; } asmlinkage void do_syscall_trace_exit(struct pt_regs *regs) -- cgit v0.10.2 From 1addc57e111b920f94edb867094ccaa7c1e820b8 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 2 Jun 2016 19:55:09 -0700 Subject: powerpc/ptrace: run seccomp after ptrace Close the hole where ptrace can change a syscall out from under seccomp. Signed-off-by: Kees Cook Cc: Michael Ellerman Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: linuxppc-dev@lists.ozlabs.org diff --git a/arch/powerpc/kernel/ptrace.c b/arch/powerpc/kernel/ptrace.c index ed799e9..5dc47eb 100644 --- a/arch/powerpc/kernel/ptrace.c +++ b/arch/powerpc/kernel/ptrace.c @@ -1788,7 +1788,7 @@ static int do_seccomp(struct pt_regs *regs) /* * The syscall was allowed by seccomp, restore the register - * state to what ptrace and audit expect. + * state to what audit expects. * Note that we use orig_gpr3, which means a seccomp tracer can * modify the first syscall parameter (in orig_gpr3) and also * allow the syscall to proceed. @@ -1822,22 +1822,25 @@ static inline int do_seccomp(struct pt_regs *regs) { return 0; } */ long do_syscall_trace_enter(struct pt_regs *regs) { - bool abort = false; - user_exit(); + /* + * The tracer may decide to abort the syscall, if so tracehook + * will return !0. Note that the tracer may also just change + * regs->gpr[0] to an invalid syscall number, that is handled + * below on the exit path. + */ + if (test_thread_flag(TIF_SYSCALL_TRACE) && + tracehook_report_syscall_entry(regs)) + goto skip; + + /* Run seccomp after ptrace; allow it to set gpr[3]. */ if (do_seccomp(regs)) return -1; - if (test_thread_flag(TIF_SYSCALL_TRACE)) { - /* - * The tracer may decide to abort the syscall, if so tracehook - * will return !0. Note that the tracer may also just change - * regs->gpr[0] to an invalid syscall number, that is handled - * below on the exit path. - */ - abort = tracehook_report_syscall_entry(regs) != 0; - } + /* Avoid trace and audit when syscall is invalid. */ + if (regs->gpr[0] >= NR_syscalls) + goto skip; if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT))) trace_sys_enter(regs, regs->gpr[0]); @@ -1854,17 +1857,16 @@ long do_syscall_trace_enter(struct pt_regs *regs) regs->gpr[5] & 0xffffffff, regs->gpr[6] & 0xffffffff); - if (abort || regs->gpr[0] >= NR_syscalls) { - /* - * If we are aborting explicitly, or if the syscall number is - * now invalid, set the return value to -ENOSYS. - */ - regs->gpr[3] = -ENOSYS; - return -1; - } - /* Return the possibly modified but valid syscall number */ return regs->gpr[0]; + +skip: + /* + * If we are aborting explicitly, or if the syscall number is + * now invalid, set the return value to -ENOSYS. + */ + regs->gpr[3] = -ENOSYS; + return -1; } void do_syscall_trace_leave(struct pt_regs *regs) -- cgit v0.10.2 From 635efc702984857b45a2a2491ed7a529e14cbdf9 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 2 Jun 2016 19:56:43 -0700 Subject: tile/ptrace: run seccomp after ptrace Close the hole where ptrace can change a syscall out from under seccomp. Signed-off-by: Kees Cook Cc: Chris Metcalf diff --git a/arch/tile/kernel/ptrace.c b/arch/tile/kernel/ptrace.c index 8c6d2f2..d89b701 100644 --- a/arch/tile/kernel/ptrace.c +++ b/arch/tile/kernel/ptrace.c @@ -255,14 +255,15 @@ int do_syscall_trace_enter(struct pt_regs *regs) { u32 work = ACCESS_ONCE(current_thread_info()->flags); - if (secure_computing(NULL) == -1) + if ((work & _TIF_SYSCALL_TRACE) && + tracehook_report_syscall_entry(regs)) { + regs->regs[TREG_SYSCALL_NR] = -1; return -1; - - if (work & _TIF_SYSCALL_TRACE) { - if (tracehook_report_syscall_entry(regs)) - regs->regs[TREG_SYSCALL_NR] = -1; } + if (secure_computing(NULL) == -1) + return -1; + if (work & _TIF_SYSCALL_TRACEPOINT) trace_sys_enter(regs, regs->regs[TREG_SYSCALL_NR]); -- cgit v0.10.2 From 26703c636c1f3272b39bd0f6d04d2e970984f1b6 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 2 Jun 2016 19:59:42 -0700 Subject: um/ptrace: run seccomp after ptrace Close the hole where ptrace can change a syscall out from under seccomp. Signed-off-by: Kees Cook Cc: Jeff Dike Cc: Richard Weinberger Cc: user-mode-linux-devel@lists.sourceforge.net diff --git a/arch/um/kernel/skas/syscall.c b/arch/um/kernel/skas/syscall.c index 9c5570f..ef4b8f9 100644 --- a/arch/um/kernel/skas/syscall.c +++ b/arch/um/kernel/skas/syscall.c @@ -20,12 +20,12 @@ void handle_syscall(struct uml_pt_regs *r) UPT_SYSCALL_NR(r) = PT_SYSCALL_NR(r->gp); PT_REGS_SET_SYSCALL_RETURN(regs, -ENOSYS); - /* Do the secure computing check first; failures should be fast. */ - if (secure_computing(NULL) == -1) + if (syscall_trace_enter(regs)) return; - if (syscall_trace_enter(regs)) - goto out; + /* Do the seccomp check after ptrace; failures should be fast. */ + if (secure_computing(NULL) == -1) + return; /* Update the syscall number after orig_ax has potentially been updated * with ptrace. @@ -37,6 +37,5 @@ void handle_syscall(struct uml_pt_regs *r) PT_REGS_SET_SYSCALL_RETURN(regs, EXECUTE_SYSCALL(syscall, regs)); -out: syscall_trace_leave(regs); } -- cgit v0.10.2 From 309c5fad5de44fb9b1703a8f7bd814a223c57d60 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 10 Jun 2016 23:14:26 +0200 Subject: selinux: fix type mismatch avc_cache_threshold is of type unsigned int. Do not use a signed new_value in sscanf(page, "%u", &new_value). Signed-off-by: Heinrich Schuchardt [PM: subject prefix fix, description cleanup] Signed-off-by: Paul Moore diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 1b1fd27..0765c5b 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1347,7 +1347,7 @@ static ssize_t sel_write_avc_cache_threshold(struct file *file, { char *page; ssize_t ret; - int new_value; + unsigned int new_value; ret = task_has_security(current, SECURITY__SETSECPARAM); if (ret) -- cgit v0.10.2 From 9feaab5dd27c5caf13368055ced9d58a8302ff21 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:24 +0100 Subject: tpm/st33zp24/spi: Remove nbr_dummy_bytes variable usage nbr_dummy_bytes variable could be easily replaced by phy->latency in st33zp24_spi_send and st33zp24_spi_recv. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index f974c94..74c5fcc 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -111,7 +111,7 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) { u8 data = 0; - int total_length = 0, nbr_dummy_bytes = 0, ret = 0; + int total_length = 0, ret = 0; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf; @@ -133,14 +133,13 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, memcpy(&tx_buf[total_length], tpm_data, tpm_size); total_length += tpm_size; - nbr_dummy_bytes = phy->latency; - memset(&tx_buf[total_length], TPM_DUMMY_BYTE, nbr_dummy_bytes); + memset(&tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency); - phy->spi_xfer.len = total_length + nbr_dummy_bytes; + phy->spi_xfer.len = total_length + phy->latency; ret = spi_sync_transfer(dev, &phy->spi_xfer, 1); if (ret == 0) - ret = rx_buf[total_length + nbr_dummy_bytes - 1]; + ret = rx_buf[total_length + phy->latency - 1]; return st33zp24_status_to_errno(ret); } /* st33zp24_spi_send() */ @@ -157,7 +156,7 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) { u8 data = 0; - int total_length = 0, nbr_dummy_bytes, ret; + int total_length = 0, ret; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf; @@ -171,18 +170,17 @@ static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) memcpy(tx_buf + total_length, &data, sizeof(data)); total_length++; - nbr_dummy_bytes = phy->latency; memset(&tx_buf[total_length], TPM_DUMMY_BYTE, - nbr_dummy_bytes + tpm_size); + phy->latency + tpm_size); - phy->spi_xfer.len = total_length + nbr_dummy_bytes + tpm_size; + phy->spi_xfer.len = total_length + phy->latency + tpm_size; /* header + status byte + size of the data + status byte */ ret = spi_sync_transfer(dev, &phy->spi_xfer, 1); if (tpm_size > 0 && ret == 0) { - ret = rx_buf[total_length + nbr_dummy_bytes - 1]; + ret = rx_buf[total_length + phy->latency - 1]; - memcpy(tpm_data, rx_buf + total_length + nbr_dummy_bytes, + memcpy(tpm_data, rx_buf + total_length + phy->latency, tpm_size); } -- cgit v0.10.2 From b83ada0caabd8d99329518b765f657ce1d0da24a Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:25 +0100 Subject: tpm/st33zp24/spi: Use functions name with st33zp24_spi_ prefix Make sure every function name use st33zp24_spi_ prefix. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 74c5fcc..f42c443 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -145,7 +145,7 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, } /* st33zp24_spi_send() */ /* - * read8_recv + * st33zp24_spi_read8_recv * Recv byte from the TIS register according to the ST33ZP24 SPI protocol. * @param: phy_id, the phy description * @param: tpm_register, the tpm tis register where the data should be read @@ -153,7 +153,8 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, * @param: tpm_size, tpm TPM response size to read. * @return: should be zero if success else a negative error code. */ -static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) +static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, + int tpm_size) { u8 data = 0; int total_length = 0, ret; @@ -185,7 +186,7 @@ static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) } return ret; -} /* read8_reg() */ +} /* st33zp24_spi_read8_reg() */ /* * st33zp24_spi_recv @@ -201,13 +202,13 @@ static int st33zp24_spi_recv(void *phy_id, u8 tpm_register, u8 *tpm_data, { int ret; - ret = read8_reg(phy_id, tpm_register, tpm_data, tpm_size); + ret = st33zp24_spi_read8_reg(phy_id, tpm_register, tpm_data, tpm_size); if (!st33zp24_status_to_errno(ret)) return tpm_size; return ret; } /* st33zp24_spi_recv() */ -static int evaluate_latency(void *phy_id) +static int st33zp24_spi_evaluate_latency(void *phy_id) { struct st33zp24_spi_phy *phy = phy_id; int latency = 1, status = 0; @@ -215,7 +216,8 @@ static int evaluate_latency(void *phy_id) while (!status && latency < MAX_SPI_LATENCY) { phy->latency = latency; - status = read8_reg(phy_id, TPM_INTF_CAPABILITY, &data, 1); + status = st33zp24_spi_read8_reg(phy_id, TPM_INTF_CAPABILITY, + &data, 1); latency++; } return latency - 1; @@ -227,7 +229,7 @@ static const struct st33zp24_phy_ops spi_phy_ops = { }; #ifdef CONFIG_OF -static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy) +static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) { struct device_node *pp; struct spi_device *dev = phy->spi_device; @@ -265,14 +267,14 @@ static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy) return 0; } #else -static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy) +static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) { return -ENODEV; } #endif -static int tpm_stm_spi_request_resources(struct spi_device *dev, - struct st33zp24_spi_phy *phy) +static int st33zp24_spi_request_resources(struct spi_device *dev, + struct st33zp24_spi_phy *phy) { struct st33zp24_platform_data *pdata; int ret; @@ -301,13 +303,12 @@ static int tpm_stm_spi_request_resources(struct spi_device *dev, } /* - * tpm_st33_spi_probe initialize the TPM device + * st33zp24_spi_probe initialize the TPM device * @param: dev, the spi_device drescription (TPM SPI description). * @return: 0 in case of success. * or a negative value describing the error. */ -static int -tpm_st33_spi_probe(struct spi_device *dev) +static int st33zp24_spi_probe(struct spi_device *dev) { int ret; struct st33zp24_platform_data *pdata; @@ -328,11 +329,11 @@ tpm_st33_spi_probe(struct spi_device *dev) phy->spi_device = dev; pdata = dev->dev.platform_data; if (!pdata && dev->dev.of_node) { - ret = tpm_stm_spi_of_request_resources(phy); + ret = st33zp24_spi_of_request_resources(phy); if (ret) return ret; } else if (pdata) { - ret = tpm_stm_spi_request_resources(dev, phy); + ret = st33zp24_spi_request_resources(dev, phy); if (ret) return ret; } @@ -340,7 +341,7 @@ tpm_st33_spi_probe(struct spi_device *dev) phy->spi_xfer.tx_buf = phy->tx_buf; phy->spi_xfer.rx_buf = phy->rx_buf; - phy->latency = evaluate_latency(phy); + phy->latency = st33zp24_spi_evaluate_latency(phy); if (phy->latency <= 0) return -ENODEV; @@ -349,11 +350,11 @@ tpm_st33_spi_probe(struct spi_device *dev) } /* - * tpm_st33_spi_remove remove the TPM device + * st33zp24_spi_remove remove the TPM device * @param: client, the spi_device drescription (TPM SPI description). * @return: 0 in case of success. */ -static int tpm_st33_spi_remove(struct spi_device *dev) +static int st33zp24_spi_remove(struct spi_device *dev) { struct tpm_chip *chip = spi_get_drvdata(dev); @@ -377,18 +378,18 @@ MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match); static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend, st33zp24_pm_resume); -static struct spi_driver tpm_st33_spi_driver = { +static struct spi_driver st33zp24_spi_driver = { .driver = { .name = TPM_ST33_SPI, .pm = &st33zp24_spi_ops, .of_match_table = of_match_ptr(of_st33zp24_spi_match), }, - .probe = tpm_st33_spi_probe, - .remove = tpm_st33_spi_remove, + .probe = st33zp24_spi_probe, + .remove = st33zp24_spi_remove, .id_table = st33zp24_spi_id, }; -module_spi_driver(tpm_st33_spi_driver); +module_spi_driver(st33zp24_spi_driver); MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)"); MODULE_DESCRIPTION("STM TPM 1.2 SPI ST33 Driver"); -- cgit v0.10.2 From 6e0219c751149c89f4d1f17dbd87086444933713 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:26 +0100 Subject: tpm/st33zp24/spi: Remove useless use of memcpy. An affectation is enough when copying 1 byte. Remove memcpy usage where possible. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index f42c443..08ffbfe 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -110,7 +110,6 @@ static int st33zp24_status_to_errno(u8 code) static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) { - u8 data = 0; int total_length = 0, ret = 0; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; @@ -118,12 +117,8 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, u8 *rx_buf = phy->spi_xfer.rx_buf; /* Pre-Header */ - data = TPM_WRITE_DIRECTION | LOCALITY0; - memcpy(tx_buf + total_length, &data, sizeof(data)); - total_length++; - data = tpm_register; - memcpy(tx_buf + total_length, &data, sizeof(data)); - total_length++; + tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0; + tx_buf[total_length++] = tpm_register; if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) { tx_buf[total_length++] = tpm_size >> 8; @@ -156,7 +151,6 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) { - u8 data = 0; int total_length = 0, ret; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; @@ -164,12 +158,8 @@ static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, u8 *rx_buf = phy->spi_xfer.rx_buf; /* Pre-Header */ - data = LOCALITY0; - memcpy(tx_buf + total_length, &data, sizeof(data)); - total_length++; - data = tpm_register; - memcpy(tx_buf + total_length, &data, sizeof(data)); - total_length++; + tx_buf[total_length++] = LOCALITY0; + tx_buf[total_length++] = tpm_register; memset(&tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency + tpm_size); -- cgit v0.10.2 From b637a366c2ca2327fbe042e8a214d5cc16272788 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:27 +0100 Subject: tpm/st33zp24/spi: Remove field spi_xfer from st33zp24_spi_phy structure Remove spi_xfer from st33zp24_spi_phy structure and declare local spi_xfer when needed instead. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 08ffbfe..f851053 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -66,7 +66,7 @@ struct st33zp24_spi_phy { struct spi_device *spi_device; - struct spi_transfer spi_xfer; + u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE]; u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE]; @@ -113,28 +113,30 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, int total_length = 0, ret = 0; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; - u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf; - u8 *rx_buf = phy->spi_xfer.rx_buf; + struct spi_transfer spi_xfer = { + .tx_buf = phy->tx_buf, + .rx_buf = phy->rx_buf, + }; /* Pre-Header */ - tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0; - tx_buf[total_length++] = tpm_register; + phy->tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0; + phy->tx_buf[total_length++] = tpm_register; if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) { - tx_buf[total_length++] = tpm_size >> 8; - tx_buf[total_length++] = tpm_size; + phy->tx_buf[total_length++] = tpm_size >> 8; + phy->tx_buf[total_length++] = tpm_size; } - memcpy(&tx_buf[total_length], tpm_data, tpm_size); + memcpy(&phy->tx_buf[total_length], tpm_data, tpm_size); total_length += tpm_size; - memset(&tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency); + memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency); - phy->spi_xfer.len = total_length + phy->latency; + spi_xfer.len = total_length + phy->latency; - ret = spi_sync_transfer(dev, &phy->spi_xfer, 1); + ret = spi_sync_transfer(dev, &spi_xfer, 1); if (ret == 0) - ret = rx_buf[total_length + phy->latency - 1]; + ret = phy->rx_buf[total_length + phy->latency - 1]; return st33zp24_status_to_errno(ret); } /* st33zp24_spi_send() */ @@ -154,24 +156,26 @@ static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int total_length = 0, ret; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; - u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf; - u8 *rx_buf = phy->spi_xfer.rx_buf; + struct spi_transfer spi_xfer = { + .tx_buf = phy->tx_buf, + .rx_buf = phy->rx_buf, + }; /* Pre-Header */ - tx_buf[total_length++] = LOCALITY0; - tx_buf[total_length++] = tpm_register; + phy->tx_buf[total_length++] = LOCALITY0; + phy->tx_buf[total_length++] = tpm_register; - memset(&tx_buf[total_length], TPM_DUMMY_BYTE, + memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency + tpm_size); - phy->spi_xfer.len = total_length + phy->latency + tpm_size; + spi_xfer.len = total_length + phy->latency + tpm_size; /* header + status byte + size of the data + status byte */ - ret = spi_sync_transfer(dev, &phy->spi_xfer, 1); + ret = spi_sync_transfer(dev, &spi_xfer, 1); if (tpm_size > 0 && ret == 0) { - ret = rx_buf[total_length + phy->latency - 1]; + ret = phy->rx_buf[total_length + phy->latency - 1]; - memcpy(tpm_data, rx_buf + total_length + phy->latency, + memcpy(tpm_data, phy->rx_buf + total_length + phy->latency, tpm_size); } @@ -328,9 +332,6 @@ static int st33zp24_spi_probe(struct spi_device *dev) return ret; } - phy->spi_xfer.tx_buf = phy->tx_buf; - phy->spi_xfer.rx_buf = phy->rx_buf; - phy->latency = st33zp24_spi_evaluate_latency(phy); if (phy->latency <= 0) return -ENODEV; -- cgit v0.10.2 From 8d8d218e89e45717dfed6ac410e706f9c66d8255 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:28 +0100 Subject: tpm/st33zp24: Remove unneeded CONFIG_OF switches DT headers already define NOOP routines when CONFIG_OF is not defined. [jarkko.sakkinen@linux.intel.com: I tested that the driver compiles without warnings and errors with and without CONFIG_OF flag.] Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index 309d276..a05fbd8 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -108,7 +108,6 @@ static const struct st33zp24_phy_ops i2c_phy_ops = { .recv = st33zp24_i2c_recv, }; -#ifdef CONFIG_OF static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) { struct device_node *pp; @@ -146,12 +145,6 @@ static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) return 0; } -#else -static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) -{ - return -ENODEV; -} -#endif static int st33zp24_i2c_request_resources(struct i2c_client *client, struct st33zp24_i2c_phy *phy) @@ -245,13 +238,11 @@ static const struct i2c_device_id st33zp24_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id); -#ifdef CONFIG_OF static const struct of_device_id of_st33zp24_i2c_match[] = { { .compatible = "st,st33zp24-i2c", }, {} }; MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); -#endif static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, st33zp24_pm_resume); diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index f851053..08e6bf5 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -222,7 +222,6 @@ static const struct st33zp24_phy_ops spi_phy_ops = { .recv = st33zp24_spi_recv, }; -#ifdef CONFIG_OF static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) { struct device_node *pp; @@ -260,12 +259,6 @@ static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) return 0; } -#else -static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) -{ - return -ENODEV; -} -#endif static int st33zp24_spi_request_resources(struct spi_device *dev, struct st33zp24_spi_phy *phy) @@ -358,13 +351,11 @@ static const struct spi_device_id st33zp24_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, st33zp24_spi_id); -#ifdef CONFIG_OF static const struct of_device_id of_st33zp24_spi_match[] = { { .compatible = "st,st33zp24-spi", }, {} }; MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match); -#endif static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend, st33zp24_pm_resume); -- cgit v0.10.2 From b55c8e9f14710d4f91579b2cb9a03734ec38bdab Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:29 +0100 Subject: tpm/st33zp24: Auto-select core module The core st33zp24 module is useless without either the I2C or the SPI access module. So hide NFC_ST_NCI and select it automatically if either TCG_TIS_ST33ZP24_I2C or TCG_TIS_ST33ZP24_SPI is selected. This avoids presenting TCG_TIS_ST33ZP24 when neither TCG_TIS_ST33ZP24_I2C nor TCG_TIS_ST33ZP24_SPI can be selected. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/Kconfig b/drivers/char/tpm/st33zp24/Kconfig index 19c0074..e74c6f2 100644 --- a/drivers/char/tpm/st33zp24/Kconfig +++ b/drivers/char/tpm/st33zp24/Kconfig @@ -1,6 +1,5 @@ config TCG_TIS_ST33ZP24 - tristate "STMicroelectronics TPM Interface Specification 1.2 Interface" - depends on GPIOLIB || COMPILE_TEST + tristate ---help--- STMicroelectronics ST33ZP24 core driver. It implements the core TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will @@ -10,9 +9,9 @@ config TCG_TIS_ST33ZP24 tpm_st33zp24. config TCG_TIS_ST33ZP24_I2C - tristate "TPM 1.2 ST33ZP24 I2C support" - depends on TCG_TIS_ST33ZP24 + tristate "STMicroelectronics TPM Interface Specification 1.2 Interface (I2C)" depends on I2C + select TCG_TIS_ST33ZP24 ---help--- This module adds support for the STMicroelectronics TPM security chip ST33ZP24 with i2c interface. @@ -20,9 +19,9 @@ config TCG_TIS_ST33ZP24_I2C called tpm_st33zp24_i2c. config TCG_TIS_ST33ZP24_SPI - tristate "TPM 1.2 ST33ZP24 SPI support" - depends on TCG_TIS_ST33ZP24 + tristate "STMicroelectronics TPM Interface Specification 1.2 Interface (SPI)" depends on SPI + select TCG_TIS_ST33ZP24 ---help--- This module adds support for the STMicroelectronics TPM security chip ST33ZP24 with spi interface. -- cgit v0.10.2 From 10460bc1cfdea19dbab2289a63b4be0c874d29f2 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:30 +0100 Subject: tpm/st33zp24/spi: Improve st33zp24_spi_evaluate_latency Add check in st33zp24_spi_evaluate_latency helping to diagnose if the chip is present or in a bad state. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 08e6bf5..a2ef8ec 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -214,6 +214,11 @@ static int st33zp24_spi_evaluate_latency(void *phy_id) &data, 1); latency++; } + if (status < 0) + return status; + if (latency == MAX_SPI_LATENCY) + return -ENODEV; + return latency - 1; } /* evaluate_latency() */ -- cgit v0.10.2 From 2c2b217a13ed11ff6d8f4583f9bee3a54e8f1034 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:31 +0100 Subject: tpm/st33zp24: Extend Copyright headers Extend copyright header to 2016 Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index a05fbd8..a49b6f1 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -1,6 +1,6 @@ /* * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 - * Copyright (C) 2009 - 2015 STMicroelectronics + * Copyright (C) 2009 - 2016 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 diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index a2ef8ec..eb351ba 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -1,6 +1,6 @@ /* * STMicroelectronics TPM SPI Linux driver for TPM ST33ZP24 - * Copyright (C) 2009 - 2015 STMicroelectronics + * Copyright (C) 2009 - 2016 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 diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index 8d62678..944d279 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -1,6 +1,6 @@ /* * STMicroelectronics TPM Linux driver for TPM ST33ZP24 - * Copyright (C) 2009 - 2015 STMicroelectronics + * Copyright (C) 2009 - 2016 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 diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h index c207ceb..bcbd5ff 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.h +++ b/drivers/char/tpm/st33zp24/st33zp24.h @@ -1,6 +1,6 @@ /* * STMicroelectronics TPM Linux driver for TPM ST33ZP24 - * Copyright (C) 2009 - 2015 STMicroelectronics + * Copyright (C) 2009 - 2016 STMicroelectronics * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, diff --git a/include/linux/platform_data/st33zp24.h b/include/linux/platform_data/st33zp24.h index 817dfdb..6f0fb6e 100644 --- a/include/linux/platform_data/st33zp24.h +++ b/include/linux/platform_data/st33zp24.h @@ -1,6 +1,6 @@ /* * STMicroelectronics TPM Linux driver for TPM 1.2 ST33ZP24 - * Copyright (C) 2009 - 2015 STMicroelectronics + * Copyright (C) 2009 - 2016 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 -- cgit v0.10.2 From fec60f2991c00504f6f862f0a6790da78c4ba904 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:32 +0100 Subject: tpm/st33zp24: Add support for acpi probing for i2c device. Add support for acpi probing. SMO3324 is used for st33zp24. It has been tested with the following acpi node on Minnowboard: Device (TPM1) { Name (_ADR, Zero) // _ADR: Address Name (_HID, "SMO3324") // _HID: Hardware ID Name (_CID, "SMO3324") // _CID: Compatible ID Name (_DDN, "SMO TPM") // _DDN: DOS Device Name Name (_UID, One) // _UID: Unique ID Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (SBUF, ResourceTemplate () { I2cSerialBus (0x0013, ControllerInitiated, 400000, AddressingMode7Bit, "\\_SB.I2C7", 0x00, ResourceConsumer, ,) GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone, 0x0000, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0001 } GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0002, } }) Return (SBUF) /* \_SB_.I2C7.TPM1._CRS.SBUF */ } Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } } Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index a49b6f1..b20cc2b 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -108,6 +110,43 @@ static const struct st33zp24_phy_ops i2c_phy_ops = { .recv = st33zp24_i2c_recv, }; +static int st33zp24_i2c_acpi_request_resources(struct st33zp24_i2c_phy *phy) +{ + struct i2c_client *client = phy->client; + const struct acpi_device_id *id; + struct gpio_desc *gpiod_lpcpd; + struct device *dev; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* Get LPCPD GPIO from ACPI */ + gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, + GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_lpcpd)) { + dev_err(&client->dev, + "Failed to retrieve lpcpd-gpios from acpi.\n"); + phy->io_lpcpd = -1; + /* + * lpcpd pin is not specified. This is not an issue as + * power management can be also managed by TPM specific + * commands. So leave with a success status code. + */ + return 0; + } + + phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd); + + return 0; +} + static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) { struct device_node *pp; @@ -214,6 +253,10 @@ static int st33zp24_i2c_probe(struct i2c_client *client, ret = st33zp24_i2c_request_resources(client, phy); if (ret) return ret; + } else if (ACPI_HANDLE(&client->dev)) { + ret = st33zp24_i2c_acpi_request_resources(phy); + if (ret) + return ret; } return st33zp24_probe(phy, &i2c_phy_ops, &client->dev, client->irq, @@ -244,6 +287,12 @@ static const struct of_device_id of_st33zp24_i2c_match[] = { }; MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); +static const struct acpi_device_id st33zp24_i2c_acpi_match[] = { + {"SMO3324"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st33zp24_i2c_acpi_match); + static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, st33zp24_pm_resume); @@ -252,6 +301,7 @@ static struct i2c_driver st33zp24_i2c_driver = { .name = TPM_ST33_I2C, .pm = &st33zp24_i2c_ops, .of_match_table = of_match_ptr(of_st33zp24_i2c_match), + .acpi_match_table = ACPI_PTR(st33zp24_i2c_acpi_match), }, .probe = st33zp24_i2c_probe, .remove = st33zp24_i2c_remove, -- cgit v0.10.2 From 160beb40d51b426c8557598f6f0f475dcd3e6d02 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Tue, 23 Feb 2016 22:25:49 +0100 Subject: tpm: st33zp24: Add support for acpi probing for spi device. Add support for acpi probing. SMO3324 is used for st33zp24. It has been tested with the following acpi node on Minnowboard: Device (TPM1) { Name (_ADR, Zero) // _ADR: Address Name (_HID, "SMO3324") // _HID: Hardware ID Name (_CID, "SMO3324") // _CID: Compatible ID Name (_DDN, "SMO TPM") // _DDN: DOS Device Name Name (_UID, One) // _UID: Unique ID Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (SBUF, ResourceTemplate () { SpiSerialBus (0, PolarityLow, FourWireMode, 8, ControllerInitiated, 4000000, ClockPolarityLow, ClockPhaseFirst, "\\_SB.SPI1", 0x00, ResourceConsumer, ,) GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone, 0x0000, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0001 } GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0002, } }) Return (SBUF) /* \_SB_.SPI1.TPM1._CRS.SBUF */ } Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } } Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index eb351ba..173db72 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -227,6 +229,42 @@ static const struct st33zp24_phy_ops spi_phy_ops = { .recv = st33zp24_spi_recv, }; +static int st33zp24_spi_acpi_request_resources(struct st33zp24_spi_phy *phy) +{ + struct spi_device *spi_dev = phy->spi_device; + const struct acpi_device_id *id; + struct gpio_desc *gpiod_lpcpd; + struct device *dev; + + if (!spi_dev) + return -EINVAL; + + dev = &spi_dev->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* Get LPCPD GPIO from ACPI */ + gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, + GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_lpcpd)) { + dev_err(dev, "Failed to retrieve lpcpd-gpios from acpi.\n"); + phy->io_lpcpd = -1; + /* + * lpcpd pin is not specified. This is not an issue as + * power management can be also managed by TPM specific + * commands. So leave with a success status code. + */ + return 0; + } + + phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd); + + return 0; +} + static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) { struct device_node *pp; @@ -328,6 +366,10 @@ static int st33zp24_spi_probe(struct spi_device *dev) ret = st33zp24_spi_request_resources(dev, phy); if (ret) return ret; + } else if (ACPI_HANDLE(&dev->dev)) { + ret = st33zp24_spi_acpi_request_resources(phy); + if (ret) + return ret; } phy->latency = st33zp24_spi_evaluate_latency(phy); @@ -362,6 +404,12 @@ static const struct of_device_id of_st33zp24_spi_match[] = { }; MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match); +static const struct acpi_device_id st33zp24_spi_acpi_match[] = { + {"SMO3324"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st33zp24_spi_acpi_match); + static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend, st33zp24_pm_resume); @@ -370,6 +418,7 @@ static struct spi_driver st33zp24_spi_driver = { .name = TPM_ST33_SPI, .pm = &st33zp24_spi_ops, .of_match_table = of_match_ptr(of_st33zp24_spi_match), + .acpi_match_table = ACPI_PTR(st33zp24_spi_acpi_match), }, .probe = st33zp24_spi_probe, .remove = st33zp24_spi_remove, -- cgit v0.10.2 From 6388b659533bf76ce62a3fb5f4f30a69bb65c098 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Tue, 23 Feb 2016 22:25:50 +0100 Subject: tpm/st33zp24/i2c: Change xxx_request_resources header Simplify st33zp24_i2c_acpi_request_resources, st33zp24_i2c_of_request_resources and st33zp24_i2c_request_resources to have the same prototype and using i2c_get_clientdata. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index b20cc2b..f179aad 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -110,9 +110,9 @@ static const struct st33zp24_phy_ops i2c_phy_ops = { .recv = st33zp24_i2c_recv, }; -static int st33zp24_i2c_acpi_request_resources(struct st33zp24_i2c_phy *phy) +static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client) { - struct i2c_client *client = phy->client; + struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); const struct acpi_device_id *id; struct gpio_desc *gpiod_lpcpd; struct device *dev; @@ -147,10 +147,10 @@ static int st33zp24_i2c_acpi_request_resources(struct st33zp24_i2c_phy *phy) return 0; } -static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) +static int st33zp24_i2c_of_request_resources(struct i2c_client *client) { + struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); struct device_node *pp; - struct i2c_client *client = phy->client; int gpio; int ret; @@ -185,10 +185,10 @@ static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) return 0; } -static int st33zp24_i2c_request_resources(struct i2c_client *client, - struct st33zp24_i2c_phy *phy) +static int st33zp24_i2c_request_resources(struct i2c_client *client) { struct st33zp24_platform_data *pdata; + struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); int ret; pdata = client->dev.platform_data; @@ -244,17 +244,20 @@ static int st33zp24_i2c_probe(struct i2c_client *client, return -ENOMEM; phy->client = client; + + i2c_set_clientdata(client, phy); + pdata = client->dev.platform_data; if (!pdata && client->dev.of_node) { - ret = st33zp24_i2c_of_request_resources(phy); + ret = st33zp24_i2c_of_request_resources(client); if (ret) return ret; } else if (pdata) { - ret = st33zp24_i2c_request_resources(client, phy); + ret = st33zp24_i2c_request_resources(client); if (ret) return ret; } else if (ACPI_HANDLE(&client->dev)) { - ret = st33zp24_i2c_acpi_request_resources(phy); + ret = st33zp24_i2c_acpi_request_resources(client); if (ret) return ret; } -- cgit v0.10.2 From add40d6df6babc43e587e9c8c6a2c1320bc78c98 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Tue, 23 Feb 2016 22:25:51 +0100 Subject: tpm/st33zp24/spi: Change xxx_request_resources header Simplify st33zp24_spi_acpi_request_resources, st33zp24_spi_of_request_resources and st33zp24_spi_request_resources to have the same prototype and using spi_get_drvdata. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 173db72..6be51ac 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -229,9 +229,9 @@ static const struct st33zp24_phy_ops spi_phy_ops = { .recv = st33zp24_spi_recv, }; -static int st33zp24_spi_acpi_request_resources(struct st33zp24_spi_phy *phy) +static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev) { - struct spi_device *spi_dev = phy->spi_device; + struct st33zp24_spi_phy *phy = spi_get_drvdata(spi_dev); const struct acpi_device_id *id; struct gpio_desc *gpiod_lpcpd; struct device *dev; @@ -265,23 +265,23 @@ static int st33zp24_spi_acpi_request_resources(struct st33zp24_spi_phy *phy) return 0; } -static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) +static int st33zp24_spi_of_request_resources(struct spi_device *spi_dev) { + struct st33zp24_spi_phy *phy = spi_get_drvdata(spi_dev); struct device_node *pp; - struct spi_device *dev = phy->spi_device; int gpio; int ret; - pp = dev->dev.of_node; + pp = spi_dev->dev.of_node; if (!pp) { - dev_err(&dev->dev, "No platform data\n"); + dev_err(&spi_dev->dev, "No platform data\n"); return -ENODEV; } /* Get GPIO from device tree */ gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0); if (gpio < 0) { - dev_err(&dev->dev, + dev_err(&spi_dev->dev, "Failed to retrieve lpcpd-gpios from dts.\n"); phy->io_lpcpd = -1; /* @@ -292,10 +292,10 @@ static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) return 0; } /* GPIO request and configuration */ - ret = devm_gpio_request_one(&dev->dev, gpio, + ret = devm_gpio_request_one(&spi_dev->dev, gpio, GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD"); if (ret) { - dev_err(&dev->dev, "Failed to request lpcpd pin\n"); + dev_err(&spi_dev->dev, "Failed to request lpcpd pin\n"); return -ENODEV; } phy->io_lpcpd = gpio; @@ -303,9 +303,9 @@ static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) return 0; } -static int st33zp24_spi_request_resources(struct spi_device *dev, - struct st33zp24_spi_phy *phy) +static int st33zp24_spi_request_resources(struct spi_device *dev) { + struct st33zp24_spi_phy *phy = spi_get_drvdata(dev); struct st33zp24_platform_data *pdata; int ret; @@ -357,17 +357,20 @@ static int st33zp24_spi_probe(struct spi_device *dev) return -ENOMEM; phy->spi_device = dev; + + spi_set_drvdata(dev, phy); + pdata = dev->dev.platform_data; if (!pdata && dev->dev.of_node) { - ret = st33zp24_spi_of_request_resources(phy); + ret = st33zp24_spi_of_request_resources(dev); if (ret) return ret; } else if (pdata) { - ret = st33zp24_spi_request_resources(dev, phy); + ret = st33zp24_spi_request_resources(dev); if (ret) return ret; } else if (ACPI_HANDLE(&dev->dev)) { - ret = st33zp24_spi_acpi_request_resources(phy); + ret = st33zp24_spi_acpi_request_resources(dev); if (ret) return ret; } -- cgit v0.10.2 From 604e57888499c149d0aca4b1ac39da07da040fcb Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:24 +0100 Subject: tpm/st33zp24/spi: Remove nbr_dummy_bytes variable usage nbr_dummy_bytes variable could be easily replaced by phy->latency in st33zp24_spi_send and st33zp24_spi_recv. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 6be51ac..74c5fcc 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -1,6 +1,6 @@ /* * STMicroelectronics TPM SPI Linux driver for TPM ST33ZP24 - * Copyright (C) 2009 - 2016 STMicroelectronics + * Copyright (C) 2009 - 2015 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 @@ -19,10 +19,8 @@ #include #include #include -#include #include #include -#include #include #include @@ -68,7 +66,7 @@ struct st33zp24_spi_phy { struct spi_device *spi_device; - + struct spi_transfer spi_xfer; u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE]; u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE]; @@ -112,39 +110,42 @@ static int st33zp24_status_to_errno(u8 code) static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) { + u8 data = 0; int total_length = 0, ret = 0; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; - struct spi_transfer spi_xfer = { - .tx_buf = phy->tx_buf, - .rx_buf = phy->rx_buf, - }; + u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf; + u8 *rx_buf = phy->spi_xfer.rx_buf; /* Pre-Header */ - phy->tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0; - phy->tx_buf[total_length++] = tpm_register; + data = TPM_WRITE_DIRECTION | LOCALITY0; + memcpy(tx_buf + total_length, &data, sizeof(data)); + total_length++; + data = tpm_register; + memcpy(tx_buf + total_length, &data, sizeof(data)); + total_length++; if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) { - phy->tx_buf[total_length++] = tpm_size >> 8; - phy->tx_buf[total_length++] = tpm_size; + tx_buf[total_length++] = tpm_size >> 8; + tx_buf[total_length++] = tpm_size; } - memcpy(&phy->tx_buf[total_length], tpm_data, tpm_size); + memcpy(&tx_buf[total_length], tpm_data, tpm_size); total_length += tpm_size; - memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency); + memset(&tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency); - spi_xfer.len = total_length + phy->latency; + phy->spi_xfer.len = total_length + phy->latency; - ret = spi_sync_transfer(dev, &spi_xfer, 1); + ret = spi_sync_transfer(dev, &phy->spi_xfer, 1); if (ret == 0) - ret = phy->rx_buf[total_length + phy->latency - 1]; + ret = rx_buf[total_length + phy->latency - 1]; return st33zp24_status_to_errno(ret); } /* st33zp24_spi_send() */ /* - * st33zp24_spi_read8_recv + * read8_recv * Recv byte from the TIS register according to the ST33ZP24 SPI protocol. * @param: phy_id, the phy description * @param: tpm_register, the tpm tis register where the data should be read @@ -152,37 +153,39 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, * @param: tpm_size, tpm TPM response size to read. * @return: should be zero if success else a negative error code. */ -static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, - int tpm_size) +static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) { + u8 data = 0; int total_length = 0, ret; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; - struct spi_transfer spi_xfer = { - .tx_buf = phy->tx_buf, - .rx_buf = phy->rx_buf, - }; + u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf; + u8 *rx_buf = phy->spi_xfer.rx_buf; /* Pre-Header */ - phy->tx_buf[total_length++] = LOCALITY0; - phy->tx_buf[total_length++] = tpm_register; - - memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE, + data = LOCALITY0; + memcpy(tx_buf + total_length, &data, sizeof(data)); + total_length++; + data = tpm_register; + memcpy(tx_buf + total_length, &data, sizeof(data)); + total_length++; + + memset(&tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency + tpm_size); - spi_xfer.len = total_length + phy->latency + tpm_size; + phy->spi_xfer.len = total_length + phy->latency + tpm_size; /* header + status byte + size of the data + status byte */ - ret = spi_sync_transfer(dev, &spi_xfer, 1); + ret = spi_sync_transfer(dev, &phy->spi_xfer, 1); if (tpm_size > 0 && ret == 0) { - ret = phy->rx_buf[total_length + phy->latency - 1]; + ret = rx_buf[total_length + phy->latency - 1]; - memcpy(tpm_data, phy->rx_buf + total_length + phy->latency, + memcpy(tpm_data, rx_buf + total_length + phy->latency, tpm_size); } return ret; -} /* st33zp24_spi_read8_reg() */ +} /* read8_reg() */ /* * st33zp24_spi_recv @@ -198,13 +201,13 @@ static int st33zp24_spi_recv(void *phy_id, u8 tpm_register, u8 *tpm_data, { int ret; - ret = st33zp24_spi_read8_reg(phy_id, tpm_register, tpm_data, tpm_size); + ret = read8_reg(phy_id, tpm_register, tpm_data, tpm_size); if (!st33zp24_status_to_errno(ret)) return tpm_size; return ret; } /* st33zp24_spi_recv() */ -static int st33zp24_spi_evaluate_latency(void *phy_id) +static int evaluate_latency(void *phy_id) { struct st33zp24_spi_phy *phy = phy_id; int latency = 1, status = 0; @@ -212,15 +215,9 @@ static int st33zp24_spi_evaluate_latency(void *phy_id) while (!status && latency < MAX_SPI_LATENCY) { phy->latency = latency; - status = st33zp24_spi_read8_reg(phy_id, TPM_INTF_CAPABILITY, - &data, 1); + status = read8_reg(phy_id, TPM_INTF_CAPABILITY, &data, 1); latency++; } - if (status < 0) - return status; - if (latency == MAX_SPI_LATENCY) - return -ENODEV; - return latency - 1; } /* evaluate_latency() */ @@ -229,59 +226,24 @@ static const struct st33zp24_phy_ops spi_phy_ops = { .recv = st33zp24_spi_recv, }; -static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev) +#ifdef CONFIG_OF +static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy) { - struct st33zp24_spi_phy *phy = spi_get_drvdata(spi_dev); - const struct acpi_device_id *id; - struct gpio_desc *gpiod_lpcpd; - struct device *dev; - - if (!spi_dev) - return -EINVAL; - - dev = &spi_dev->dev; - - /* Match the struct device against a given list of ACPI IDs */ - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return -ENODEV; - - /* Get LPCPD GPIO from ACPI */ - gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, - GPIOD_OUT_HIGH); - if (IS_ERR(gpiod_lpcpd)) { - dev_err(dev, "Failed to retrieve lpcpd-gpios from acpi.\n"); - phy->io_lpcpd = -1; - /* - * lpcpd pin is not specified. This is not an issue as - * power management can be also managed by TPM specific - * commands. So leave with a success status code. - */ - return 0; - } - - phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd); - - return 0; -} - -static int st33zp24_spi_of_request_resources(struct spi_device *spi_dev) -{ - struct st33zp24_spi_phy *phy = spi_get_drvdata(spi_dev); struct device_node *pp; + struct spi_device *dev = phy->spi_device; int gpio; int ret; - pp = spi_dev->dev.of_node; + pp = dev->dev.of_node; if (!pp) { - dev_err(&spi_dev->dev, "No platform data\n"); + dev_err(&dev->dev, "No platform data\n"); return -ENODEV; } /* Get GPIO from device tree */ gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0); if (gpio < 0) { - dev_err(&spi_dev->dev, + dev_err(&dev->dev, "Failed to retrieve lpcpd-gpios from dts.\n"); phy->io_lpcpd = -1; /* @@ -292,20 +254,26 @@ static int st33zp24_spi_of_request_resources(struct spi_device *spi_dev) return 0; } /* GPIO request and configuration */ - ret = devm_gpio_request_one(&spi_dev->dev, gpio, + ret = devm_gpio_request_one(&dev->dev, gpio, GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD"); if (ret) { - dev_err(&spi_dev->dev, "Failed to request lpcpd pin\n"); + dev_err(&dev->dev, "Failed to request lpcpd pin\n"); return -ENODEV; } phy->io_lpcpd = gpio; return 0; } +#else +static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy) +{ + return -ENODEV; +} +#endif -static int st33zp24_spi_request_resources(struct spi_device *dev) +static int tpm_stm_spi_request_resources(struct spi_device *dev, + struct st33zp24_spi_phy *phy) { - struct st33zp24_spi_phy *phy = spi_get_drvdata(dev); struct st33zp24_platform_data *pdata; int ret; @@ -333,12 +301,13 @@ static int st33zp24_spi_request_resources(struct spi_device *dev) } /* - * st33zp24_spi_probe initialize the TPM device + * tpm_st33_spi_probe initialize the TPM device * @param: dev, the spi_device drescription (TPM SPI description). * @return: 0 in case of success. * or a negative value describing the error. */ -static int st33zp24_spi_probe(struct spi_device *dev) +static int +tpm_st33_spi_probe(struct spi_device *dev) { int ret; struct st33zp24_platform_data *pdata; @@ -357,25 +326,21 @@ static int st33zp24_spi_probe(struct spi_device *dev) return -ENOMEM; phy->spi_device = dev; - - spi_set_drvdata(dev, phy); - pdata = dev->dev.platform_data; if (!pdata && dev->dev.of_node) { - ret = st33zp24_spi_of_request_resources(dev); + ret = tpm_stm_spi_of_request_resources(phy); if (ret) return ret; } else if (pdata) { - ret = st33zp24_spi_request_resources(dev); - if (ret) - return ret; - } else if (ACPI_HANDLE(&dev->dev)) { - ret = st33zp24_spi_acpi_request_resources(dev); + ret = tpm_stm_spi_request_resources(dev, phy); if (ret) return ret; } - phy->latency = st33zp24_spi_evaluate_latency(phy); + phy->spi_xfer.tx_buf = phy->tx_buf; + phy->spi_xfer.rx_buf = phy->rx_buf; + + phy->latency = evaluate_latency(phy); if (phy->latency <= 0) return -ENODEV; @@ -384,11 +349,11 @@ static int st33zp24_spi_probe(struct spi_device *dev) } /* - * st33zp24_spi_remove remove the TPM device + * tpm_st33_spi_remove remove the TPM device * @param: client, the spi_device drescription (TPM SPI description). * @return: 0 in case of success. */ -static int st33zp24_spi_remove(struct spi_device *dev) +static int tpm_st33_spi_remove(struct spi_device *dev) { struct tpm_chip *chip = spi_get_drvdata(dev); @@ -401,34 +366,29 @@ static const struct spi_device_id st33zp24_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, st33zp24_spi_id); +#ifdef CONFIG_OF static const struct of_device_id of_st33zp24_spi_match[] = { { .compatible = "st,st33zp24-spi", }, {} }; MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match); - -static const struct acpi_device_id st33zp24_spi_acpi_match[] = { - {"SMO3324"}, - {} -}; -MODULE_DEVICE_TABLE(acpi, st33zp24_spi_acpi_match); +#endif static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend, st33zp24_pm_resume); -static struct spi_driver st33zp24_spi_driver = { +static struct spi_driver tpm_st33_spi_driver = { .driver = { .name = TPM_ST33_SPI, .pm = &st33zp24_spi_ops, .of_match_table = of_match_ptr(of_st33zp24_spi_match), - .acpi_match_table = ACPI_PTR(st33zp24_spi_acpi_match), }, - .probe = st33zp24_spi_probe, - .remove = st33zp24_spi_remove, + .probe = tpm_st33_spi_probe, + .remove = tpm_st33_spi_remove, .id_table = st33zp24_spi_id, }; -module_spi_driver(st33zp24_spi_driver); +module_spi_driver(tpm_st33_spi_driver); MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)"); MODULE_DESCRIPTION("STM TPM 1.2 SPI ST33 Driver"); -- cgit v0.10.2 From 8a7450039a7b2a67f61f671a9aeb11b5176e2adc Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:25 +0100 Subject: tpm/st33zp24/spi: Use functions name with st33zp24_spi_ prefix Make sure every function name use st33zp24_spi_ prefix. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 74c5fcc..f42c443 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -145,7 +145,7 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, } /* st33zp24_spi_send() */ /* - * read8_recv + * st33zp24_spi_read8_recv * Recv byte from the TIS register according to the ST33ZP24 SPI protocol. * @param: phy_id, the phy description * @param: tpm_register, the tpm tis register where the data should be read @@ -153,7 +153,8 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, * @param: tpm_size, tpm TPM response size to read. * @return: should be zero if success else a negative error code. */ -static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) +static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, + int tpm_size) { u8 data = 0; int total_length = 0, ret; @@ -185,7 +186,7 @@ static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) } return ret; -} /* read8_reg() */ +} /* st33zp24_spi_read8_reg() */ /* * st33zp24_spi_recv @@ -201,13 +202,13 @@ static int st33zp24_spi_recv(void *phy_id, u8 tpm_register, u8 *tpm_data, { int ret; - ret = read8_reg(phy_id, tpm_register, tpm_data, tpm_size); + ret = st33zp24_spi_read8_reg(phy_id, tpm_register, tpm_data, tpm_size); if (!st33zp24_status_to_errno(ret)) return tpm_size; return ret; } /* st33zp24_spi_recv() */ -static int evaluate_latency(void *phy_id) +static int st33zp24_spi_evaluate_latency(void *phy_id) { struct st33zp24_spi_phy *phy = phy_id; int latency = 1, status = 0; @@ -215,7 +216,8 @@ static int evaluate_latency(void *phy_id) while (!status && latency < MAX_SPI_LATENCY) { phy->latency = latency; - status = read8_reg(phy_id, TPM_INTF_CAPABILITY, &data, 1); + status = st33zp24_spi_read8_reg(phy_id, TPM_INTF_CAPABILITY, + &data, 1); latency++; } return latency - 1; @@ -227,7 +229,7 @@ static const struct st33zp24_phy_ops spi_phy_ops = { }; #ifdef CONFIG_OF -static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy) +static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) { struct device_node *pp; struct spi_device *dev = phy->spi_device; @@ -265,14 +267,14 @@ static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy) return 0; } #else -static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy) +static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) { return -ENODEV; } #endif -static int tpm_stm_spi_request_resources(struct spi_device *dev, - struct st33zp24_spi_phy *phy) +static int st33zp24_spi_request_resources(struct spi_device *dev, + struct st33zp24_spi_phy *phy) { struct st33zp24_platform_data *pdata; int ret; @@ -301,13 +303,12 @@ static int tpm_stm_spi_request_resources(struct spi_device *dev, } /* - * tpm_st33_spi_probe initialize the TPM device + * st33zp24_spi_probe initialize the TPM device * @param: dev, the spi_device drescription (TPM SPI description). * @return: 0 in case of success. * or a negative value describing the error. */ -static int -tpm_st33_spi_probe(struct spi_device *dev) +static int st33zp24_spi_probe(struct spi_device *dev) { int ret; struct st33zp24_platform_data *pdata; @@ -328,11 +329,11 @@ tpm_st33_spi_probe(struct spi_device *dev) phy->spi_device = dev; pdata = dev->dev.platform_data; if (!pdata && dev->dev.of_node) { - ret = tpm_stm_spi_of_request_resources(phy); + ret = st33zp24_spi_of_request_resources(phy); if (ret) return ret; } else if (pdata) { - ret = tpm_stm_spi_request_resources(dev, phy); + ret = st33zp24_spi_request_resources(dev, phy); if (ret) return ret; } @@ -340,7 +341,7 @@ tpm_st33_spi_probe(struct spi_device *dev) phy->spi_xfer.tx_buf = phy->tx_buf; phy->spi_xfer.rx_buf = phy->rx_buf; - phy->latency = evaluate_latency(phy); + phy->latency = st33zp24_spi_evaluate_latency(phy); if (phy->latency <= 0) return -ENODEV; @@ -349,11 +350,11 @@ tpm_st33_spi_probe(struct spi_device *dev) } /* - * tpm_st33_spi_remove remove the TPM device + * st33zp24_spi_remove remove the TPM device * @param: client, the spi_device drescription (TPM SPI description). * @return: 0 in case of success. */ -static int tpm_st33_spi_remove(struct spi_device *dev) +static int st33zp24_spi_remove(struct spi_device *dev) { struct tpm_chip *chip = spi_get_drvdata(dev); @@ -377,18 +378,18 @@ MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match); static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend, st33zp24_pm_resume); -static struct spi_driver tpm_st33_spi_driver = { +static struct spi_driver st33zp24_spi_driver = { .driver = { .name = TPM_ST33_SPI, .pm = &st33zp24_spi_ops, .of_match_table = of_match_ptr(of_st33zp24_spi_match), }, - .probe = tpm_st33_spi_probe, - .remove = tpm_st33_spi_remove, + .probe = st33zp24_spi_probe, + .remove = st33zp24_spi_remove, .id_table = st33zp24_spi_id, }; -module_spi_driver(tpm_st33_spi_driver); +module_spi_driver(st33zp24_spi_driver); MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)"); MODULE_DESCRIPTION("STM TPM 1.2 SPI ST33 Driver"); -- cgit v0.10.2 From d34306e28891257e3c4e8c172af19d70f84fa272 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:26 +0100 Subject: tpm/st33zp24/spi: Remove useless use of memcpy. An affectation is enough when copying 1 byte. Remove memcpy usage where possible. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index f42c443..08ffbfe 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -110,7 +110,6 @@ static int st33zp24_status_to_errno(u8 code) static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) { - u8 data = 0; int total_length = 0, ret = 0; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; @@ -118,12 +117,8 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, u8 *rx_buf = phy->spi_xfer.rx_buf; /* Pre-Header */ - data = TPM_WRITE_DIRECTION | LOCALITY0; - memcpy(tx_buf + total_length, &data, sizeof(data)); - total_length++; - data = tpm_register; - memcpy(tx_buf + total_length, &data, sizeof(data)); - total_length++; + tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0; + tx_buf[total_length++] = tpm_register; if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) { tx_buf[total_length++] = tpm_size >> 8; @@ -156,7 +151,6 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) { - u8 data = 0; int total_length = 0, ret; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; @@ -164,12 +158,8 @@ static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, u8 *rx_buf = phy->spi_xfer.rx_buf; /* Pre-Header */ - data = LOCALITY0; - memcpy(tx_buf + total_length, &data, sizeof(data)); - total_length++; - data = tpm_register; - memcpy(tx_buf + total_length, &data, sizeof(data)); - total_length++; + tx_buf[total_length++] = LOCALITY0; + tx_buf[total_length++] = tpm_register; memset(&tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency + tpm_size); -- cgit v0.10.2 From a5392e912031dff0b74a5d923c49c9839cea5bb3 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:27 +0100 Subject: tpm/st33zp24/spi: Remove field spi_xfer from st33zp24_spi_phy structure Remove spi_xfer from st33zp24_spi_phy structure and declare local spi_xfer when needed instead. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 08ffbfe..f851053 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -66,7 +66,7 @@ struct st33zp24_spi_phy { struct spi_device *spi_device; - struct spi_transfer spi_xfer; + u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE]; u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE]; @@ -113,28 +113,30 @@ static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, int total_length = 0, ret = 0; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; - u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf; - u8 *rx_buf = phy->spi_xfer.rx_buf; + struct spi_transfer spi_xfer = { + .tx_buf = phy->tx_buf, + .rx_buf = phy->rx_buf, + }; /* Pre-Header */ - tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0; - tx_buf[total_length++] = tpm_register; + phy->tx_buf[total_length++] = TPM_WRITE_DIRECTION | LOCALITY0; + phy->tx_buf[total_length++] = tpm_register; if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) { - tx_buf[total_length++] = tpm_size >> 8; - tx_buf[total_length++] = tpm_size; + phy->tx_buf[total_length++] = tpm_size >> 8; + phy->tx_buf[total_length++] = tpm_size; } - memcpy(&tx_buf[total_length], tpm_data, tpm_size); + memcpy(&phy->tx_buf[total_length], tpm_data, tpm_size); total_length += tpm_size; - memset(&tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency); + memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency); - phy->spi_xfer.len = total_length + phy->latency; + spi_xfer.len = total_length + phy->latency; - ret = spi_sync_transfer(dev, &phy->spi_xfer, 1); + ret = spi_sync_transfer(dev, &spi_xfer, 1); if (ret == 0) - ret = rx_buf[total_length + phy->latency - 1]; + ret = phy->rx_buf[total_length + phy->latency - 1]; return st33zp24_status_to_errno(ret); } /* st33zp24_spi_send() */ @@ -154,24 +156,26 @@ static int st33zp24_spi_read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int total_length = 0, ret; struct st33zp24_spi_phy *phy = phy_id; struct spi_device *dev = phy->spi_device; - u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf; - u8 *rx_buf = phy->spi_xfer.rx_buf; + struct spi_transfer spi_xfer = { + .tx_buf = phy->tx_buf, + .rx_buf = phy->rx_buf, + }; /* Pre-Header */ - tx_buf[total_length++] = LOCALITY0; - tx_buf[total_length++] = tpm_register; + phy->tx_buf[total_length++] = LOCALITY0; + phy->tx_buf[total_length++] = tpm_register; - memset(&tx_buf[total_length], TPM_DUMMY_BYTE, + memset(&phy->tx_buf[total_length], TPM_DUMMY_BYTE, phy->latency + tpm_size); - phy->spi_xfer.len = total_length + phy->latency + tpm_size; + spi_xfer.len = total_length + phy->latency + tpm_size; /* header + status byte + size of the data + status byte */ - ret = spi_sync_transfer(dev, &phy->spi_xfer, 1); + ret = spi_sync_transfer(dev, &spi_xfer, 1); if (tpm_size > 0 && ret == 0) { - ret = rx_buf[total_length + phy->latency - 1]; + ret = phy->rx_buf[total_length + phy->latency - 1]; - memcpy(tpm_data, rx_buf + total_length + phy->latency, + memcpy(tpm_data, phy->rx_buf + total_length + phy->latency, tpm_size); } @@ -328,9 +332,6 @@ static int st33zp24_spi_probe(struct spi_device *dev) return ret; } - phy->spi_xfer.tx_buf = phy->tx_buf; - phy->spi_xfer.rx_buf = phy->rx_buf; - phy->latency = st33zp24_spi_evaluate_latency(phy); if (phy->latency <= 0) return -ENODEV; -- cgit v0.10.2 From 300796cdb5ba624031a5e1e5992e670eb457c231 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:28 +0100 Subject: tpm/st33zp24: Remove unneeded CONFIG_OF switches DT headers already define NOOP routines when CONFIG_OF is not defined. [jarkko.sakkinen@linux.intel.com: I tested that the driver compiles without warnings and errors with and without CONFIG_OF flag.] Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index f179aad..a05fbd8 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -1,6 +1,6 @@ /* * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 - * Copyright (C) 2009 - 2016 STMicroelectronics + * Copyright (C) 2009 - 2015 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 @@ -19,10 +19,8 @@ #include #include #include -#include #include #include -#include #include #include @@ -110,47 +108,10 @@ static const struct st33zp24_phy_ops i2c_phy_ops = { .recv = st33zp24_i2c_recv, }; -static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client) +static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) { - struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); - const struct acpi_device_id *id; - struct gpio_desc *gpiod_lpcpd; - struct device *dev; - - if (!client) - return -EINVAL; - - dev = &client->dev; - - /* Match the struct device against a given list of ACPI IDs */ - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return -ENODEV; - - /* Get LPCPD GPIO from ACPI */ - gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, - GPIOD_OUT_HIGH); - if (IS_ERR(gpiod_lpcpd)) { - dev_err(&client->dev, - "Failed to retrieve lpcpd-gpios from acpi.\n"); - phy->io_lpcpd = -1; - /* - * lpcpd pin is not specified. This is not an issue as - * power management can be also managed by TPM specific - * commands. So leave with a success status code. - */ - return 0; - } - - phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd); - - return 0; -} - -static int st33zp24_i2c_of_request_resources(struct i2c_client *client) -{ - struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); struct device_node *pp; + struct i2c_client *client = phy->client; int gpio; int ret; @@ -185,10 +146,10 @@ static int st33zp24_i2c_of_request_resources(struct i2c_client *client) return 0; } -static int st33zp24_i2c_request_resources(struct i2c_client *client) +static int st33zp24_i2c_request_resources(struct i2c_client *client, + struct st33zp24_i2c_phy *phy) { struct st33zp24_platform_data *pdata; - struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); int ret; pdata = client->dev.platform_data; @@ -244,20 +205,13 @@ static int st33zp24_i2c_probe(struct i2c_client *client, return -ENOMEM; phy->client = client; - - i2c_set_clientdata(client, phy); - pdata = client->dev.platform_data; if (!pdata && client->dev.of_node) { - ret = st33zp24_i2c_of_request_resources(client); + ret = st33zp24_i2c_of_request_resources(phy); if (ret) return ret; } else if (pdata) { - ret = st33zp24_i2c_request_resources(client); - if (ret) - return ret; - } else if (ACPI_HANDLE(&client->dev)) { - ret = st33zp24_i2c_acpi_request_resources(client); + ret = st33zp24_i2c_request_resources(client, phy); if (ret) return ret; } @@ -290,12 +244,6 @@ static const struct of_device_id of_st33zp24_i2c_match[] = { }; MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); -static const struct acpi_device_id st33zp24_i2c_acpi_match[] = { - {"SMO3324"}, - {} -}; -MODULE_DEVICE_TABLE(acpi, st33zp24_i2c_acpi_match); - static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, st33zp24_pm_resume); @@ -304,7 +252,6 @@ static struct i2c_driver st33zp24_i2c_driver = { .name = TPM_ST33_I2C, .pm = &st33zp24_i2c_ops, .of_match_table = of_match_ptr(of_st33zp24_i2c_match), - .acpi_match_table = ACPI_PTR(st33zp24_i2c_acpi_match), }, .probe = st33zp24_i2c_probe, .remove = st33zp24_i2c_remove, diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index f851053..08e6bf5 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -222,7 +222,6 @@ static const struct st33zp24_phy_ops spi_phy_ops = { .recv = st33zp24_spi_recv, }; -#ifdef CONFIG_OF static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) { struct device_node *pp; @@ -260,12 +259,6 @@ static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) return 0; } -#else -static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) -{ - return -ENODEV; -} -#endif static int st33zp24_spi_request_resources(struct spi_device *dev, struct st33zp24_spi_phy *phy) @@ -358,13 +351,11 @@ static const struct spi_device_id st33zp24_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, st33zp24_spi_id); -#ifdef CONFIG_OF static const struct of_device_id of_st33zp24_spi_match[] = { { .compatible = "st,st33zp24-spi", }, {} }; MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match); -#endif static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend, st33zp24_pm_resume); -- cgit v0.10.2 From 4ef2aa3c1bfe237cb349a52ed6f68e4cbfc5ccb9 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:30 +0100 Subject: tpm/st33zp24/spi: Improve st33zp24_spi_evaluate_latency Add check in st33zp24_spi_evaluate_latency helping to diagnose if the chip is present or in a bad state. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 08e6bf5..a2ef8ec 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -214,6 +214,11 @@ static int st33zp24_spi_evaluate_latency(void *phy_id) &data, 1); latency++; } + if (status < 0) + return status; + if (latency == MAX_SPI_LATENCY) + return -ENODEV; + return latency - 1; } /* evaluate_latency() */ -- cgit v0.10.2 From 8bb273f213a9ddd1c288baafcdfc02f02f0e471c Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:31 +0100 Subject: tpm/st33zp24: Extend Copyright headers Extend copyright header to 2016 Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index a05fbd8..a49b6f1 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -1,6 +1,6 @@ /* * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 - * Copyright (C) 2009 - 2015 STMicroelectronics + * Copyright (C) 2009 - 2016 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 diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index a2ef8ec..eb351ba 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -1,6 +1,6 @@ /* * STMicroelectronics TPM SPI Linux driver for TPM ST33ZP24 - * Copyright (C) 2009 - 2015 STMicroelectronics + * Copyright (C) 2009 - 2016 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 -- cgit v0.10.2 From 22eb90db936755e5d600a4561a0cc7a82c791eb9 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Sat, 13 Feb 2016 16:15:32 +0100 Subject: tpm/st33zp24: Add support for acpi probing for i2c device. Add support for acpi probing. SMO3324 is used for st33zp24. It has been tested with the following acpi node on Minnowboard: Device (TPM1) { Name (_ADR, Zero) // _ADR: Address Name (_HID, "SMO3324") // _HID: Hardware ID Name (_CID, "SMO3324") // _CID: Compatible ID Name (_DDN, "SMO TPM") // _DDN: DOS Device Name Name (_UID, One) // _UID: Unique ID Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (SBUF, ResourceTemplate () { I2cSerialBus (0x0013, ControllerInitiated, 400000, AddressingMode7Bit, "\\_SB.I2C7", 0x00, ResourceConsumer, ,) GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone, 0x0000, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0001 } GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0002, } }) Return (SBUF) /* \_SB_.I2C7.TPM1._CRS.SBUF */ } Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } } Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index a49b6f1..b20cc2b 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -108,6 +110,43 @@ static const struct st33zp24_phy_ops i2c_phy_ops = { .recv = st33zp24_i2c_recv, }; +static int st33zp24_i2c_acpi_request_resources(struct st33zp24_i2c_phy *phy) +{ + struct i2c_client *client = phy->client; + const struct acpi_device_id *id; + struct gpio_desc *gpiod_lpcpd; + struct device *dev; + + if (!client) + return -EINVAL; + + dev = &client->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* Get LPCPD GPIO from ACPI */ + gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, + GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_lpcpd)) { + dev_err(&client->dev, + "Failed to retrieve lpcpd-gpios from acpi.\n"); + phy->io_lpcpd = -1; + /* + * lpcpd pin is not specified. This is not an issue as + * power management can be also managed by TPM specific + * commands. So leave with a success status code. + */ + return 0; + } + + phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd); + + return 0; +} + static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) { struct device_node *pp; @@ -214,6 +253,10 @@ static int st33zp24_i2c_probe(struct i2c_client *client, ret = st33zp24_i2c_request_resources(client, phy); if (ret) return ret; + } else if (ACPI_HANDLE(&client->dev)) { + ret = st33zp24_i2c_acpi_request_resources(phy); + if (ret) + return ret; } return st33zp24_probe(phy, &i2c_phy_ops, &client->dev, client->irq, @@ -244,6 +287,12 @@ static const struct of_device_id of_st33zp24_i2c_match[] = { }; MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); +static const struct acpi_device_id st33zp24_i2c_acpi_match[] = { + {"SMO3324"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st33zp24_i2c_acpi_match); + static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, st33zp24_pm_resume); @@ -252,6 +301,7 @@ static struct i2c_driver st33zp24_i2c_driver = { .name = TPM_ST33_I2C, .pm = &st33zp24_i2c_ops, .of_match_table = of_match_ptr(of_st33zp24_i2c_match), + .acpi_match_table = ACPI_PTR(st33zp24_i2c_acpi_match), }, .probe = st33zp24_i2c_probe, .remove = st33zp24_i2c_remove, -- cgit v0.10.2 From 86ff205b8e7b5b85ef1eb5e13f92f37c40a7181a Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Tue, 23 Feb 2016 22:25:49 +0100 Subject: tpm: st33zp24: Add support for acpi probing for spi device. Add support for acpi probing. SMO3324 is used for st33zp24. It has been tested with the following acpi node on Minnowboard: Device (TPM1) { Name (_ADR, Zero) // _ADR: Address Name (_HID, "SMO3324") // _HID: Hardware ID Name (_CID, "SMO3324") // _CID: Compatible ID Name (_DDN, "SMO TPM") // _DDN: DOS Device Name Name (_UID, One) // _UID: Unique ID Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings { Name (SBUF, ResourceTemplate () { SpiSerialBus (0, PolarityLow, FourWireMode, 8, ControllerInitiated, 4000000, ClockPolarityLow, ClockPhaseFirst, "\\_SB.SPI1", 0x00, ResourceConsumer, ,) GpioInt (Edge, ActiveHigh, ExclusiveAndWake, PullNone, 0x0000, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0001 } GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly, "\\_SB.GPO2", 0x00, ResourceConsumer, ,) { // Pin list 0x0002, } }) Return (SBUF) /* \_SB_.SPI1.TPM1._CRS.SBUF */ } Method (_STA, 0, NotSerialized) // _STA: Status { Return (0x0F) } } Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index eb351ba..173db72 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -19,8 +19,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -227,6 +229,42 @@ static const struct st33zp24_phy_ops spi_phy_ops = { .recv = st33zp24_spi_recv, }; +static int st33zp24_spi_acpi_request_resources(struct st33zp24_spi_phy *phy) +{ + struct spi_device *spi_dev = phy->spi_device; + const struct acpi_device_id *id; + struct gpio_desc *gpiod_lpcpd; + struct device *dev; + + if (!spi_dev) + return -EINVAL; + + dev = &spi_dev->dev; + + /* Match the struct device against a given list of ACPI IDs */ + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return -ENODEV; + + /* Get LPCPD GPIO from ACPI */ + gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, + GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_lpcpd)) { + dev_err(dev, "Failed to retrieve lpcpd-gpios from acpi.\n"); + phy->io_lpcpd = -1; + /* + * lpcpd pin is not specified. This is not an issue as + * power management can be also managed by TPM specific + * commands. So leave with a success status code. + */ + return 0; + } + + phy->io_lpcpd = desc_to_gpio(gpiod_lpcpd); + + return 0; +} + static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) { struct device_node *pp; @@ -328,6 +366,10 @@ static int st33zp24_spi_probe(struct spi_device *dev) ret = st33zp24_spi_request_resources(dev, phy); if (ret) return ret; + } else if (ACPI_HANDLE(&dev->dev)) { + ret = st33zp24_spi_acpi_request_resources(phy); + if (ret) + return ret; } phy->latency = st33zp24_spi_evaluate_latency(phy); @@ -362,6 +404,12 @@ static const struct of_device_id of_st33zp24_spi_match[] = { }; MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match); +static const struct acpi_device_id st33zp24_spi_acpi_match[] = { + {"SMO3324"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, st33zp24_spi_acpi_match); + static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend, st33zp24_pm_resume); @@ -370,6 +418,7 @@ static struct spi_driver st33zp24_spi_driver = { .name = TPM_ST33_SPI, .pm = &st33zp24_spi_ops, .of_match_table = of_match_ptr(of_st33zp24_spi_match), + .acpi_match_table = ACPI_PTR(st33zp24_spi_acpi_match), }, .probe = st33zp24_spi_probe, .remove = st33zp24_spi_remove, -- cgit v0.10.2 From 740ec346f32b3459ecf8ae27df76cfa60e94ead0 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Tue, 23 Feb 2016 22:25:50 +0100 Subject: tpm/st33zp24/i2c: Change xxx_request_resources header Simplify st33zp24_i2c_acpi_request_resources, st33zp24_i2c_of_request_resources and st33zp24_i2c_request_resources to have the same prototype and using i2c_get_clientdata. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index b20cc2b..f179aad 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -110,9 +110,9 @@ static const struct st33zp24_phy_ops i2c_phy_ops = { .recv = st33zp24_i2c_recv, }; -static int st33zp24_i2c_acpi_request_resources(struct st33zp24_i2c_phy *phy) +static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client) { - struct i2c_client *client = phy->client; + struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); const struct acpi_device_id *id; struct gpio_desc *gpiod_lpcpd; struct device *dev; @@ -147,10 +147,10 @@ static int st33zp24_i2c_acpi_request_resources(struct st33zp24_i2c_phy *phy) return 0; } -static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) +static int st33zp24_i2c_of_request_resources(struct i2c_client *client) { + struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); struct device_node *pp; - struct i2c_client *client = phy->client; int gpio; int ret; @@ -185,10 +185,10 @@ static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) return 0; } -static int st33zp24_i2c_request_resources(struct i2c_client *client, - struct st33zp24_i2c_phy *phy) +static int st33zp24_i2c_request_resources(struct i2c_client *client) { struct st33zp24_platform_data *pdata; + struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); int ret; pdata = client->dev.platform_data; @@ -244,17 +244,20 @@ static int st33zp24_i2c_probe(struct i2c_client *client, return -ENOMEM; phy->client = client; + + i2c_set_clientdata(client, phy); + pdata = client->dev.platform_data; if (!pdata && client->dev.of_node) { - ret = st33zp24_i2c_of_request_resources(phy); + ret = st33zp24_i2c_of_request_resources(client); if (ret) return ret; } else if (pdata) { - ret = st33zp24_i2c_request_resources(client, phy); + ret = st33zp24_i2c_request_resources(client); if (ret) return ret; } else if (ACPI_HANDLE(&client->dev)) { - ret = st33zp24_i2c_acpi_request_resources(phy); + ret = st33zp24_i2c_acpi_request_resources(client); if (ret) return ret; } -- cgit v0.10.2 From 4d8007ee2626066dbfd3917e813f7c2e47f96392 Mon Sep 17 00:00:00 2001 From: Christophe RICARD Date: Tue, 23 Feb 2016 22:25:51 +0100 Subject: tpm/st33zp24/spi: Change xxx_request_resources header Simplify st33zp24_spi_acpi_request_resources, st33zp24_spi_of_request_resources and st33zp24_spi_request_resources to have the same prototype and using spi_get_drvdata. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 173db72..6be51ac 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -229,9 +229,9 @@ static const struct st33zp24_phy_ops spi_phy_ops = { .recv = st33zp24_spi_recv, }; -static int st33zp24_spi_acpi_request_resources(struct st33zp24_spi_phy *phy) +static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev) { - struct spi_device *spi_dev = phy->spi_device; + struct st33zp24_spi_phy *phy = spi_get_drvdata(spi_dev); const struct acpi_device_id *id; struct gpio_desc *gpiod_lpcpd; struct device *dev; @@ -265,23 +265,23 @@ static int st33zp24_spi_acpi_request_resources(struct st33zp24_spi_phy *phy) return 0; } -static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) +static int st33zp24_spi_of_request_resources(struct spi_device *spi_dev) { + struct st33zp24_spi_phy *phy = spi_get_drvdata(spi_dev); struct device_node *pp; - struct spi_device *dev = phy->spi_device; int gpio; int ret; - pp = dev->dev.of_node; + pp = spi_dev->dev.of_node; if (!pp) { - dev_err(&dev->dev, "No platform data\n"); + dev_err(&spi_dev->dev, "No platform data\n"); return -ENODEV; } /* Get GPIO from device tree */ gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0); if (gpio < 0) { - dev_err(&dev->dev, + dev_err(&spi_dev->dev, "Failed to retrieve lpcpd-gpios from dts.\n"); phy->io_lpcpd = -1; /* @@ -292,10 +292,10 @@ static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) return 0; } /* GPIO request and configuration */ - ret = devm_gpio_request_one(&dev->dev, gpio, + ret = devm_gpio_request_one(&spi_dev->dev, gpio, GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD"); if (ret) { - dev_err(&dev->dev, "Failed to request lpcpd pin\n"); + dev_err(&spi_dev->dev, "Failed to request lpcpd pin\n"); return -ENODEV; } phy->io_lpcpd = gpio; @@ -303,9 +303,9 @@ static int st33zp24_spi_of_request_resources(struct st33zp24_spi_phy *phy) return 0; } -static int st33zp24_spi_request_resources(struct spi_device *dev, - struct st33zp24_spi_phy *phy) +static int st33zp24_spi_request_resources(struct spi_device *dev) { + struct st33zp24_spi_phy *phy = spi_get_drvdata(dev); struct st33zp24_platform_data *pdata; int ret; @@ -357,17 +357,20 @@ static int st33zp24_spi_probe(struct spi_device *dev) return -ENOMEM; phy->spi_device = dev; + + spi_set_drvdata(dev, phy); + pdata = dev->dev.platform_data; if (!pdata && dev->dev.of_node) { - ret = st33zp24_spi_of_request_resources(phy); + ret = st33zp24_spi_of_request_resources(dev); if (ret) return ret; } else if (pdata) { - ret = st33zp24_spi_request_resources(dev, phy); + ret = st33zp24_spi_request_resources(dev); if (ret) return ret; } else if (ACPI_HANDLE(&dev->dev)) { - ret = st33zp24_spi_acpi_request_resources(phy); + ret = st33zp24_spi_acpi_request_resources(dev); if (ret) return ret; } -- cgit v0.10.2 From 8cfffc9d4d3786d3b496a021d7224e06328bac7d Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 29 Feb 2016 12:29:47 -0500 Subject: tpm: Get rid of chip->pdev This is a hold over from before the struct device conversion. - All prints should be using &chip->dev, which is the Linux standard. This changes prints to use tpm0 as the device name, not the PnP/etc ID. - The few places involving sysfs/modules that really do need the parent just use chip->dev.parent instead - We no longer need to get_device(pdev) in any places since it is no longer used by any of the code. The kref on the parent is held by the device core during device_add and dropped in device_del Signed-off-by: Jason Gunthorpe Signed-off-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 274dd01..12829dd 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -49,7 +49,7 @@ struct tpm_chip *tpm_chip_find_get(int chip_num) if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num) continue; - if (try_module_get(pos->pdev->driver->owner)) { + if (try_module_get(pos->dev.parent->driver->owner)) { chip = pos; break; } @@ -113,13 +113,11 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, scnprintf(chip->devname, sizeof(chip->devname), "tpm%d", chip->dev_num); - chip->pdev = dev; - dev_set_drvdata(dev, chip); chip->dev.class = tpm_class; chip->dev.release = tpm_dev_release; - chip->dev.parent = chip->pdev; + chip->dev.parent = dev; #ifdef CONFIG_ACPI chip->dev.groups = chip->groups; #endif @@ -134,7 +132,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, device_initialize(&chip->dev); cdev_init(&chip->cdev, &tpm_fops); - chip->cdev.owner = chip->pdev->driver->owner; + chip->cdev.owner = dev->driver->owner; chip->cdev.kobj.parent = &chip->dev.kobj; rc = devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev); @@ -241,9 +239,8 @@ int tpm_chip_register(struct tpm_chip *chip) chip->flags |= TPM_CHIP_FLAG_REGISTERED; if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { - rc = __compat_only_sysfs_link_entry_to_kobj(&chip->pdev->kobj, - &chip->dev.kobj, - "ppi"); + rc = __compat_only_sysfs_link_entry_to_kobj( + &chip->dev.parent->kobj, &chip->dev.kobj, "ppi"); if (rc && rc != -ENOENT) { tpm_chip_unregister(chip); return rc; @@ -278,7 +275,7 @@ void tpm_chip_unregister(struct tpm_chip *chip) synchronize_rcu(); if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) - sysfs_remove_link(&chip->pdev->kobj, "ppi"); + sysfs_remove_link(&chip->dev.parent->kobj, "ppi"); tpm1_chip_unregister(chip); tpm_del_char_device(chip); diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index de0337e..4009765 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -61,7 +61,7 @@ static int tpm_open(struct inode *inode, struct file *file) * by the check of is_open variable, which is protected * by driver_lock. */ if (test_and_set_bit(0, &chip->is_open)) { - dev_dbg(chip->pdev, "Another process owns this TPM\n"); + dev_dbg(&chip->dev, "Another process owns this TPM\n"); return -EBUSY; } @@ -79,7 +79,6 @@ static int tpm_open(struct inode *inode, struct file *file) INIT_WORK(&priv->work, timeout_work); file->private_data = priv; - get_device(chip->pdev); return 0; } @@ -166,7 +165,6 @@ static int tpm_release(struct inode *inode, struct file *file) file->private_data = NULL; atomic_set(&priv->data_pending, 0); clear_bit(0, &priv->chip->is_open); - put_device(priv->chip->pdev); kfree(priv); return 0; } diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index e2fa89c..483f86f 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -345,7 +345,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, if (count == 0) return -ENODATA; if (count > bufsiz) { - dev_err(chip->pdev, + dev_err(&chip->dev, "invalid count value %x %zx\n", count, bufsiz); return -E2BIG; } @@ -354,7 +354,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, rc = chip->ops->send(chip, (u8 *) buf, count); if (rc < 0) { - dev_err(chip->pdev, + dev_err(&chip->dev, "tpm_transmit: tpm_send: error %zd\n", rc); goto out; } @@ -373,7 +373,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, goto out_recv; if (chip->ops->req_canceled(chip, status)) { - dev_err(chip->pdev, "Operation Canceled\n"); + dev_err(&chip->dev, "Operation Canceled\n"); rc = -ECANCELED; goto out; } @@ -383,14 +383,14 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, } while (time_before(jiffies, stop)); chip->ops->cancel(chip); - dev_err(chip->pdev, "Operation Timed out\n"); + dev_err(&chip->dev, "Operation Timed out\n"); rc = -ETIME; goto out; out_recv: rc = chip->ops->recv(chip, (u8 *) buf, bufsiz); if (rc < 0) - dev_err(chip->pdev, + dev_err(&chip->dev, "tpm_transmit: tpm_recv: error %zd\n", rc); out: mutex_unlock(&chip->tpm_mutex); @@ -416,7 +416,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, err = be32_to_cpu(header->return_code); if (err != 0 && desc) - dev_err(chip->pdev, "A TPM error (%d) occurred %s\n", err, + dev_err(&chip->dev, "A TPM error (%d) occurred %s\n", err, desc); return err; @@ -527,7 +527,7 @@ int tpm_get_timeouts(struct tpm_chip *chip) 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->pdev, "Issuing TPM_STARTUP"); + dev_info(&chip->dev, "Issuing TPM_STARTUP"); if (tpm_startup(chip, TPM_ST_CLEAR)) return rc; @@ -539,7 +539,7 @@ int tpm_get_timeouts(struct tpm_chip *chip) NULL); } if (rc) { - dev_err(chip->pdev, + dev_err(&chip->dev, "A TPM error (%zd) occurred attempting to determine the timeouts\n", rc); goto duration; @@ -578,7 +578,7 @@ int tpm_get_timeouts(struct tpm_chip *chip) /* Report adjusted timeouts */ if (chip->vendor.timeout_adjusted) { - dev_info(chip->pdev, + dev_info(&chip->dev, HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n", old_timeout[0], new_timeout[0], old_timeout[1], new_timeout[1], @@ -625,7 +625,7 @@ duration: chip->vendor.duration[TPM_MEDIUM] *= 1000; chip->vendor.duration[TPM_LONG] *= 1000; chip->vendor.duration_adjusted = true; - dev_info(chip->pdev, "Adjusting TPM timeout parameters."); + dev_info(&chip->dev, "Adjusting TPM timeout parameters."); } return 0; } @@ -815,7 +815,9 @@ int tpm_do_selftest(struct tpm_chip *chip) * around 300ms while the self test is ongoing, keep trying * until the self test duration expires. */ if (rc == -ETIME) { - dev_info(chip->pdev, HW_ERR "TPM command timed out during continue self test"); + dev_info( + &chip->dev, HW_ERR + "TPM command timed out during continue self test"); msleep(delay_msec); continue; } @@ -825,7 +827,7 @@ int tpm_do_selftest(struct tpm_chip *chip) rc = be32_to_cpu(cmd.header.out.return_code); if (rc == TPM_ERR_DISABLED || rc == TPM_ERR_DEACTIVATED) { - dev_info(chip->pdev, + dev_info(&chip->dev, "TPM is disabled/deactivated (0x%X)\n", rc); /* TPM is disabled and/or deactivated; driver can * proceed and TPM does handle commands for @@ -978,10 +980,10 @@ int tpm_pm_suspend(struct device *dev) } if (rc) - dev_err(chip->pdev, + dev_err(&chip->dev, "Error (%d) sending savestate before suspend\n", rc); else if (try > 0) - dev_warn(chip->pdev, "TPM savestate took %dms\n", + dev_warn(&chip->dev, "TPM savestate took %dms\n", try * TPM_TIMEOUT_RETRY); return rc; diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index ee66fd4..d93736a 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -284,16 +284,16 @@ static const struct attribute_group tpm_dev_group = { int tpm_sysfs_add_device(struct tpm_chip *chip) { int err; - err = sysfs_create_group(&chip->pdev->kobj, + err = sysfs_create_group(&chip->dev.parent->kobj, &tpm_dev_group); if (err) - dev_err(chip->pdev, + dev_err(&chip->dev, "failed to create sysfs attributes, %d\n", err); return err; } void tpm_sysfs_del_device(struct tpm_chip *chip) { - sysfs_remove_group(&chip->pdev->kobj, &tpm_dev_group); + sysfs_remove_group(&chip->dev.parent->kobj, &tpm_dev_group); } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 28b477e..9c9be6c 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -167,7 +167,6 @@ enum tpm_chip_flags { }; struct tpm_chip { - struct device *pdev; /* Device stuff */ struct device dev; struct cdev cdev; @@ -199,7 +198,7 @@ struct tpm_chip { static inline void tpm_chip_put(struct tpm_chip *chip) { - module_put(chip->pdev->driver->owner); + module_put(chip->dev.parent->driver->owner); } static inline int tpm_read_index(int base, int index) diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index b28e4da..5fc0e7c 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -597,7 +597,7 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle) rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT); if (rc) { - dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n", + dev_warn(&chip->dev, "0x%08x was not flushed, out of memory\n", handle); return; } @@ -606,7 +606,7 @@ static void tpm2_flush_context(struct tpm_chip *chip, u32 handle) rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context"); if (rc) - dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle, + dev_warn(&chip->dev, "0x%08x was not flushed, rc=%d\n", handle, rc); tpm_buf_destroy(&buf); @@ -770,7 +770,7 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) * except print the error code on a system failure. */ if (rc < 0) - dev_warn(chip->pdev, "transmit returned %d while stopping the TPM", + dev_warn(&chip->dev, "transmit returned %d while stopping the TPM", rc); } EXPORT_SYMBOL_GPL(tpm2_shutdown); @@ -837,7 +837,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full) * immediately. This is a workaround for that. */ if (rc == TPM2_RC_TESTING) { - dev_warn(chip->pdev, "Got RC_TESTING, ignoring\n"); + dev_warn(&chip->dev, "Got RC_TESTING, ignoring\n"); rc = 0; } diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index dfadad0..a48a878 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -49,7 +49,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) for (i = 0; i < 6; i++) { status = ioread8(chip->vendor.iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { - dev_err(chip->pdev, "error reading header\n"); + dev_err(&chip->dev, "error reading header\n"); return -EIO; } *buf++ = ioread8(chip->vendor.iobase); @@ -60,12 +60,12 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) size = be32_to_cpu(*native_size); if (count < size) { - dev_err(chip->pdev, + dev_err(&chip->dev, "Recv size(%d) less than available space\n", size); for (; i < size; i++) { /* clear the waiting data anyway */ status = ioread8(chip->vendor.iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { - dev_err(chip->pdev, "error reading data\n"); + dev_err(&chip->dev, "error reading data\n"); return -EIO; } } @@ -76,7 +76,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) for (; i < size; i++) { status = ioread8(chip->vendor.iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { - dev_err(chip->pdev, "error reading data\n"); + dev_err(&chip->dev, "error reading data\n"); return -EIO; } *buf++ = ioread8(chip->vendor.iobase); @@ -86,7 +86,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) status = ioread8(chip->vendor.iobase + 1); if (status & ATML_STATUS_DATA_AVAIL) { - dev_err(chip->pdev, "data available is stuck\n"); + dev_err(&chip->dev, "data available is stuck\n"); return -EIO; } @@ -97,9 +97,9 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) { int i; - dev_dbg(chip->pdev, "tpm_atml_send:\n"); + dev_dbg(&chip->dev, "tpm_atml_send:\n"); for (i = 0; i < count; i++) { - dev_dbg(chip->pdev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); + dev_dbg(&chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); iowrite8(buf[i], chip->vendor.iobase); } diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 8dfb88b..dd8f0eb 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -52,7 +52,7 @@ struct priv_data { static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) { struct priv_data *priv = chip->vendor.priv; - struct i2c_client *client = to_i2c_client(chip->pdev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); s32 status; priv->len = 0; @@ -62,7 +62,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) status = i2c_master_send(client, buf, len); - dev_dbg(chip->pdev, + dev_dbg(&chip->dev, "%s(buf=%*ph len=%0zx) -> sts=%d\n", __func__, (int)min_t(size_t, 64, len), buf, len, status); return status; @@ -71,7 +71,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) { struct priv_data *priv = chip->vendor.priv; - struct i2c_client *client = to_i2c_client(chip->pdev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); struct tpm_output_header *hdr = (struct tpm_output_header *)priv->buffer; u32 expected_len; @@ -88,7 +88,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) return -ENOMEM; if (priv->len >= expected_len) { - dev_dbg(chip->pdev, + dev_dbg(&chip->dev, "%s early(buf=%*ph count=%0zx) -> ret=%d\n", __func__, (int)min_t(size_t, 64, expected_len), buf, count, expected_len); @@ -97,7 +97,7 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) } rc = i2c_master_recv(client, buf, expected_len); - dev_dbg(chip->pdev, + dev_dbg(&chip->dev, "%s reread(buf=%*ph count=%0zx) -> ret=%d\n", __func__, (int)min_t(size_t, 64, expected_len), buf, count, expected_len); @@ -106,13 +106,13 @@ static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) static void i2c_atmel_cancel(struct tpm_chip *chip) { - dev_err(chip->pdev, "TPM operation cancellation was requested, but is not supported"); + dev_err(&chip->dev, "TPM operation cancellation was requested, but is not supported"); } static u8 i2c_atmel_read_status(struct tpm_chip *chip) { struct priv_data *priv = chip->vendor.priv; - struct i2c_client *client = to_i2c_client(chip->pdev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); int rc; /* The TPM fails the I2C read until it is ready, so we do the entire @@ -125,7 +125,7 @@ static u8 i2c_atmel_read_status(struct tpm_chip *chip) /* Once the TPM has completed the command the command remains readable * until another command is issued. */ rc = i2c_master_recv(client, priv->buffer, sizeof(priv->buffer)); - dev_dbg(chip->pdev, + dev_dbg(&chip->dev, "%s: sts=%d", __func__, rc); if (rc <= 0) return 0; diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 63d5d22..f2aa99e 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -446,7 +446,7 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) /* read first 10 bytes, including tag, paramsize, and result */ size = recv_data(chip, buf, TPM_HEADER_SIZE); if (size < TPM_HEADER_SIZE) { - dev_err(chip->pdev, "Unable to read header\n"); + dev_err(&chip->dev, "Unable to read header\n"); goto out; } @@ -459,14 +459,14 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) size += recv_data(chip, &buf[TPM_HEADER_SIZE], expected - TPM_HEADER_SIZE); if (size < expected) { - dev_err(chip->pdev, "Unable to read remainder of result\n"); + dev_err(&chip->dev, "Unable to read remainder of result\n"); size = -ETIME; goto out; } wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); if (status & TPM_STS_DATA_AVAIL) { /* retry? */ - dev_err(chip->pdev, "Error left over data\n"); + dev_err(&chip->dev, "Error left over data\n"); size = -EIO; goto out; } diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 847f159..a1e1474 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -96,13 +96,13 @@ static s32 i2c_nuvoton_write_buf(struct i2c_client *client, u8 offset, u8 size, /* read TPM_STS register */ static u8 i2c_nuvoton_read_status(struct tpm_chip *chip) { - struct i2c_client *client = to_i2c_client(chip->pdev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); s32 status; u8 data; status = i2c_nuvoton_read_buf(client, TPM_STS, 1, &data); if (status <= 0) { - dev_err(chip->pdev, "%s() error return %d\n", __func__, + dev_err(&chip->dev, "%s() error return %d\n", __func__, status); data = TPM_STS_ERR_VAL; } @@ -127,13 +127,13 @@ static s32 i2c_nuvoton_write_status(struct i2c_client *client, u8 data) /* write commandReady to TPM_STS register */ static void i2c_nuvoton_ready(struct tpm_chip *chip) { - struct i2c_client *client = to_i2c_client(chip->pdev); + struct i2c_client *client = to_i2c_client(chip->dev.parent); s32 status; /* this causes the current command to be aborted */ status = i2c_nuvoton_write_status(client, TPM_STS_COMMAND_READY); if (status < 0) - dev_err(chip->pdev, + dev_err(&chip->dev, "%s() fail to write TPM_STS.commandReady\n", __func__); } @@ -212,7 +212,7 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value, return 0; } while (time_before(jiffies, stop)); } - dev_err(chip->pdev, "%s(%02x, %02x) -> timeout\n", __func__, mask, + dev_err(&chip->dev, "%s(%02x, %02x) -> timeout\n", __func__, mask, value); return -ETIMEDOUT; } @@ -240,7 +240,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client, &chip->vendor.read_queue) == 0) { burst_count = i2c_nuvoton_get_burstcount(client, chip); if (burst_count < 0) { - dev_err(chip->pdev, + dev_err(&chip->dev, "%s() fail to read burstCount=%d\n", __func__, burst_count); return -EIO; @@ -249,12 +249,12 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client, rc = i2c_nuvoton_read_buf(client, TPM_DATA_FIFO_R, bytes2read, &buf[size]); if (rc < 0) { - dev_err(chip->pdev, + dev_err(&chip->dev, "%s() fail on i2c_nuvoton_read_buf()=%d\n", __func__, rc); return -EIO; } - dev_dbg(chip->pdev, "%s(%d):", __func__, bytes2read); + dev_dbg(&chip->dev, "%s(%d):", __func__, bytes2read); size += bytes2read; } @@ -264,7 +264,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client, /* Read TPM command results */ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct device *dev = chip->pdev; + struct device *dev = chip->dev.parent; struct i2c_client *client = to_i2c_client(dev); s32 rc; int expected, status, burst_count, retries, size = 0; @@ -334,7 +334,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) break; } i2c_nuvoton_ready(chip); - dev_dbg(chip->pdev, "%s() -> %d\n", __func__, size); + dev_dbg(&chip->dev, "%s() -> %d\n", __func__, size); return size; } @@ -347,7 +347,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) */ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) { - struct device *dev = chip->pdev; + struct device *dev = chip->dev.parent; struct i2c_client *client = to_i2c_client(dev); u32 ordinal; size_t count = 0; diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 6c488e6..e3cf9f3 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -195,9 +195,9 @@ static int wait(struct tpm_chip *chip, int wait_for_bit) } if (i == TPM_MAX_TRIES) { /* timeout occurs */ if (wait_for_bit == STAT_XFE) - dev_err(chip->pdev, "Timeout in wait(STAT_XFE)\n"); + dev_err(&chip->dev, "Timeout in wait(STAT_XFE)\n"); if (wait_for_bit == STAT_RDA) - dev_err(chip->pdev, "Timeout in wait(STAT_RDA)\n"); + dev_err(&chip->dev, "Timeout in wait(STAT_RDA)\n"); return -EIO; } return 0; @@ -220,7 +220,7 @@ static void wait_and_send(struct tpm_chip *chip, u8 sendbyte) static void tpm_wtx(struct tpm_chip *chip) { number_of_wtx++; - dev_info(chip->pdev, "Granting WTX (%02d / %02d)\n", + dev_info(&chip->dev, "Granting WTX (%02d / %02d)\n", number_of_wtx, TPM_MAX_WTX_PACKAGES); wait_and_send(chip, TPM_VL_VER); wait_and_send(chip, TPM_CTRL_WTX); @@ -231,7 +231,7 @@ static void tpm_wtx(struct tpm_chip *chip) static void tpm_wtx_abort(struct tpm_chip *chip) { - dev_info(chip->pdev, "Aborting WTX\n"); + dev_info(&chip->dev, "Aborting WTX\n"); wait_and_send(chip, TPM_VL_VER); wait_and_send(chip, TPM_CTRL_WTX_ABORT); wait_and_send(chip, 0x00); @@ -257,7 +257,7 @@ recv_begin: } if (buf[0] != TPM_VL_VER) { - dev_err(chip->pdev, + dev_err(&chip->dev, "Wrong transport protocol implementation!\n"); return -EIO; } @@ -272,7 +272,7 @@ recv_begin: } if ((size == 0x6D00) && (buf[1] == 0x80)) { - dev_err(chip->pdev, "Error handling on vendor layer!\n"); + dev_err(&chip->dev, "Error handling on vendor layer!\n"); return -EIO; } @@ -284,7 +284,7 @@ recv_begin: } if (buf[1] == TPM_CTRL_WTX) { - dev_info(chip->pdev, "WTX-package received\n"); + dev_info(&chip->dev, "WTX-package received\n"); if (number_of_wtx < TPM_MAX_WTX_PACKAGES) { tpm_wtx(chip); goto recv_begin; @@ -295,14 +295,14 @@ recv_begin: } if (buf[1] == TPM_CTRL_WTX_ABORT_ACK) { - dev_info(chip->pdev, "WTX-abort acknowledged\n"); + dev_info(&chip->dev, "WTX-abort acknowledged\n"); return size; } if (buf[1] == TPM_CTRL_ERROR) { - dev_err(chip->pdev, "ERROR-package received:\n"); + dev_err(&chip->dev, "ERROR-package received:\n"); if (buf[4] == TPM_INF_NAK) - dev_err(chip->pdev, + dev_err(&chip->dev, "-> Negative acknowledgement" " - retransmit command!\n"); return -EIO; @@ -321,7 +321,7 @@ static int tpm_inf_send(struct tpm_chip *chip, u8 * buf, size_t count) ret = empty_fifo(chip, 1); if (ret) { - dev_err(chip->pdev, "Timeout while clearing FIFO\n"); + dev_err(&chip->dev, "Timeout while clearing FIFO\n"); return -EIO; } diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 289389e..766370b 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -113,7 +113,7 @@ static int nsc_wait_for_ready(struct tpm_chip *chip) } while (time_before(jiffies, stop)); - dev_info(chip->pdev, "wait for ready failed\n"); + dev_info(&chip->dev, "wait for ready failed\n"); return -EBUSY; } @@ -129,12 +129,12 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; if (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0) { - dev_err(chip->pdev, "F0 timeout\n"); + dev_err(&chip->dev, "F0 timeout\n"); return -EIO; } if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) { - dev_err(chip->pdev, "not in normal mode (0x%x)\n", + dev_err(&chip->dev, "not in normal mode (0x%x)\n", data); return -EIO; } @@ -143,7 +143,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) for (p = buffer; p < &buffer[count]; p++) { if (wait_for_stat (chip, NSC_STATUS_OBF, NSC_STATUS_OBF, &data) < 0) { - dev_err(chip->pdev, + dev_err(&chip->dev, "OBF timeout (while reading data)\n"); return -EIO; } @@ -154,11 +154,11 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) if ((data & NSC_STATUS_F0) == 0 && (wait_for_stat(chip, NSC_STATUS_F0, NSC_STATUS_F0, &data) < 0)) { - dev_err(chip->pdev, "F0 not set\n"); + dev_err(&chip->dev, "F0 not set\n"); return -EIO; } if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) { - dev_err(chip->pdev, + dev_err(&chip->dev, "expected end of command(0x%x)\n", data); return -EIO; } @@ -189,19 +189,19 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { - dev_err(chip->pdev, "IBF timeout\n"); + dev_err(&chip->dev, "IBF timeout\n"); return -EIO; } outb(NSC_COMMAND_NORMAL, chip->vendor.base + NSC_COMMAND); if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) { - dev_err(chip->pdev, "IBR timeout\n"); + dev_err(&chip->dev, "IBR timeout\n"); return -EIO; } for (i = 0; i < count; i++) { if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { - dev_err(chip->pdev, + dev_err(&chip->dev, "IBF timeout (while writing data)\n"); return -EIO; } @@ -209,7 +209,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) } if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { - dev_err(chip->pdev, "IBF timeout\n"); + dev_err(&chip->dev, "IBF timeout\n"); return -EIO; } outb(NSC_COMMAND_EOC, chip->vendor.base + NSC_COMMAND); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index a507006..0a9aee9 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -260,7 +260,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) /* read first 10 bytes, including tag, paramsize, and result */ if ((size = recv_data(chip, buf, TPM_HEADER_SIZE)) < TPM_HEADER_SIZE) { - dev_err(chip->pdev, "Unable to read header\n"); + dev_err(&chip->dev, "Unable to read header\n"); goto out; } @@ -273,7 +273,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) if ((size += recv_data(chip, &buf[TPM_HEADER_SIZE], expected - TPM_HEADER_SIZE)) < expected) { - dev_err(chip->pdev, "Unable to read remainder of result\n"); + dev_err(&chip->dev, "Unable to read remainder of result\n"); size = -ETIME; goto out; } @@ -282,7 +282,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) &chip->vendor.int_queue, false); status = tpm_tis_status(chip); if (status & TPM_STS_DATA_AVAIL) { /* retry? */ - dev_err(chip->pdev, "Error left over data\n"); + dev_err(&chip->dev, "Error left over data\n"); size = -EIO; goto out; } @@ -368,7 +368,7 @@ static void disable_interrupts(struct tpm_chip *chip) iowrite32(intmask, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); - devm_free_irq(chip->pdev, chip->vendor.irq, chip); + devm_free_irq(&chip->dev, chip->vendor.irq, chip); chip->vendor.irq = 0; } @@ -497,7 +497,7 @@ static int probe_itpm(struct tpm_chip *chip) rc = tpm_tis_send_data(chip, cmd_getticks, len); if (rc == 0) { - dev_info(chip->pdev, "Detected an iTPM.\n"); + dev_info(&chip->dev, "Detected an iTPM.\n"); rc = 1; } else rc = -EFAULT; @@ -576,9 +576,9 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, struct priv_data *priv = chip->vendor.priv; u8 original_int_vec; - if (devm_request_irq(chip->pdev, irq, tis_int_handler, flags, + if (devm_request_irq(&chip->dev, irq, tis_int_handler, flags, chip->devname, chip) != 0) { - dev_info(chip->pdev, "Unable to request irq: %d for probe\n", + dev_info(&chip->dev, "Unable to request irq: %d for probe\n", irq); return -1; } @@ -779,7 +779,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, tpm_info->irq); if (!chip->vendor.irq) - dev_err(chip->pdev, FW_BUG + dev_err(&chip->dev, FW_BUG "TPM interrupt not working, polling instead\n"); } else tpm_tis_probe_irq(chip, intmask); -- cgit v0.10.2 From 3635e2ec7cbb9aa054f8d4361dec27b0ca625905 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 29 Feb 2016 12:29:48 -0500 Subject: tpm: Get rid of devname Now that we have a proper struct device just use dev_name() to access this value instead of keeping two copies. Signed-off-by: Jason Gunthorpe Signed-off-by: Stefan Berger Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 12829dd..c21d81c 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -111,7 +111,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, set_bit(chip->dev_num, dev_mask); - scnprintf(chip->devname, sizeof(chip->devname), "tpm%d", chip->dev_num); + device_initialize(&chip->dev); dev_set_drvdata(dev, chip); @@ -127,9 +127,9 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, else chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num); - dev_set_name(&chip->dev, "%s", chip->devname); - - device_initialize(&chip->dev); + rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num); + if (rc) + goto out; cdev_init(&chip->cdev, &tpm_fops); chip->cdev.owner = dev->driver->owner; @@ -142,6 +142,10 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, } return chip; + +out: + put_device(&chip->dev); + return ERR_PTR(rc); } EXPORT_SYMBOL_GPL(tpmm_chip_alloc); @@ -153,7 +157,7 @@ static int tpm_add_char_device(struct tpm_chip *chip) if (rc) { dev_err(&chip->dev, "unable to cdev_add() %s, major %d, minor %d, err=%d\n", - chip->devname, MAJOR(chip->dev.devt), + dev_name(&chip->dev), MAJOR(chip->dev.devt), MINOR(chip->dev.devt), rc); return rc; @@ -163,7 +167,7 @@ static int tpm_add_char_device(struct tpm_chip *chip) if (rc) { dev_err(&chip->dev, "unable to device_register() %s, major %d, minor %d, err=%d\n", - chip->devname, MAJOR(chip->dev.devt), + dev_name(&chip->dev), MAJOR(chip->dev.devt), MINOR(chip->dev.devt), rc); cdev_del(&chip->cdev); @@ -190,7 +194,7 @@ static int tpm1_chip_register(struct tpm_chip *chip) if (rc) return rc; - chip->bios_dir = tpm_bios_log_setup(chip->devname); + chip->bios_dir = tpm_bios_log_setup(dev_name(&chip->dev)); return 0; } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 9c9be6c..5d33ba5 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -174,7 +174,6 @@ struct tpm_chip { unsigned int flags; int dev_num; /* /dev/tpm# */ - char devname[7]; unsigned long is_open; /* only one allowed */ int time_expired; diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c index 4e6940a..e722886 100644 --- a/drivers/char/tpm/tpm_eventlog.c +++ b/drivers/char/tpm/tpm_eventlog.c @@ -403,7 +403,7 @@ static int is_bad(void *p) return 0; } -struct dentry **tpm_bios_log_setup(char *name) +struct dentry **tpm_bios_log_setup(const char *name) { struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file; diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h index 267bfbd..cc9672f 100644 --- a/drivers/char/tpm/tpm_eventlog.h +++ b/drivers/char/tpm/tpm_eventlog.h @@ -77,7 +77,7 @@ int read_log(struct tpm_bios_log *log); #if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \ defined(CONFIG_ACPI) -extern struct dentry **tpm_bios_log_setup(char *); +extern struct dentry **tpm_bios_log_setup(const char *); extern void tpm_bios_log_teardown(struct dentry **); #else static inline struct dentry **tpm_bios_log_setup(char *name) diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index a1e1474..d61d43f 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -560,7 +560,7 @@ static int i2c_nuvoton_probe(struct i2c_client *client, rc = devm_request_irq(dev, chip->vendor.irq, i2c_nuvoton_int_handler, IRQF_TRIGGER_LOW, - chip->devname, + dev_name(&chip->dev), chip); if (rc) { dev_err(dev, "%s() Unable to request irq: %d for use\n", diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 0a9aee9..eed3bf5 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -577,7 +577,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, u8 original_int_vec; if (devm_request_irq(&chip->dev, irq, tis_int_handler, flags, - chip->devname, chip) != 0) { + dev_name(&chip->dev), chip) != 0) { dev_info(&chip->dev, "Unable to request irq: %d for probe\n", irq); return -1; -- cgit v0.10.2 From 4e26195f240d73150e8308ae42874702e3df8d2c Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Fri, 12 Feb 2016 20:29:53 -0700 Subject: tpm: Provide strong locking for device removal Add a read/write semaphore around the ops function pointers so ops can be set to null when the driver un-registers. Previously the tpm core expected module locking to be enough to ensure that tpm_unregister could not be called during certain times, however that hasn't been sufficient for a long time. Introduce a read/write semaphore around 'ops' so the core can set it to null when unregistering. This provides a strong fence around the driver callbacks, guaranteeing to the driver that no callbacks are running or will run again. For now the ops_lock is placed very high in the call stack, it could be pushed down and made more granular in future if necessary. Signed-off-by: Jason Gunthorpe Reviewed-by: Stefan Berger Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index c21d81c..5793ea1 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -36,10 +36,60 @@ static DEFINE_SPINLOCK(driver_lock); struct class *tpm_class; dev_t tpm_devt; -/* - * tpm_chip_find_get - return tpm_chip for a given chip number - * @chip_num the device number for the chip +/** + * tpm_try_get_ops() - Get a ref to the tpm_chip + * @chip: Chip to ref + * + * The caller must already have some kind of locking to ensure that chip is + * valid. This function will lock the chip so that the ops member can be + * accessed safely. The locking prevents tpm_chip_unregister from + * completing, so it should not be held for long periods. + * + * Returns -ERRNO if the chip could not be got. */ +int tpm_try_get_ops(struct tpm_chip *chip) +{ + int rc = -EIO; + + get_device(&chip->dev); + + down_read(&chip->ops_sem); + if (!chip->ops) + goto out_lock; + + if (!try_module_get(chip->dev.parent->driver->owner)) + goto out_lock; + + return 0; +out_lock: + up_read(&chip->ops_sem); + put_device(&chip->dev); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_try_get_ops); + +/** + * tpm_put_ops() - Release a ref to the tpm_chip + * @chip: Chip to put + * + * This is the opposite pair to tpm_try_get_ops(). After this returns chip may + * be kfree'd. + */ +void tpm_put_ops(struct tpm_chip *chip) +{ + module_put(chip->dev.parent->driver->owner); + up_read(&chip->ops_sem); + put_device(&chip->dev); +} +EXPORT_SYMBOL_GPL(tpm_put_ops); + +/** + * tpm_chip_find_get() - return tpm_chip for a given chip number + * @chip_num: id to find + * + * The return'd chip has been tpm_try_get_ops'd and must be released via + * tpm_put_ops + */ struct tpm_chip *tpm_chip_find_get(int chip_num) { struct tpm_chip *pos, *chip = NULL; @@ -49,10 +99,10 @@ struct tpm_chip *tpm_chip_find_get(int chip_num) if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num) continue; - if (try_module_get(pos->dev.parent->driver->owner)) { + /* rcu prevents chip from being free'd */ + if (!tpm_try_get_ops(pos)) chip = pos; - break; - } + break; } rcu_read_unlock(); return chip; @@ -95,6 +145,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, return ERR_PTR(-ENOMEM); mutex_init(&chip->tpm_mutex); + init_rwsem(&chip->ops_sem); INIT_LIST_HEAD(&chip->list); chip->ops = ops; @@ -180,6 +231,12 @@ static int tpm_add_char_device(struct tpm_chip *chip) static void tpm_del_char_device(struct tpm_chip *chip) { cdev_del(&chip->cdev); + + /* Make the driver uncallable. */ + down_write(&chip->ops_sem); + chip->ops = NULL; + up_write(&chip->ops_sem); + device_del(&chip->dev); } @@ -265,6 +322,9 @@ EXPORT_SYMBOL_GPL(tpm_chip_register); * Takes the chip first away from the list of available TPM chips and then * cleans up all the resources reserved by tpm_chip_register(). * + * Once this function returns the driver call backs in 'op's will not be + * running and will no longer start. + * * NOTE: This function should be only called before deinitializing chip * resources. */ diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c index 4009765..f5d4521 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -136,9 +136,18 @@ static ssize_t tpm_write(struct file *file, const char __user *buf, return -EFAULT; } - /* atomic tpm command send and result receive */ + /* atomic tpm command send and result receive. We only hold the ops + * lock during this period so that the tpm can be unregistered even if + * the char dev is held open. + */ + if (tpm_try_get_ops(priv->chip)) { + mutex_unlock(&priv->buffer_mutex); + return -EPIPE; + } out_size = tpm_transmit(priv->chip, priv->data_buffer, sizeof(priv->data_buffer)); + + tpm_put_ops(priv->chip); if (out_size < 0) { mutex_unlock(&priv->buffer_mutex); return out_size; diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 483f86f..5caf154 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -700,7 +700,7 @@ int tpm_is_tpm2(u32 chip_num) rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0; - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } @@ -729,7 +729,7 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) rc = tpm2_pcr_read(chip, pcr_idx, res_buf); else rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf); - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_pcr_read); @@ -764,7 +764,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) if (chip->flags & TPM_CHIP_FLAG_TPM2) { rc = tpm2_pcr_extend(chip, pcr_idx, hash); - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } @@ -774,7 +774,7 @@ int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) rc = tpm_transmit_cmd(chip, &cmd, EXTEND_PCR_RESULT_SIZE, "attempting extend a PCR value"); - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_pcr_extend); @@ -855,7 +855,7 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen) rc = tpm_transmit_cmd(chip, cmd, buflen, "attempting tpm_cmd"); - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_send); @@ -1037,7 +1037,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) if (chip->flags & TPM_CHIP_FLAG_TPM2) { err = tpm2_get_random(chip, out, max); - tpm_chip_put(chip); + tpm_put_ops(chip); return err; } @@ -1059,7 +1059,7 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) num_bytes -= recd; } while (retries-- && total < max); - tpm_chip_put(chip); + tpm_put_ops(chip); return total ? total : -EIO; } EXPORT_SYMBOL_GPL(tpm_get_random); @@ -1085,7 +1085,7 @@ int tpm_seal_trusted(u32 chip_num, struct trusted_key_payload *payload, rc = tpm2_seal_trusted(chip, payload, options); - tpm_chip_put(chip); + tpm_put_ops(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_seal_trusted); @@ -1111,7 +1111,8 @@ int tpm_unseal_trusted(u32 chip_num, struct trusted_key_payload *payload, rc = tpm2_unseal_trusted(chip, payload, options); - tpm_chip_put(chip); + tpm_put_ops(chip); + return rc; } EXPORT_SYMBOL_GPL(tpm_unseal_trusted); diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index d93736a..34e7fc7 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -295,5 +295,10 @@ int tpm_sysfs_add_device(struct tpm_chip *chip) void tpm_sysfs_del_device(struct tpm_chip *chip) { + /* The sysfs routines rely on an implicit tpm_try_get_ops, this + * function is called before ops is null'd and the sysfs core + * synchronizes this removal so that no callbacks are running or can + * run again + */ sysfs_remove_group(&chip->dev.parent->kobj, &tpm_dev_group); } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 5d33ba5..c6376b1 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -170,7 +170,13 @@ struct tpm_chip { struct device dev; struct cdev cdev; + /* A driver callback under ops cannot be run unless ops_sem is held + * (sometimes implicitly, eg for the sysfs code). ops becomes null + * when the driver is unregistered, see tpm_try_get_ops. + */ + struct rw_semaphore ops_sem; const struct tpm_class_ops *ops; + unsigned int flags; int dev_num; /* /dev/tpm# */ @@ -195,11 +201,6 @@ struct tpm_chip { #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) -static inline void tpm_chip_put(struct tpm_chip *chip) -{ - module_put(chip->dev.parent->driver->owner); -} - static inline int tpm_read_index(int base, int index) { outb(index, base); @@ -507,6 +508,9 @@ extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, wait_queue_head_t *, bool); struct tpm_chip *tpm_chip_find_get(int chip_num); +__must_check int tpm_try_get_ops(struct tpm_chip *chip); +void tpm_put_ops(struct tpm_chip *chip); + extern struct tpm_chip *tpmm_chip_alloc(struct device *dev, const struct tpm_class_ops *ops); extern int tpm_chip_register(struct tpm_chip *chip); -- cgit v0.10.2 From 2072df40ec19d9703adfcdbd15f30c879bb65b2a Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 29 Feb 2016 08:53:01 -0500 Subject: tpm: Get rid of module locking Now that the tpm core has strong locking around 'ops' it is possible to remove a TPM driver, module and all, even while user space still has things like /dev/tpmX open. For consistency and simplicity, drop the module locking entirely. Signed-off-by: Stefan Berger Reviewed-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 5793ea1..6636728 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -57,9 +57,6 @@ int tpm_try_get_ops(struct tpm_chip *chip) if (!chip->ops) goto out_lock; - if (!try_module_get(chip->dev.parent->driver->owner)) - goto out_lock; - return 0; out_lock: up_read(&chip->ops_sem); @@ -77,7 +74,6 @@ EXPORT_SYMBOL_GPL(tpm_try_get_ops); */ void tpm_put_ops(struct tpm_chip *chip) { - module_put(chip->dev.parent->driver->owner); up_read(&chip->ops_sem); put_device(&chip->dev); } @@ -183,7 +179,7 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, goto out; cdev_init(&chip->cdev, &tpm_fops); - chip->cdev.owner = dev->driver->owner; + chip->cdev.owner = THIS_MODULE; chip->cdev.kobj.parent = &chip->dev.kobj; rc = devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev); -- cgit v0.10.2 From 3897cd9c8d1d1a9ff81fed893502911c2b9f4a79 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Thu, 11 Feb 2016 12:45:48 -0700 Subject: tpm: Split out the devm stuff from tpmm_chip_alloc tpm_chip_alloc becomes a typical subsystem allocate call. Signed-off-by: Jason Gunthorpe Reviewed-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 6636728..5880377 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -121,17 +121,17 @@ static void tpm_dev_release(struct device *dev) } /** - * tpmm_chip_alloc() - allocate a new struct tpm_chip instance - * @dev: device to which the chip is associated + * tpm_chip_alloc() - allocate a new struct tpm_chip instance + * @pdev: device to which the chip is associated + * At this point pdev mst be initialized, but does not have to + * be registered * @ops: struct tpm_class_ops instance * * Allocates a new struct tpm_chip instance and assigns a free - * device number for it. Caller does not have to worry about - * freeing the allocated resources. When the devices is removed - * devres calls tpmm_chip_remove() to do the job. + * device number for it. Must be paired with put_device(&chip->dev). */ -struct tpm_chip *tpmm_chip_alloc(struct device *dev, - const struct tpm_class_ops *ops) +struct tpm_chip *tpm_chip_alloc(struct device *dev, + const struct tpm_class_ops *ops) { struct tpm_chip *chip; int rc; @@ -160,8 +160,6 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, device_initialize(&chip->dev); - dev_set_drvdata(dev, chip); - chip->dev.class = tpm_class; chip->dev.release = tpm_dev_release; chip->dev.parent = dev; @@ -182,17 +180,40 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, chip->cdev.owner = THIS_MODULE; chip->cdev.kobj.parent = &chip->dev.kobj; - rc = devm_add_action(dev, (void (*)(void *)) put_device, &chip->dev); + return chip; + +out: + put_device(&chip->dev); + return ERR_PTR(rc); +} +EXPORT_SYMBOL_GPL(tpm_chip_alloc); + +/** + * tpmm_chip_alloc() - allocate a new struct tpm_chip instance + * @pdev: parent device to which the chip is associated + * @ops: struct tpm_class_ops instance + * + * Same as tpm_chip_alloc except devm is used to do the put_device + */ +struct tpm_chip *tpmm_chip_alloc(struct device *pdev, + const struct tpm_class_ops *ops) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_alloc(pdev, ops); + if (IS_ERR(chip)) + return chip; + + rc = devm_add_action(pdev, (void (*)(void *)) put_device, &chip->dev); if (rc) { put_device(&chip->dev); return ERR_PTR(rc); } - return chip; + dev_set_drvdata(pdev, chip); -out: - put_device(&chip->dev); - return ERR_PTR(rc); + return chip; } EXPORT_SYMBOL_GPL(tpmm_chip_alloc); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index c6376b1..5fcf788 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -511,7 +511,9 @@ struct tpm_chip *tpm_chip_find_get(int chip_num); __must_check int tpm_try_get_ops(struct tpm_chip *chip); void tpm_put_ops(struct tpm_chip *chip); -extern struct tpm_chip *tpmm_chip_alloc(struct device *dev, +extern struct tpm_chip *tpm_chip_alloc(struct device *dev, + const struct tpm_class_ops *ops); +extern struct tpm_chip *tpmm_chip_alloc(struct device *pdev, const struct tpm_class_ops *ops); extern int tpm_chip_register(struct tpm_chip *chip); extern void tpm_chip_unregister(struct tpm_chip *chip); -- cgit v0.10.2 From 15516788e581eb32ec1c50e5f00aba3faf95d817 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 29 Feb 2016 08:53:02 -0500 Subject: tpm: Replace device number bitmap with IDR Replace the device number bitmap with IDR. Extend the number of devices we can create to 64k. Since an IDR allows us to associate a pointer with an ID, we use this now to rewrite tpm_chip_find_get() to simply look up the chip pointer by the given device ID. Protect the IDR calls with a mutex. Signed-off-by: Stefan Berger Reviewed-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 5880377..f62c851 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -29,9 +29,8 @@ #include "tpm.h" #include "tpm_eventlog.h" -static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES); -static LIST_HEAD(tpm_chip_list); -static DEFINE_SPINLOCK(driver_lock); +DEFINE_IDR(dev_nums_idr); +static DEFINE_MUTEX(idr_lock); struct class *tpm_class; dev_t tpm_devt; @@ -88,20 +87,30 @@ EXPORT_SYMBOL_GPL(tpm_put_ops); */ struct tpm_chip *tpm_chip_find_get(int chip_num) { - struct tpm_chip *pos, *chip = NULL; + struct tpm_chip *chip, *res = NULL; + int chip_prev; + + mutex_lock(&idr_lock); + + if (chip_num == TPM_ANY_NUM) { + chip_num = 0; + do { + chip_prev = chip_num; + chip = idr_get_next(&dev_nums_idr, &chip_num); + if (chip && !tpm_try_get_ops(chip)) { + res = chip; + break; + } + } while (chip_prev != chip_num); + } else { + chip = idr_find_slowpath(&dev_nums_idr, chip_num); + if (chip && !tpm_try_get_ops(chip)) + res = chip; + } - rcu_read_lock(); - list_for_each_entry_rcu(pos, &tpm_chip_list, list) { - if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num) - continue; + mutex_unlock(&idr_lock); - /* rcu prevents chip from being free'd */ - if (!tpm_try_get_ops(pos)) - chip = pos; - break; - } - rcu_read_unlock(); - return chip; + return res; } /** @@ -114,9 +123,10 @@ static void tpm_dev_release(struct device *dev) { struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev); - spin_lock(&driver_lock); - clear_bit(chip->dev_num, dev_mask); - spin_unlock(&driver_lock); + mutex_lock(&idr_lock); + idr_remove(&dev_nums_idr, chip->dev_num); + mutex_unlock(&idr_lock); + kfree(chip); } @@ -142,21 +152,18 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev, mutex_init(&chip->tpm_mutex); init_rwsem(&chip->ops_sem); - INIT_LIST_HEAD(&chip->list); chip->ops = ops; - spin_lock(&driver_lock); - chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES); - spin_unlock(&driver_lock); - - if (chip->dev_num >= TPM_NUM_DEVICES) { + mutex_lock(&idr_lock); + rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL); + mutex_unlock(&idr_lock); + if (rc < 0) { dev_err(dev, "No available tpm device numbers\n"); kfree(chip); - return ERR_PTR(-ENOMEM); + return ERR_PTR(rc); } - - set_bit(chip->dev_num, dev_mask); + chip->dev_num = rc; device_initialize(&chip->dev); @@ -242,19 +249,28 @@ static int tpm_add_char_device(struct tpm_chip *chip) return rc; } + /* Make the chip available. */ + mutex_lock(&idr_lock); + idr_replace(&dev_nums_idr, chip, chip->dev_num); + mutex_unlock(&idr_lock); + return rc; } static void tpm_del_char_device(struct tpm_chip *chip) { cdev_del(&chip->cdev); + device_del(&chip->dev); + + /* Make the chip unavailable. */ + mutex_lock(&idr_lock); + idr_replace(&dev_nums_idr, NULL, chip->dev_num); + mutex_unlock(&idr_lock); /* Make the driver uncallable. */ down_write(&chip->ops_sem); chip->ops = NULL; up_write(&chip->ops_sem); - - device_del(&chip->dev); } static int tpm1_chip_register(struct tpm_chip *chip) @@ -309,11 +325,6 @@ int tpm_chip_register(struct tpm_chip *chip) if (rc) goto out_err; - /* Make the chip available. */ - spin_lock(&driver_lock); - list_add_tail_rcu(&chip->list, &tpm_chip_list); - spin_unlock(&driver_lock); - chip->flags |= TPM_CHIP_FLAG_REGISTERED; if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { @@ -350,11 +361,6 @@ void tpm_chip_unregister(struct tpm_chip *chip) if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED)) return; - spin_lock(&driver_lock); - list_del_rcu(&chip->list); - spin_unlock(&driver_lock); - synchronize_rcu(); - if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) sysfs_remove_link(&chip->dev.parent->kobj, "ppi"); diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 5caf154..5397b64 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -1139,6 +1139,7 @@ static int __init tpm_init(void) static void __exit tpm_exit(void) { + idr_destroy(&dev_nums_idr); class_destroy(tpm_class); unregister_chrdev_region(tpm_devt, TPM_NUM_DEVICES); } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 5fcf788..928b47f 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -34,7 +34,7 @@ enum tpm_const { TPM_MINOR = 224, /* officially assigned */ TPM_BUFSIZE = 4096, - TPM_NUM_DEVICES = 256, + TPM_NUM_DEVICES = 65536, TPM_RETRY = 50, /* 5 seconds */ }; @@ -195,8 +195,6 @@ struct tpm_chip { acpi_handle acpi_dev_handle; char ppi_version[TPM_PPI_VERSION_LEN + 1]; #endif /* CONFIG_ACPI */ - - struct list_head list; }; #define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) @@ -492,6 +490,7 @@ static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value) extern struct class *tpm_class; extern dev_t tpm_devt; extern const struct file_operations tpm_fops; +extern struct idr dev_nums_idr; ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *); ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, -- cgit v0.10.2 From 796d803070c91f6e469e212acfdd618180f59e01 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 16 Mar 2016 09:19:48 +0100 Subject: tpm: fix tpm_bios_log_setup stub prototype A cleanup patch changed the prototype of the regular tpm_bios_log_setup function, but not that of the stub that is used when the TPM is disabled, causing a harmless build warning: drivers/char/tpm/tpm-chip.c: In function 'tpm1_chip_register': drivers/char/tpm/tpm-chip.c:287:38: error: passing argument 1 of 'tpm_bios_log_setup' discards 'const' qualifier from pointer target type [-Werror=discarded-qualifiers] chip->bios_dir = tpm_bios_log_setup(dev_name(&chip->dev)); In file included from ../drivers/char/tpm/tpm-chip.c:30:0: ../drivers/char/tpm/tpm_eventlog.h:83:31: note: expected 'char *' but argument is of type 'const char *' static inline struct dentry **tpm_bios_log_setup(char *name) This changes the stub function to match the normal prototype, avoiding that warning. Signed-off-by: Arnd Bergmann Fixes: aca8db8088c3 ("tpm: Get rid of devname") Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h index cc9672f..8de62b0 100644 --- a/drivers/char/tpm/tpm_eventlog.h +++ b/drivers/char/tpm/tpm_eventlog.h @@ -80,7 +80,7 @@ int read_log(struct tpm_bios_log *log); extern struct dentry **tpm_bios_log_setup(const char *); extern void tpm_bios_log_teardown(struct dentry **); #else -static inline struct dentry **tpm_bios_log_setup(char *name) +static inline struct dentry **tpm_bios_log_setup(const char *name) { return NULL; } -- cgit v0.10.2 From d30b8e4f68ef182f8dd84c76af9f148ca5842ea3 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Thu, 31 Mar 2016 13:05:36 +0300 Subject: tpm: cleanup tpm_tis_remove() Created a local variable pointing to the INT_ENABLE_x register. The expression clearing INT_ENABLE_x.globalIntEnable is unreadable and hard to modify without surpassing the 80 char boundary. Signed-off-by: Jarkko Sakkinen Tested-by: Christophe Ricard diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index eed3bf5..18236f6 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -650,15 +650,13 @@ MODULE_PARM_DESC(interrupts, "Enable interrupts"); static void tpm_tis_remove(struct tpm_chip *chip) { + void __iomem *reg = chip->vendor.iobase + + TPM_INT_ENABLE(chip->vendor.locality); + if (chip->flags & TPM_CHIP_FLAG_TPM2) tpm2_shutdown(chip, TPM2_SU_CLEAR); - iowrite32(~TPM_GLOBAL_INT_ENABLE & - ioread32(chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor. - locality)), - chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor.locality)); + iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(reg), reg); release_locality(chip, chip->vendor.locality, 1); } -- cgit v0.10.2 From dcbeab194645486dc482b49b44ef5b8634153034 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Mon, 11 Apr 2016 14:20:57 +0300 Subject: tpm: fix crash in tpm_tis deinitialization rmmod crashes the driver because tpm_chip_unregister() already sets ops to NULL. This commit fixes the issue by moving tpm2_shutdown() to tpm_chip_unregister(). This commit is also cleanup because it removes duplicate code from tpm_crb and tpm_tis to the core. Fixes: 4d3eac5e156a ("tpm: Provide strong locking for device removal") Signed-off-by: Jarkko Sakkinen Reviewed-by: Jason Gunthorpe diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index f62c851..5bc530c 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -269,6 +269,7 @@ static void tpm_del_char_device(struct tpm_chip *chip) /* Make the driver uncallable. */ down_write(&chip->ops_sem); + tpm2_shutdown(chip, TPM2_SU_CLEAR); chip->ops = NULL; up_write(&chip->ops_sem); } diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 5fc0e7c..e422665 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -773,7 +773,6 @@ void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type) dev_warn(&chip->dev, "transmit returned %d while stopping the TPM", rc); } -EXPORT_SYMBOL_GPL(tpm2_shutdown); /* * tpm2_calc_ordinal_duration() - maximum duration for a command diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index a12b319..ec734de 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -344,9 +344,6 @@ static int crb_acpi_remove(struct acpi_device *device) struct device *dev = &device->dev; struct tpm_chip *chip = dev_get_drvdata(dev); - if (chip->flags & TPM_CHIP_FLAG_TPM2) - tpm2_shutdown(chip, TPM2_SU_CLEAR); - tpm_chip_unregister(chip); return 0; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 18236f6..0b0d44f 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -653,9 +653,6 @@ static void tpm_tis_remove(struct tpm_chip *chip) void __iomem *reg = chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality); - if (chip->flags & TPM_CHIP_FLAG_TPM2) - tpm2_shutdown(chip, TPM2_SU_CLEAR); - iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(reg), reg); release_locality(chip, chip->vendor.locality, 1); } -- cgit v0.10.2 From 14ddfbf488a0223b19abf7e4634e6e676a91a12d Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 15 Mar 2016 21:41:40 +0200 Subject: tpm_crb: drop struct resource res from struct crb_priv The iomem resource is needed only temporarily so it is better to pass it on instead of storing it permanently. Named the variable as io_res so that the code better documents itself. Signed-off-by: Jarkko Sakkinen Reviewed-by: Stefan Berger diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index ec734de..733cd0e 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -77,7 +77,6 @@ enum crb_flags { struct crb_priv { unsigned int flags; - struct resource res; void __iomem *iobase; struct crb_control_area __iomem *cca; u8 __iomem *cmd; @@ -224,19 +223,19 @@ static int crb_init(struct acpi_device *device, struct crb_priv *priv) static int crb_check_resource(struct acpi_resource *ares, void *data) { - struct crb_priv *priv = data; + struct resource *io_res = data; struct resource res; if (acpi_dev_resource_memory(ares, &res)) { - priv->res = res; - priv->res.name = NULL; + *io_res = res; + io_res->name = NULL; } return 1; } static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, - u64 start, u32 size) + struct resource *io_res, u64 start, u32 size) { struct resource new_res = { .start = start, @@ -248,50 +247,54 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, if (start != new_res.start) return ERR_PTR(-EINVAL); - if (!resource_contains(&priv->res, &new_res)) + if (!resource_contains(io_res, &new_res)) return devm_ioremap_resource(dev, &new_res); - return priv->iobase + (new_res.start - priv->res.start); + return priv->iobase + (new_res.start - io_res->start); } static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, struct acpi_table_tpm2 *buf) { struct list_head resources; + struct resource io_res; struct device *dev = &device->dev; u64 pa; int ret; INIT_LIST_HEAD(&resources); ret = acpi_dev_get_resources(device, &resources, crb_check_resource, - priv); + &io_res); if (ret < 0) return ret; acpi_dev_free_resource_list(&resources); - if (resource_type(&priv->res) != IORESOURCE_MEM) { + if (resource_type(&io_res) != IORESOURCE_MEM) { dev_err(dev, FW_BUG "TPM2 ACPI table does not define a memory resource\n"); return -EINVAL; } - priv->iobase = devm_ioremap_resource(dev, &priv->res); + priv->iobase = devm_ioremap_resource(dev, &io_res); if (IS_ERR(priv->iobase)) return PTR_ERR(priv->iobase); - priv->cca = crb_map_res(dev, priv, buf->control_address, 0x1000); + priv->cca = crb_map_res(dev, priv, &io_res, buf->control_address, + 0x1000); if (IS_ERR(priv->cca)) return PTR_ERR(priv->cca); pa = ((u64) ioread32(&priv->cca->cmd_pa_high) << 32) | (u64) ioread32(&priv->cca->cmd_pa_low); - priv->cmd = crb_map_res(dev, priv, pa, ioread32(&priv->cca->cmd_size)); + priv->cmd = crb_map_res(dev, priv, &io_res, pa, + ioread32(&priv->cca->cmd_size)); if (IS_ERR(priv->cmd)) return PTR_ERR(priv->cmd); memcpy_fromio(&pa, &priv->cca->rsp_pa, 8); pa = le64_to_cpu(pa); - priv->rsp = crb_map_res(dev, priv, pa, ioread32(&priv->cca->rsp_size)); + priv->rsp = crb_map_res(dev, priv, &io_res, pa, + ioread32(&priv->cca->rsp_size)); return PTR_ERR_OR_ZERO(priv->rsp); } -- cgit v0.10.2 From 32da5633dcb9115d477f1ce8893ac4da087f56da Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Mar 2016 08:55:32 +0100 Subject: tpm/st33zp24/i2c: Drop two useless checks in ACPI probe path When st33zp24_i2c_acpi_request_resources() gets called we already know that the entries in ->acpi_match_table have matched ACPI ID of the device. In addition I2C client pointer cannot be NULL in any case (otherwise I2C core would not call ->probe() for the driver in the first place). Drop the two useless checks from the driver. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index f179aad..f8e8123 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -113,19 +113,8 @@ static const struct st33zp24_phy_ops i2c_phy_ops = { static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client) { struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); - const struct acpi_device_id *id; struct gpio_desc *gpiod_lpcpd; - struct device *dev; - - if (!client) - return -EINVAL; - - dev = &client->dev; - - /* Match the struct device against a given list of ACPI IDs */ - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return -ENODEV; + struct device *dev = &client->dev; /* Get LPCPD GPIO from ACPI */ gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, -- cgit v0.10.2 From 20ab99f89d574ba3c3944a6682bec5a6e0c0dcd9 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Mar 2016 08:55:33 +0100 Subject: tpm/st33zp24/spi: Drop two useless checks in ACPI probe path When st33zp24_spi_acpi_request_resources() gets called we already know that the entries in ->acpi_match_table have matched ACPI ID of the device. In addition spi_device pointer cannot be NULL in any case (otherwise I2C core would not call ->probe() for the driver in the first place). Drop the two useless checks from the driver. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 6be51ac..608dbc6 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -232,19 +232,8 @@ static const struct st33zp24_phy_ops spi_phy_ops = { static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev) { struct st33zp24_spi_phy *phy = spi_get_drvdata(spi_dev); - const struct acpi_device_id *id; struct gpio_desc *gpiod_lpcpd; - struct device *dev; - - if (!spi_dev) - return -EINVAL; - - dev = &spi_dev->dev; - - /* Match the struct device against a given list of ACPI IDs */ - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return -ENODEV; + struct device *dev = &spi_dev->dev; /* Get LPCPD GPIO from ACPI */ gpiod_lpcpd = devm_gpiod_get_index(dev, "TPM IO LPCPD", 1, -- cgit v0.10.2 From ddab0e34288a233ffd8763aa7c938d61ab128c0b Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Mar 2016 08:55:34 +0100 Subject: tpm/st33zp24: Remove unneeded tpm_reg in get_burstcount We can get rid of tpm_reg variable in get_burstcount. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index 944d279..9e91ca7 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -208,21 +208,21 @@ static int get_burstcount(struct tpm_chip *chip) { unsigned long stop; int burstcnt, status; - u8 tpm_reg, temp; + u8 temp; struct st33zp24_dev *tpm_dev; tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); stop = jiffies + chip->vendor.timeout_d; do { - tpm_reg = TPM_STS + 1; - status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1); + status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 1, + &temp, 1); if (status < 0) return -EBUSY; - tpm_reg = TPM_STS + 2; burstcnt = temp; - status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1); + status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 2, + &temp, 1); if (status < 0) return -EBUSY; -- cgit v0.10.2 From 422eac3f7deae34dbaffd08e03e27f37a5394a56 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 19 Apr 2016 12:54:18 +0300 Subject: tpm_crb: fix mapping of the buffers On my Lenovo x250 the following situation occurs: [18697.813871] tpm_crb MSFT0101:00: can't request region for resource [mem 0xacdff080-0xacdfffff] The mapping of the control area overlaps the mapping of the command buffer. The control area is mapped over page, which is not right. It should mapped over sizeof(struct crb_control_area). Fixing this issue unmasks another issue. Command and response buffers can overlap and they do interleave on this machine. According to the PTP specification the overlapping means that they are mapped to the same buffer. The commit has been also on a Haswell NUC where things worked before applying this fix so that the both code paths for response buffer initialization are tested. Cc: stable@vger.kernel.org Fixes: 1bd047be37d9 ("tpm_crb: Use devm_ioremap_resource") Signed-off-by: Jarkko Sakkinen Reviewed-by: Jason Gunthorpe diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 733cd0e..5afe684 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -259,7 +259,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, struct list_head resources; struct resource io_res; struct device *dev = &device->dev; - u64 pa; + u64 cmd_pa; + u32 cmd_size; + u64 rsp_pa; + u32 rsp_size; int ret; INIT_LIST_HEAD(&resources); @@ -280,22 +283,36 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, return PTR_ERR(priv->iobase); priv->cca = crb_map_res(dev, priv, &io_res, buf->control_address, - 0x1000); + sizeof(struct crb_control_area)); if (IS_ERR(priv->cca)) return PTR_ERR(priv->cca); - pa = ((u64) ioread32(&priv->cca->cmd_pa_high) << 32) | - (u64) ioread32(&priv->cca->cmd_pa_low); - priv->cmd = crb_map_res(dev, priv, &io_res, pa, - ioread32(&priv->cca->cmd_size)); + cmd_pa = ((u64) ioread32(&priv->cca->cmd_pa_high) << 32) | + (u64) ioread32(&priv->cca->cmd_pa_low); + cmd_size = ioread32(&priv->cca->cmd_size); + priv->cmd = crb_map_res(dev, priv, &io_res, cmd_pa, cmd_size); if (IS_ERR(priv->cmd)) return PTR_ERR(priv->cmd); - memcpy_fromio(&pa, &priv->cca->rsp_pa, 8); - pa = le64_to_cpu(pa); - priv->rsp = crb_map_res(dev, priv, &io_res, pa, - ioread32(&priv->cca->rsp_size)); - return PTR_ERR_OR_ZERO(priv->rsp); + memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8); + rsp_pa = le64_to_cpu(rsp_pa); + rsp_size = ioread32(&priv->cca->rsp_size); + + if (cmd_pa != rsp_pa) { + priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size); + return PTR_ERR_OR_ZERO(priv->rsp); + } + + /* According to the PTP specification, overlapping command and response + * buffer sizes must be identical. + */ + if (cmd_size != rsp_size) { + dev_err(dev, FW_BUG "overlapping command and response buffer sizes are not identical"); + return -EINVAL; + } + + priv->rsp = priv->cmd; + return 0; } static int crb_acpi_add(struct acpi_device *device) -- cgit v0.10.2 From c0dff1f4519e3b3c322415b822724d4207156859 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Mon, 25 Apr 2016 12:20:07 +0300 Subject: tpm: check for TPM_CHIP_FLAG_TPM2 before calling tpm2_shutdown() Fixes: 20e0152393b41 ("tpm: fix crash in tpm_tis deinitialization") Signed-off-by: Jarkko Sakkinen Reported-by: Stefan Berger Tested-by: Stefan Berger Reviewed-By: Jason Gunthorpe diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 5bc530c..2ea2f15 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -269,7 +269,8 @@ static void tpm_del_char_device(struct tpm_chip *chip) /* Make the driver uncallable. */ down_write(&chip->ops_sem); - tpm2_shutdown(chip, TPM2_SU_CLEAR); + if (chip->flags & TPM_CHIP_FLAG_TPM2) + tpm2_shutdown(chip, TPM2_SU_CLEAR); chip->ops = NULL; up_write(&chip->ops_sem); } -- cgit v0.10.2 From ec5fd99d3677efe5eebef3960168a1f9889322b9 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 22 Mar 2016 06:20:09 +0200 Subject: tpm: drop int_queue from tpm_vendor_specific Drop field int_queue from tpm_vendor_specific as it is used only by tpm_tis. Probably all of the fields should be eventually dropped and moved to the private structures of different drivers but it is better to do this one step at a time in order not to break anything. Signed-off-by: Jarkko Sakkinen Reviewed-by: Jason Gunthorpe diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 928b47f..f5e1846 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -148,7 +148,6 @@ struct tpm_vendor_specific { void *priv; wait_queue_head_t read_queue; - wait_queue_head_t int_queue; u16 manufacturer_id; }; diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index d61d43f..a43b5f3 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -540,7 +540,6 @@ static int i2c_nuvoton_probe(struct i2c_client *client, return -ENOMEM; init_waitqueue_head(&chip->vendor.read_queue); - init_waitqueue_head(&chip->vendor.int_queue); /* Default timeouts */ chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 0b0d44f..9c3a52d 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -95,6 +95,7 @@ struct tpm_info { struct priv_data { bool irq_tested; + wait_queue_head_t int_queue; }; #if defined(CONFIG_PNP) && defined(CONFIG_ACPI) @@ -157,6 +158,7 @@ static void release_locality(struct tpm_chip *chip, int l, int force) static int request_locality(struct tpm_chip *chip, int l) { + struct priv_data *priv = chip->vendor.priv; unsigned long stop, timeout; long rc; @@ -173,7 +175,7 @@ again: timeout = stop - jiffies; if ((long)timeout <= 0) return -1; - rc = wait_event_interruptible_timeout(chip->vendor.int_queue, + rc = wait_event_interruptible_timeout(priv->int_queue, (check_locality (chip, l) >= 0), timeout); @@ -249,6 +251,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) { + struct priv_data *priv = chip->vendor.priv; int size = 0; int expected, status; @@ -279,7 +282,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, false); + &priv->int_queue, false); status = tpm_tis_status(chip); if (status & TPM_STS_DATA_AVAIL) { /* retry? */ dev_err(&chip->dev, "Error left over data\n"); @@ -304,6 +307,7 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)"); */ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) { + struct priv_data *priv = chip->vendor.priv; int rc, status, burstcnt; size_t count = 0; @@ -315,7 +319,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, false) < 0) { + &priv->int_queue, false) < 0) { rc = -ETIME; goto out_err; } @@ -330,7 +334,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, false); + &priv->int_queue, false); status = tpm_tis_status(chip); if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) { rc = -EIO; @@ -342,7 +346,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, false); + &priv->int_queue, false); status = tpm_tis_status(chip); if ((status & TPM_STS_DATA_EXPECT) != 0) { rc = -EIO; @@ -537,6 +541,7 @@ static const struct tpm_class_ops tpm_tis = { static irqreturn_t tis_int_handler(int dummy, void *dev_id) { struct tpm_chip *chip = dev_id; + struct priv_data *priv = chip->vendor.priv; u32 interrupt; int i; @@ -556,7 +561,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) if (interrupt & (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT | TPM_INTF_CMD_READY_INT)) - wake_up_interruptible(&chip->vendor.int_queue); + wake_up_interruptible(&priv->int_queue); /* Clear interrupts handled with TPM_EOI */ iowrite32(interrupt, @@ -768,7 +773,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, /* INTERRUPT Setup */ init_waitqueue_head(&chip->vendor.read_queue); - init_waitqueue_head(&chip->vendor.int_queue); + init_waitqueue_head(&priv->int_queue); if (interrupts && tpm_info->irq != -1) { if (tpm_info->irq) { tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, -- cgit v0.10.2 From 23d06ff700f546a6a1d63c85ade095dbd75b7ab5 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Wed, 23 Mar 2016 07:10:22 +0200 Subject: tpm: drop tpm_atmel specific fields from tpm_vendor_specific Introduced a private struct tpm_atmel_priv that contains the variables have_region and region_size that were previously located in struct tpm_vendor_specific. These fields were only used by tpm_atmel. Signed-off-by: Jarkko Sakkinen Reviewed-by: Jason Gunthorpe diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index f5e1846..c5f8d9e 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -136,9 +136,6 @@ struct tpm_vendor_specific { int irq; - int region_size; - int have_region; - struct list_head list; int locality; unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */ diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index a48a878..0267d85 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -136,12 +136,13 @@ static struct platform_device *pdev; static void atml_plat_remove(void) { struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); + struct tpm_atmel_priv *priv = chip->vendor.priv; if (chip) { tpm_chip_unregister(chip); - if (chip->vendor.have_region) + if (priv->have_region) atmel_release_region(chip->vendor.base, - chip->vendor.region_size); + priv->region_size); atmel_put_base_addr(chip->vendor.iobase); platform_device_unregister(pdev); } @@ -163,6 +164,7 @@ static int __init init_atmel(void) int have_region, region_size; unsigned long base; struct tpm_chip *chip; + struct tpm_atmel_priv *priv; rc = platform_driver_register(&atml_drv); if (rc) @@ -183,6 +185,15 @@ static int __init init_atmel(void) goto err_rel_reg; } + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + rc = -ENOMEM; + goto err_unreg_dev; + } + + priv->have_region = have_region; + priv->region_size = region_size; + chip = tpmm_chip_alloc(&pdev->dev, &tpm_atmel); if (IS_ERR(chip)) { rc = PTR_ERR(chip); @@ -191,8 +202,7 @@ static int __init init_atmel(void) chip->vendor.iobase = iobase; chip->vendor.base = base; - chip->vendor.have_region = have_region; - chip->vendor.region_size = region_size; + chip->vendor.priv = priv; rc = tpm_chip_register(chip); if (rc) diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h index 6c831f9..3fd929d 100644 --- a/drivers/char/tpm/tpm_atmel.h +++ b/drivers/char/tpm/tpm_atmel.h @@ -22,6 +22,11 @@ * */ +struct tpm_atmel_priv { + int region_size; + int have_region; +}; + #ifdef CONFIG_PPC64 #include -- cgit v0.10.2 From d4956524f1b0ae010c6d6cc1369a7fae63b52165 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Wed, 23 Mar 2016 07:31:56 +0200 Subject: tpm: drop manufacturer_id from struct tpm_vendor_specific Dropped manufacturer_id from struct tpm_vendor_specific and redeclared it in the private struct priv_data that tpm_tis uses because the field is only used tpm_tis. Signed-off-by: Jarkko Sakkinen Reviewed-by: Jason Gunthorpe diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index c5f8d9e..6586b6a 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -145,8 +145,6 @@ struct tpm_vendor_specific { void *priv; wait_queue_head_t read_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 9c3a52d..bf9d331 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -94,6 +94,7 @@ struct tpm_info { #define TPM_RID(l) (0x0F04 | ((l) << 12)) struct priv_data { + u16 manufacturer_id; bool irq_tested; wait_queue_head_t int_queue; }; @@ -516,7 +517,9 @@ out: static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status) { - switch (chip->vendor.manufacturer_id) { + struct priv_data *priv = chip->vendor.priv; + + switch (priv->manufacturer_id) { case TPM_VID_WINBOND: return ((status == TPM_STS_VALID) || (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY))); @@ -717,7 +720,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, goto out_err; vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); - chip->vendor.manufacturer_id = vendor; + priv->manufacturer_id = vendor; dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n", (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2", -- cgit v0.10.2 From ee1779840d093ebf6893c97115422fb5171b54d7 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Wed, 23 Mar 2016 08:16:09 +0200 Subject: tpm: drop 'base' from struct tpm_vendor_specific Dropped the field 'base' from struct tpm_vendor_specific and migrated it to the private structures of tpm_atmel and tpm_nsc. Signed-off-by: Jarkko Sakkinen Reviewed-by: Jason Gunthorpe diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 6586b6a..b9a5b25 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -132,7 +132,6 @@ struct tpm_chip; struct tpm_vendor_specific { void __iomem *iobase; /* ioremapped address */ - unsigned long base; /* TPM base address */ int irq; diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index 0267d85..68d5c09 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -141,8 +141,7 @@ static void atml_plat_remove(void) if (chip) { tpm_chip_unregister(chip); if (priv->have_region) - atmel_release_region(chip->vendor.base, - priv->region_size); + atmel_release_region(priv->base, priv->region_size); atmel_put_base_addr(chip->vendor.iobase); platform_device_unregister(pdev); } @@ -191,6 +190,7 @@ static int __init init_atmel(void) goto err_unreg_dev; } + priv->base = base; priv->have_region = have_region; priv->region_size = region_size; @@ -201,7 +201,6 @@ static int __init init_atmel(void) } chip->vendor.iobase = iobase; - chip->vendor.base = base; chip->vendor.priv = priv; rc = tpm_chip_register(chip); diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h index 3fd929d..bced678 100644 --- a/drivers/char/tpm/tpm_atmel.h +++ b/drivers/char/tpm/tpm_atmel.h @@ -25,8 +25,14 @@ struct tpm_atmel_priv { int region_size; int have_region; + unsigned long base; }; +static inline struct tpm_atmel_priv *atmel_get_priv(struct tpm_chip *chip) +{ + return chip->vendor.priv; +} + #ifdef CONFIG_PPC64 #include @@ -83,8 +89,9 @@ static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) return ioremap(*base, *region_size); } #else -#define atmel_getb(chip, offset) inb(chip->vendor->base + offset) -#define atmel_putb(val, chip, offset) outb(val, chip->vendor->base + offset) +#define atmel_getb(chip, offset) inb(atmel_get_priv(chip)->base + offset) +#define atmel_putb(val, chip, offset) \ + outb(val, atmel_get_priv(chip)->base + offset) #define atmel_request_region request_region #define atmel_release_region release_region /* Atmel definitions */ diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 766370b..07163a4 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -64,6 +64,16 @@ enum tpm_nsc_cmd_mode { NSC_COMMAND_EOC = 0x03, NSC_COMMAND_CANCEL = 0x22 }; + +struct tpm_nsc_priv { + unsigned long base; +}; + +static inline struct tpm_nsc_priv *nsc_get_priv(struct tpm_chip *chip) +{ + return chip->vendor.priv; +} + /* * Wait for a certain status to appear */ @@ -72,7 +82,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) unsigned long stop; /* status immediately available check */ - *data = inb(chip->vendor.base + NSC_STATUS); + *data = inb(nsc_get_priv(chip)->base + NSC_STATUS); if ((*data & mask) == val) return 0; @@ -80,7 +90,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) stop = jiffies + 10 * HZ; do { msleep(TPM_TIMEOUT); - *data = inb(chip->vendor.base + 1); + *data = inb(nsc_get_priv(chip)->base + 1); if ((*data & mask) == val) return 0; } @@ -95,9 +105,9 @@ static int nsc_wait_for_ready(struct tpm_chip *chip) unsigned long stop; /* status immediately available check */ - status = inb(chip->vendor.base + NSC_STATUS); + status = inb(nsc_get_priv(chip)->base + NSC_STATUS); if (status & NSC_STATUS_OBF) - status = inb(chip->vendor.base + NSC_DATA); + status = inb(nsc_get_priv(chip)->base + NSC_DATA); if (status & NSC_STATUS_RDY) return 0; @@ -105,9 +115,9 @@ static int nsc_wait_for_ready(struct tpm_chip *chip) stop = jiffies + 100; do { msleep(TPM_TIMEOUT); - status = inb(chip->vendor.base + NSC_STATUS); + status = inb(nsc_get_priv(chip)->base + NSC_STATUS); if (status & NSC_STATUS_OBF) - status = inb(chip->vendor.base + NSC_DATA); + status = inb(nsc_get_priv(chip)->base + NSC_DATA); if (status & NSC_STATUS_RDY) return 0; } @@ -132,8 +142,9 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) dev_err(&chip->dev, "F0 timeout\n"); return -EIO; } - if ((data = - inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_NORMAL) { + + data = inb(nsc_get_priv(chip)->base + NSC_DATA); + if (data != NSC_COMMAND_NORMAL) { dev_err(&chip->dev, "not in normal mode (0x%x)\n", data); return -EIO; @@ -149,7 +160,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) } if (data & NSC_STATUS_F0) break; - *p = inb(chip->vendor.base + NSC_DATA); + *p = inb(nsc_get_priv(chip)->base + NSC_DATA); } if ((data & NSC_STATUS_F0) == 0 && @@ -157,7 +168,9 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) dev_err(&chip->dev, "F0 not set\n"); return -EIO; } - if ((data = inb(chip->vendor.base + NSC_DATA)) != NSC_COMMAND_EOC) { + + data = inb(nsc_get_priv(chip)->base + NSC_DATA); + if (data != NSC_COMMAND_EOC) { dev_err(&chip->dev, "expected end of command(0x%x)\n", data); return -EIO; @@ -183,7 +196,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) * fix it. Not sure why this is needed, we followed the flow * chart in the manual to the letter. */ - outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND); + outb(NSC_COMMAND_CANCEL, nsc_get_priv(chip)->base + NSC_COMMAND); if (nsc_wait_for_ready(chip) != 0) return -EIO; @@ -193,7 +206,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; } - outb(NSC_COMMAND_NORMAL, chip->vendor.base + NSC_COMMAND); + outb(NSC_COMMAND_NORMAL, nsc_get_priv(chip)->base + NSC_COMMAND); if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) { dev_err(&chip->dev, "IBR timeout\n"); return -EIO; @@ -205,26 +218,26 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) "IBF timeout (while writing data)\n"); return -EIO; } - outb(buf[i], chip->vendor.base + NSC_DATA); + outb(buf[i], nsc_get_priv(chip)->base + NSC_DATA); } if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { dev_err(&chip->dev, "IBF timeout\n"); return -EIO; } - outb(NSC_COMMAND_EOC, chip->vendor.base + NSC_COMMAND); + outb(NSC_COMMAND_EOC, nsc_get_priv(chip)->base + NSC_COMMAND); return count; } static void tpm_nsc_cancel(struct tpm_chip *chip) { - outb(NSC_COMMAND_CANCEL, chip->vendor.base + NSC_COMMAND); + outb(NSC_COMMAND_CANCEL, nsc_get_priv(chip)->base + NSC_COMMAND); } static u8 tpm_nsc_status(struct tpm_chip *chip) { - return inb(chip->vendor.base + NSC_STATUS); + return inb(nsc_get_priv(chip)->base + NSC_STATUS); } static bool tpm_nsc_req_canceled(struct tpm_chip *chip, u8 status) @@ -249,7 +262,7 @@ static void tpm_nsc_remove(struct device *dev) struct tpm_chip *chip = dev_get_drvdata(dev); tpm_chip_unregister(chip); - release_region(chip->vendor.base, 2); + release_region(nsc_get_priv(chip)->base, 2); } static SIMPLE_DEV_PM_OPS(tpm_nsc_pm, tpm_pm_suspend, tpm_pm_resume); @@ -268,6 +281,7 @@ static int __init init_nsc(void) int nscAddrBase = TPM_ADDR; struct tpm_chip *chip; unsigned long base; + struct tpm_nsc_priv *priv; /* verify that it is a National part (SID) */ if (tpm_read_index(TPM_ADDR, NSC_SID_INDEX) != 0xEF) { @@ -301,6 +315,14 @@ static int __init init_nsc(void) if ((rc = platform_device_add(pdev)) < 0) goto err_put_dev; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + rc = -ENOMEM; + goto err_del_dev; + } + + priv->base = base; + if (request_region(base, 2, "tpm_nsc0") == NULL ) { rc = -EBUSY; goto err_del_dev; @@ -312,6 +334,8 @@ static int __init init_nsc(void) goto err_rel_reg; } + chip->vendor.priv = priv; + rc = tpm_chip_register(chip); if (rc) goto err_rel_reg; @@ -349,8 +373,6 @@ static int __init init_nsc(void) "NSC TPM revision %d\n", tpm_read_index(nscAddrBase, 0x27) & 0x1F); - chip->vendor.base = base; - return 0; err_rel_reg: -- cgit v0.10.2 From c5b65fa152a8e62969a97d04929c1ea73b77ba37 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Wed, 23 Mar 2016 08:23:39 +0200 Subject: tpm: drop the field 'time_expired' from struct tpm_chip Removed the field because it is not used for anything. Signed-off-by: Jarkko Sakkinen Reviewed-by: Jason Gunthorpe diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index b9a5b25..7317bec 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -174,7 +174,6 @@ struct tpm_chip { int dev_num; /* /dev/tpm# */ unsigned long is_open; /* only one allowed */ - int time_expired; struct mutex tpm_mutex; /* tpm is processing */ -- cgit v0.10.2 From 003d305d84e911d3f434eabf9e9656e75203f077 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 23 Mar 2016 21:17:17 +0100 Subject: tpm: drop list from struct tpm_vendor_specific Dropped list from struct tpm_vendor_specific as it is not used in any place. It is initialized in tpm_i2c_infineon but not used at all in the code. Signed-off-by: Christophe Ricard Reviewed-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 7317bec..cd780c7 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -135,7 +135,6 @@ struct tpm_vendor_specific { int irq; - struct list_head list; int locality; unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */ bool timeout_adjusted; diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index f2aa99e..e74f1c1 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -619,7 +619,6 @@ static int tpm_tis_i2c_init(struct device *dev) dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16); - INIT_LIST_HEAD(&chip->vendor.list); tpm_dev.chip = chip; tpm_get_timeouts(chip); -- cgit v0.10.2 From 4eea703caaac025dd422bd3916b14523977f4332 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 31 Mar 2016 22:56:55 +0200 Subject: tpm: drop 'iobase' from struct tpm_vendor_specific Dropped the field 'iobase' from struct tpm_vendor_specific and migrated it to the private structures of tpm_atmel and tpm_tis. Signed-off-by: Christophe Ricard Reviewed-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index cd780c7..357ac14 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -131,8 +131,6 @@ enum tpm2_startup_types { struct tpm_chip; struct tpm_vendor_specific { - void __iomem *iobase; /* ioremapped address */ - int irq; int locality; diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index 68d5c09..b769299 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -37,6 +37,7 @@ enum tpm_atmel_read_status { static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) { + struct tpm_atmel_priv *priv = chip->vendor.priv; u8 status, *hdr = buf; u32 size; int i; @@ -47,12 +48,12 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) return -EIO; for (i = 0; i < 6; i++) { - status = ioread8(chip->vendor.iobase + 1); + status = ioread8(priv->iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { dev_err(&chip->dev, "error reading header\n"); return -EIO; } - *buf++ = ioread8(chip->vendor.iobase); + *buf++ = ioread8(priv->iobase); } /* size of the data received */ @@ -63,7 +64,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) dev_err(&chip->dev, "Recv size(%d) less than available space\n", size); for (; i < size; i++) { /* clear the waiting data anyway */ - status = ioread8(chip->vendor.iobase + 1); + status = ioread8(priv->iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { dev_err(&chip->dev, "error reading data\n"); return -EIO; @@ -74,16 +75,16 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) /* read all the data available */ for (; i < size; i++) { - status = ioread8(chip->vendor.iobase + 1); + status = ioread8(priv->iobase + 1); if ((status & ATML_STATUS_DATA_AVAIL) == 0) { dev_err(&chip->dev, "error reading data\n"); return -EIO; } - *buf++ = ioread8(chip->vendor.iobase); + *buf++ = ioread8(priv->iobase); } /* make sure data available is gone */ - status = ioread8(chip->vendor.iobase + 1); + status = ioread8(priv->iobase + 1); if (status & ATML_STATUS_DATA_AVAIL) { dev_err(&chip->dev, "data available is stuck\n"); @@ -95,12 +96,13 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) { + struct tpm_atmel_priv *priv = chip->vendor.priv; int i; dev_dbg(&chip->dev, "tpm_atml_send:\n"); for (i = 0; i < count; i++) { dev_dbg(&chip->dev, "%d 0x%x(%d)\n", i, buf[i], buf[i]); - iowrite8(buf[i], chip->vendor.iobase); + iowrite8(buf[i], priv->iobase); } return count; @@ -108,12 +110,16 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) static void tpm_atml_cancel(struct tpm_chip *chip) { - iowrite8(ATML_STATUS_ABORT, chip->vendor.iobase + 1); + struct tpm_atmel_priv *priv = chip->vendor.priv; + + iowrite8(ATML_STATUS_ABORT, priv->iobase + 1); } static u8 tpm_atml_status(struct tpm_chip *chip) { - return ioread8(chip->vendor.iobase + 1); + struct tpm_atmel_priv *priv = chip->vendor.priv; + + return ioread8(priv->iobase + 1); } static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status) @@ -142,7 +148,7 @@ static void atml_plat_remove(void) tpm_chip_unregister(chip); if (priv->have_region) atmel_release_region(priv->base, priv->region_size); - atmel_put_base_addr(chip->vendor.iobase); + atmel_put_base_addr(priv->iobase); platform_device_unregister(pdev); } } @@ -190,6 +196,7 @@ static int __init init_atmel(void) goto err_unreg_dev; } + priv->iobase = iobase; priv->base = base; priv->have_region = have_region; priv->region_size = region_size; @@ -200,7 +207,6 @@ static int __init init_atmel(void) goto err_unreg_dev; } - chip->vendor.iobase = iobase; chip->vendor.priv = priv; rc = tpm_chip_register(chip); diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h index bced678..1938c0b 100644 --- a/drivers/char/tpm/tpm_atmel.h +++ b/drivers/char/tpm/tpm_atmel.h @@ -26,6 +26,7 @@ struct tpm_atmel_priv { int region_size; int have_region; unsigned long base; + void __iomem *iobase; }; static inline struct tpm_atmel_priv *atmel_get_priv(struct tpm_chip *chip) @@ -37,8 +38,8 @@ static inline struct tpm_atmel_priv *atmel_get_priv(struct tpm_chip *chip) #include -#define atmel_getb(chip, offset) readb(chip->vendor->iobase + offset); -#define atmel_putb(val, chip, offset) writeb(val, chip->vendor->iobase + offset) +#define atmel_getb(priv, offset) readb(priv->iobase + offset) +#define atmel_putb(val, priv, offset) writeb(val, priv->iobase + offset) #define atmel_request_region request_mem_region #define atmel_release_region release_mem_region diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index bf9d331..41c4b60 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -94,6 +94,7 @@ struct tpm_info { #define TPM_RID(l) (0x0F04 | ((l) << 12)) struct priv_data { + void __iomem *iobase; u16 manufacturer_id; bool irq_tested; wait_queue_head_t int_queue; @@ -128,9 +129,10 @@ static inline int is_itpm(struct acpi_device *dev) * correct values in the other bits.' */ static int wait_startup(struct tpm_chip *chip, int l) { + struct priv_data *priv = chip->vendor.priv; unsigned long stop = jiffies + chip->vendor.timeout_a; do { - if (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & + if (ioread8(priv->iobase + TPM_ACCESS(l)) & TPM_ACCESS_VALID) return 0; msleep(TPM_TIMEOUT); @@ -140,7 +142,9 @@ static int wait_startup(struct tpm_chip *chip, int l) static int check_locality(struct tpm_chip *chip, int l) { - if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & + struct priv_data *priv = chip->vendor.priv; + + if ((ioread8(priv->iobase + TPM_ACCESS(l)) & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) return chip->vendor.locality = l; @@ -150,11 +154,13 @@ static int check_locality(struct tpm_chip *chip, int l) static void release_locality(struct tpm_chip *chip, int l, int force) { - if (force || (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) & + struct priv_data *priv = chip->vendor.priv; + + if (force || (ioread8(priv->iobase + TPM_ACCESS(l)) & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) iowrite8(TPM_ACCESS_ACTIVE_LOCALITY, - chip->vendor.iobase + TPM_ACCESS(l)); + priv->iobase + TPM_ACCESS(l)); } static int request_locality(struct tpm_chip *chip, int l) @@ -167,7 +173,7 @@ static int request_locality(struct tpm_chip *chip, int l) return l; iowrite8(TPM_ACCESS_REQUEST_USE, - chip->vendor.iobase + TPM_ACCESS(l)); + priv->iobase + TPM_ACCESS(l)); stop = jiffies + chip->vendor.timeout_a; @@ -200,19 +206,24 @@ again: static u8 tpm_tis_status(struct tpm_chip *chip) { - return ioread8(chip->vendor.iobase + + struct priv_data *priv = chip->vendor.priv; + + return ioread8(priv->iobase + TPM_STS(chip->vendor.locality)); } static void tpm_tis_ready(struct tpm_chip *chip) { + struct priv_data *priv = chip->vendor.priv; + /* this causes the current command to be aborted */ iowrite8(TPM_STS_COMMAND_READY, - chip->vendor.iobase + TPM_STS(chip->vendor.locality)); + priv->iobase + TPM_STS(chip->vendor.locality)); } static int get_burstcount(struct tpm_chip *chip) { + struct priv_data *priv = chip->vendor.priv; unsigned long stop; int burstcnt; @@ -220,9 +231,9 @@ static int get_burstcount(struct tpm_chip *chip) /* which timeout value, spec has 2 answers (c & d) */ stop = jiffies + chip->vendor.timeout_d; do { - burstcnt = ioread8(chip->vendor.iobase + + burstcnt = ioread8(priv->iobase + TPM_STS(chip->vendor.locality) + 1); - burstcnt += ioread8(chip->vendor.iobase + + burstcnt += ioread8(priv->iobase + TPM_STS(chip->vendor.locality) + 2) << 8; if (burstcnt) @@ -234,6 +245,7 @@ static int get_burstcount(struct tpm_chip *chip) static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) { + struct priv_data *priv = chip->vendor.priv; int size = 0, burstcnt; while (size < count && wait_for_tpm_stat(chip, @@ -243,7 +255,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) == 0) { burstcnt = get_burstcount(chip); for (; burstcnt > 0 && size < count; burstcnt--) - buf[size++] = ioread8(chip->vendor.iobase + + buf[size++] = ioread8(priv->iobase + TPM_DATA_FIFO(chip->vendor. locality)); } @@ -329,7 +341,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) while (count < len - 1) { burstcnt = get_burstcount(chip); for (; burstcnt > 0 && count < len - 1; burstcnt--) { - iowrite8(buf[count], chip->vendor.iobase + + iowrite8(buf[count], priv->iobase + TPM_DATA_FIFO(chip->vendor.locality)); count++; } @@ -345,7 +357,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) /* write last byte */ iowrite8(buf[count], - chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality)); + priv->iobase + TPM_DATA_FIFO(chip->vendor.locality)); wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &priv->int_queue, false); status = tpm_tis_status(chip); @@ -364,15 +376,15 @@ out_err: static void disable_interrupts(struct tpm_chip *chip) { + struct priv_data *priv = chip->vendor.priv; u32 intmask; intmask = - ioread32(chip->vendor.iobase + + ioread32(priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); intmask &= ~TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, - chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor.locality)); + priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); devm_free_irq(&chip->dev, chip->vendor.irq, chip); chip->vendor.irq = 0; } @@ -384,6 +396,7 @@ static void disable_interrupts(struct tpm_chip *chip) */ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) { + struct priv_data *priv = chip->vendor.priv; int rc; u32 ordinal; unsigned long dur; @@ -394,7 +407,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) /* go and do it */ iowrite8(TPM_STS_GO, - chip->vendor.iobase + TPM_STS(chip->vendor.locality)); + priv->iobase + TPM_STS(chip->vendor.locality)); if (chip->vendor.irq) { ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); @@ -453,10 +466,11 @@ static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = { static bool tpm_tis_update_timeouts(struct tpm_chip *chip, unsigned long *timeout_cap) { + struct priv_data *priv = chip->vendor.priv; int i; u32 did_vid; - did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); + did_vid = ioread32(priv->iobase + TPM_DID_VID(0)); for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) { if (vendor_timeout_overrides[i].did_vid != did_vid) @@ -476,6 +490,7 @@ static bool tpm_tis_update_timeouts(struct tpm_chip *chip, */ static int probe_itpm(struct tpm_chip *chip) { + struct priv_data *priv = chip->vendor.priv; int rc = 0; u8 cmd_getticks[] = { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, @@ -483,7 +498,7 @@ static int probe_itpm(struct tpm_chip *chip) }; size_t len = sizeof(cmd_getticks); bool rem_itpm = itpm; - u16 vendor = ioread16(chip->vendor.iobase + TPM_DID_VID(0)); + u16 vendor = ioread16(priv->iobase + TPM_DID_VID(0)); /* probe only iTPMS */ if (vendor != TPM_VID_INTEL) @@ -548,7 +563,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) u32 interrupt; int i; - interrupt = ioread32(chip->vendor.iobase + + interrupt = ioread32(priv->iobase + TPM_INT_STATUS(chip->vendor.locality)); if (interrupt == 0) @@ -568,9 +583,9 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) /* Clear interrupts handled with TPM_EOI */ iowrite32(interrupt, - chip->vendor.iobase + + priv->iobase + TPM_INT_STATUS(chip->vendor.locality)); - ioread32(chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality)); + ioread32(priv->iobase + TPM_INT_STATUS(chip->vendor.locality)); return IRQ_HANDLED; } @@ -592,19 +607,19 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, } chip->vendor.irq = irq; - original_int_vec = ioread8(chip->vendor.iobase + + original_int_vec = ioread8(priv->iobase + TPM_INT_VECTOR(chip->vendor.locality)); iowrite8(irq, - chip->vendor.iobase + TPM_INT_VECTOR(chip->vendor.locality)); + priv->iobase + TPM_INT_VECTOR(chip->vendor.locality)); /* Clear all existing */ - iowrite32(ioread32(chip->vendor.iobase + + iowrite32(ioread32(priv->iobase + TPM_INT_STATUS(chip->vendor.locality)), - chip->vendor.iobase + TPM_INT_STATUS(chip->vendor.locality)); + priv->iobase + TPM_INT_STATUS(chip->vendor.locality)); /* Turn on */ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, - chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); + priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); priv->irq_tested = false; @@ -621,8 +636,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, */ if (!chip->vendor.irq) { iowrite8(original_int_vec, - chip->vendor.iobase + - TPM_INT_VECTOR(chip->vendor.locality)); + priv->iobase + TPM_INT_VECTOR(chip->vendor.locality)); return 1; } @@ -635,10 +649,11 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, */ static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask) { + struct priv_data *priv = chip->vendor.priv; u8 original_int_vec; int i; - original_int_vec = ioread8(chip->vendor.iobase + + original_int_vec = ioread8(priv->iobase + TPM_INT_VECTOR(chip->vendor.locality)); if (!original_int_vec) { @@ -658,7 +673,8 @@ MODULE_PARM_DESC(interrupts, "Enable interrupts"); static void tpm_tis_remove(struct tpm_chip *chip) { - void __iomem *reg = chip->vendor.iobase + + struct priv_data *priv = chip->vendor.priv; + void __iomem *reg = priv->iobase + TPM_INT_ENABLE(chip->vendor.locality); iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(reg), reg); @@ -686,9 +702,9 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, chip->acpi_dev_handle = acpi_dev_handle; #endif - chip->vendor.iobase = devm_ioremap_resource(dev, &tpm_info->res); - if (IS_ERR(chip->vendor.iobase)) - return PTR_ERR(chip->vendor.iobase); + priv->iobase = devm_ioremap_resource(dev, &tpm_info->res); + if (IS_ERR(priv->iobase)) + return PTR_ERR(priv->iobase); /* Maximum timeouts */ chip->vendor.timeout_a = TIS_TIMEOUT_A_MAX; @@ -702,13 +718,13 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, } /* Take control of the TPM's interrupt hardware and shut it off */ - intmask = ioread32(chip->vendor.iobase + + intmask = ioread32(priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; intmask &= ~TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, - chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); + priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); if (request_locality(chip, 0) != 0) { rc = -ENODEV; @@ -719,12 +735,12 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, if (rc) goto out_err; - vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); + vendor = ioread32(priv->iobase + TPM_DID_VID(0)); priv->manufacturer_id = vendor; dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n", (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2", - vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); + vendor >> 16, ioread8(priv->iobase + TPM_RID(0))); if (!itpm) { probe = probe_itpm(chip); @@ -741,7 +757,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, /* Figure out the capabilities */ intfcaps = - ioread32(chip->vendor.iobase + + ioread32(priv->iobase + TPM_INTF_CAPS(chip->vendor.locality)); dev_dbg(dev, "TPM interface capabilities (0x%x):\n", intfcaps); @@ -820,23 +836,23 @@ out_err: #ifdef CONFIG_PM_SLEEP static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) { + struct priv_data *priv = chip->vendor.priv; u32 intmask; /* reenable interrupts that device may have lost or BIOS/firmware may have disabled */ - iowrite8(chip->vendor.irq, chip->vendor.iobase + + iowrite8(chip->vendor.irq, priv->iobase + TPM_INT_VECTOR(chip->vendor.locality)); intmask = - ioread32(chip->vendor.iobase + - TPM_INT_ENABLE(chip->vendor.locality)); + ioread32(priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, - chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); + priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); } static int tpm_tis_resume(struct device *dev) -- cgit v0.10.2 From 570a36097f302c0bae4fb5478f1287b9b3626155 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 31 Mar 2016 22:56:56 +0200 Subject: tpm: drop 'irq' from struct tpm_vendor_specific Dropped the field 'irq' from struct tpm_vendor_specific and make it available to the various private structures in the drivers using irqs. A dedicated flag TPM_CHIP_FLAG_IRQ is added for the upper layers. In st33zp24, struct st33zp24_dev declaration is moved to st33zp24.h in order to make accessible irq from other phy's(i2c, spi). In tpm_i2c_nuvoton, chip->vendor.priv is not directly allocated. We can access irq field from priv_data in a cleaner way. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index 9e91ca7..f4a44ad 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -73,14 +73,6 @@ enum tis_defaults { TIS_LONG_TIMEOUT = 2000, }; -struct st33zp24_dev { - struct tpm_chip *chip; - void *phy_id; - const struct st33zp24_phy_ops *ops; - u32 intrs; - int io_lpcpd; -}; - /* * clear_interruption clear the pending interrupt. * @param: tpm_dev, the tpm device device. @@ -288,10 +280,10 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, stop = jiffies + timeout; - if (chip->vendor.irq) { + if (chip->flags & TPM_CHIP_FLAG_IRQ) { cur_intrs = tpm_dev->intrs; clear_interruption(tpm_dev); - enable_irq(chip->vendor.irq); + enable_irq(tpm_dev->irq); do { if (ret == -ERESTARTSYS && freezing(current)) @@ -314,7 +306,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, } } while (ret == -ERESTARTSYS && freezing(current)); - disable_irq_nosync(chip->vendor.irq); + disable_irq_nosync(tpm_dev->irq); } else { do { @@ -376,7 +368,7 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) tpm_dev->intrs++; wake_up_interruptible(&chip->vendor.read_queue); - disable_irq_nosync(chip->vendor.irq); + disable_irq_nosync(tpm_dev->irq); return IRQ_HANDLED; } /* tpm_ioserirq_handler() */ @@ -456,7 +448,7 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, if (ret < 0) goto out_err; - if (chip->vendor.irq) { + if (chip->flags & TPM_CHIP_FLAG_IRQ) { ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); ret = wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, @@ -611,9 +603,10 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, if (ret < 0) goto _tpm_clean_answer; - chip->vendor.irq = irq; + tpm_dev->irq = irq; + chip->flags |= TPM_CHIP_FLAG_IRQ; - disable_irq_nosync(chip->vendor.irq); + disable_irq_nosync(tpm_dev->irq); tpm_gen_interrupt(chip); } diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h index bcbd5ff..27e7564 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.h +++ b/drivers/char/tpm/st33zp24/st33zp24.h @@ -21,6 +21,16 @@ #define TPM_WRITE_DIRECTION 0x80 #define TPM_BUFSIZE 2048 +struct st33zp24_dev { + struct tpm_chip *chip; + void *phy_id; + const struct st33zp24_phy_ops *ops; + int irq; + u32 intrs; + int io_lpcpd; +}; + + struct st33zp24_phy_ops { int (*send)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size); int (*recv)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size); diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 5397b64..101bb47 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -359,7 +359,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, goto out; } - if (chip->vendor.irq) + if (chip->flags & TPM_CHIP_FLAG_IRQ) goto out_recv; if (chip->flags & TPM_CHIP_FLAG_TPM2) @@ -890,7 +890,7 @@ int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, stop = jiffies + timeout; - if (chip->vendor.irq) { + if (chip->flags & TPM_CHIP_FLAG_IRQ) { again: timeout = stop - jiffies; if ((long)timeout <= 0) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 357ac14..ad4799c 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -131,8 +131,6 @@ enum tpm2_startup_types { struct tpm_chip; struct tpm_vendor_specific { - int irq; - int locality; unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */ bool timeout_adjusted; @@ -154,6 +152,7 @@ struct tpm_vendor_specific { enum tpm_chip_flags { TPM_CHIP_FLAG_REGISTERED = BIT(0), TPM_CHIP_FLAG_TPM2 = BIT(1), + TPM_CHIP_FLAG_IRQ = BIT(2), }; struct tpm_chip { diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index dd8f0eb..55fa51f 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -173,7 +173,6 @@ static int i2c_atmel_probe(struct i2c_client *client, chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT); chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); - chip->vendor.irq = 0; /* There is no known way to probe for this device, and all version * information seems to be read via TPM commands. Thus we rely on the diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index e74f1c1..093daf9 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -585,9 +585,6 @@ static int tpm_tis_i2c_init(struct device *dev) if (IS_ERR(chip)) return PTR_ERR(chip); - /* Disable interrupts */ - chip->vendor.irq = 0; - /* Default timeouts */ chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index a43b5f3..75a80e466 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -55,6 +55,7 @@ #define I2C_DRIVER_NAME "tpm_i2c_nuvoton" struct priv_data { + int irq; unsigned int intrs; }; @@ -176,12 +177,12 @@ static bool i2c_nuvoton_check_status(struct tpm_chip *chip, u8 mask, u8 value) static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value, u32 timeout, wait_queue_head_t *queue) { - if (chip->vendor.irq && queue) { + if ((chip->flags & TPM_CHIP_FLAG_IRQ) && queue) { s32 rc; struct priv_data *priv = chip->vendor.priv; unsigned int cur_intrs = priv->intrs; - enable_irq(chip->vendor.irq); + enable_irq(priv->irq); rc = wait_event_interruptible_timeout(*queue, cur_intrs != priv->intrs, timeout); @@ -477,7 +478,7 @@ static irqreturn_t i2c_nuvoton_int_handler(int dummy, void *dev_id) priv->intrs++; wake_up(&chip->vendor.read_queue); - disable_irq_nosync(chip->vendor.irq); + disable_irq_nosync(priv->irq); return IRQ_HANDLED; } @@ -521,6 +522,7 @@ static int i2c_nuvoton_probe(struct i2c_client *client, int rc; struct tpm_chip *chip; struct device *dev = &client->dev; + struct priv_data *priv; u32 vid = 0; rc = get_vid(client, &vid); @@ -534,10 +536,10 @@ static int i2c_nuvoton_probe(struct i2c_client *client, if (IS_ERR(chip)) return PTR_ERR(chip); - chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data), - GFP_KERNEL); - if (!chip->vendor.priv) + priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL); + if (!priv) return -ENOMEM; + chip->vendor.priv = priv; init_waitqueue_head(&chip->vendor.read_queue); @@ -552,19 +554,21 @@ static int i2c_nuvoton_probe(struct i2c_client *client, * TPM_INTF_INT_LEVEL_LOW | TPM_INTF_DATA_AVAIL_INT * The IRQ should be set in the i2c_board_info (which is done * automatically in of_i2c_register_devices, for device tree users */ - chip->vendor.irq = client->irq; + chip->flags |= TPM_CHIP_FLAG_IRQ; + priv->irq = client->irq; - if (chip->vendor.irq) { - dev_dbg(dev, "%s() chip-vendor.irq\n", __func__); - rc = devm_request_irq(dev, chip->vendor.irq, + if (chip->flags & TPM_CHIP_FLAG_IRQ) { + dev_dbg(dev, "%s() priv->irq\n", __func__); + rc = devm_request_irq(dev, client->irq, i2c_nuvoton_int_handler, IRQF_TRIGGER_LOW, dev_name(&chip->dev), chip); if (rc) { dev_err(dev, "%s() Unable to request irq: %d for use\n", - __func__, chip->vendor.irq); - chip->vendor.irq = 0; + __func__, priv->irq); + chip->flags &= ~TPM_CHIP_FLAG_IRQ; + priv->irq = 0; } else { /* Clear any pending interrupt */ i2c_nuvoton_ready(chip); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 41c4b60..ee511f1 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -96,6 +96,7 @@ struct tpm_info { struct priv_data { void __iomem *iobase; u16 manufacturer_id; + int irq; bool irq_tested; wait_queue_head_t int_queue; }; @@ -177,7 +178,7 @@ static int request_locality(struct tpm_chip *chip, int l) stop = jiffies + chip->vendor.timeout_a; - if (chip->vendor.irq) { + if (chip->flags & TPM_CHIP_FLAG_IRQ) { again: timeout = stop - jiffies; if ((long)timeout <= 0) @@ -385,8 +386,9 @@ static void disable_interrupts(struct tpm_chip *chip) intmask &= ~TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); - devm_free_irq(&chip->dev, chip->vendor.irq, chip); - chip->vendor.irq = 0; + devm_free_irq(&chip->dev, priv->irq, chip); + priv->irq = 0; + chip->flags &= ~TPM_CHIP_FLAG_IRQ; } /* @@ -409,7 +411,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) iowrite8(TPM_STS_GO, priv->iobase + TPM_STS(chip->vendor.locality)); - if (chip->vendor.irq) { + if (chip->flags & TPM_CHIP_FLAG_IRQ) { ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); if (chip->flags & TPM_CHIP_FLAG_TPM2) @@ -436,14 +438,16 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) int rc, irq; struct priv_data *priv = chip->vendor.priv; - if (!chip->vendor.irq || priv->irq_tested) + if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || priv->irq_tested) return tpm_tis_send_main(chip, buf, len); /* Verify receipt of the expected IRQ */ - irq = chip->vendor.irq; - chip->vendor.irq = 0; + irq = priv->irq; + priv->irq = 0; + chip->flags &= ~TPM_CHIP_FLAG_IRQ; rc = tpm_tis_send_main(chip, buf, len); - chip->vendor.irq = irq; + priv->irq = irq; + chip->flags |= TPM_CHIP_FLAG_IRQ; if (!priv->irq_tested) msleep(1); if (!priv->irq_tested) @@ -605,7 +609,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, irq); return -1; } - chip->vendor.irq = irq; + priv->irq = irq; original_int_vec = ioread8(priv->iobase + TPM_INT_VECTOR(chip->vendor.locality)); @@ -634,7 +638,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, /* tpm_tis_send will either confirm the interrupt is working or it * will call disable_irq which undoes all of the above. */ - if (!chip->vendor.irq) { + if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { iowrite8(original_int_vec, priv->iobase + TPM_INT_VECTOR(chip->vendor.locality)); return 1; @@ -797,7 +801,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, if (tpm_info->irq) { tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, tpm_info->irq); - if (!chip->vendor.irq) + if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) dev_err(&chip->dev, FW_BUG "TPM interrupt not working, polling instead\n"); } else @@ -841,7 +845,7 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) /* reenable interrupts that device may have lost or BIOS/firmware may have disabled */ - iowrite8(chip->vendor.irq, priv->iobase + + iowrite8(priv->irq, priv->iobase + TPM_INT_VECTOR(chip->vendor.locality)); intmask = @@ -860,7 +864,7 @@ static int tpm_tis_resume(struct device *dev) struct tpm_chip *chip = dev_get_drvdata(dev); int ret; - if (chip->vendor.irq) + if (chip->flags & TPM_CHIP_FLAG_IRQ) tpm_tis_reenable_interrupts(chip); ret = tpm_pm_resume(dev); diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index 3111f27..329941d 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -28,6 +28,7 @@ struct tpm_private { unsigned int evtchn; int ring_ref; domid_t backend_id; + int irq; }; enum status_bits { @@ -217,7 +218,7 @@ static int setup_ring(struct xenbus_device *dev, struct tpm_private *priv) xenbus_dev_fatal(dev, rv, "allocating TPM irq"); return rv; } - priv->chip->vendor.irq = rv; + priv->irq = rv; again: rv = xenbus_transaction_start(&xbt); @@ -277,8 +278,8 @@ static void ring_free(struct tpm_private *priv) else free_page((unsigned long)priv->shr); - if (priv->chip && priv->chip->vendor.irq) - unbind_from_irqhandler(priv->chip->vendor.irq, priv); + if (priv->irq) + unbind_from_irqhandler(priv->irq, priv); kfree(priv); } -- cgit v0.10.2 From 6e599f6f261fe61582e6bedcc0aff924ac5da614 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 31 Mar 2016 22:56:57 +0200 Subject: tpm: drop 'read_queue' from struct tpm_vendor_specific Dropped the field 'read_queue' from struct tpm_vendor_specific and make it available to the various private structures in the drivers. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index f4a44ad..d2e0175 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -338,7 +338,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, chip->vendor.timeout_c, - &chip->vendor.read_queue, true) == 0) { + &tpm_dev->read_queue, true) == 0) { burstcnt = get_burstcount(chip); if (burstcnt < 0) return burstcnt; @@ -367,7 +367,7 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); tpm_dev->intrs++; - wake_up_interruptible(&chip->vendor.read_queue); + wake_up_interruptible(&tpm_dev->read_queue); disable_irq_nosync(tpm_dev->irq); return IRQ_HANDLED; @@ -407,7 +407,7 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, st33zp24_cancel(chip); if (wait_for_stat (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, - &chip->vendor.read_queue, false) < 0) { + &tpm_dev->read_queue, false) < 0) { ret = -ETIME; goto out_err; } @@ -453,7 +453,7 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, ret = wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, tpm_calc_ordinal_duration(chip, ordinal), - &chip->vendor.read_queue, false); + &tpm_dev->read_queue, false); if (ret < 0) goto out_err; } @@ -570,7 +570,7 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, if (irq) { /* INTERRUPT Setup */ - init_waitqueue_head(&chip->vendor.read_queue); + init_waitqueue_head(&tpm_dev->read_queue); tpm_dev->intrs = 0; if (request_locality(chip) != LOCALITY0) { @@ -674,7 +674,7 @@ int st33zp24_pm_resume(struct device *dev) gpio_set_value(tpm_dev->io_lpcpd, 1); ret = wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_b, - &chip->vendor.read_queue, false); + &tpm_dev->read_queue, false); } else { ret = tpm_pm_resume(dev); if (!ret) diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h index 27e7564..9b5cdc7 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.h +++ b/drivers/char/tpm/st33zp24/st33zp24.h @@ -28,6 +28,7 @@ struct st33zp24_dev { int irq; u32 intrs; int io_lpcpd; + wait_queue_head_t read_queue; }; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index ad4799c..62e711b 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -137,8 +137,6 @@ struct tpm_vendor_specific { unsigned long duration[3]; /* jiffies */ bool duration_adjusted; void *priv; - - wait_queue_head_t read_queue; }; #define TPM_VPRIV(c) ((c)->vendor.priv) diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 75a80e466..2c1fa26 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -57,6 +57,7 @@ struct priv_data { int irq; unsigned int intrs; + wait_queue_head_t read_queue; }; static s32 i2c_nuvoton_read_buf(struct i2c_client *client, u8 offset, u8 size, @@ -232,13 +233,14 @@ static int i2c_nuvoton_wait_for_data_avail(struct tpm_chip *chip, u32 timeout, static int i2c_nuvoton_recv_data(struct i2c_client *client, struct tpm_chip *chip, u8 *buf, size_t count) { + struct priv_data *priv = chip->vendor.priv; s32 rc; int burst_count, bytes2read, size = 0; while (size < count && i2c_nuvoton_wait_for_data_avail(chip, chip->vendor.timeout_c, - &chip->vendor.read_queue) == 0) { + &priv->read_queue) == 0) { burst_count = i2c_nuvoton_get_burstcount(client, chip); if (burst_count < 0) { dev_err(&chip->dev, @@ -265,6 +267,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client, /* Read TPM command results */ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) { + struct priv_data *priv = chip->vendor.priv; struct device *dev = chip->dev.parent; struct i2c_client *client = to_i2c_client(dev); s32 rc; @@ -286,7 +289,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) * tag, paramsize, and result */ status = i2c_nuvoton_wait_for_data_avail( - chip, chip->vendor.timeout_c, &chip->vendor.read_queue); + chip, chip->vendor.timeout_c, &priv->read_queue); if (status != 0) { dev_err(dev, "%s() timeout on dataAvail\n", __func__); size = -ETIMEDOUT; @@ -348,6 +351,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) */ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) { + struct priv_data *priv = chip->vendor.priv; struct device *dev = chip->dev.parent; struct i2c_client *client = to_i2c_client(dev); u32 ordinal; @@ -440,7 +444,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) rc = i2c_nuvoton_wait_for_data_avail(chip, tpm_calc_ordinal_duration(chip, ordinal), - &chip->vendor.read_queue); + &priv->read_queue); if (rc) { dev_err(dev, "%s() timeout command duration\n", __func__); i2c_nuvoton_ready(chip); @@ -477,7 +481,7 @@ static irqreturn_t i2c_nuvoton_int_handler(int dummy, void *dev_id) struct priv_data *priv = chip->vendor.priv; priv->intrs++; - wake_up(&chip->vendor.read_queue); + wake_up(&priv->read_queue); disable_irq_nosync(priv->irq); return IRQ_HANDLED; } @@ -541,7 +545,7 @@ static int i2c_nuvoton_probe(struct i2c_client *client, return -ENOMEM; chip->vendor.priv = priv; - init_waitqueue_head(&chip->vendor.read_queue); + init_waitqueue_head(&priv->read_queue); /* Default timeouts */ chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index ee511f1..38240ac 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -99,6 +99,7 @@ struct priv_data { int irq; bool irq_tested; wait_queue_head_t int_queue; + wait_queue_head_t read_queue; }; #if defined(CONFIG_PNP) && defined(CONFIG_ACPI) @@ -252,7 +253,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, true) + &priv->read_queue, true) == 0) { burstcnt = get_burstcount(chip); for (; burstcnt > 0 && size < count; burstcnt--) @@ -421,7 +422,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) if (wait_for_tpm_stat (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur, - &chip->vendor.read_queue, false) < 0) { + &priv->read_queue, false) < 0) { rc = -ETIME; goto out_err; } @@ -575,7 +576,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) ((struct priv_data *)chip->vendor.priv)->irq_tested = true; if (interrupt & TPM_INTF_DATA_AVAIL_INT) - wake_up_interruptible(&chip->vendor.read_queue); + wake_up_interruptible(&priv->read_queue); if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) for (i = 0; i < 5; i++) if (check_locality(chip, i) >= 0) @@ -795,7 +796,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, } /* INTERRUPT Setup */ - init_waitqueue_head(&chip->vendor.read_queue); + init_waitqueue_head(&priv->read_queue); init_waitqueue_head(&priv->int_queue); if (interrupts && tpm_info->irq != -1) { if (tpm_info->irq) { diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index 329941d..cca89d9 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -29,6 +29,7 @@ struct tpm_private { int ring_ref; domid_t backend_id; int irq; + wait_queue_head_t read_queue; }; enum status_bits { @@ -89,7 +90,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) /* Wait for completion of any existing command or cancellation */ if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, chip->vendor.timeout_c, - &chip->vendor.read_queue, true) < 0) { + &priv->read_queue, true) < 0) { vtpm_cancel(chip); return -ETIME; } @@ -105,7 +106,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) duration = tpm_calc_ordinal_duration(chip, ordinal); if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, duration, - &chip->vendor.read_queue, true) < 0) { + &priv->read_queue, true) < 0) { /* got a signal or timeout, try to cancel */ vtpm_cancel(chip); return -ETIME; @@ -126,7 +127,7 @@ static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) /* In theory the wait at the end of _send makes this one unnecessary */ if (wait_for_tpm_stat(chip, VTPM_STATUS_RESULT, chip->vendor.timeout_c, - &chip->vendor.read_queue, true) < 0) { + &priv->read_queue, true) < 0) { vtpm_cancel(chip); return -ETIME; } @@ -162,7 +163,7 @@ static irqreturn_t tpmif_interrupt(int dummy, void *dev_id) switch (priv->shr->state) { case VTPM_STATE_IDLE: case VTPM_STATE_FINISH: - wake_up_interruptible(&priv->chip->vendor.read_queue); + wake_up_interruptible(&priv->read_queue); break; case VTPM_STATE_SUBMIT: case VTPM_STATE_CANCEL: @@ -180,7 +181,7 @@ static int setup_chip(struct device *dev, struct tpm_private *priv) if (IS_ERR(chip)) return PTR_ERR(chip); - init_waitqueue_head(&chip->vendor.read_queue); + init_waitqueue_head(&priv->read_queue); priv->chip = chip; TPM_VPRIV(chip) = priv; -- cgit v0.10.2 From 56671c893e0e3ee237bc8e229923a9e6555c2fc9 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 31 Mar 2016 22:56:58 +0200 Subject: tpm: drop 'locality' from struct tpm_vendor_specific Dropped the field 'locality' from struct tpm_vendor_specific migrated it to the private structures of st33zp24, tpm_i2c_infineon and tpm_tis. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index d2e0175..3802c58 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -136,7 +136,7 @@ static int check_locality(struct tpm_chip *chip) if (status && (data & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) - return chip->vendor.locality; + return tpm_dev->locality; return -EACCES; } /* check_locality() */ @@ -153,11 +153,11 @@ static int request_locality(struct tpm_chip *chip) struct st33zp24_dev *tpm_dev; u8 data; - if (check_locality(chip) == chip->vendor.locality) - return chip->vendor.locality; - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + if (check_locality(chip) == tpm_dev->locality) + return tpm_dev->locality; + data = TPM_ACCESS_REQUEST_USE; ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1); if (ret < 0) @@ -168,7 +168,7 @@ static int request_locality(struct tpm_chip *chip) /* Request locality is usually effective after the request */ do { if (check_locality(chip) >= 0) - return chip->vendor.locality; + return tpm_dev->locality; msleep(TPM_TIMEOUT); } while (time_before(jiffies, stop)); @@ -566,7 +566,7 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.locality = LOCALITY0; + tpm_dev->locality = LOCALITY0; if (irq) { /* INTERRUPT Setup */ diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h index 9b5cdc7..6f4a419 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.h +++ b/drivers/char/tpm/st33zp24/st33zp24.h @@ -25,6 +25,7 @@ struct st33zp24_dev { struct tpm_chip *chip; void *phy_id; const struct st33zp24_phy_ops *ops; + int locality; int irq; u32 intrs; int io_lpcpd; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 62e711b..68a40c6 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -131,7 +131,6 @@ enum tpm2_startup_types { struct tpm_chip; struct tpm_vendor_specific { - int locality; unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */ bool timeout_adjusted; unsigned long duration[3]; /* jiffies */ diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 093daf9..8318946 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -66,6 +66,7 @@ enum i2c_chip_type { /* Structure to store I2C TPM specific stuff */ struct tpm_inf_dev { struct i2c_client *client; + int locality; u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */ struct tpm_chip *chip; enum i2c_chip_type chip_type; @@ -288,7 +289,7 @@ static int check_locality(struct tpm_chip *chip, int loc) if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { - chip->vendor.locality = loc; + tpm_dev.locality = loc; return loc; } @@ -337,7 +338,7 @@ static u8 tpm_tis_i2c_status(struct tpm_chip *chip) u8 i = 0; do { - if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0) + if (iic_tpm_read(TPM_STS(tpm_dev.locality), &buf, 1) < 0) return 0; i++; @@ -351,7 +352,7 @@ static void tpm_tis_i2c_ready(struct tpm_chip *chip) { /* this causes the current command to be aborted */ u8 buf = TPM_STS_COMMAND_READY; - iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1); + iic_tpm_write_long(TPM_STS(tpm_dev.locality), &buf, 1); } static ssize_t get_burstcount(struct tpm_chip *chip) @@ -365,7 +366,7 @@ static ssize_t get_burstcount(struct tpm_chip *chip) stop = jiffies + chip->vendor.timeout_d; do { /* Note: STS is little endian */ - if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0) + if (iic_tpm_read(TPM_STS(tpm_dev.locality)+1, buf, 3) < 0) burstcnt = 0; else burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0]; @@ -419,7 +420,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) if (burstcnt > (count - size)) burstcnt = count - size; - rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality), + rc = iic_tpm_read(TPM_DATA_FIFO(tpm_dev.locality), &(buf[size]), burstcnt); if (rc == 0) size += burstcnt; @@ -477,7 +478,7 @@ out: * so we sleep rather than keeping the bus busy */ usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI); - release_locality(chip, chip->vendor.locality, 0); + release_locality(chip, tpm_dev.locality, 0); return size; } @@ -516,7 +517,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) if (burstcnt > (len - 1 - count)) burstcnt = len - 1 - count; - rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), + rc = iic_tpm_write(TPM_DATA_FIFO(tpm_dev.locality), &(buf[count]), burstcnt); if (rc == 0) count += burstcnt; @@ -539,7 +540,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) } /* write last byte */ - iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1); + iic_tpm_write(TPM_DATA_FIFO(tpm_dev.locality), &(buf[count]), 1); wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); if ((status & TPM_STS_DATA_EXPECT) != 0) { rc = -EIO; @@ -547,7 +548,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) } /* go and do it */ - iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1); + iic_tpm_write(TPM_STS(tpm_dev.locality), &sts, 1); return len; out_err: @@ -556,7 +557,7 @@ out_err: * so we sleep rather than keeping the bus busy */ usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI); - release_locality(chip, chip->vendor.locality, 0); + release_locality(chip, tpm_dev.locality, 0); return rc; } @@ -623,7 +624,7 @@ static int tpm_tis_i2c_init(struct device *dev) return tpm_chip_register(chip); out_release: - release_locality(chip, chip->vendor.locality, 1); + release_locality(chip, tpm_dev.locality, 1); tpm_dev.client = NULL; out_err: return rc; @@ -695,7 +696,7 @@ static int tpm_tis_i2c_remove(struct i2c_client *client) struct tpm_chip *chip = tpm_dev.chip; tpm_chip_unregister(chip); - release_locality(chip, chip->vendor.locality, 1); + release_locality(chip, tpm_dev.locality, 1); tpm_dev.client = NULL; return 0; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 38240ac..9e2ba1d 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -96,6 +96,7 @@ struct tpm_info { struct priv_data { void __iomem *iobase; u16 manufacturer_id; + int locality; int irq; bool irq_tested; wait_queue_head_t int_queue; @@ -149,7 +150,7 @@ static int check_locality(struct tpm_chip *chip, int l) if ((ioread8(priv->iobase + TPM_ACCESS(l)) & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) - return chip->vendor.locality = l; + return priv->locality = l; return -1; } @@ -211,7 +212,7 @@ static u8 tpm_tis_status(struct tpm_chip *chip) struct priv_data *priv = chip->vendor.priv; return ioread8(priv->iobase + - TPM_STS(chip->vendor.locality)); + TPM_STS(priv->locality)); } static void tpm_tis_ready(struct tpm_chip *chip) @@ -220,7 +221,7 @@ static void tpm_tis_ready(struct tpm_chip *chip) /* this causes the current command to be aborted */ iowrite8(TPM_STS_COMMAND_READY, - priv->iobase + TPM_STS(chip->vendor.locality)); + priv->iobase + TPM_STS(priv->locality)); } static int get_burstcount(struct tpm_chip *chip) @@ -234,9 +235,9 @@ static int get_burstcount(struct tpm_chip *chip) stop = jiffies + chip->vendor.timeout_d; do { burstcnt = ioread8(priv->iobase + - TPM_STS(chip->vendor.locality) + 1); + TPM_STS(priv->locality) + 1); burstcnt += ioread8(priv->iobase + - TPM_STS(chip->vendor.locality) + + TPM_STS(priv->locality) + 2) << 8; if (burstcnt) return burstcnt; @@ -258,8 +259,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) burstcnt = get_burstcount(chip); for (; burstcnt > 0 && size < count; burstcnt--) buf[size++] = ioread8(priv->iobase + - TPM_DATA_FIFO(chip->vendor. - locality)); + TPM_DATA_FIFO(priv->locality)); } return size; } @@ -307,7 +307,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) out: tpm_tis_ready(chip); - release_locality(chip, chip->vendor.locality, 0); + release_locality(chip, priv->locality, 0); return size; } @@ -344,7 +344,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) burstcnt = get_burstcount(chip); for (; burstcnt > 0 && count < len - 1; burstcnt--) { iowrite8(buf[count], priv->iobase + - TPM_DATA_FIFO(chip->vendor.locality)); + TPM_DATA_FIFO(priv->locality)); count++; } @@ -359,7 +359,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) /* write last byte */ iowrite8(buf[count], - priv->iobase + TPM_DATA_FIFO(chip->vendor.locality)); + priv->iobase + TPM_DATA_FIFO(priv->locality)); wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &priv->int_queue, false); status = tpm_tis_status(chip); @@ -372,7 +372,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) out_err: tpm_tis_ready(chip); - release_locality(chip, chip->vendor.locality, 0); + release_locality(chip, priv->locality, 0); return rc; } @@ -383,10 +383,10 @@ static void disable_interrupts(struct tpm_chip *chip) intmask = ioread32(priv->iobase + - TPM_INT_ENABLE(chip->vendor.locality)); + TPM_INT_ENABLE(priv->locality)); intmask &= ~TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, - priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); + priv->iobase + TPM_INT_ENABLE(priv->locality)); devm_free_irq(&chip->dev, priv->irq, chip); priv->irq = 0; chip->flags &= ~TPM_CHIP_FLAG_IRQ; @@ -410,7 +410,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) /* go and do it */ iowrite8(TPM_STS_GO, - priv->iobase + TPM_STS(chip->vendor.locality)); + priv->iobase + TPM_STS(priv->locality)); if (chip->flags & TPM_CHIP_FLAG_IRQ) { ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); @@ -430,7 +430,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) return len; out_err: tpm_tis_ready(chip); - release_locality(chip, chip->vendor.locality, 0); + release_locality(chip, priv->locality, 0); return rc; } @@ -516,7 +516,7 @@ static int probe_itpm(struct tpm_chip *chip) goto out; tpm_tis_ready(chip); - release_locality(chip, chip->vendor.locality, 0); + release_locality(chip, priv->locality, 0); itpm = true; @@ -530,7 +530,7 @@ static int probe_itpm(struct tpm_chip *chip) out: itpm = rem_itpm; tpm_tis_ready(chip); - release_locality(chip, chip->vendor.locality, 0); + release_locality(chip, priv->locality, 0); return rc; } @@ -569,7 +569,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) int i; interrupt = ioread32(priv->iobase + - TPM_INT_STATUS(chip->vendor.locality)); + TPM_INT_STATUS(priv->locality)); if (interrupt == 0) return IRQ_NONE; @@ -589,8 +589,8 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) /* Clear interrupts handled with TPM_EOI */ iowrite32(interrupt, priv->iobase + - TPM_INT_STATUS(chip->vendor.locality)); - ioread32(priv->iobase + TPM_INT_STATUS(chip->vendor.locality)); + TPM_INT_STATUS(priv->locality)); + ioread32(priv->iobase + TPM_INT_STATUS(priv->locality)); return IRQ_HANDLED; } @@ -613,18 +613,18 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, priv->irq = irq; original_int_vec = ioread8(priv->iobase + - TPM_INT_VECTOR(chip->vendor.locality)); + TPM_INT_VECTOR(priv->locality)); iowrite8(irq, - priv->iobase + TPM_INT_VECTOR(chip->vendor.locality)); + priv->iobase + TPM_INT_VECTOR(priv->locality)); /* Clear all existing */ iowrite32(ioread32(priv->iobase + - TPM_INT_STATUS(chip->vendor.locality)), - priv->iobase + TPM_INT_STATUS(chip->vendor.locality)); + TPM_INT_STATUS(priv->locality)), + priv->iobase + TPM_INT_STATUS(priv->locality)); /* Turn on */ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, - priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); + priv->iobase + TPM_INT_ENABLE(priv->locality)); priv->irq_tested = false; @@ -641,7 +641,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, */ if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { iowrite8(original_int_vec, - priv->iobase + TPM_INT_VECTOR(chip->vendor.locality)); + priv->iobase + TPM_INT_VECTOR(priv->locality)); return 1; } @@ -659,7 +659,7 @@ static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask) int i; original_int_vec = ioread8(priv->iobase + - TPM_INT_VECTOR(chip->vendor.locality)); + TPM_INT_VECTOR(priv->locality)); if (!original_int_vec) { if (IS_ENABLED(CONFIG_X86)) @@ -679,11 +679,10 @@ MODULE_PARM_DESC(interrupts, "Enable interrupts"); static void tpm_tis_remove(struct tpm_chip *chip) { struct priv_data *priv = chip->vendor.priv; - void __iomem *reg = priv->iobase + - TPM_INT_ENABLE(chip->vendor.locality); + void __iomem *reg = priv->iobase + TPM_INT_ENABLE(priv->locality); iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(reg), reg); - release_locality(chip, chip->vendor.locality, 1); + release_locality(chip, priv->locality, 1); } static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, @@ -724,12 +723,12 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, /* Take control of the TPM's interrupt hardware and shut it off */ intmask = ioread32(priv->iobase + - TPM_INT_ENABLE(chip->vendor.locality)); + TPM_INT_ENABLE(priv->locality)); intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; intmask &= ~TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, - priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); + priv->iobase + TPM_INT_ENABLE(priv->locality)); if (request_locality(chip, 0) != 0) { rc = -ENODEV; @@ -763,7 +762,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, /* Figure out the capabilities */ intfcaps = ioread32(priv->iobase + - TPM_INTF_CAPS(chip->vendor.locality)); + TPM_INTF_CAPS(priv->locality)); dev_dbg(dev, "TPM interface capabilities (0x%x):\n", intfcaps); if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) @@ -847,17 +846,17 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) /* reenable interrupts that device may have lost or BIOS/firmware may have disabled */ iowrite8(priv->irq, priv->iobase + - TPM_INT_VECTOR(chip->vendor.locality)); + TPM_INT_VECTOR(priv->locality)); intmask = - ioread32(priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); + ioread32(priv->iobase + TPM_INT_ENABLE(priv->locality)); intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, - priv->iobase + TPM_INT_ENABLE(chip->vendor.locality)); + priv->iobase + TPM_INT_ENABLE(priv->locality)); } static int tpm_tis_resume(struct device *dev) -- cgit v0.10.2 From af782f339a5d6ea202652c9f06880e1a28c43813 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 31 Mar 2016 22:56:59 +0200 Subject: tpm: Move tpm_vendor_specific data related with PTP specification to tpm_chip Move tpm_vendor_specific data related to TCG PTP specification to tpm_chip. Move all fields directly linked with well known TCG concepts and used in TPM drivers (tpm_i2c_atmel, tpm_i2c_infineon, tpm_i2c_nuvoton, tpm_tis and xen-tpmfront) as well as in TPM core files (tpm-sysfs, tpm-interface and tpm2-cmd) in tpm_chip. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index 3802c58..07531e9 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -163,7 +163,7 @@ static int request_locality(struct tpm_chip *chip) if (ret < 0) return ret; - stop = jiffies + chip->vendor.timeout_a; + stop = jiffies + chip->timeout_a; /* Request locality is usually effective after the request */ do { @@ -205,7 +205,7 @@ static int get_burstcount(struct tpm_chip *chip) tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); - stop = jiffies + chip->vendor.timeout_d; + stop = jiffies + chip->timeout_d; do { status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS + 1, &temp, 1); @@ -337,7 +337,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) while (size < count && wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, - chip->vendor.timeout_c, + chip->timeout_c, &tpm_dev->read_queue, true) == 0) { burstcnt = get_burstcount(chip); if (burstcnt < 0) @@ -406,7 +406,7 @@ static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, if ((status & TPM_STS_COMMAND_READY) == 0) { st33zp24_cancel(chip); if (wait_for_stat - (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, + (chip, TPM_STS_COMMAND_READY, chip->timeout_b, &tpm_dev->read_queue, false) < 0) { ret = -ETIME; goto out_err; @@ -561,10 +561,10 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, tpm_dev->phy_id = phy_id; tpm_dev->ops = ops; - 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->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); tpm_dev->locality = LOCALITY0; @@ -673,7 +673,7 @@ int st33zp24_pm_resume(struct device *dev) if (gpio_is_valid(tpm_dev->io_lpcpd)) { gpio_set_value(tpm_dev->io_lpcpd, 1); ret = wait_for_stat(chip, - TPM_STS_VALID, chip->vendor.timeout_b, + TPM_STS_VALID, chip->timeout_b, &tpm_dev->read_queue, false); } else { ret = tpm_pm_resume(dev); diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 101bb47..7cba092 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -319,7 +319,7 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, duration_idx = tpm_ordinal_duration[ordinal]; if (duration_idx != TPM_UNDEFINED) - duration = chip->vendor.duration[duration_idx]; + duration = chip->duration[duration_idx]; if (duration <= 0) return 2 * 60 * HZ; else @@ -505,15 +505,15 @@ int tpm_get_timeouts(struct tpm_chip *chip) if (chip->flags & TPM_CHIP_FLAG_TPM2) { /* Fixed timeouts for TPM2 */ - chip->vendor.timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A); - chip->vendor.timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B); - chip->vendor.timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C); - chip->vendor.timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D); - chip->vendor.duration[TPM_SHORT] = + chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A); + chip->timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B); + chip->timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C); + chip->timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D); + chip->duration[TPM_SHORT] = msecs_to_jiffies(TPM2_DURATION_SHORT); - chip->vendor.duration[TPM_MEDIUM] = + chip->duration[TPM_MEDIUM] = msecs_to_jiffies(TPM2_DURATION_MEDIUM); - chip->vendor.duration[TPM_LONG] = + chip->duration[TPM_LONG] = msecs_to_jiffies(TPM2_DURATION_LONG); return 0; } @@ -561,10 +561,10 @@ int tpm_get_timeouts(struct tpm_chip *chip) * of misreporting. */ if (chip->ops->update_timeouts != NULL) - chip->vendor.timeout_adjusted = + chip->timeout_adjusted = chip->ops->update_timeouts(chip, new_timeout); - if (!chip->vendor.timeout_adjusted) { + if (!chip->timeout_adjusted) { /* Don't overwrite default if value is 0 */ if (new_timeout[0] != 0 && new_timeout[0] < 1000) { int i; @@ -572,12 +572,12 @@ int tpm_get_timeouts(struct tpm_chip *chip) /* timeouts in msec rather usec */ for (i = 0; i != ARRAY_SIZE(new_timeout); i++) new_timeout[i] *= 1000; - chip->vendor.timeout_adjusted = true; + chip->timeout_adjusted = true; } } /* Report adjusted timeouts */ - if (chip->vendor.timeout_adjusted) { + if (chip->timeout_adjusted) { dev_info(&chip->dev, HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n", old_timeout[0], new_timeout[0], @@ -586,10 +586,10 @@ int tpm_get_timeouts(struct tpm_chip *chip) old_timeout[3], new_timeout[3]); } - chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]); - chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]); - chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]); - chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]); + chip->timeout_a = usecs_to_jiffies(new_timeout[0]); + chip->timeout_b = usecs_to_jiffies(new_timeout[1]); + chip->timeout_c = usecs_to_jiffies(new_timeout[2]); + chip->timeout_d = usecs_to_jiffies(new_timeout[3]); duration: tpm_cmd.header.in = tpm_getcap_header; @@ -608,11 +608,11 @@ duration: return -EINVAL; duration_cap = &tpm_cmd.params.getcap_out.cap.duration; - chip->vendor.duration[TPM_SHORT] = + chip->duration[TPM_SHORT] = usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short)); - chip->vendor.duration[TPM_MEDIUM] = + chip->duration[TPM_MEDIUM] = usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium)); - chip->vendor.duration[TPM_LONG] = + chip->duration[TPM_LONG] = usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long)); /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above @@ -620,11 +620,11 @@ duration: * fix up the resulting too-small TPM_SHORT value to make things work. * We also scale the TPM_MEDIUM and -_LONG values by 1000. */ - if (chip->vendor.duration[TPM_SHORT] < (HZ / 100)) { - chip->vendor.duration[TPM_SHORT] = HZ; - chip->vendor.duration[TPM_MEDIUM] *= 1000; - chip->vendor.duration[TPM_LONG] *= 1000; - chip->vendor.duration_adjusted = true; + if (chip->duration[TPM_SHORT] < (HZ / 100)) { + chip->duration[TPM_SHORT] = HZ; + chip->duration[TPM_MEDIUM] *= 1000; + chip->duration[TPM_LONG] *= 1000; + chip->duration_adjusted = true; dev_info(&chip->dev, "Adjusting TPM timeout parameters."); } return 0; diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index 34e7fc7..a7c3473 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -236,14 +236,14 @@ static ssize_t durations_show(struct device *dev, struct device_attribute *attr, { struct tpm_chip *chip = dev_get_drvdata(dev); - if (chip->vendor.duration[TPM_LONG] == 0) + if (chip->duration[TPM_LONG] == 0) return 0; return sprintf(buf, "%d %d %d [%s]\n", - jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]), - jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]), - jiffies_to_usecs(chip->vendor.duration[TPM_LONG]), - chip->vendor.duration_adjusted + jiffies_to_usecs(chip->duration[TPM_SHORT]), + jiffies_to_usecs(chip->duration[TPM_MEDIUM]), + jiffies_to_usecs(chip->duration[TPM_LONG]), + chip->duration_adjusted ? "adjusted" : "original"); } static DEVICE_ATTR_RO(durations); @@ -254,11 +254,11 @@ static ssize_t timeouts_show(struct device *dev, struct device_attribute *attr, struct tpm_chip *chip = dev_get_drvdata(dev); return sprintf(buf, "%d %d %d %d [%s]\n", - jiffies_to_usecs(chip->vendor.timeout_a), - jiffies_to_usecs(chip->vendor.timeout_b), - jiffies_to_usecs(chip->vendor.timeout_c), - jiffies_to_usecs(chip->vendor.timeout_d), - chip->vendor.timeout_adjusted + jiffies_to_usecs(chip->timeout_a), + jiffies_to_usecs(chip->timeout_b), + jiffies_to_usecs(chip->timeout_c), + jiffies_to_usecs(chip->timeout_d), + chip->timeout_adjusted ? "adjusted" : "original"); } static DEVICE_ATTR_RO(timeouts); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 68a40c6..93757e8 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -131,10 +131,6 @@ enum tpm2_startup_types { struct tpm_chip; struct tpm_vendor_specific { - unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */ - bool timeout_adjusted; - unsigned long duration[3]; /* jiffies */ - bool duration_adjusted; void *priv; }; @@ -171,6 +167,13 @@ struct tpm_chip { struct mutex tpm_mutex; /* tpm is processing */ struct tpm_vendor_specific vendor; + unsigned long timeout_a; /* jiffies */ + unsigned long timeout_b; /* jiffies */ + unsigned long timeout_c; /* jiffies */ + unsigned long timeout_d; /* jiffies */ + bool timeout_adjusted; + unsigned long duration[3]; /* jiffies */ + bool duration_adjusted; struct dentry **bios_dir; diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index e422665..a1673dc 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -792,7 +792,7 @@ unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal) index = tpm2_ordinal_duration[ordinal - TPM2_CC_FIRST]; if (index != TPM_UNDEFINED) - duration = chip->vendor.duration[index]; + duration = chip->duration[index]; if (duration <= 0) duration = 2 * 60 * HZ; diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 55fa51f..1b66da6 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -169,10 +169,10 @@ static int i2c_atmel_probe(struct i2c_client *client, return -ENOMEM; /* Default timeouts */ - chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); - chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT); - chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); - chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); + chip->timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); + chip->timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT); + chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); + chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); /* There is no known way to probe for this device, and all version * information seems to be read via TPM commands. Thus we rely on the diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 8318946..a426b6f 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -321,7 +321,7 @@ static int request_locality(struct tpm_chip *chip, int loc) iic_tpm_write(TPM_ACCESS(loc), &buf, 1); /* wait for burstcount */ - stop = jiffies + chip->vendor.timeout_a; + stop = jiffies + chip->timeout_a; do { if (check_locality(chip, loc) >= 0) return loc; @@ -363,7 +363,7 @@ static ssize_t get_burstcount(struct tpm_chip *chip) /* wait for burstcount */ /* which timeout value, spec has 2 answers (c & d) */ - stop = jiffies + chip->vendor.timeout_d; + stop = jiffies + chip->timeout_d; do { /* Note: STS is little endian */ if (iic_tpm_read(TPM_STS(tpm_dev.locality)+1, buf, 3) < 0) @@ -465,7 +465,7 @@ static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) goto out; } - wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); + wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c, &status); if (status & TPM_STS_DATA_AVAIL) { /* retry? */ dev_err(&chip->dev, "Error left over data\n"); size = -EIO; @@ -501,7 +501,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) tpm_tis_i2c_ready(chip); if (wait_for_stat (chip, TPM_STS_COMMAND_READY, - chip->vendor.timeout_b, &status) < 0) { + chip->timeout_b, &status) < 0) { rc = -ETIME; goto out_err; } @@ -531,7 +531,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) } wait_for_stat(chip, TPM_STS_VALID, - chip->vendor.timeout_c, &status); + chip->timeout_c, &status); if ((status & TPM_STS_DATA_EXPECT) == 0) { rc = -EIO; @@ -541,7 +541,7 @@ static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) /* write last byte */ iic_tpm_write(TPM_DATA_FIFO(tpm_dev.locality), &(buf[count]), 1); - wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); + wait_for_stat(chip, TPM_STS_VALID, chip->timeout_c, &status); if ((status & TPM_STS_DATA_EXPECT) != 0) { rc = -EIO; goto out_err; @@ -587,10 +587,10 @@ static int tpm_tis_i2c_init(struct device *dev) return PTR_ERR(chip); /* Default timeouts */ - 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->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); if (request_locality(chip, 0) != 0) { dev_err(dev, "could not request locality\n"); diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 2c1fa26..491a16a 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -144,7 +144,7 @@ static void i2c_nuvoton_ready(struct tpm_chip *chip) static int i2c_nuvoton_get_burstcount(struct i2c_client *client, struct tpm_chip *chip) { - unsigned long stop = jiffies + chip->vendor.timeout_d; + unsigned long stop = jiffies + chip->timeout_d; s32 status; int burst_count = -1; u8 data; @@ -239,7 +239,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client, while (size < count && i2c_nuvoton_wait_for_data_avail(chip, - chip->vendor.timeout_c, + chip->timeout_c, &priv->read_queue) == 0) { burst_count = i2c_nuvoton_get_burstcount(client, chip); if (burst_count < 0) { @@ -289,7 +289,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) * tag, paramsize, and result */ status = i2c_nuvoton_wait_for_data_avail( - chip, chip->vendor.timeout_c, &priv->read_queue); + chip, chip->timeout_c, &priv->read_queue); if (status != 0) { dev_err(dev, "%s() timeout on dataAvail\n", __func__); size = -ETIMEDOUT; @@ -329,7 +329,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) } if (i2c_nuvoton_wait_for_stat( chip, TPM_STS_VALID | TPM_STS_DATA_AVAIL, - TPM_STS_VALID, chip->vendor.timeout_c, + TPM_STS_VALID, chip->timeout_c, NULL)) { dev_err(dev, "%s() error left over data\n", __func__); size = -ETIMEDOUT; @@ -362,7 +362,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) i2c_nuvoton_ready(chip); if (i2c_nuvoton_wait_for_stat(chip, TPM_STS_COMMAND_READY, TPM_STS_COMMAND_READY, - chip->vendor.timeout_b, NULL)) { + chip->timeout_b, NULL)) { dev_err(dev, "%s() timeout on commandReady\n", __func__); rc = -EIO; @@ -394,7 +394,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) TPM_STS_EXPECT, TPM_STS_VALID | TPM_STS_EXPECT, - chip->vendor.timeout_c, + chip->timeout_c, NULL); if (rc < 0) { dev_err(dev, "%s() timeout on Expect\n", @@ -419,7 +419,7 @@ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) rc = i2c_nuvoton_wait_for_stat(chip, TPM_STS_VALID | TPM_STS_EXPECT, TPM_STS_VALID, - chip->vendor.timeout_c, NULL); + chip->timeout_c, NULL); if (rc) { dev_err(dev, "%s() timeout on Expect to clear\n", __func__); @@ -548,10 +548,10 @@ static int i2c_nuvoton_probe(struct i2c_client *client, init_waitqueue_head(&priv->read_queue); /* Default timeouts */ - chip->vendor.timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); - chip->vendor.timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT); - chip->vendor.timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); - chip->vendor.timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); + chip->timeout_a = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); + chip->timeout_b = msecs_to_jiffies(TPM_I2C_LONG_TIMEOUT); + chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); + chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); /* * I2C intfcaps (interrupt capabilitieis) in the chip are hard coded to: @@ -580,7 +580,7 @@ static int i2c_nuvoton_probe(struct i2c_client *client, rc = i2c_nuvoton_wait_for_stat(chip, TPM_STS_COMMAND_READY, TPM_STS_COMMAND_READY, - chip->vendor.timeout_b, + chip->timeout_b, NULL); if (rc == 0) { /* diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 9e2ba1d..fdb6c48 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -133,7 +133,7 @@ static inline int is_itpm(struct acpi_device *dev) static int wait_startup(struct tpm_chip *chip, int l) { struct priv_data *priv = chip->vendor.priv; - unsigned long stop = jiffies + chip->vendor.timeout_a; + unsigned long stop = jiffies + chip->timeout_a; do { if (ioread8(priv->iobase + TPM_ACCESS(l)) & TPM_ACCESS_VALID) @@ -178,7 +178,7 @@ static int request_locality(struct tpm_chip *chip, int l) iowrite8(TPM_ACCESS_REQUEST_USE, priv->iobase + TPM_ACCESS(l)); - stop = jiffies + chip->vendor.timeout_a; + stop = jiffies + chip->timeout_a; if (chip->flags & TPM_CHIP_FLAG_IRQ) { again: @@ -232,7 +232,7 @@ static int get_burstcount(struct tpm_chip *chip) /* wait for burstcount */ /* which timeout value, spec has 2 answers (c & d) */ - stop = jiffies + chip->vendor.timeout_d; + stop = jiffies + chip->timeout_d; do { burstcnt = ioread8(priv->iobase + TPM_STS(priv->locality) + 1); @@ -253,7 +253,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) while (size < count && wait_for_tpm_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, - chip->vendor.timeout_c, + chip->timeout_c, &priv->read_queue, true) == 0) { burstcnt = get_burstcount(chip); @@ -296,7 +296,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) goto out; } - wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, + wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, &priv->int_queue, false); status = tpm_tis_status(chip); if (status & TPM_STS_DATA_AVAIL) { /* retry? */ @@ -333,7 +333,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) if ((status & TPM_STS_COMMAND_READY) == 0) { tpm_tis_ready(chip); if (wait_for_tpm_stat - (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, + (chip, TPM_STS_COMMAND_READY, chip->timeout_b, &priv->int_queue, false) < 0) { rc = -ETIME; goto out_err; @@ -348,7 +348,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) count++; } - wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, + wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, &priv->int_queue, false); status = tpm_tis_status(chip); if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) { @@ -360,7 +360,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) /* write last byte */ iowrite8(buf[count], priv->iobase + TPM_DATA_FIFO(priv->locality)); - wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, + wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, &priv->int_queue, false); status = tpm_tis_status(chip); if ((status & TPM_STS_DATA_EXPECT) != 0) { @@ -711,10 +711,10 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, return PTR_ERR(priv->iobase); /* Maximum timeouts */ - chip->vendor.timeout_a = TIS_TIMEOUT_A_MAX; - chip->vendor.timeout_b = TIS_TIMEOUT_B_MAX; - chip->vendor.timeout_c = TIS_TIMEOUT_C_MAX; - chip->vendor.timeout_d = TIS_TIMEOUT_D_MAX; + chip->timeout_a = TIS_TIMEOUT_A_MAX; + chip->timeout_b = TIS_TIMEOUT_B_MAX; + chip->timeout_c = TIS_TIMEOUT_C_MAX; + chip->timeout_d = TIS_TIMEOUT_D_MAX; if (wait_startup(chip, 0) != 0) { rc = -ENODEV; diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index cca89d9..1b28f96 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -89,7 +89,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) return -EINVAL; /* Wait for completion of any existing command or cancellation */ - if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, chip->vendor.timeout_c, + if (wait_for_tpm_stat(chip, VTPM_STATUS_IDLE, chip->timeout_c, &priv->read_queue, true) < 0) { vtpm_cancel(chip); return -ETIME; @@ -126,7 +126,7 @@ static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) return -ECANCELED; /* In theory the wait at the end of _send makes this one unnecessary */ - if (wait_for_tpm_stat(chip, VTPM_STATUS_RESULT, chip->vendor.timeout_c, + if (wait_for_tpm_stat(chip, VTPM_STATUS_RESULT, chip->timeout_c, &priv->read_queue, true) < 0) { vtpm_cancel(chip); return -ETIME; -- cgit v0.10.2 From 9e0d39d8a6a0a8805d05fba22e3fbe80b5c8c4cb Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 31 Mar 2016 22:57:00 +0200 Subject: tpm: Remove useless priv field in struct tpm_vendor_specific Remove useless priv field in struct tpm_vendor_specific and take benefit of chip->dev.driver_data. As priv is the latest field available in struct tpm_vendor_specific, remove any reference to that structure. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index f8e8123..028a9cd 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -26,6 +26,7 @@ #include #include +#include "../tpm.h" #include "st33zp24.h" #define TPM_DUMMY_BYTE 0xAA @@ -112,7 +113,9 @@ static const struct st33zp24_phy_ops i2c_phy_ops = { static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client) { - struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); + struct tpm_chip *chip = i2c_get_clientdata(client); + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); + struct st33zp24_i2c_phy *phy = tpm_dev->phy_id; struct gpio_desc *gpiod_lpcpd; struct device *dev = &client->dev; @@ -138,7 +141,9 @@ static int st33zp24_i2c_acpi_request_resources(struct i2c_client *client) static int st33zp24_i2c_of_request_resources(struct i2c_client *client) { - struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); + struct tpm_chip *chip = i2c_get_clientdata(client); + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); + struct st33zp24_i2c_phy *phy = tpm_dev->phy_id; struct device_node *pp; int gpio; int ret; @@ -176,8 +181,10 @@ static int st33zp24_i2c_of_request_resources(struct i2c_client *client) static int st33zp24_i2c_request_resources(struct i2c_client *client) { + struct tpm_chip *chip = i2c_get_clientdata(client); + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); + struct st33zp24_i2c_phy *phy = tpm_dev->phy_id; struct st33zp24_platform_data *pdata; - struct st33zp24_i2c_phy *phy = i2c_get_clientdata(client); int ret; pdata = client->dev.platform_data; @@ -234,8 +241,6 @@ static int st33zp24_i2c_probe(struct i2c_client *client, phy->client = client; - i2c_set_clientdata(client, phy); - pdata = client->dev.platform_data; if (!pdata && client->dev.of_node) { ret = st33zp24_i2c_of_request_resources(client); diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c index 608dbc6..9f5a011 100644 --- a/drivers/char/tpm/st33zp24/spi.c +++ b/drivers/char/tpm/st33zp24/spi.c @@ -26,6 +26,7 @@ #include #include +#include "../tpm.h" #include "st33zp24.h" #define TPM_DATA_FIFO 0x24 @@ -231,7 +232,9 @@ static const struct st33zp24_phy_ops spi_phy_ops = { static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev) { - struct st33zp24_spi_phy *phy = spi_get_drvdata(spi_dev); + struct tpm_chip *chip = spi_get_drvdata(spi_dev); + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); + struct st33zp24_spi_phy *phy = tpm_dev->phy_id; struct gpio_desc *gpiod_lpcpd; struct device *dev = &spi_dev->dev; @@ -256,7 +259,9 @@ static int st33zp24_spi_acpi_request_resources(struct spi_device *spi_dev) static int st33zp24_spi_of_request_resources(struct spi_device *spi_dev) { - struct st33zp24_spi_phy *phy = spi_get_drvdata(spi_dev); + struct tpm_chip *chip = spi_get_drvdata(spi_dev); + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); + struct st33zp24_spi_phy *phy = tpm_dev->phy_id; struct device_node *pp; int gpio; int ret; @@ -294,7 +299,9 @@ static int st33zp24_spi_of_request_resources(struct spi_device *spi_dev) static int st33zp24_spi_request_resources(struct spi_device *dev) { - struct st33zp24_spi_phy *phy = spi_get_drvdata(dev); + struct tpm_chip *chip = spi_get_drvdata(dev); + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); + struct st33zp24_spi_phy *phy = tpm_dev->phy_id; struct st33zp24_platform_data *pdata; int ret; @@ -347,8 +354,6 @@ static int st33zp24_spi_probe(struct spi_device *dev) phy->spi_device = dev; - spi_set_drvdata(dev, phy); - pdata = dev->dev.platform_data; if (!pdata && dev->dev.of_node) { ret = st33zp24_spi_of_request_resources(dev); diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index 07531e9..a7c99a2 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -94,11 +94,9 @@ static u8 clear_interruption(struct st33zp24_dev *tpm_dev) */ static void st33zp24_cancel(struct tpm_chip *chip) { - struct st33zp24_dev *tpm_dev; + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); u8 data; - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); - data = TPM_STS_COMMAND_READY; tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1); } /* st33zp24_cancel() */ @@ -110,11 +108,9 @@ static void st33zp24_cancel(struct tpm_chip *chip) */ static u8 st33zp24_status(struct tpm_chip *chip) { - struct st33zp24_dev *tpm_dev; + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); u8 data; - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); - tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1); return data; } /* st33zp24_status() */ @@ -126,12 +122,10 @@ static u8 st33zp24_status(struct tpm_chip *chip) */ static int check_locality(struct tpm_chip *chip) { - struct st33zp24_dev *tpm_dev; + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); u8 data; u8 status; - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); - status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1); if (status && (data & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == @@ -148,13 +142,11 @@ static int check_locality(struct tpm_chip *chip) */ static int request_locality(struct tpm_chip *chip) { + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); unsigned long stop; long ret; - struct st33zp24_dev *tpm_dev; u8 data; - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); - if (check_locality(chip) == tpm_dev->locality) return tpm_dev->locality; @@ -182,10 +174,9 @@ static int request_locality(struct tpm_chip *chip) */ static void release_locality(struct tpm_chip *chip) { - struct st33zp24_dev *tpm_dev; + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); u8 data; - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); data = TPM_ACCESS_ACTIVE_LOCALITY; tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1); @@ -198,12 +189,10 @@ static void release_locality(struct tpm_chip *chip) */ static int get_burstcount(struct tpm_chip *chip) { + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); unsigned long stop; int burstcnt, status; u8 temp; - struct st33zp24_dev *tpm_dev; - - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); stop = jiffies + chip->timeout_d; do { @@ -263,15 +252,13 @@ static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, wait_queue_head_t *queue, bool check_cancel) { + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); unsigned long stop; int ret = 0; bool canceled = false; bool condition; u32 cur_intrs; u8 status; - struct st33zp24_dev *tpm_dev; - - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); /* check current status */ status = st33zp24_status(chip); @@ -329,10 +316,8 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, */ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) { + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); int size = 0, burstcnt, len, ret; - struct st33zp24_dev *tpm_dev; - - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); while (size < count && wait_for_stat(chip, @@ -362,9 +347,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) { struct tpm_chip *chip = dev_id; - struct st33zp24_dev *tpm_dev; - - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); tpm_dev->intrs++; wake_up_interruptible(&tpm_dev->read_queue); @@ -385,19 +368,17 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, size_t len) { + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); u32 status, i, size, ordinal; int burstcnt = 0; int ret; u8 data; - struct st33zp24_dev *tpm_dev; if (!chip) return -EBUSY; if (len < TPM_HEADER_SIZE) return -EBUSY; - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); - ret = request_locality(chip); if (ret < 0) return ret; @@ -557,9 +538,9 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, if (!tpm_dev) return -ENOMEM; - TPM_VPRIV(chip) = tpm_dev; tpm_dev->phy_id = phy_id; tpm_dev->ops = ops; + dev_set_drvdata(&chip->dev, tpm_dev); chip->timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); chip->timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); @@ -643,10 +624,9 @@ EXPORT_SYMBOL(st33zp24_remove); int st33zp24_pm_suspend(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); - struct st33zp24_dev *tpm_dev; - int ret = 0; + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + int ret = 0; if (gpio_is_valid(tpm_dev->io_lpcpd)) gpio_set_value(tpm_dev->io_lpcpd, 0); @@ -665,11 +645,9 @@ EXPORT_SYMBOL(st33zp24_pm_suspend); int st33zp24_pm_resume(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); - struct st33zp24_dev *tpm_dev; + struct st33zp24_dev *tpm_dev = dev_get_drvdata(&chip->dev); int ret = 0; - tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); - if (gpio_is_valid(tpm_dev->io_lpcpd)) { gpio_set_value(tpm_dev->io_lpcpd, 1); ret = wait_for_stat(chip, diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 93757e8..8bc6fb8 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -128,14 +128,6 @@ enum tpm2_startup_types { TPM2_SU_STATE = 0x0001, }; -struct tpm_chip; - -struct tpm_vendor_specific { - void *priv; -}; - -#define TPM_VPRIV(c) ((c)->vendor.priv) - #define TPM_VID_INTEL 0x8086 #define TPM_VID_WINBOND 0x1050 #define TPM_VID_STM 0x104A @@ -166,7 +158,6 @@ struct tpm_chip { struct mutex tpm_mutex; /* tpm is processing */ - struct tpm_vendor_specific vendor; unsigned long timeout_a; /* jiffies */ unsigned long timeout_b; /* jiffies */ unsigned long timeout_c; /* jiffies */ diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index b769299..0d322ab 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -37,7 +37,7 @@ enum tpm_atmel_read_status { static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct tpm_atmel_priv *priv = chip->vendor.priv; + struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); u8 status, *hdr = buf; u32 size; int i; @@ -96,7 +96,7 @@ static int tpm_atml_recv(struct tpm_chip *chip, u8 *buf, size_t count) static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) { - struct tpm_atmel_priv *priv = chip->vendor.priv; + struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); int i; dev_dbg(&chip->dev, "tpm_atml_send:\n"); @@ -110,14 +110,14 @@ static int tpm_atml_send(struct tpm_chip *chip, u8 *buf, size_t count) static void tpm_atml_cancel(struct tpm_chip *chip) { - struct tpm_atmel_priv *priv = chip->vendor.priv; + struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); iowrite8(ATML_STATUS_ABORT, priv->iobase + 1); } static u8 tpm_atml_status(struct tpm_chip *chip) { - struct tpm_atmel_priv *priv = chip->vendor.priv; + struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); return ioread8(priv->iobase + 1); } @@ -142,7 +142,7 @@ static struct platform_device *pdev; static void atml_plat_remove(void) { struct tpm_chip *chip = dev_get_drvdata(&pdev->dev); - struct tpm_atmel_priv *priv = chip->vendor.priv; + struct tpm_atmel_priv *priv = dev_get_drvdata(&chip->dev); if (chip) { tpm_chip_unregister(chip); @@ -207,7 +207,7 @@ static int __init init_atmel(void) goto err_unreg_dev; } - chip->vendor.priv = priv; + dev_set_drvdata(&chip->dev, priv); rc = tpm_chip_register(chip); if (rc) diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h index 1938c0b..4f96d80 100644 --- a/drivers/char/tpm/tpm_atmel.h +++ b/drivers/char/tpm/tpm_atmel.h @@ -29,11 +29,6 @@ struct tpm_atmel_priv { void __iomem *iobase; }; -static inline struct tpm_atmel_priv *atmel_get_priv(struct tpm_chip *chip) -{ - return chip->vendor.priv; -} - #ifdef CONFIG_PPC64 #include diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 5afe684..1547636 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -87,7 +87,7 @@ static SIMPLE_DEV_PM_OPS(crb_pm, tpm_pm_suspend, tpm_pm_resume); static u8 crb_status(struct tpm_chip *chip) { - struct crb_priv *priv = chip->vendor.priv; + struct crb_priv *priv = dev_get_drvdata(&chip->dev); u8 sts = 0; if ((ioread32(&priv->cca->start) & CRB_START_INVOKE) != @@ -99,7 +99,7 @@ static u8 crb_status(struct tpm_chip *chip) static int crb_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct crb_priv *priv = chip->vendor.priv; + struct crb_priv *priv = dev_get_drvdata(&chip->dev); unsigned int expected; /* sanity check */ @@ -139,7 +139,7 @@ static int crb_do_acpi_start(struct tpm_chip *chip) static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) { - struct crb_priv *priv = chip->vendor.priv; + struct crb_priv *priv = dev_get_drvdata(&chip->dev); int rc = 0; if (len > ioread32(&priv->cca->cmd_size)) { @@ -166,7 +166,7 @@ static int crb_send(struct tpm_chip *chip, u8 *buf, size_t len) static void crb_cancel(struct tpm_chip *chip) { - struct crb_priv *priv = chip->vendor.priv; + struct crb_priv *priv = dev_get_drvdata(&chip->dev); iowrite32(cpu_to_le32(CRB_CANCEL_INVOKE), &priv->cca->cancel); @@ -181,7 +181,7 @@ static void crb_cancel(struct tpm_chip *chip) static bool crb_req_canceled(struct tpm_chip *chip, u8 status) { - struct crb_priv *priv = chip->vendor.priv; + struct crb_priv *priv = dev_get_drvdata(&chip->dev); u32 cancel = ioread32(&priv->cca->cancel); return (cancel & CRB_CANCEL_INVOKE) == CRB_CANCEL_INVOKE; @@ -206,7 +206,7 @@ static int crb_init(struct acpi_device *device, struct crb_priv *priv) if (IS_ERR(chip)) return PTR_ERR(chip); - chip->vendor.priv = priv; + dev_set_drvdata(&chip->dev, priv); chip->acpi_dev_handle = device->handle; chip->flags = TPM_CHIP_FLAG_TPM2; diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 1b66da6..c37aa72 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -51,7 +51,7 @@ struct priv_data { static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); struct i2c_client *client = to_i2c_client(chip->dev.parent); s32 status; @@ -70,7 +70,7 @@ static int i2c_atmel_send(struct tpm_chip *chip, u8 *buf, size_t len) static int i2c_atmel_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); struct i2c_client *client = to_i2c_client(chip->dev.parent); struct tpm_output_header *hdr = (struct tpm_output_header *)priv->buffer; @@ -111,7 +111,7 @@ static void i2c_atmel_cancel(struct tpm_chip *chip) static u8 i2c_atmel_read_status(struct tpm_chip *chip) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); struct i2c_client *client = to_i2c_client(chip->dev.parent); int rc; @@ -155,6 +155,7 @@ static int i2c_atmel_probe(struct i2c_client *client, { struct tpm_chip *chip; struct device *dev = &client->dev; + struct priv_data *priv; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; @@ -163,9 +164,8 @@ static int i2c_atmel_probe(struct i2c_client *client, if (IS_ERR(chip)) return PTR_ERR(chip); - chip->vendor.priv = devm_kzalloc(dev, sizeof(struct priv_data), - GFP_KERNEL); - if (!chip->vendor.priv) + priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL); + if (!priv) return -ENOMEM; /* Default timeouts */ @@ -174,6 +174,8 @@ static int i2c_atmel_probe(struct i2c_client *client, chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); + dev_set_drvdata(&chip->dev, priv); + /* There is no known way to probe for this device, and all version * information seems to be read via TPM commands. Thus we rely on the * TPM startup process in the common code to detect the device. */ diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 491a16a..4e32094 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -180,7 +180,7 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value, { if ((chip->flags & TPM_CHIP_FLAG_IRQ) && queue) { s32 rc; - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); unsigned int cur_intrs = priv->intrs; enable_irq(priv->irq); @@ -233,7 +233,7 @@ static int i2c_nuvoton_wait_for_data_avail(struct tpm_chip *chip, u32 timeout, static int i2c_nuvoton_recv_data(struct i2c_client *client, struct tpm_chip *chip, u8 *buf, size_t count) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); s32 rc; int burst_count, bytes2read, size = 0; @@ -267,7 +267,7 @@ static int i2c_nuvoton_recv_data(struct i2c_client *client, /* Read TPM command results */ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); struct device *dev = chip->dev.parent; struct i2c_client *client = to_i2c_client(dev); s32 rc; @@ -351,7 +351,7 @@ static int i2c_nuvoton_recv(struct tpm_chip *chip, u8 *buf, size_t count) */ static int i2c_nuvoton_send(struct tpm_chip *chip, u8 *buf, size_t len) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); struct device *dev = chip->dev.parent; struct i2c_client *client = to_i2c_client(dev); u32 ordinal; @@ -478,7 +478,7 @@ static const struct tpm_class_ops tpm_i2c = { static irqreturn_t i2c_nuvoton_int_handler(int dummy, void *dev_id) { struct tpm_chip *chip = dev_id; - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); priv->intrs++; wake_up(&priv->read_queue); @@ -543,7 +543,6 @@ static int i2c_nuvoton_probe(struct i2c_client *client, priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL); if (!priv) return -ENOMEM; - chip->vendor.priv = priv; init_waitqueue_head(&priv->read_queue); @@ -553,6 +552,8 @@ static int i2c_nuvoton_probe(struct i2c_client *client, chip->timeout_c = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); chip->timeout_d = msecs_to_jiffies(TPM_I2C_SHORT_TIMEOUT); + dev_set_drvdata(&chip->dev, priv); + /* * I2C intfcaps (interrupt capabilitieis) in the chip are hard coded to: * TPM_INTF_INT_LEVEL_LOW | TPM_INTF_DATA_AVAIL_INT @@ -619,8 +620,8 @@ static int i2c_nuvoton_probe(struct i2c_client *client, static int i2c_nuvoton_remove(struct i2c_client *client) { - struct device *dev = &(client->dev); - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = i2c_get_clientdata(client); + tpm_chip_unregister(chip); return 0; } diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index b0a9a9e..6b22826 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -54,21 +54,6 @@ static int ibmvtpm_send_crq(struct vio_dev *vdev, u64 w1, u64 w2) } /** - * ibmvtpm_get_data - Retrieve ibm vtpm data - * @dev: device struct - * - * Return value: - * vtpm device struct - */ -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 *)TPM_VPRIV(chip); - return NULL; -} - -/** * tpm_ibmvtpm_recv - Receive data after send * @chip: tpm chip struct * @buf: buffer to read @@ -79,12 +64,10 @@ static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev) */ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct ibmvtpm_dev *ibmvtpm; + struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); u16 len; int sig; - ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip); - if (!ibmvtpm->rtce_buf) { dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n"); return 0; @@ -122,13 +105,11 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) */ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) { - struct ibmvtpm_dev *ibmvtpm; + struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); struct ibmvtpm_crq crq; __be64 *word = (__be64 *)&crq; int rc, sig; - ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip); - if (!ibmvtpm->rtce_buf) { dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n"); return 0; @@ -289,8 +270,8 @@ static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm) */ static int tpm_ibmvtpm_remove(struct vio_dev *vdev) { - struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev); - struct tpm_chip *chip = dev_get_drvdata(ibmvtpm->dev); + struct tpm_chip *chip = dev_get_drvdata(&vdev->dev); + struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); int rc = 0; tpm_chip_unregister(chip); @@ -327,7 +308,8 @@ static int tpm_ibmvtpm_remove(struct vio_dev *vdev) */ static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev) { - struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev); + struct tpm_chip *chip = dev_get_drvdata(&vdev->dev); + struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); /* ibmvtpm initializes at probe time, so the data we are * asking for may not be set yet. Estimate that 4K required @@ -348,7 +330,8 @@ static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev) */ static int tpm_ibmvtpm_suspend(struct device *dev) { - struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev); + struct tpm_chip *chip = dev_get_drvdata(dev); + struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); struct ibmvtpm_crq crq; u64 *buf = (u64 *) &crq; int rc = 0; @@ -400,7 +383,8 @@ static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm) */ static int tpm_ibmvtpm_resume(struct device *dev) { - struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev); + struct tpm_chip *chip = dev_get_drvdata(dev); + struct ibmvtpm_dev *ibmvtpm = dev_get_drvdata(&chip->dev); int rc = 0; do { @@ -643,7 +627,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev, crq_q->index = 0; - TPM_VPRIV(chip) = (void *)ibmvtpm; + dev_set_drvdata(&chip->dev, ibmvtpm) spin_lock_init(&ibmvtpm->rtce_lock); diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 07163a4..9ff0e07 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -69,20 +69,16 @@ struct tpm_nsc_priv { unsigned long base; }; -static inline struct tpm_nsc_priv *nsc_get_priv(struct tpm_chip *chip) -{ - return chip->vendor.priv; -} - /* * Wait for a certain status to appear */ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) { + struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev); unsigned long stop; /* status immediately available check */ - *data = inb(nsc_get_priv(chip)->base + NSC_STATUS); + *data = inb(priv->base + NSC_STATUS); if ((*data & mask) == val) return 0; @@ -90,7 +86,7 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) stop = jiffies + 10 * HZ; do { msleep(TPM_TIMEOUT); - *data = inb(nsc_get_priv(chip)->base + 1); + *data = inb(priv->base + 1); if ((*data & mask) == val) return 0; } @@ -101,13 +97,14 @@ static int wait_for_stat(struct tpm_chip *chip, u8 mask, u8 val, u8 * data) static int nsc_wait_for_ready(struct tpm_chip *chip) { + struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev); int status; unsigned long stop; /* status immediately available check */ - status = inb(nsc_get_priv(chip)->base + NSC_STATUS); + status = inb(priv->base + NSC_STATUS); if (status & NSC_STATUS_OBF) - status = inb(nsc_get_priv(chip)->base + NSC_DATA); + status = inb(priv->base + NSC_DATA); if (status & NSC_STATUS_RDY) return 0; @@ -115,9 +112,9 @@ static int nsc_wait_for_ready(struct tpm_chip *chip) stop = jiffies + 100; do { msleep(TPM_TIMEOUT); - status = inb(nsc_get_priv(chip)->base + NSC_STATUS); + status = inb(priv->base + NSC_STATUS); if (status & NSC_STATUS_OBF) - status = inb(nsc_get_priv(chip)->base + NSC_DATA); + status = inb(priv->base + NSC_DATA); if (status & NSC_STATUS_RDY) return 0; } @@ -130,6 +127,7 @@ static int nsc_wait_for_ready(struct tpm_chip *chip) static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) { + struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev); u8 *buffer = buf; u8 data, *p; u32 size; @@ -143,7 +141,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; } - data = inb(nsc_get_priv(chip)->base + NSC_DATA); + data = inb(priv->base + NSC_DATA); if (data != NSC_COMMAND_NORMAL) { dev_err(&chip->dev, "not in normal mode (0x%x)\n", data); @@ -160,7 +158,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) } if (data & NSC_STATUS_F0) break; - *p = inb(nsc_get_priv(chip)->base + NSC_DATA); + *p = inb(priv->base + NSC_DATA); } if ((data & NSC_STATUS_F0) == 0 && @@ -169,7 +167,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; } - data = inb(nsc_get_priv(chip)->base + NSC_DATA); + data = inb(priv->base + NSC_DATA); if (data != NSC_COMMAND_EOC) { dev_err(&chip->dev, "expected end of command(0x%x)\n", data); @@ -187,6 +185,7 @@ static int tpm_nsc_recv(struct tpm_chip *chip, u8 * buf, size_t count) static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) { + struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev); u8 data; int i; @@ -196,7 +195,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) * fix it. Not sure why this is needed, we followed the flow * chart in the manual to the letter. */ - outb(NSC_COMMAND_CANCEL, nsc_get_priv(chip)->base + NSC_COMMAND); + outb(NSC_COMMAND_CANCEL, priv->base + NSC_COMMAND); if (nsc_wait_for_ready(chip) != 0) return -EIO; @@ -206,7 +205,7 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) return -EIO; } - outb(NSC_COMMAND_NORMAL, nsc_get_priv(chip)->base + NSC_COMMAND); + outb(NSC_COMMAND_NORMAL, priv->base + NSC_COMMAND); if (wait_for_stat(chip, NSC_STATUS_IBR, NSC_STATUS_IBR, &data) < 0) { dev_err(&chip->dev, "IBR timeout\n"); return -EIO; @@ -218,26 +217,30 @@ static int tpm_nsc_send(struct tpm_chip *chip, u8 * buf, size_t count) "IBF timeout (while writing data)\n"); return -EIO; } - outb(buf[i], nsc_get_priv(chip)->base + NSC_DATA); + outb(buf[i], priv->base + NSC_DATA); } if (wait_for_stat(chip, NSC_STATUS_IBF, 0, &data) < 0) { dev_err(&chip->dev, "IBF timeout\n"); return -EIO; } - outb(NSC_COMMAND_EOC, nsc_get_priv(chip)->base + NSC_COMMAND); + outb(NSC_COMMAND_EOC, priv->base + NSC_COMMAND); return count; } static void tpm_nsc_cancel(struct tpm_chip *chip) { - outb(NSC_COMMAND_CANCEL, nsc_get_priv(chip)->base + NSC_COMMAND); + struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev); + + outb(NSC_COMMAND_CANCEL, priv->base + NSC_COMMAND); } static u8 tpm_nsc_status(struct tpm_chip *chip) { - return inb(nsc_get_priv(chip)->base + NSC_STATUS); + struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev); + + return inb(priv->base + NSC_STATUS); } static bool tpm_nsc_req_canceled(struct tpm_chip *chip, u8 status) @@ -260,9 +263,10 @@ static struct platform_device *pdev = NULL; static void tpm_nsc_remove(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_nsc_priv *priv = dev_get_drvdata(&chip->dev); tpm_chip_unregister(chip); - release_region(nsc_get_priv(chip)->base, 2); + release_region(priv->base, 2); } static SIMPLE_DEV_PM_OPS(tpm_nsc_pm, tpm_pm_suspend, tpm_pm_resume); @@ -334,7 +338,7 @@ static int __init init_nsc(void) goto err_rel_reg; } - chip->vendor.priv = priv; + dev_set_drvdata(&chip->dev, priv); rc = tpm_chip_register(chip); if (rc) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index fdb6c48..a6b2d46 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -132,7 +132,7 @@ static inline int is_itpm(struct acpi_device *dev) * correct values in the other bits.' */ static int wait_startup(struct tpm_chip *chip, int l) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); unsigned long stop = jiffies + chip->timeout_a; do { if (ioread8(priv->iobase + TPM_ACCESS(l)) & @@ -145,7 +145,7 @@ static int wait_startup(struct tpm_chip *chip, int l) static int check_locality(struct tpm_chip *chip, int l) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); if ((ioread8(priv->iobase + TPM_ACCESS(l)) & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == @@ -157,7 +157,7 @@ static int check_locality(struct tpm_chip *chip, int l) static void release_locality(struct tpm_chip *chip, int l, int force) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); if (force || (ioread8(priv->iobase + TPM_ACCESS(l)) & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == @@ -168,7 +168,7 @@ static void release_locality(struct tpm_chip *chip, int l, int force) static int request_locality(struct tpm_chip *chip, int l) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); unsigned long stop, timeout; long rc; @@ -209,7 +209,7 @@ again: static u8 tpm_tis_status(struct tpm_chip *chip) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); return ioread8(priv->iobase + TPM_STS(priv->locality)); @@ -217,7 +217,7 @@ static u8 tpm_tis_status(struct tpm_chip *chip) static void tpm_tis_ready(struct tpm_chip *chip) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); /* this causes the current command to be aborted */ iowrite8(TPM_STS_COMMAND_READY, @@ -226,7 +226,7 @@ static void tpm_tis_ready(struct tpm_chip *chip) static int get_burstcount(struct tpm_chip *chip) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); unsigned long stop; int burstcnt; @@ -248,7 +248,7 @@ static int get_burstcount(struct tpm_chip *chip) static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); int size = 0, burstcnt; while (size < count && wait_for_tpm_stat(chip, @@ -266,7 +266,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); int size = 0; int expected, status; @@ -322,7 +322,7 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)"); */ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); int rc, status, burstcnt; size_t count = 0; @@ -378,7 +378,7 @@ out_err: static void disable_interrupts(struct tpm_chip *chip) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); u32 intmask; intmask = @@ -399,7 +399,7 @@ static void disable_interrupts(struct tpm_chip *chip) */ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); int rc; u32 ordinal; unsigned long dur; @@ -437,7 +437,7 @@ out_err: static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) { int rc, irq; - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || priv->irq_tested) return tpm_tis_send_main(chip, buf, len); @@ -471,7 +471,7 @@ static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = { static bool tpm_tis_update_timeouts(struct tpm_chip *chip, unsigned long *timeout_cap) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); int i; u32 did_vid; @@ -495,7 +495,7 @@ static bool tpm_tis_update_timeouts(struct tpm_chip *chip, */ static int probe_itpm(struct tpm_chip *chip) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); int rc = 0; u8 cmd_getticks[] = { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, @@ -537,7 +537,7 @@ out: static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); switch (priv->manufacturer_id) { case TPM_VID_WINBOND: @@ -564,7 +564,7 @@ static const struct tpm_class_ops tpm_tis = { static irqreturn_t tis_int_handler(int dummy, void *dev_id) { struct tpm_chip *chip = dev_id; - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); u32 interrupt; int i; @@ -574,7 +574,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) if (interrupt == 0) return IRQ_NONE; - ((struct priv_data *)chip->vendor.priv)->irq_tested = true; + priv->irq_tested = true; if (interrupt & TPM_INTF_DATA_AVAIL_INT) wake_up_interruptible(&priv->read_queue); if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) @@ -601,7 +601,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, int flags, int irq) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); u8 original_int_vec; if (devm_request_irq(&chip->dev, irq, tis_int_handler, flags, @@ -654,7 +654,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, */ static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); u8 original_int_vec; int i; @@ -678,7 +678,7 @@ MODULE_PARM_DESC(interrupts, "Enable interrupts"); static void tpm_tis_remove(struct tpm_chip *chip) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); void __iomem *reg = priv->iobase + TPM_INT_ENABLE(priv->locality); iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(reg), reg); @@ -701,7 +701,6 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, if (IS_ERR(chip)) return PTR_ERR(chip); - chip->vendor.priv = priv; #ifdef CONFIG_ACPI chip->acpi_dev_handle = acpi_dev_handle; #endif @@ -716,6 +715,8 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, chip->timeout_c = TIS_TIMEOUT_C_MAX; chip->timeout_d = TIS_TIMEOUT_D_MAX; + dev_set_drvdata(&chip->dev, priv); + if (wait_startup(chip, 0) != 0) { rc = -ENODEV; goto out_err; @@ -840,7 +841,7 @@ out_err: #ifdef CONFIG_PM_SLEEP static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) { - struct priv_data *priv = chip->vendor.priv; + struct priv_data *priv = dev_get_drvdata(&chip->dev); u32 intmask; /* reenable interrupts that device may have lost or diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index 1b28f96..62028f4 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -41,7 +41,7 @@ enum status_bits { static u8 vtpm_status(struct tpm_chip *chip) { - struct tpm_private *priv = TPM_VPRIV(chip); + struct tpm_private *priv = dev_get_drvdata(&chip->dev); switch (priv->shr->state) { case VTPM_STATE_IDLE: return VTPM_STATUS_IDLE | VTPM_STATUS_CANCELED; @@ -62,7 +62,7 @@ static bool vtpm_req_canceled(struct tpm_chip *chip, u8 status) static void vtpm_cancel(struct tpm_chip *chip) { - struct tpm_private *priv = TPM_VPRIV(chip); + struct tpm_private *priv = dev_get_drvdata(&chip->dev); priv->shr->state = VTPM_STATE_CANCEL; wmb(); notify_remote_via_evtchn(priv->evtchn); @@ -75,7 +75,7 @@ static unsigned int shr_data_offset(struct vtpm_shared_page *shr) static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) { - struct tpm_private *priv = TPM_VPRIV(chip); + struct tpm_private *priv = dev_get_drvdata(&chip->dev); struct vtpm_shared_page *shr = priv->shr; unsigned int offset = shr_data_offset(shr); @@ -117,7 +117,7 @@ static int vtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct tpm_private *priv = TPM_VPRIV(chip); + struct tpm_private *priv = dev_get_drvdata(&chip->dev); struct vtpm_shared_page *shr = priv->shr; unsigned int offset = shr_data_offset(shr); size_t length = shr->length; @@ -184,7 +184,7 @@ static int setup_chip(struct device *dev, struct tpm_private *priv) init_waitqueue_head(&priv->read_queue); priv->chip = chip; - TPM_VPRIV(chip) = priv; + dev_set_drvdata(&chip->dev, priv); return 0; } @@ -320,10 +320,10 @@ static int tpmfront_probe(struct xenbus_device *dev, static int tpmfront_remove(struct xenbus_device *dev) { struct tpm_chip *chip = dev_get_drvdata(&dev->dev); - struct tpm_private *priv = TPM_VPRIV(chip); + struct tpm_private *priv = dev_get_drvdata(&chip->dev); tpm_chip_unregister(chip); ring_free(priv); - TPM_VPRIV(chip) = NULL; + dev_set_drvdata(&chip->dev, NULL); return 0; } -- cgit v0.10.2 From 062807f20e3f363dc5db0c7927bb6223dd1f46a2 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Mon, 18 Apr 2016 13:26:13 -0400 Subject: tpm: Remove all uses of drvdata from the TPM Core The final thing preventing this was the way the sysfs files were attached to the pdev. Follow the approach developed for ppi and move the sysfs files to the chip->dev with symlinks from the pdev for compatibility. Everything in the core now sanely uses container_of to get the chip. Signed-off-by: Jason Gunthorpe Signed-off-by: Stefan Berger Tested-by: Jarkko Sakkinen Tested-by: Stefan Berger Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 2ea2f15..57d9794 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -170,9 +170,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev, chip->dev.class = tpm_class; chip->dev.release = tpm_dev_release; chip->dev.parent = dev; -#ifdef CONFIG_ACPI chip->dev.groups = chip->groups; -#endif if (chip->dev_num == 0) chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); @@ -277,14 +275,10 @@ static void tpm_del_char_device(struct tpm_chip *chip) static int tpm1_chip_register(struct tpm_chip *chip) { - int rc; - if (chip->flags & TPM_CHIP_FLAG_TPM2) return 0; - rc = tpm_sysfs_add_device(chip); - if (rc) - return rc; + tpm_sysfs_add_device(chip); chip->bios_dir = tpm_bios_log_setup(dev_name(&chip->dev)); @@ -298,10 +292,50 @@ static void tpm1_chip_unregister(struct tpm_chip *chip) if (chip->bios_dir) tpm_bios_log_teardown(chip->bios_dir); +} + +static void tpm_del_legacy_sysfs(struct tpm_chip *chip) +{ + struct attribute **i; + + if (chip->flags & TPM_CHIP_FLAG_TPM2) + return; - tpm_sysfs_del_device(chip); + sysfs_remove_link(&chip->dev.parent->kobj, "ppi"); + + for (i = chip->groups[0]->attrs; *i != NULL; ++i) + sysfs_remove_link(&chip->dev.parent->kobj, (*i)->name); } +/* For compatibility with legacy sysfs paths we provide symlinks from the + * parent dev directory to selected names within the tpm chip directory. Old + * kernel versions created these files directly under the parent. + */ +static int tpm_add_legacy_sysfs(struct tpm_chip *chip) +{ + struct attribute **i; + int rc; + + if (chip->flags & TPM_CHIP_FLAG_TPM2) + return 0; + + rc = __compat_only_sysfs_link_entry_to_kobj( + &chip->dev.parent->kobj, &chip->dev.kobj, "ppi"); + if (rc && rc != -ENOENT) + return rc; + + /* All the names from tpm-sysfs */ + for (i = chip->groups[0]->attrs; *i != NULL; ++i) { + rc = __compat_only_sysfs_link_entry_to_kobj( + &chip->dev.parent->kobj, &chip->dev.kobj, (*i)->name); + if (rc) { + tpm_del_legacy_sysfs(chip); + return rc; + } + } + + return 0; +} /* * tpm_chip_register() - create a character device for the TPM chip * @chip: TPM chip to use. @@ -324,24 +358,20 @@ int tpm_chip_register(struct tpm_chip *chip) tpm_add_ppi(chip); rc = tpm_add_char_device(chip); - if (rc) - goto out_err; + if (rc) { + tpm1_chip_unregister(chip); + return rc; + } chip->flags |= TPM_CHIP_FLAG_REGISTERED; - if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { - rc = __compat_only_sysfs_link_entry_to_kobj( - &chip->dev.parent->kobj, &chip->dev.kobj, "ppi"); - if (rc && rc != -ENOENT) { - tpm_chip_unregister(chip); - return rc; - } + rc = tpm_add_legacy_sysfs(chip); + if (rc) { + tpm_chip_unregister(chip); + return rc; } return 0; -out_err: - tpm1_chip_unregister(chip); - return rc; } EXPORT_SYMBOL_GPL(tpm_chip_register); @@ -363,8 +393,7 @@ void tpm_chip_unregister(struct tpm_chip *chip) if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED)) return; - if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) - sysfs_remove_link(&chip->dev.parent->kobj, "ppi"); + tpm_del_legacy_sysfs(chip); tpm1_chip_unregister(chip); tpm_del_char_device(chip); diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 7cba092..080dade 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -432,12 +432,11 @@ static const struct tpm_input_header tpm_getcap_header = { .ordinal = TPM_ORD_GET_CAP }; -ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap, +ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap, const char *desc) { struct tpm_cmd_t tpm_cmd; int rc; - struct tpm_chip *chip = dev_get_drvdata(dev); tpm_cmd.header.in = tpm_getcap_header; if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) { @@ -935,7 +934,7 @@ static struct tpm_input_header savestate_header = { */ int tpm_pm_suspend(struct device *dev) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); struct tpm_cmd_t cmd; int rc, try; @@ -996,7 +995,7 @@ EXPORT_SYMBOL_GPL(tpm_pm_suspend); */ int tpm_pm_resume(struct device *dev) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); if (chip == NULL) return -ENODEV; diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c index a7c3473..b46cf70 100644 --- a/drivers/char/tpm/tpm-sysfs.c +++ b/drivers/char/tpm/tpm-sysfs.c @@ -36,7 +36,7 @@ static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, int i, rc; char *str = buf; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); tpm_cmd.header.in = tpm_readpubek_header; err = tpm_transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, @@ -92,9 +92,9 @@ static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr, ssize_t rc; int i, j, num_pcrs; char *str = buf; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); - rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap, + rc = tpm_getcap(chip, TPM_CAP_PROP_PCR, &cap, "attempting to determine the number of PCRS"); if (rc) return 0; @@ -119,8 +119,8 @@ static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, cap_t cap; ssize_t rc; - rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap, - "attempting to determine the permanent enabled state"); + rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap, + "attempting to determine the permanent enabled state"); if (rc) return 0; @@ -135,8 +135,8 @@ static ssize_t active_show(struct device *dev, struct device_attribute *attr, cap_t cap; ssize_t rc; - rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap, - "attempting to determine the permanent active state"); + rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_PERM, &cap, + "attempting to determine the permanent active state"); if (rc) return 0; @@ -151,8 +151,8 @@ static ssize_t owned_show(struct device *dev, struct device_attribute *attr, cap_t cap; ssize_t rc; - rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap, - "attempting to determine the owner state"); + rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_PROP_OWNER, &cap, + "attempting to determine the owner state"); if (rc) return 0; @@ -167,8 +167,8 @@ static ssize_t temp_deactivated_show(struct device *dev, cap_t cap; ssize_t rc; - rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap, - "attempting to determine the temporary state"); + rc = tpm_getcap(to_tpm_chip(dev), TPM_CAP_FLAG_VOL, &cap, + "attempting to determine the temporary state"); if (rc) return 0; @@ -180,11 +180,12 @@ static DEVICE_ATTR_RO(temp_deactivated); static ssize_t caps_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct tpm_chip *chip = to_tpm_chip(dev); cap_t cap; ssize_t rc; char *str = buf; - rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap, + rc = tpm_getcap(chip, TPM_CAP_PROP_MANUFACTURER, &cap, "attempting to determine the manufacturer"); if (rc) return 0; @@ -192,8 +193,8 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, be32_to_cpu(cap.manufacturer_id)); /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ - rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap, - "attempting to determine the 1.2 version"); + rc = tpm_getcap(chip, CAP_VERSION_1_2, &cap, + "attempting to determine the 1.2 version"); if (!rc) { str += sprintf(str, "TCG version: %d.%d\nFirmware version: %d.%d\n", @@ -203,7 +204,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, cap.tpm_version_1_2.revMinor); } else { /* Otherwise just use TPM_STRUCT_VER */ - rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap, + rc = tpm_getcap(chip, CAP_VERSION_1_1, &cap, "attempting to determine the 1.1 version"); if (rc) return 0; @@ -222,7 +223,7 @@ static DEVICE_ATTR_RO(caps); static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); if (chip == NULL) return 0; @@ -234,7 +235,7 @@ static DEVICE_ATTR_WO(cancel); static ssize_t durations_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); if (chip->duration[TPM_LONG] == 0) return 0; @@ -251,7 +252,7 @@ static DEVICE_ATTR_RO(durations); static ssize_t timeouts_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); return sprintf(buf, "%d %d %d %d [%s]\n", jiffies_to_usecs(chip->timeout_a), @@ -281,24 +282,12 @@ static const struct attribute_group tpm_dev_group = { .attrs = tpm_dev_attrs, }; -int tpm_sysfs_add_device(struct tpm_chip *chip) +void tpm_sysfs_add_device(struct tpm_chip *chip) { - int err; - err = sysfs_create_group(&chip->dev.parent->kobj, - &tpm_dev_group); - - if (err) - dev_err(&chip->dev, - "failed to create sysfs attributes, %d\n", err); - return err; -} - -void tpm_sysfs_del_device(struct tpm_chip *chip) -{ - /* The sysfs routines rely on an implicit tpm_try_get_ops, this - * function is called before ops is null'd and the sysfs core - * synchronizes this removal so that no callbacks are running or can - * run again + /* The sysfs routines rely on an implicit tpm_try_get_ops, device_del + * is called before ops is null'd and the sysfs core synchronizes this + * removal so that no callbacks are running or can run again */ - sysfs_remove_group(&chip->dev.parent->kobj, &tpm_dev_group); + WARN_ON(chip->groups_cnt != 0); + chip->groups[chip->groups_cnt++] = &tpm_dev_group; } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 8bc6fb8..508e8e0 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -168,9 +168,9 @@ struct tpm_chip { struct dentry **bios_dir; -#ifdef CONFIG_ACPI - const struct attribute_group *groups[2]; + const struct attribute_group *groups[3]; unsigned int groups_cnt; +#ifdef CONFIG_ACPI acpi_handle acpi_dev_handle; char ppi_version[TPM_PPI_VERSION_LEN + 1]; #endif /* CONFIG_ACPI */ @@ -471,7 +471,8 @@ extern dev_t tpm_devt; extern const struct file_operations tpm_fops; extern struct idr dev_nums_idr; -ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *); +ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap, + const char *desc); ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, size_t bufsiz); ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, int len, @@ -496,8 +497,7 @@ extern struct tpm_chip *tpmm_chip_alloc(struct device *pdev, extern int tpm_chip_register(struct tpm_chip *chip); extern void tpm_chip_unregister(struct tpm_chip *chip); -int tpm_sysfs_add_device(struct tpm_chip *chip); -void tpm_sysfs_del_device(struct tpm_chip *chip); +void tpm_sysfs_add_device(struct tpm_chip *chip); int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); -- cgit v0.10.2 From 2f9f53776dcd8ca3cc3d357d516ed76c3f038637 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 18 Apr 2016 13:26:14 -0400 Subject: tpm: Introduce TPM_CHIP_FLAG_VIRTUAL Introduce TPM_CHIP_FLAG_VIRTUAL to be used when the chip device has no parent device. Prevent sysfs entries requiring a parent device from being created. Signed-off-by: Stefan Berger Reviewed-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 57d9794..1965dc7 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -181,6 +181,9 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev, if (rc) goto out; + if (!dev) + chip->flags |= TPM_CHIP_FLAG_VIRTUAL; + cdev_init(&chip->cdev, &tpm_fops); chip->cdev.owner = THIS_MODULE; chip->cdev.kobj.parent = &chip->dev.kobj; @@ -298,7 +301,7 @@ static void tpm_del_legacy_sysfs(struct tpm_chip *chip) { struct attribute **i; - if (chip->flags & TPM_CHIP_FLAG_TPM2) + if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) return; sysfs_remove_link(&chip->dev.parent->kobj, "ppi"); @@ -316,7 +319,7 @@ static int tpm_add_legacy_sysfs(struct tpm_chip *chip) struct attribute **i; int rc; - if (chip->flags & TPM_CHIP_FLAG_TPM2) + if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) return 0; rc = __compat_only_sysfs_link_entry_to_kobj( diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 508e8e0..ff4e53c 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -138,6 +138,7 @@ enum tpm_chip_flags { TPM_CHIP_FLAG_REGISTERED = BIT(0), TPM_CHIP_FLAG_TPM2 = BIT(1), TPM_CHIP_FLAG_IRQ = BIT(2), + TPM_CHIP_FLAG_VIRTUAL = BIT(3), }; struct tpm_chip { -- cgit v0.10.2 From 6f99612e250041a2402d3b1694bccb149cd424a4 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 18 Apr 2016 13:26:15 -0400 Subject: tpm: Proxy driver for supporting multiple emulated TPMs This patch implements a proxy driver for supporting multiple emulated TPMs in a system. The driver implements a device /dev/vtpmx that is used to created a client device pair /dev/tpmX (e.g., /dev/tpm10) and a server side that is accessed using a file descriptor returned by an ioctl. The device /dev/tpmX is the usual TPM device created by the core TPM driver. Applications or kernel subsystems can send TPM commands to it and the corresponding server-side file descriptor receives these commands and delivers them to an emulated TPM. The driver retrievs the TPM 1.2 durations and timeouts. Since this requires the startup of the TPM, we send a startup for TPM 1.2 as well as TPM 2. Signed-off-by: Stefan Berger Reviewed-by: Jason Gunthorpe CC: linux-kernel@vger.kernel.org CC: linux-doc@vger.kernel.org CC: linux-api@vger.kernel.org Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 3b84a8b..0eac596 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -122,5 +122,15 @@ config TCG_CRB from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_crb. +config TCG_VTPM_PROXY + tristate "VTPM Proxy Interface" + depends on TCG_TPM + ---help--- + This driver proxies for an emulated TPM (vTPM) running in userspace. + A device /dev/vtpmx is provided that creates a device pair + /dev/vtpmX and a server-side file descriptor on which the vTPM + can receive commands. + + source "drivers/char/tpm/st33zp24/Kconfig" endif # TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 56e8f1f..98de5e6 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -23,3 +23,4 @@ obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/ obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o obj-$(CONFIG_TCG_CRB) += tpm_crb.o +obj-$(CONFIG_TCG_VTPM_PROXY) += tpm_vtpm_proxy.o diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c new file mode 100644 index 0000000..4384042 --- /dev/null +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -0,0 +1,644 @@ +/* + * Copyright (C) 2015, 2016 IBM Corporation + * + * Author: Stefan Berger + * + * Maintained by: + * + * Device driver for vTPM (vTPM proxy driver) + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpm.h" + +#define VTPM_PROXY_REQ_COMPLETE_FLAG BIT(0) + +struct proxy_dev { + struct tpm_chip *chip; + + u32 flags; /* public API flags */ + + wait_queue_head_t wq; + + struct mutex buf_lock; /* protect buffer and flags */ + + long state; /* internal state */ +#define STATE_OPENED_FLAG BIT(0) +#define STATE_WAIT_RESPONSE_FLAG BIT(1) /* waiting for emulator response */ + + size_t req_len; /* length of queued TPM request */ + size_t resp_len; /* length of queued TPM response */ + u8 buffer[TPM_BUFSIZE]; /* request/response buffer */ + + struct work_struct work; /* task that retrieves TPM timeouts */ +}; + +/* all supported flags */ +#define VTPM_PROXY_FLAGS_ALL (VTPM_PROXY_FLAG_TPM2) + +static struct workqueue_struct *workqueue; + +static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev); + +/* + * Functions related to 'server side' + */ + +/** + * vtpm_proxy_fops_read - Read TPM commands on 'server side' + * + * Return value: + * Number of bytes read or negative error code + */ +static ssize_t vtpm_proxy_fops_read(struct file *filp, char __user *buf, + size_t count, loff_t *off) +{ + struct proxy_dev *proxy_dev = filp->private_data; + size_t len; + int sig, rc; + + sig = wait_event_interruptible(proxy_dev->wq, + proxy_dev->req_len != 0 || + !(proxy_dev->state & STATE_OPENED_FLAG)); + if (sig) + return -EINTR; + + mutex_lock(&proxy_dev->buf_lock); + + if (!(proxy_dev->state & STATE_OPENED_FLAG)) { + mutex_unlock(&proxy_dev->buf_lock); + return -EPIPE; + } + + len = proxy_dev->req_len; + + if (count < len) { + mutex_unlock(&proxy_dev->buf_lock); + pr_debug("Invalid size in recv: count=%zd, req_len=%zd\n", + count, len); + return -EIO; + } + + rc = copy_to_user(buf, proxy_dev->buffer, len); + memset(proxy_dev->buffer, 0, len); + proxy_dev->req_len = 0; + + if (!rc) + proxy_dev->state |= STATE_WAIT_RESPONSE_FLAG; + + mutex_unlock(&proxy_dev->buf_lock); + + if (rc) + return -EFAULT; + + return len; +} + +/** + * vtpm_proxy_fops_write - Write TPM responses on 'server side' + * + * Return value: + * Number of bytes read or negative error value + */ +static ssize_t vtpm_proxy_fops_write(struct file *filp, const char __user *buf, + size_t count, loff_t *off) +{ + struct proxy_dev *proxy_dev = filp->private_data; + + mutex_lock(&proxy_dev->buf_lock); + + if (!(proxy_dev->state & STATE_OPENED_FLAG)) { + mutex_unlock(&proxy_dev->buf_lock); + return -EPIPE; + } + + if (count > sizeof(proxy_dev->buffer) || + !(proxy_dev->state & STATE_WAIT_RESPONSE_FLAG)) { + mutex_unlock(&proxy_dev->buf_lock); + return -EIO; + } + + proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG; + + proxy_dev->req_len = 0; + + if (copy_from_user(proxy_dev->buffer, buf, count)) { + mutex_unlock(&proxy_dev->buf_lock); + return -EFAULT; + } + + proxy_dev->resp_len = count; + + mutex_unlock(&proxy_dev->buf_lock); + + wake_up_interruptible(&proxy_dev->wq); + + return count; +} + +/* + * vtpm_proxy_fops_poll: Poll status on 'server side' + * + * Return value: + * Poll flags + */ +static unsigned int vtpm_proxy_fops_poll(struct file *filp, poll_table *wait) +{ + struct proxy_dev *proxy_dev = filp->private_data; + unsigned ret; + + poll_wait(filp, &proxy_dev->wq, wait); + + ret = POLLOUT; + + mutex_lock(&proxy_dev->buf_lock); + + if (proxy_dev->req_len) + ret |= POLLIN | POLLRDNORM; + + if (!(proxy_dev->state & STATE_OPENED_FLAG)) + ret |= POLLHUP; + + mutex_unlock(&proxy_dev->buf_lock); + + return ret; +} + +/* + * vtpm_proxy_fops_open - Open vTPM device on 'server side' + * + * Called when setting up the anonymous file descriptor + */ +static void vtpm_proxy_fops_open(struct file *filp) +{ + struct proxy_dev *proxy_dev = filp->private_data; + + proxy_dev->state |= STATE_OPENED_FLAG; +} + +/** + * vtpm_proxy_fops_undo_open - counter-part to vtpm_fops_open + * + * Call to undo vtpm_proxy_fops_open + */ +static void vtpm_proxy_fops_undo_open(struct proxy_dev *proxy_dev) +{ + mutex_lock(&proxy_dev->buf_lock); + + proxy_dev->state &= ~STATE_OPENED_FLAG; + + mutex_unlock(&proxy_dev->buf_lock); + + /* no more TPM responses -- wake up anyone waiting for them */ + wake_up_interruptible(&proxy_dev->wq); +} + +/* + * vtpm_proxy_fops_release: Close 'server side' + * + * Return value: + * Always returns 0. + */ +static int vtpm_proxy_fops_release(struct inode *inode, struct file *filp) +{ + struct proxy_dev *proxy_dev = filp->private_data; + + filp->private_data = NULL; + + vtpm_proxy_delete_device(proxy_dev); + + return 0; +} + +static const struct file_operations vtpm_proxy_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = vtpm_proxy_fops_read, + .write = vtpm_proxy_fops_write, + .poll = vtpm_proxy_fops_poll, + .release = vtpm_proxy_fops_release, +}; + +/* + * Functions invoked by the core TPM driver to send TPM commands to + * 'server side' and receive responses from there. + */ + +/* + * Called when core TPM driver reads TPM responses from 'server side' + * + * Return value: + * Number of TPM response bytes read, negative error value otherwise + */ +static int vtpm_proxy_tpm_op_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev); + size_t len; + + /* process gone ? */ + mutex_lock(&proxy_dev->buf_lock); + + if (!(proxy_dev->state & STATE_OPENED_FLAG)) { + mutex_unlock(&proxy_dev->buf_lock); + return -EPIPE; + } + + len = proxy_dev->resp_len; + if (count < len) { + dev_err(&chip->dev, + "Invalid size in recv: count=%zd, resp_len=%zd\n", + count, len); + len = -EIO; + goto out; + } + + memcpy(buf, proxy_dev->buffer, len); + proxy_dev->resp_len = 0; + +out: + mutex_unlock(&proxy_dev->buf_lock); + + return len; +} + +/* + * Called when core TPM driver forwards TPM requests to 'server side'. + * + * Return value: + * 0 in case of success, negative error value otherwise. + */ +static int vtpm_proxy_tpm_op_send(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev); + int rc = 0; + + if (count > sizeof(proxy_dev->buffer)) { + dev_err(&chip->dev, + "Invalid size in send: count=%zd, buffer size=%zd\n", + count, sizeof(proxy_dev->buffer)); + return -EIO; + } + + mutex_lock(&proxy_dev->buf_lock); + + if (!(proxy_dev->state & STATE_OPENED_FLAG)) { + mutex_unlock(&proxy_dev->buf_lock); + return -EPIPE; + } + + proxy_dev->resp_len = 0; + + proxy_dev->req_len = count; + memcpy(proxy_dev->buffer, buf, count); + + proxy_dev->state &= ~STATE_WAIT_RESPONSE_FLAG; + + mutex_unlock(&proxy_dev->buf_lock); + + wake_up_interruptible(&proxy_dev->wq); + + return rc; +} + +static void vtpm_proxy_tpm_op_cancel(struct tpm_chip *chip) +{ + /* not supported */ +} + +static u8 vtpm_proxy_tpm_op_status(struct tpm_chip *chip) +{ + struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev); + + if (proxy_dev->resp_len) + return VTPM_PROXY_REQ_COMPLETE_FLAG; + + return 0; +} + +static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip *chip, u8 status) +{ + struct proxy_dev *proxy_dev = dev_get_drvdata(&chip->dev); + bool ret; + + mutex_lock(&proxy_dev->buf_lock); + + ret = !(proxy_dev->state & STATE_OPENED_FLAG); + + mutex_unlock(&proxy_dev->buf_lock); + + return ret; +} + +static const struct tpm_class_ops vtpm_proxy_tpm_ops = { + .recv = vtpm_proxy_tpm_op_recv, + .send = vtpm_proxy_tpm_op_send, + .cancel = vtpm_proxy_tpm_op_cancel, + .status = vtpm_proxy_tpm_op_status, + .req_complete_mask = VTPM_PROXY_REQ_COMPLETE_FLAG, + .req_complete_val = VTPM_PROXY_REQ_COMPLETE_FLAG, + .req_canceled = vtpm_proxy_tpm_req_canceled, +}; + +/* + * Code related to the startup of the TPM 2 and startup of TPM 1.2 + + * retrieval of timeouts and durations. + */ + +static void vtpm_proxy_work(struct work_struct *work) +{ + struct proxy_dev *proxy_dev = container_of(work, struct proxy_dev, + work); + int rc; + + if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2) + rc = tpm2_startup(proxy_dev->chip, TPM2_SU_CLEAR); + else + rc = tpm_get_timeouts(proxy_dev->chip); + + if (rc) + goto err; + + rc = tpm_chip_register(proxy_dev->chip); + if (rc) + goto err; + + return; + +err: + vtpm_proxy_fops_undo_open(proxy_dev); +} + +/* + * vtpm_proxy_work_stop: make sure the work has finished + * + * This function is useful when user space closed the fd + * while the driver still determines timeouts. + */ +static void vtpm_proxy_work_stop(struct proxy_dev *proxy_dev) +{ + vtpm_proxy_fops_undo_open(proxy_dev); + flush_work(&proxy_dev->work); +} + +/* + * vtpm_proxy_work_start: Schedule the work for TPM 1.2 & 2 initialization + */ +static inline void vtpm_proxy_work_start(struct proxy_dev *proxy_dev) +{ + queue_work(workqueue, &proxy_dev->work); +} + +/* + * Code related to creation and deletion of device pairs + */ +static struct proxy_dev *vtpm_proxy_create_proxy_dev(void) +{ + struct proxy_dev *proxy_dev; + struct tpm_chip *chip; + int err; + + proxy_dev = kzalloc(sizeof(*proxy_dev), GFP_KERNEL); + if (proxy_dev == NULL) + return ERR_PTR(-ENOMEM); + + init_waitqueue_head(&proxy_dev->wq); + mutex_init(&proxy_dev->buf_lock); + INIT_WORK(&proxy_dev->work, vtpm_proxy_work); + + chip = tpm_chip_alloc(NULL, &vtpm_proxy_tpm_ops); + if (IS_ERR(chip)) { + err = PTR_ERR(chip); + goto err_proxy_dev_free; + } + dev_set_drvdata(&chip->dev, proxy_dev); + + proxy_dev->chip = chip; + + return proxy_dev; + +err_proxy_dev_free: + kfree(proxy_dev); + + return ERR_PTR(err); +} + +/* + * Undo what has been done in vtpm_create_proxy_dev + */ +static inline void vtpm_proxy_delete_proxy_dev(struct proxy_dev *proxy_dev) +{ + put_device(&proxy_dev->chip->dev); /* frees chip */ + kfree(proxy_dev); +} + +/* + * Create a /dev/tpm%d and 'server side' file descriptor pair + * + * Return value: + * Returns file pointer on success, an error value otherwise + */ +static struct file *vtpm_proxy_create_device( + struct vtpm_proxy_new_dev *vtpm_new_dev) +{ + struct proxy_dev *proxy_dev; + int rc, fd; + struct file *file; + + if (vtpm_new_dev->flags & ~VTPM_PROXY_FLAGS_ALL) + return ERR_PTR(-EOPNOTSUPP); + + proxy_dev = vtpm_proxy_create_proxy_dev(); + if (IS_ERR(proxy_dev)) + return ERR_CAST(proxy_dev); + + proxy_dev->flags = vtpm_new_dev->flags; + + /* setup an anonymous file for the server-side */ + fd = get_unused_fd_flags(O_RDWR); + if (fd < 0) { + rc = fd; + goto err_delete_proxy_dev; + } + + file = anon_inode_getfile("[vtpms]", &vtpm_proxy_fops, proxy_dev, + O_RDWR); + if (IS_ERR(file)) { + rc = PTR_ERR(file); + goto err_put_unused_fd; + } + + /* from now on we can unwind with put_unused_fd() + fput() */ + /* simulate an open() on the server side */ + vtpm_proxy_fops_open(file); + + if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2) + proxy_dev->chip->flags |= TPM_CHIP_FLAG_TPM2; + + vtpm_proxy_work_start(proxy_dev); + + vtpm_new_dev->fd = fd; + vtpm_new_dev->major = MAJOR(proxy_dev->chip->dev.devt); + vtpm_new_dev->minor = MINOR(proxy_dev->chip->dev.devt); + vtpm_new_dev->tpm_num = proxy_dev->chip->dev_num; + + return file; + +err_put_unused_fd: + put_unused_fd(fd); + +err_delete_proxy_dev: + vtpm_proxy_delete_proxy_dev(proxy_dev); + + return ERR_PTR(rc); +} + +/* + * Counter part to vtpm_create_device. + */ +static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev) +{ + vtpm_proxy_work_stop(proxy_dev); + + /* + * A client may hold the 'ops' lock, so let it know that the server + * side shuts down before we try to grab the 'ops' lock when + * unregistering the chip. + */ + vtpm_proxy_fops_undo_open(proxy_dev); + + tpm_chip_unregister(proxy_dev->chip); + + vtpm_proxy_delete_proxy_dev(proxy_dev); +} + +/* + * Code related to the control device /dev/vtpmx + */ + +/* + * vtpmx_fops_ioctl: ioctl on /dev/vtpmx + * + * Return value: + * Returns 0 on success, a negative error code otherwise. + */ +static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct vtpm_proxy_new_dev *vtpm_new_dev_p; + struct vtpm_proxy_new_dev vtpm_new_dev; + struct file *file; + + switch (ioctl) { + case VTPM_PROXY_IOC_NEW_DEV: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + vtpm_new_dev_p = argp; + if (copy_from_user(&vtpm_new_dev, vtpm_new_dev_p, + sizeof(vtpm_new_dev))) + return -EFAULT; + file = vtpm_proxy_create_device(&vtpm_new_dev); + if (IS_ERR(file)) + return PTR_ERR(file); + if (copy_to_user(vtpm_new_dev_p, &vtpm_new_dev, + sizeof(vtpm_new_dev))) { + put_unused_fd(vtpm_new_dev.fd); + fput(file); + return -EFAULT; + } + + fd_install(vtpm_new_dev.fd, file); + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static long vtpmx_fops_compat_ioctl(struct file *f, unsigned int ioctl, + unsigned long arg) +{ + return vtpmx_fops_ioctl(f, ioctl, (unsigned long)compat_ptr(arg)); +} +#endif + +static const struct file_operations vtpmx_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = vtpmx_fops_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vtpmx_fops_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + +static struct miscdevice vtpmx_miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "vtpmx", + .fops = &vtpmx_fops, +}; + +static int vtpmx_init(void) +{ + return misc_register(&vtpmx_miscdev); +} + +static void vtpmx_cleanup(void) +{ + misc_deregister(&vtpmx_miscdev); +} + +static int __init vtpm_module_init(void) +{ + int rc; + + rc = vtpmx_init(); + if (rc) { + pr_err("couldn't create vtpmx device\n"); + return rc; + } + + workqueue = create_workqueue("tpm-vtpm"); + if (!workqueue) { + pr_err("couldn't create workqueue\n"); + rc = -ENOMEM; + goto err_vtpmx_cleanup; + } + + return 0; + +err_vtpmx_cleanup: + vtpmx_cleanup(); + + return rc; +} + +static void __exit vtpm_module_exit(void) +{ + destroy_workqueue(workqueue); + vtpmx_cleanup(); +} + +module_init(vtpm_module_init); +module_exit(vtpm_module_exit); + +MODULE_AUTHOR("Stefan Berger (stefanb@us.ibm.com)"); +MODULE_DESCRIPTION("vTPM Driver"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 8bdae34..ad821a3 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -454,6 +454,7 @@ header-y += virtio_scsi.h header-y += virtio_types.h header-y += vm_sockets.h header-y += vt.h +header-y += vtpm_proxy.h header-y += wait.h header-y += wanrouter.h header-y += watchdog.h diff --git a/include/uapi/linux/vtpm_proxy.h b/include/uapi/linux/vtpm_proxy.h new file mode 100644 index 0000000..41e8e22 --- /dev/null +++ b/include/uapi/linux/vtpm_proxy.h @@ -0,0 +1,36 @@ +/* + * Definitions for the VTPM proxy driver + * Copyright (c) 2015, 2016, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef _UAPI_LINUX_VTPM_PROXY_H +#define _UAPI_LINUX_VTPM_PROXY_H + +#include +#include + +/* ioctls */ + +struct vtpm_proxy_new_dev { + __u32 flags; /* input */ + __u32 tpm_num; /* output */ + __u32 fd; /* output */ + __u32 major; /* output */ + __u32 minor; /* output */ +}; + +/* above flags */ +#define VTPM_PROXY_FLAG_TPM2 1 /* emulator is TPM 2 */ + +#define VTPM_PROXY_IOC_NEW_DEV _IOWR(0xa1, 0x00, struct vtpm_proxy_new_dev) + +#endif /* _UAPI_LINUX_VTPM_PROXY_H */ -- cgit v0.10.2 From 7e1dc002c00b932dfefd9af05bdffa6f3986ad55 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Mon, 18 Apr 2016 13:26:16 -0400 Subject: tpm: Add documentation for the tpm_vtpm_proxy device driver Add documentation for the tpm_vtpm device driver that implements support for providing TPM functionality to Linux containers. Parts of this documentation were recycled from the Xen vTPM device driver documentation. Update the documentation for the ioctl numbers. Signed-off-by: Stefan Berger Reviewed-by: Jarkko Sakkinen CC: linux-kernel@vger.kernel.org CC: linux-doc@vger.kernel.org CC: linux-api@vger.kernel.org Tested-by: Jarkko Sakkinen Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 9369d3b..56af5e4 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -303,6 +303,7 @@ Code Seq#(hex) Include File Comments 0xA0 all linux/sdp/sdp.h Industrial Device Project +0xA1 0 linux/vtpm_proxy.h TPM Emulator Proxy Driver 0xA2 00-0F arch/tile/include/asm/hardwall.h 0xA3 80-8F Port ACL in development: diff --git a/Documentation/tpm/tpm_vtpm_proxy.txt b/Documentation/tpm/tpm_vtpm_proxy.txt new file mode 100644 index 0000000..30d1902 --- /dev/null +++ b/Documentation/tpm/tpm_vtpm_proxy.txt @@ -0,0 +1,71 @@ +Virtual TPM Proxy Driver for Linux Containers + +Authors: Stefan Berger (IBM) + +This document describes the virtual Trusted Platform Module (vTPM) +proxy device driver for Linux containers. + +INTRODUCTION +------------ + +The goal of this work is to provide TPM functionality to each Linux +container. This allows programs to interact with a TPM in a container +the same way they interact with a TPM on the physical system. Each +container gets its own unique, emulated, software TPM. + + +DESIGN +------ + +To make an emulated software TPM available to each container, the container +management stack needs to create a device pair consisting of a client TPM +character device /dev/tpmX (with X=0,1,2...) and a 'server side' file +descriptor. The former is moved into the container by creating a character +device with the appropriate major and minor numbers while the file descriptor +is passed to the TPM emulator. Software inside the container can then send +TPM commands using the character device and the emulator will receive the +commands via the file descriptor and use it for sending back responses. + +To support this, the virtual TPM proxy driver provides a device /dev/vtpmx +that is used to create device pairs using an ioctl. The ioctl takes as +an input flags for configuring the device. The flags for example indicate +whether TPM 1.2 or TPM 2 functionality is supported by the TPM emulator. +The result of the ioctl are the file descriptor for the 'server side' +as well as the major and minor numbers of the character device that was created. +Besides that the number of the TPM character device is return. If for +example /dev/tpm10 was created, the number (dev_num) 10 is returned. + +The following is the data structure of the TPM_PROXY_IOC_NEW_DEV ioctl: + +struct vtpm_proxy_new_dev { + __u32 flags; /* input */ + __u32 tpm_num; /* output */ + __u32 fd; /* output */ + __u32 major; /* output */ + __u32 minor; /* output */ +}; + +Note that if unsupported flags are passed to the device driver, the ioctl will +fail and errno will be set to EOPNOTSUPP. Similarly, if an unsupported ioctl is +called on the device driver, the ioctl will fail and errno will be set to +ENOTTY. + +See /usr/include/linux/vtpm_proxy.h for definitions related to the public interface +of this vTPM device driver. + +Once the device has been created, the driver will immediately try to talk +to the TPM. All commands from the driver can be read from the file descriptor +returned by the ioctl. The commands should be responded to immediately. + +Depending on the version of TPM the following commands will be sent by the +driver: + +- TPM 1.2: + - the driver will send a TPM_Startup command to the TPM emulator + - the driver will send commands to read the command durations and + interface timeouts from the TPM emulator +- TPM 2: + - the driver will send a TPM2_Startup command to the TPM emulator + +The TPM device /dev/tpmX will only appear if all of the relevant commands +were responded to properly. -- cgit v0.10.2 From ced01b9ffc482d45b57d368a6382e498aa24f66a Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 27 Apr 2016 10:58:46 -0600 Subject: tpm: Fix IRQ unwind ordering in TIS The devm for the IRQ was placed on the chip, not the pdev. This can cause the irq to be still callable after the pdev has been cleaned up (eg priv kfree'd). Found by CONFIG_DEBUG_SHIRQ=y Reported-by: Stefan Berger Fixes: 233a065e0cd0 ("tpm: Get rid of chip->pdev") Signed-off-by: Jason Gunthorpe Tested-by: Stefan Berger Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index a6b2d46..d888270 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -387,7 +387,7 @@ static void disable_interrupts(struct tpm_chip *chip) intmask &= ~TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, priv->iobase + TPM_INT_ENABLE(priv->locality)); - devm_free_irq(&chip->dev, priv->irq, chip); + devm_free_irq(chip->dev.parent, priv->irq, chip); priv->irq = 0; chip->flags &= ~TPM_CHIP_FLAG_IRQ; } @@ -604,7 +604,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, struct priv_data *priv = dev_get_drvdata(&chip->dev); u8 original_int_vec; - if (devm_request_irq(&chip->dev, irq, tis_int_handler, flags, + if (devm_request_irq(chip->dev.parent, irq, tis_int_handler, flags, dev_name(&chip->dev), chip) != 0) { dev_info(&chip->dev, "Unable to request irq: %d for probe\n", irq); -- cgit v0.10.2 From a6885a53a3a2a61bf8f799712dc7a20fd2740adf Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 28 Apr 2016 12:46:13 +0200 Subject: tpm: select ANON_INODES for proxy driver The newly added vtpmx driver fails to build if CONFIG_ANON_INODES is disabled: drivers/char/built-in.o: In function `vtpmx_fops_ioctl': (.text+0x97f8): undefined reference to `anon_inode_getfile' This adds a Kconfig 'select' statement to ensure it's always there when we need it. Signed-off-by: Arnd Bergmann Fixes: 794c38e01358 ("tpm: Proxy driver for supporting multiple emulated TPMs") Acked-by: Stefan Berger Acked-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 0eac596..bfdc5c0 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -125,6 +125,7 @@ config TCG_CRB config TCG_VTPM_PROXY tristate "VTPM Proxy Interface" depends on TCG_TPM + select ANON_INODES ---help--- This driver proxies for an emulated TPM (vTPM) running in userspace. A device /dev/vtpmx is provided that creates a device pair -- cgit v0.10.2 From 75254557fb8fc9ff93dc6f39e37532410d9ae083 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Thu, 28 Apr 2016 15:27:17 +1000 Subject: tpm: fix for typo in tpm/tpm_ibmvtpm.c Fixes: 28157164b056 ("tpm: Remove useless priv field in struct tpm_vendor_specific") Signed-off-by: Stephen Rothwell Acked-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 6b22826..946025a 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -627,7 +627,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev, crq_q->index = 0; - dev_set_drvdata(&chip->dev, ibmvtpm) + dev_set_drvdata(&chip->dev, ibmvtpm); spin_lock_init(&ibmvtpm->rtce_lock); -- cgit v0.10.2 From ec03c50b132d159352f118055a58f599366084f9 Mon Sep 17 00:00:00 2001 From: Stefan Berger Date: Wed, 11 May 2016 11:28:27 -0400 Subject: tpm: Fix suspend regression Fix the suspend regression due to the wrong way of retrieving the chip structure. The suspend functions are attached to the hardware device, not the chip and thus must rely on drvdata. Fixes: e89f8b1ade9cc1a ("tpm: Remove all uses of drvdata from the TPM Core") Reported-by: Jeremiah Mahler Signed-off-by: Stefan Berger Tested-by: Stefan Berger Reviewed-by: Jason Gunthorpe Tested-by: Jeremiah Mahler Acked-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 080dade..5e3c1b6 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -934,7 +934,7 @@ static struct tpm_input_header savestate_header = { */ int tpm_pm_suspend(struct device *dev) { - struct tpm_chip *chip = to_tpm_chip(dev); + struct tpm_chip *chip = dev_get_drvdata(dev); struct tpm_cmd_t cmd; int rc, try; @@ -995,7 +995,7 @@ EXPORT_SYMBOL_GPL(tpm_pm_suspend); */ int tpm_pm_resume(struct device *dev) { - struct tpm_chip *chip = to_tpm_chip(dev); + struct tpm_chip *chip = dev_get_drvdata(dev); if (chip == NULL) return -ENODEV; -- cgit v0.10.2 From d2efee6e3ac8f5967e13f39bc8daaa81f81486e7 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Wed, 4 May 2016 21:06:22 +0200 Subject: tpm: Add include guards in tpm.h Add missing include guards in tpm.h Signed-off-by: Peter Huewe Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Stefan Berger Reviewed-by: Stefan Berger Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index ff4e53c..8890df2 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -19,6 +19,10 @@ * License. * */ + +#ifndef __TPM_H__ +#define __TPM_H__ + #include #include #include @@ -528,3 +532,4 @@ extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32); extern int tpm2_do_selftest(struct tpm_chip *chip); extern int tpm2_gen_interrupt(struct tpm_chip *chip); extern int tpm2_probe(struct tpm_chip *chip); +#endif -- cgit v0.10.2 From 57dacc2b4ce5350db3046721dad3d88c3b451aa2 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 19 May 2016 00:35:48 +0200 Subject: tpm: tpm_tis: Share common data between phys Split priv_data structure in common and phy specific structures. This will allow in future patches to reuse the same tis logic on top of new phy such as spi and i2c. Ultimately, other drivers may reuse this tis logic. (e.g: st33zp24...) iobase field is specific to TPM addressed on 0xFED4xxxx on LPC/SPI bus. This commit is based on the initial work by Peter Huewe. Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Stefan Berger Reviewed-by: Stefan Berger Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index d888270..cd5bb63 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -29,6 +29,7 @@ #include #include #include "tpm.h" +#include "tpm_tis_core.h" enum tis_access { TPM_ACCESS_VALID = 0x80, @@ -93,16 +94,16 @@ struct tpm_info { #define TPM_DID_VID(l) (0x0F00 | ((l) << 12)) #define TPM_RID(l) (0x0F04 | ((l) << 12)) -struct priv_data { +struct tpm_tis_tcg_phy { + struct tpm_tis_data priv; void __iomem *iobase; - u16 manufacturer_id; - int locality; - int irq; - bool irq_tested; - wait_queue_head_t int_queue; - wait_queue_head_t read_queue; }; +static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *data) +{ + return container_of(data, struct tpm_tis_tcg_phy, priv); +} + #if defined(CONFIG_PNP) && defined(CONFIG_ACPI) static int has_hid(struct acpi_device *dev, const char *hid) { @@ -132,10 +133,11 @@ static inline int is_itpm(struct acpi_device *dev) * correct values in the other bits.' */ static int wait_startup(struct tpm_chip *chip, int l) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); unsigned long stop = jiffies + chip->timeout_a; do { - if (ioread8(priv->iobase + TPM_ACCESS(l)) & + if (ioread8(phy->iobase + TPM_ACCESS(l)) & TPM_ACCESS_VALID) return 0; msleep(TPM_TIMEOUT); @@ -145,9 +147,10 @@ static int wait_startup(struct tpm_chip *chip, int l) static int check_locality(struct tpm_chip *chip, int l) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); - if ((ioread8(priv->iobase + TPM_ACCESS(l)) & + if ((ioread8(phy->iobase + TPM_ACCESS(l)) & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) return priv->locality = l; @@ -157,18 +160,20 @@ static int check_locality(struct tpm_chip *chip, int l) static void release_locality(struct tpm_chip *chip, int l, int force) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); - if (force || (ioread8(priv->iobase + TPM_ACCESS(l)) & + if (force || (ioread8(phy->iobase + TPM_ACCESS(l)) & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) iowrite8(TPM_ACCESS_ACTIVE_LOCALITY, - priv->iobase + TPM_ACCESS(l)); + phy->iobase + TPM_ACCESS(l)); } static int request_locality(struct tpm_chip *chip, int l) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); unsigned long stop, timeout; long rc; @@ -176,7 +181,7 @@ static int request_locality(struct tpm_chip *chip, int l) return l; iowrite8(TPM_ACCESS_REQUEST_USE, - priv->iobase + TPM_ACCESS(l)); + phy->iobase + TPM_ACCESS(l)); stop = jiffies + chip->timeout_a; @@ -209,24 +214,27 @@ again: static u8 tpm_tis_status(struct tpm_chip *chip) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); - return ioread8(priv->iobase + + return ioread8(phy->iobase + TPM_STS(priv->locality)); } static void tpm_tis_ready(struct tpm_chip *chip) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); /* this causes the current command to be aborted */ iowrite8(TPM_STS_COMMAND_READY, - priv->iobase + TPM_STS(priv->locality)); + phy->iobase + TPM_STS(priv->locality)); } static int get_burstcount(struct tpm_chip *chip) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); unsigned long stop; int burstcnt; @@ -234,9 +242,9 @@ static int get_burstcount(struct tpm_chip *chip) /* which timeout value, spec has 2 answers (c & d) */ stop = jiffies + chip->timeout_d; do { - burstcnt = ioread8(priv->iobase + + burstcnt = ioread8(phy->iobase + TPM_STS(priv->locality) + 1); - burstcnt += ioread8(priv->iobase + + burstcnt += ioread8(phy->iobase + TPM_STS(priv->locality) + 2) << 8; if (burstcnt) @@ -248,7 +256,8 @@ static int get_burstcount(struct tpm_chip *chip) static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); int size = 0, burstcnt; while (size < count && wait_for_tpm_stat(chip, @@ -258,7 +267,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) == 0) { burstcnt = get_burstcount(chip); for (; burstcnt > 0 && size < count; burstcnt--) - buf[size++] = ioread8(priv->iobase + + buf[size++] = ioread8(phy->iobase + TPM_DATA_FIFO(priv->locality)); } return size; @@ -266,7 +275,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int size = 0; int expected, status; @@ -322,7 +331,8 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)"); */ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); int rc, status, burstcnt; size_t count = 0; @@ -343,7 +353,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) while (count < len - 1) { burstcnt = get_burstcount(chip); for (; burstcnt > 0 && count < len - 1; burstcnt--) { - iowrite8(buf[count], priv->iobase + + iowrite8(buf[count], phy->iobase + TPM_DATA_FIFO(priv->locality)); count++; } @@ -359,7 +369,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) /* write last byte */ iowrite8(buf[count], - priv->iobase + TPM_DATA_FIFO(priv->locality)); + phy->iobase + TPM_DATA_FIFO(priv->locality)); wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, &priv->int_queue, false); status = tpm_tis_status(chip); @@ -378,15 +388,16 @@ out_err: static void disable_interrupts(struct tpm_chip *chip) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); u32 intmask; intmask = - ioread32(priv->iobase + + ioread32(phy->iobase + TPM_INT_ENABLE(priv->locality)); intmask &= ~TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, - priv->iobase + TPM_INT_ENABLE(priv->locality)); + phy->iobase + TPM_INT_ENABLE(priv->locality)); devm_free_irq(chip->dev.parent, priv->irq, chip); priv->irq = 0; chip->flags &= ~TPM_CHIP_FLAG_IRQ; @@ -399,7 +410,8 @@ static void disable_interrupts(struct tpm_chip *chip) */ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); int rc; u32 ordinal; unsigned long dur; @@ -410,7 +422,7 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) /* go and do it */ iowrite8(TPM_STS_GO, - priv->iobase + TPM_STS(priv->locality)); + phy->iobase + TPM_STS(priv->locality)); if (chip->flags & TPM_CHIP_FLAG_IRQ) { ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); @@ -436,8 +448,8 @@ out_err: static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) { + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); int rc, irq; - struct priv_data *priv = dev_get_drvdata(&chip->dev); if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || priv->irq_tested) return tpm_tis_send_main(chip, buf, len); @@ -471,11 +483,12 @@ static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = { static bool tpm_tis_update_timeouts(struct tpm_chip *chip, unsigned long *timeout_cap) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); int i; u32 did_vid; - did_vid = ioread32(priv->iobase + TPM_DID_VID(0)); + did_vid = ioread32(phy->iobase + TPM_DID_VID(0)); for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) { if (vendor_timeout_overrides[i].did_vid != did_vid) @@ -495,7 +508,8 @@ static bool tpm_tis_update_timeouts(struct tpm_chip *chip, */ static int probe_itpm(struct tpm_chip *chip) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); int rc = 0; u8 cmd_getticks[] = { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, @@ -503,7 +517,7 @@ static int probe_itpm(struct tpm_chip *chip) }; size_t len = sizeof(cmd_getticks); bool rem_itpm = itpm; - u16 vendor = ioread16(priv->iobase + TPM_DID_VID(0)); + u16 vendor = ioread16(phy->iobase + TPM_DID_VID(0)); /* probe only iTPMS */ if (vendor != TPM_VID_INTEL) @@ -537,7 +551,7 @@ out: static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); switch (priv->manufacturer_id) { case TPM_VID_WINBOND: @@ -564,11 +578,12 @@ static const struct tpm_class_ops tpm_tis = { static irqreturn_t tis_int_handler(int dummy, void *dev_id) { struct tpm_chip *chip = dev_id; - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); u32 interrupt; int i; - interrupt = ioread32(priv->iobase + + interrupt = ioread32(phy->iobase + TPM_INT_STATUS(priv->locality)); if (interrupt == 0) @@ -588,9 +603,9 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) /* Clear interrupts handled with TPM_EOI */ iowrite32(interrupt, - priv->iobase + + phy->iobase + TPM_INT_STATUS(priv->locality)); - ioread32(priv->iobase + TPM_INT_STATUS(priv->locality)); + ioread32(phy->iobase + TPM_INT_STATUS(priv->locality)); return IRQ_HANDLED; } @@ -601,7 +616,8 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, int flags, int irq) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); u8 original_int_vec; if (devm_request_irq(chip->dev.parent, irq, tis_int_handler, flags, @@ -612,19 +628,19 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, } priv->irq = irq; - original_int_vec = ioread8(priv->iobase + + original_int_vec = ioread8(phy->iobase + TPM_INT_VECTOR(priv->locality)); iowrite8(irq, - priv->iobase + TPM_INT_VECTOR(priv->locality)); + phy->iobase + TPM_INT_VECTOR(priv->locality)); /* Clear all existing */ - iowrite32(ioread32(priv->iobase + + iowrite32(ioread32(phy->iobase + TPM_INT_STATUS(priv->locality)), - priv->iobase + TPM_INT_STATUS(priv->locality)); + phy->iobase + TPM_INT_STATUS(priv->locality)); /* Turn on */ iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, - priv->iobase + TPM_INT_ENABLE(priv->locality)); + phy->iobase + TPM_INT_ENABLE(priv->locality)); priv->irq_tested = false; @@ -641,7 +657,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, */ if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { iowrite8(original_int_vec, - priv->iobase + TPM_INT_VECTOR(priv->locality)); + phy->iobase + TPM_INT_VECTOR(priv->locality)); return 1; } @@ -654,11 +670,12 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, */ static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); u8 original_int_vec; int i; - original_int_vec = ioread8(priv->iobase + + original_int_vec = ioread8(phy->iobase + TPM_INT_VECTOR(priv->locality)); if (!original_int_vec) { @@ -678,8 +695,9 @@ MODULE_PARM_DESC(interrupts, "Enable interrupts"); static void tpm_tis_remove(struct tpm_chip *chip) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); - void __iomem *reg = priv->iobase + TPM_INT_ENABLE(priv->locality); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); + void __iomem *reg = phy->iobase + TPM_INT_ENABLE(priv->locality); iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(reg), reg); release_locality(chip, priv->locality, 1); @@ -691,10 +709,10 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, u32 vendor, intfcaps, intmask; int rc, probe; struct tpm_chip *chip; - struct priv_data *priv; + struct tpm_tis_tcg_phy *phy; - priv = devm_kzalloc(dev, sizeof(struct priv_data), GFP_KERNEL); - if (priv == NULL) + phy = devm_kzalloc(dev, sizeof(struct tpm_tis_tcg_phy), GFP_KERNEL); + if (phy == NULL) return -ENOMEM; chip = tpmm_chip_alloc(dev, &tpm_tis); @@ -705,9 +723,9 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, chip->acpi_dev_handle = acpi_dev_handle; #endif - priv->iobase = devm_ioremap_resource(dev, &tpm_info->res); - if (IS_ERR(priv->iobase)) - return PTR_ERR(priv->iobase); + phy->iobase = devm_ioremap_resource(dev, &tpm_info->res); + if (IS_ERR(phy->iobase)) + return PTR_ERR(phy->iobase); /* Maximum timeouts */ chip->timeout_a = TIS_TIMEOUT_A_MAX; @@ -715,7 +733,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, chip->timeout_c = TIS_TIMEOUT_C_MAX; chip->timeout_d = TIS_TIMEOUT_D_MAX; - dev_set_drvdata(&chip->dev, priv); + dev_set_drvdata(&chip->dev, &phy->priv); if (wait_startup(chip, 0) != 0) { rc = -ENODEV; @@ -723,13 +741,13 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, } /* Take control of the TPM's interrupt hardware and shut it off */ - intmask = ioread32(priv->iobase + - TPM_INT_ENABLE(priv->locality)); + intmask = ioread32(phy->iobase + + TPM_INT_ENABLE(phy->priv.locality)); intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; intmask &= ~TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, - priv->iobase + TPM_INT_ENABLE(priv->locality)); + phy->iobase + TPM_INT_ENABLE(phy->priv.locality)); if (request_locality(chip, 0) != 0) { rc = -ENODEV; @@ -740,12 +758,12 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, if (rc) goto out_err; - vendor = ioread32(priv->iobase + TPM_DID_VID(0)); - priv->manufacturer_id = vendor; + vendor = ioread32(phy->iobase + TPM_DID_VID(0)); + phy->priv.manufacturer_id = vendor; dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n", (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2", - vendor >> 16, ioread8(priv->iobase + TPM_RID(0))); + vendor >> 16, ioread8(phy->iobase + TPM_RID(0))); if (!itpm) { probe = probe_itpm(chip); @@ -762,8 +780,8 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, /* Figure out the capabilities */ intfcaps = - ioread32(priv->iobase + - TPM_INTF_CAPS(priv->locality)); + ioread32(phy->iobase + + TPM_INTF_CAPS(phy->priv.locality)); dev_dbg(dev, "TPM interface capabilities (0x%x):\n", intfcaps); if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) @@ -796,8 +814,8 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, } /* INTERRUPT Setup */ - init_waitqueue_head(&priv->read_queue); - init_waitqueue_head(&priv->int_queue); + init_waitqueue_head(&phy->priv.read_queue); + init_waitqueue_head(&phy->priv.int_queue); if (interrupts && tpm_info->irq != -1) { if (tpm_info->irq) { tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, @@ -841,23 +859,24 @@ out_err: #ifdef CONFIG_PM_SLEEP static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) { - struct priv_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); u32 intmask; /* reenable interrupts that device may have lost or BIOS/firmware may have disabled */ - iowrite8(priv->irq, priv->iobase + + iowrite8(priv->irq, phy->iobase + TPM_INT_VECTOR(priv->locality)); intmask = - ioread32(priv->iobase + TPM_INT_ENABLE(priv->locality)); + ioread32(phy->iobase + TPM_INT_ENABLE(priv->locality)); intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE; iowrite32(intmask, - priv->iobase + TPM_INT_ENABLE(priv->locality)); + phy->iobase + TPM_INT_ENABLE(priv->locality)); } static int tpm_tis_resume(struct device *dev) diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h new file mode 100644 index 0000000..2260f89 --- /dev/null +++ b/drivers/char/tpm/tpm_tis_core.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2005, 2006 IBM Corporation + * Copyright (C) 2014, 2015 Intel Corporation + * + * Authors: + * Leendert van Doorn + * Kylene Hall + * + * Maintained by: + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This device driver implements the TPM interface as defined in + * the TCG TPM Interface Spec version 1.2, revision 1.0. + * + * 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. + */ + +#ifndef __TPM_TIS_CORE_H__ +#define __TPM_TIS_CORE_H__ + +#include "tpm.h" + +struct tpm_tis_data { + u16 manufacturer_id; + int locality; + int irq; + bool irq_tested; + wait_queue_head_t int_queue; + wait_queue_head_t read_queue; +}; + +#endif -- cgit v0.10.2 From 1107d065fdf136f0c1af287fc9183a0139bd6ae6 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 19 May 2016 00:35:49 +0200 Subject: tpm_tis: Introduce intermediate layer for TPM access This splits tpm_tis in a high-level protocol part and a low-level interface for the actual TPM communication. The low-level interface can then be implemented by additional drivers to provide access to TPMs using other mechanisms, for example native I2C or SPI transfers, while still reusing the same TIS protocol implementation. Though the ioread/iowrite calls cannot fail, other implementations of this interface might want to return error codes if their communication fails. This follows the usual pattern of negative values representing errors and zero representing success. Positive values are not used (yet). Errors are passed back to the caller if possible. If the interface of a function does not allow that, it tries to do the most sensible thing it can, but this might also mean ignoring the error in this instance. This commit is based on the initial work by Peter Huewe. Signed-off-by: Alexander Steffen Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Stefan Berger Reviewed-by: Stefan Berger Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index cd5bb63..29254f0 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -134,11 +134,16 @@ static inline int is_itpm(struct acpi_device *dev) static int wait_startup(struct tpm_chip *chip, int l) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); unsigned long stop = jiffies + chip->timeout_a; + int rc; + u8 access; + do { - if (ioread8(phy->iobase + TPM_ACCESS(l)) & - TPM_ACCESS_VALID) + rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); + if (rc < 0) + return rc; + + if (access & TPM_ACCESS_VALID) return 0; msleep(TPM_TIMEOUT); } while (time_before(jiffies, stop)); @@ -148,10 +153,14 @@ static int wait_startup(struct tpm_chip *chip, int l) static int check_locality(struct tpm_chip *chip, int l) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); + int rc; + u8 access; + + rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); + if (rc < 0) + return rc; - if ((ioread8(phy->iobase + TPM_ACCESS(l)) & - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + if ((access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) return priv->locality = l; @@ -161,27 +170,32 @@ static int check_locality(struct tpm_chip *chip, int l) static void release_locality(struct tpm_chip *chip, int l, int force) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); + int rc; + u8 access; + + rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); + if (rc < 0) + return; - if (force || (ioread8(phy->iobase + TPM_ACCESS(l)) & + if (force || (access & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) - iowrite8(TPM_ACCESS_ACTIVE_LOCALITY, - phy->iobase + TPM_ACCESS(l)); + tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); + } static int request_locality(struct tpm_chip *chip, int l) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); unsigned long stop, timeout; long rc; if (check_locality(chip, l) >= 0) return l; - iowrite8(TPM_ACCESS_REQUEST_USE, - phy->iobase + TPM_ACCESS(l)); + rc = tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_REQUEST_USE); + if (rc < 0) + return rc; stop = jiffies + chip->timeout_a; @@ -215,38 +229,45 @@ again: static u8 tpm_tis_status(struct tpm_chip *chip) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); + int rc; + u8 status; + + rc = tpm_tis_read8(priv, TPM_STS(priv->locality), &status); + if (rc < 0) + return 0; - return ioread8(phy->iobase + - TPM_STS(priv->locality)); + return status; } static void tpm_tis_ready(struct tpm_chip *chip) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); /* this causes the current command to be aborted */ - iowrite8(TPM_STS_COMMAND_READY, - phy->iobase + TPM_STS(priv->locality)); + tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_COMMAND_READY); } static int get_burstcount(struct tpm_chip *chip) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); unsigned long stop; - int burstcnt; + int burstcnt, rc; + u8 value; /* wait for burstcount */ /* which timeout value, spec has 2 answers (c & d) */ stop = jiffies + chip->timeout_d; do { - burstcnt = ioread8(phy->iobase + - TPM_STS(priv->locality) + 1); - burstcnt += ioread8(phy->iobase + - TPM_STS(priv->locality) + - 2) << 8; + rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 1, &value); + if (rc < 0) + return rc; + + burstcnt = value; + rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 2, &value); + if (rc < 0) + return rc; + + burstcnt += value << 8; if (burstcnt) return burstcnt; msleep(TPM_TIMEOUT); @@ -257,18 +278,20 @@ static int get_burstcount(struct tpm_chip *chip) static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); - int size = 0, burstcnt; + int size = 0, burstcnt, rc; + while (size < count && wait_for_tpm_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, chip->timeout_c, - &priv->read_queue, true) - == 0) { - burstcnt = get_burstcount(chip); - for (; burstcnt > 0 && size < count; burstcnt--) - buf[size++] = ioread8(phy->iobase + - TPM_DATA_FIFO(priv->locality)); + &priv->read_queue, true) == 0) { + burstcnt = min_t(int, get_burstcount(chip), count - size); + rc = tpm_tis_read_bytes(priv, TPM_DATA_FIFO(priv->locality), + burstcnt, buf + size); + if (rc < 0) + return rc; + + size += burstcnt; } return size; } @@ -332,7 +355,6 @@ MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)"); static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); int rc, status, burstcnt; size_t count = 0; @@ -351,12 +373,13 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) } while (count < len - 1) { - burstcnt = get_burstcount(chip); - for (; burstcnt > 0 && count < len - 1; burstcnt--) { - iowrite8(buf[count], phy->iobase + - TPM_DATA_FIFO(priv->locality)); - count++; - } + burstcnt = min_t(int, get_burstcount(chip), len - count - 1); + rc = tpm_tis_write_bytes(priv, TPM_DATA_FIFO(priv->locality), + burstcnt, buf + count); + if (rc < 0) + goto out_err; + + count += burstcnt; wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, &priv->int_queue, false); @@ -368,8 +391,10 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) } /* write last byte */ - iowrite8(buf[count], - phy->iobase + TPM_DATA_FIFO(priv->locality)); + rc = tpm_tis_write8(priv, TPM_DATA_FIFO(priv->locality), buf[count]); + if (rc < 0) + goto out_err; + wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, &priv->int_queue, false); status = tpm_tis_status(chip); @@ -389,15 +414,16 @@ out_err: static void disable_interrupts(struct tpm_chip *chip) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); u32 intmask; + int rc; + + rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); + if (rc < 0) + intmask = 0; - intmask = - ioread32(phy->iobase + - TPM_INT_ENABLE(priv->locality)); intmask &= ~TPM_GLOBAL_INT_ENABLE; - iowrite32(intmask, - phy->iobase + TPM_INT_ENABLE(priv->locality)); + tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); + devm_free_irq(chip->dev.parent, priv->irq, chip); priv->irq = 0; chip->flags &= ~TPM_CHIP_FLAG_IRQ; @@ -411,7 +437,6 @@ static void disable_interrupts(struct tpm_chip *chip) static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); int rc; u32 ordinal; unsigned long dur; @@ -421,8 +446,9 @@ static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) return rc; /* go and do it */ - iowrite8(TPM_STS_GO, - phy->iobase + TPM_STS(priv->locality)); + rc = tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_GO); + if (rc < 0) + return rc; if (chip->flags & TPM_CHIP_FLAG_IRQ) { ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); @@ -484,11 +510,12 @@ static bool tpm_tis_update_timeouts(struct tpm_chip *chip, unsigned long *timeout_cap) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); - int i; + int i, rc; u32 did_vid; - did_vid = ioread32(phy->iobase + TPM_DID_VID(0)); + rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid); + if (rc < 0) + return rc; for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) { if (vendor_timeout_overrides[i].did_vid != did_vid) @@ -509,7 +536,6 @@ static bool tpm_tis_update_timeouts(struct tpm_chip *chip, static int probe_itpm(struct tpm_chip *chip) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); int rc = 0; u8 cmd_getticks[] = { 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, @@ -517,7 +543,11 @@ static int probe_itpm(struct tpm_chip *chip) }; size_t len = sizeof(cmd_getticks); bool rem_itpm = itpm; - u16 vendor = ioread16(phy->iobase + TPM_DID_VID(0)); + u16 vendor; + + rc = tpm_tis_read16(priv, TPM_DID_VID(0), &vendor); + if (rc < 0) + return rc; /* probe only iTPMS */ if (vendor != TPM_VID_INTEL) @@ -575,16 +605,68 @@ static const struct tpm_class_ops tpm_tis = { .req_canceled = tpm_tis_req_canceled, }; +static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *result) +{ + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data); + + while (len--) + *result++ = ioread8(phy->iobase + addr); + return 0; +} + +static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *value) +{ + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data); + + while (len--) + iowrite8(*value++, phy->iobase + addr); + return 0; +} + +static int tpm_tcg_read16(struct tpm_tis_data *data, u32 addr, u16 *result) +{ + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data); + + *result = ioread16(phy->iobase + addr); + return 0; +} + +static int tpm_tcg_read32(struct tpm_tis_data *data, u32 addr, u32 *result) +{ + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data); + + *result = ioread32(phy->iobase + addr); + return 0; +} + +static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value) +{ + struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data); + + iowrite32(value, phy->iobase + addr); + return 0; +} + +static const struct tpm_tis_phy_ops tpm_tcg = { + .read_bytes = tpm_tcg_read_bytes, + .write_bytes = tpm_tcg_write_bytes, + .read16 = tpm_tcg_read16, + .read32 = tpm_tcg_read32, + .write32 = tpm_tcg_write32, +}; + static irqreturn_t tis_int_handler(int dummy, void *dev_id) { struct tpm_chip *chip = dev_id; struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); u32 interrupt; - int i; + int i, rc; - interrupt = ioread32(phy->iobase + - TPM_INT_STATUS(priv->locality)); + rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt); + if (rc < 0) + return IRQ_NONE; if (interrupt == 0) return IRQ_NONE; @@ -602,10 +684,11 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id) wake_up_interruptible(&priv->int_queue); /* Clear interrupts handled with TPM_EOI */ - iowrite32(interrupt, - phy->iobase + - TPM_INT_STATUS(priv->locality)); - ioread32(phy->iobase + TPM_INT_STATUS(priv->locality)); + rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt); + if (rc < 0) + return IRQ_NONE; + + tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt); return IRQ_HANDLED; } @@ -617,8 +700,9 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, int flags, int irq) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); u8 original_int_vec; + int rc; + u32 int_status; if (devm_request_irq(chip->dev.parent, irq, tis_int_handler, flags, dev_name(&chip->dev), chip) != 0) { @@ -628,19 +712,29 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, } priv->irq = irq; - original_int_vec = ioread8(phy->iobase + - TPM_INT_VECTOR(priv->locality)); - iowrite8(irq, - phy->iobase + TPM_INT_VECTOR(priv->locality)); + rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality), + &original_int_vec); + if (rc < 0) + return rc; + + rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), irq); + if (rc < 0) + return rc; + + rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &int_status); + if (rc < 0) + return rc; /* Clear all existing */ - iowrite32(ioread32(phy->iobase + - TPM_INT_STATUS(priv->locality)), - phy->iobase + TPM_INT_STATUS(priv->locality)); + rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), int_status); + if (rc < 0) + return rc; /* Turn on */ - iowrite32(intmask | TPM_GLOBAL_INT_ENABLE, - phy->iobase + TPM_INT_ENABLE(priv->locality)); + rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), + intmask | TPM_GLOBAL_INT_ENABLE); + if (rc < 0) + return rc; priv->irq_tested = false; @@ -656,8 +750,11 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, * will call disable_irq which undoes all of the above. */ if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { - iowrite8(original_int_vec, - phy->iobase + TPM_INT_VECTOR(priv->locality)); + rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), + original_int_vec); + if (rc < 0) + return rc; + return 1; } @@ -671,12 +768,13 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); u8 original_int_vec; - int i; + int i, rc; - original_int_vec = ioread8(phy->iobase + - TPM_INT_VECTOR(priv->locality)); + rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality), + &original_int_vec); + if (rc < 0) + return; if (!original_int_vec) { if (IS_ENABLED(CONFIG_X86)) @@ -696,10 +794,15 @@ MODULE_PARM_DESC(interrupts, "Enable interrupts"); static void tpm_tis_remove(struct tpm_chip *chip) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); - void __iomem *reg = phy->iobase + TPM_INT_ENABLE(priv->locality); + u32 reg = TPM_INT_ENABLE(priv->locality); + u32 interrupt; + int rc; + + rc = tpm_tis_read32(priv, reg, &interrupt); + if (rc < 0) + interrupt = 0; - iowrite32(~TPM_GLOBAL_INT_ENABLE & ioread32(reg), reg); + tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt); release_locality(chip, priv->locality, 1); } @@ -707,6 +810,7 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, acpi_handle acpi_dev_handle) { u32 vendor, intfcaps, intmask; + u8 rid; int rc, probe; struct tpm_chip *chip; struct tpm_tis_tcg_phy *phy; @@ -727,6 +831,8 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, if (IS_ERR(phy->iobase)) return PTR_ERR(phy->iobase); + phy->priv.phy_ops = &tpm_tcg; + /* Maximum timeouts */ chip->timeout_a = TIS_TIMEOUT_A_MAX; chip->timeout_b = TIS_TIMEOUT_B_MAX; @@ -741,13 +847,16 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, } /* Take control of the TPM's interrupt hardware and shut it off */ - intmask = ioread32(phy->iobase + - TPM_INT_ENABLE(phy->priv.locality)); + rc = tpm_tis_read32(&phy->priv, TPM_INT_ENABLE(phy->priv.locality), + &intmask); + if (rc < 0) + goto out_err; + intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; intmask &= ~TPM_GLOBAL_INT_ENABLE; - iowrite32(intmask, - phy->iobase + TPM_INT_ENABLE(phy->priv.locality)); + tpm_tis_write32(&phy->priv, TPM_INT_ENABLE(phy->priv.locality), + intmask); if (request_locality(chip, 0) != 0) { rc = -ENODEV; @@ -758,12 +867,19 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, if (rc) goto out_err; - vendor = ioread32(phy->iobase + TPM_DID_VID(0)); + rc = tpm_tis_read32(&phy->priv, TPM_DID_VID(0), &vendor); + if (rc < 0) + goto out_err; + phy->priv.manufacturer_id = vendor; + rc = tpm_tis_read8(&phy->priv, TPM_RID(0), &rid); + if (rc < 0) + goto out_err; + dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n", (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2", - vendor >> 16, ioread8(phy->iobase + TPM_RID(0))); + vendor >> 16, rid); if (!itpm) { probe = probe_itpm(chip); @@ -777,11 +893,12 @@ static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, if (itpm) dev_info(dev, "Intel iTPM workaround enabled\n"); - /* Figure out the capabilities */ - intfcaps = - ioread32(phy->iobase + - TPM_INTF_CAPS(phy->priv.locality)); + rc = tpm_tis_read32(&phy->priv, TPM_INTF_CAPS(phy->priv.locality), + &intfcaps); + if (rc < 0) + goto out_err; + dev_dbg(dev, "TPM interface capabilities (0x%x):\n", intfcaps); if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) @@ -860,23 +977,24 @@ out_err: static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) { struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(priv); u32 intmask; + int rc; /* reenable interrupts that device may have lost or BIOS/firmware may have disabled */ - iowrite8(priv->irq, phy->iobase + - TPM_INT_VECTOR(priv->locality)); + rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), priv->irq); + if (rc < 0) + return; - intmask = - ioread32(phy->iobase + TPM_INT_ENABLE(priv->locality)); + rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); + if (rc < 0) + return; intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE; - iowrite32(intmask, - phy->iobase + TPM_INT_ENABLE(priv->locality)); + tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); } static int tpm_tis_resume(struct device *dev) diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index 2260f89..b922100 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -32,6 +32,57 @@ struct tpm_tis_data { bool irq_tested; wait_queue_head_t int_queue; wait_queue_head_t read_queue; + const struct tpm_tis_phy_ops *phy_ops; }; +struct tpm_tis_phy_ops { + int (*read_bytes)(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *result); + int (*write_bytes)(struct tpm_tis_data *data, u32 addr, u16 len, + u8 *value); + int (*read16)(struct tpm_tis_data *data, u32 addr, u16 *result); + int (*read32)(struct tpm_tis_data *data, u32 addr, u32 *result); + int (*write32)(struct tpm_tis_data *data, u32 addr, u32 src); +}; + +static inline int tpm_tis_read_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, u8 *result) +{ + return data->phy_ops->read_bytes(data, addr, len, result); +} + +static inline int tpm_tis_read8(struct tpm_tis_data *data, u32 addr, u8 *result) +{ + return data->phy_ops->read_bytes(data, addr, 1, result); +} + +static inline int tpm_tis_read16(struct tpm_tis_data *data, u32 addr, + u16 *result) +{ + return data->phy_ops->read16(data, addr, result); +} + +static inline int tpm_tis_read32(struct tpm_tis_data *data, u32 addr, + u32 *result) +{ + return data->phy_ops->read32(data, addr, result); +} + +static inline int tpm_tis_write_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, u8 *value) +{ + return data->phy_ops->write_bytes(data, addr, len, value); +} + +static inline int tpm_tis_write8(struct tpm_tis_data *data, u32 addr, u8 value) +{ + return data->phy_ops->write_bytes(data, addr, 1, &value); +} + +static inline int tpm_tis_write32(struct tpm_tis_data *data, u32 addr, + u32 value) +{ + return data->phy_ops->write32(data, addr, value); +} + #endif -- cgit v0.10.2 From 04bb2d8ee9a2eae73eab025739d3ce413feed3a8 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 19 May 2016 00:35:50 +0200 Subject: devicetree: Add infineon to vendor-prefix.txt Add missing vendor to vendor-prefix.txt This commit is based on the initial work by Peter Huewe. Cc: devicetree@vger.kernel.org Signed-off-by: Christophe Ricard Acked-by: Rob Herring Acked-by: Jarkko Sakkinen Tested-by: Stefan Berger Reviewed-by: Stefan Berger Signed-off-by: Jarkko Sakkinen diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index a7440bc..f090d2e 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -128,6 +128,7 @@ idt Integrated Device Technologies, Inc. ifi Ingenieurburo Fur Ic-Technologie (I/F/I) iom Iomega Corporation img Imagination Technologies Ltd. +infineon Infineon Technologies inforce Inforce Computing ingenic Ingenic Semiconductor innolux Innolux Corporation -- cgit v0.10.2 From 92eb0f73e3517b63337b7fccc2de150b4e6147ce Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 19 May 2016 00:35:51 +0200 Subject: devicetree: Add Trusted Computing Group to vendor-prefix.txt Add missing vendor to vendor-prefix.txt. Trusted Computing Group design common specifications for TPM (Trusted Platform Module) vendors. TCG designates a TPM answering to a public specification. This commit is based on the initial work by Peter Huewe. Cc: devicetree@vger.kernel.org Signed-off-by: Christophe Ricard Acked-by: Rob Herring Acked-by: Jarkko Sakkinen Tested-by: Stefan Berger Reviewed-by: Stefan Berger Signed-off-by: Jarkko Sakkinen diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt index f090d2e..fe78617 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.txt +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -256,6 +256,7 @@ synology Synology, Inc. SUNW Sun Microsystems, Inc tbs TBS Technologies tcl Toby Churchill Ltd. +tcg Trusted Computing Group technologic Technologic Systems thine THine Electronics, Inc. ti Texas Instruments -- cgit v0.10.2 From 41a5e1cf1fe151ed48b4b3106c748d03a85133ce Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 19 May 2016 00:35:52 +0200 Subject: tpm/tpm_tis: Split tpm_tis driver into a core and TCG TIS compliant phy To avoid code duplication between the old tpm_tis and the new and future native tcg tis driver(ie: spi, i2c...), the tpm_tis driver was reworked, so that all common logic is extracted and can be reused from all drivers. The core methods can also be used from other TIS like drivers. itpm workaround is now managed with a specific tis flag TPM_TIS_ITPM_POSSIBLE. This commit is based on the initial work by Peter Huewe. Signed-off-by: Peter Huewe Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Stefan Berger Reviewed-by: Stefan Berger Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index bfdc5c0..241ff4c 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -24,9 +24,16 @@ menuconfig TCG_TPM if TCG_TPM +config TCG_TIS_CORE + tristate + ---help--- + TCG TIS TPM core driver. It implements the TPM TCG TIS logic and hooks + into the TPM kernel APIs. Physical layers will register against it. + config TCG_TIS tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface" depends on X86 + select TCG_TIS_CORE ---help--- If you have a TPM security chip that is compliant with the TCG TIS 1.2 TPM specification (TPM1.2) or the TCG PTP FIFO diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 98de5e6..662221f 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -12,6 +12,7 @@ ifdef CONFIG_TCG_IBMVTPM tpm-y += tpm_eventlog.o tpm_of.o endif endif +obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o obj-$(CONFIG_TCG_TIS) += tpm_tis.o obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 29254f0..eaf5730 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -31,40 +31,6 @@ #include "tpm.h" #include "tpm_tis_core.h" -enum tis_access { - TPM_ACCESS_VALID = 0x80, - TPM_ACCESS_ACTIVE_LOCALITY = 0x20, - TPM_ACCESS_REQUEST_PENDING = 0x04, - TPM_ACCESS_REQUEST_USE = 0x02, -}; - -enum tis_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 tis_int_flags { - TPM_GLOBAL_INT_ENABLE = 0x80000000, - TPM_INTF_BURST_COUNT_STATIC = 0x100, - TPM_INTF_CMD_READY_INT = 0x080, - TPM_INTF_INT_EDGE_FALLING = 0x040, - TPM_INTF_INT_EDGE_RISING = 0x020, - TPM_INTF_INT_LEVEL_LOW = 0x010, - TPM_INTF_INT_LEVEL_HIGH = 0x008, - TPM_INTF_LOCALITY_CHANGE_INT = 0x004, - TPM_INTF_STS_VALID_INT = 0x002, - TPM_INTF_DATA_AVAIL_INT = 0x001, -}; - -enum tis_defaults { - TIS_MEM_LEN = 0x5000, - TIS_SHORT_TIMEOUT = 750, /* ms */ - TIS_LONG_TIMEOUT = 2000, /* 2 sec */ -}; - struct tpm_info { struct resource res; /* irq > 0 means: use irq $irq; @@ -74,26 +40,6 @@ struct tpm_info { int irq; }; -/* Some timeout values are needed before it is known whether the chip is - * TPM 1.0 or TPM 2.0. - */ -#define TIS_TIMEOUT_A_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A) -#define TIS_TIMEOUT_B_MAX max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B) -#define TIS_TIMEOUT_C_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C) -#define TIS_TIMEOUT_D_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D) - -#define TPM_ACCESS(l) (0x0000 | ((l) << 12)) -#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) -#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12)) -#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12)) -#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12)) -#define TPM_STS(l) (0x0018 | ((l) << 12)) -#define TPM_STS3(l) (0x001b | ((l) << 12)) -#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12)) - -#define TPM_DID_VID(l) (0x0F00 | ((l) << 12)) -#define TPM_RID(l) (0x0F04 | ((l) << 12)) - struct tpm_tis_tcg_phy { struct tpm_tis_data priv; void __iomem *iobase; @@ -104,6 +50,20 @@ static inline struct tpm_tis_tcg_phy *to_tpm_tis_tcg_phy(struct tpm_tis_data *da return container_of(data, struct tpm_tis_tcg_phy, priv); } +static bool interrupts = true; +module_param(interrupts, bool, 0444); +MODULE_PARM_DESC(interrupts, "Enable interrupts"); + +static bool itpm; +module_param(itpm, bool, 0444); +MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)"); + +static bool force; +#ifdef CONFIG_X86 +module_param(force, bool, 0444); +MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry"); +#endif + #if defined(CONFIG_PNP) && defined(CONFIG_ACPI) static int has_hid(struct acpi_device *dev, const char *hid) { @@ -127,484 +87,6 @@ static inline int is_itpm(struct acpi_device *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) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - unsigned long stop = jiffies + chip->timeout_a; - int rc; - u8 access; - - do { - rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); - if (rc < 0) - return rc; - - if (access & 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) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int rc; - u8 access; - - rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); - if (rc < 0) - return rc; - - if ((access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) - return priv->locality = l; - - return -1; -} - -static void release_locality(struct tpm_chip *chip, int l, int force) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int rc; - u8 access; - - rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); - if (rc < 0) - return; - - if (force || (access & - (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == - (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) - tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); - -} - -static int request_locality(struct tpm_chip *chip, int l) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - unsigned long stop, timeout; - long rc; - - if (check_locality(chip, l) >= 0) - return l; - - rc = tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_REQUEST_USE); - if (rc < 0) - return rc; - - stop = jiffies + chip->timeout_a; - - if (chip->flags & TPM_CHIP_FLAG_IRQ) { -again: - timeout = stop - jiffies; - if ((long)timeout <= 0) - return -1; - rc = wait_event_interruptible_timeout(priv->int_queue, - (check_locality - (chip, l) >= 0), - timeout); - if (rc > 0) - return l; - if (rc == -ERESTARTSYS && freezing(current)) { - clear_thread_flag(TIF_SIGPENDING); - goto again; - } - } else { - /* wait for burstcount */ - do { - if (check_locality(chip, l) >= 0) - return l; - msleep(TPM_TIMEOUT); - } - while (time_before(jiffies, stop)); - } - return -1; -} - -static u8 tpm_tis_status(struct tpm_chip *chip) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int rc; - u8 status; - - rc = tpm_tis_read8(priv, TPM_STS(priv->locality), &status); - if (rc < 0) - return 0; - - return status; -} - -static void tpm_tis_ready(struct tpm_chip *chip) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - - /* this causes the current command to be aborted */ - tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_COMMAND_READY); -} - -static int get_burstcount(struct tpm_chip *chip) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - unsigned long stop; - int burstcnt, rc; - u8 value; - - /* wait for burstcount */ - /* which timeout value, spec has 2 answers (c & d) */ - stop = jiffies + chip->timeout_d; - do { - rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 1, &value); - if (rc < 0) - return rc; - - burstcnt = value; - rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 2, &value); - if (rc < 0) - return rc; - - burstcnt += value << 8; - if (burstcnt) - return burstcnt; - msleep(TPM_TIMEOUT); - } while (time_before(jiffies, stop)); - return -EBUSY; -} - -static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int size = 0, burstcnt, rc; - - while (size < count && - wait_for_tpm_stat(chip, - TPM_STS_DATA_AVAIL | TPM_STS_VALID, - chip->timeout_c, - &priv->read_queue, true) == 0) { - burstcnt = min_t(int, get_burstcount(chip), count - size); - rc = tpm_tis_read_bytes(priv, TPM_DATA_FIFO(priv->locality), - burstcnt, buf + size); - if (rc < 0) - return rc; - - size += burstcnt; - } - return size; -} - -static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int size = 0; - int expected, status; - - if (count < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } - - /* read first 10 bytes, including tag, paramsize, and result */ - if ((size = - recv_data(chip, buf, TPM_HEADER_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; - } - - if ((size += - recv_data(chip, &buf[TPM_HEADER_SIZE], - expected - TPM_HEADER_SIZE)) < expected) { - dev_err(&chip->dev, "Unable to read remainder of result\n"); - size = -ETIME; - goto out; - } - - wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, - &priv->int_queue, false); - status = tpm_tis_status(chip); - if (status & TPM_STS_DATA_AVAIL) { /* retry? */ - dev_err(&chip->dev, "Error left over data\n"); - size = -EIO; - goto out; - } - -out: - tpm_tis_ready(chip); - release_locality(chip, priv->locality, 0); - return size; -} - -static bool itpm; -module_param(itpm, bool, 0444); -MODULE_PARM_DESC(itpm, "Force iTPM workarounds (found on some Lenovo laptops)"); - -/* - * If interrupts are used (signaled by an irq set in the vendor structure) - * tpm.c can skip polling for the data to be available as the interrupt is - * waited for here - */ -static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int rc, status, burstcnt; - size_t count = 0; - - if (request_locality(chip, 0) < 0) - return -EBUSY; - - status = tpm_tis_status(chip); - if ((status & TPM_STS_COMMAND_READY) == 0) { - tpm_tis_ready(chip); - if (wait_for_tpm_stat - (chip, TPM_STS_COMMAND_READY, chip->timeout_b, - &priv->int_queue, false) < 0) { - rc = -ETIME; - goto out_err; - } - } - - while (count < len - 1) { - burstcnt = min_t(int, get_burstcount(chip), len - count - 1); - rc = tpm_tis_write_bytes(priv, TPM_DATA_FIFO(priv->locality), - burstcnt, buf + count); - if (rc < 0) - goto out_err; - - count += burstcnt; - - wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, - &priv->int_queue, false); - status = tpm_tis_status(chip); - if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) { - rc = -EIO; - goto out_err; - } - } - - /* write last byte */ - rc = tpm_tis_write8(priv, TPM_DATA_FIFO(priv->locality), buf[count]); - if (rc < 0) - goto out_err; - - wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, - &priv->int_queue, false); - status = tpm_tis_status(chip); - if ((status & TPM_STS_DATA_EXPECT) != 0) { - rc = -EIO; - goto out_err; - } - - return 0; - -out_err: - tpm_tis_ready(chip); - release_locality(chip, priv->locality, 0); - return rc; -} - -static void disable_interrupts(struct tpm_chip *chip) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - u32 intmask; - int rc; - - rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); - if (rc < 0) - intmask = 0; - - intmask &= ~TPM_GLOBAL_INT_ENABLE; - tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); - - devm_free_irq(chip->dev.parent, priv->irq, chip); - priv->irq = 0; - chip->flags &= ~TPM_CHIP_FLAG_IRQ; -} - -/* - * If interrupts are used (signaled by an irq set in the vendor structure) - * tpm.c can skip polling for the data to be available as the interrupt is - * waited for here - */ -static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int rc; - u32 ordinal; - unsigned long dur; - - rc = tpm_tis_send_data(chip, buf, len); - if (rc < 0) - return rc; - - /* go and do it */ - rc = tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_GO); - if (rc < 0) - return rc; - - if (chip->flags & TPM_CHIP_FLAG_IRQ) { - ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); - - if (chip->flags & TPM_CHIP_FLAG_TPM2) - dur = tpm2_calc_ordinal_duration(chip, ordinal); - else - dur = tpm_calc_ordinal_duration(chip, ordinal); - - if (wait_for_tpm_stat - (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur, - &priv->read_queue, false) < 0) { - rc = -ETIME; - goto out_err; - } - } - return len; -out_err: - tpm_tis_ready(chip); - release_locality(chip, priv->locality, 0); - return rc; -} - -static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int rc, irq; - - if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || priv->irq_tested) - return tpm_tis_send_main(chip, buf, len); - - /* Verify receipt of the expected IRQ */ - irq = priv->irq; - priv->irq = 0; - chip->flags &= ~TPM_CHIP_FLAG_IRQ; - rc = tpm_tis_send_main(chip, buf, len); - priv->irq = irq; - chip->flags |= TPM_CHIP_FLAG_IRQ; - if (!priv->irq_tested) - msleep(1); - if (!priv->irq_tested) - disable_interrupts(chip); - priv->irq_tested = true; - return rc; -} - -struct tis_vendor_timeout_override { - u32 did_vid; - unsigned long timeout_us[4]; -}; - -static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = { - /* Atmel 3204 */ - { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000), - (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } }, -}; - -static bool tpm_tis_update_timeouts(struct tpm_chip *chip, - unsigned long *timeout_cap) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int i, rc; - u32 did_vid; - - rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid); - if (rc < 0) - return rc; - - for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) { - if (vendor_timeout_overrides[i].did_vid != did_vid) - continue; - memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us, - sizeof(vendor_timeout_overrides[i].timeout_us)); - return true; - } - - return false; -} - -/* - * Early probing for iTPM with STS_DATA_EXPECT flaw. - * Try sending command without itpm flag set and if that - * fails, repeat with itpm flag set. - */ -static int probe_itpm(struct tpm_chip *chip) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - int rc = 0; - u8 cmd_getticks[] = { - 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, - 0x00, 0x00, 0x00, 0xf1 - }; - size_t len = sizeof(cmd_getticks); - bool rem_itpm = itpm; - u16 vendor; - - rc = tpm_tis_read16(priv, TPM_DID_VID(0), &vendor); - if (rc < 0) - return rc; - - /* probe only iTPMS */ - if (vendor != TPM_VID_INTEL) - return 0; - - itpm = false; - - rc = tpm_tis_send_data(chip, cmd_getticks, len); - if (rc == 0) - goto out; - - tpm_tis_ready(chip); - release_locality(chip, priv->locality, 0); - - itpm = true; - - rc = tpm_tis_send_data(chip, cmd_getticks, len); - if (rc == 0) { - dev_info(&chip->dev, "Detected an iTPM.\n"); - rc = 1; - } else - rc = -EFAULT; - -out: - itpm = rem_itpm; - tpm_tis_ready(chip); - release_locality(chip, priv->locality, 0); - - return rc; -} - -static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - - switch (priv->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 tpm_class_ops tpm_tis = { - .status = tpm_tis_status, - .recv = tpm_tis_recv, - .send = tpm_tis_send, - .cancel = tpm_tis_ready, - .update_timeouts = tpm_tis_update_timeouts, - .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_canceled = tpm_tis_req_canceled, -}; - static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len, u8 *result) { @@ -657,367 +139,29 @@ static const struct tpm_tis_phy_ops tpm_tcg = { .write32 = tpm_tcg_write32, }; -static irqreturn_t tis_int_handler(int dummy, void *dev_id) -{ - struct tpm_chip *chip = dev_id; - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - u32 interrupt; - int i, rc; - - rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt); - if (rc < 0) - return IRQ_NONE; - - if (interrupt == 0) - return IRQ_NONE; - - priv->irq_tested = true; - if (interrupt & TPM_INTF_DATA_AVAIL_INT) - wake_up_interruptible(&priv->read_queue); - if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) - for (i = 0; i < 5; i++) - if (check_locality(chip, i) >= 0) - break; - if (interrupt & - (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT | - TPM_INTF_CMD_READY_INT)) - wake_up_interruptible(&priv->int_queue); - - /* Clear interrupts handled with TPM_EOI */ - rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt); - if (rc < 0) - return IRQ_NONE; - - tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt); - return IRQ_HANDLED; -} - -/* Register the IRQ and issue a command that will cause an interrupt. If an - * irq is seen then leave the chip setup for IRQ operation, otherwise reverse - * everything and leave in polling mode. Returns 0 on success. - */ -static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, - int flags, int irq) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - u8 original_int_vec; - int rc; - u32 int_status; - - if (devm_request_irq(chip->dev.parent, irq, tis_int_handler, flags, - dev_name(&chip->dev), chip) != 0) { - dev_info(&chip->dev, "Unable to request irq: %d for probe\n", - irq); - return -1; - } - priv->irq = irq; - - rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality), - &original_int_vec); - if (rc < 0) - return rc; - - rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), irq); - if (rc < 0) - return rc; - - rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &int_status); - if (rc < 0) - return rc; - - /* Clear all existing */ - rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), int_status); - if (rc < 0) - return rc; - - /* Turn on */ - rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), - intmask | TPM_GLOBAL_INT_ENABLE); - if (rc < 0) - return rc; - - priv->irq_tested = false; - - /* Generate an interrupt by having the core call through to - * tpm_tis_send - */ - if (chip->flags & TPM_CHIP_FLAG_TPM2) - tpm2_gen_interrupt(chip); - else - tpm_gen_interrupt(chip); - - /* tpm_tis_send will either confirm the interrupt is working or it - * will call disable_irq which undoes all of the above. - */ - if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { - rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), - original_int_vec); - if (rc < 0) - return rc; - - return 1; - } - - return 0; -} - -/* Try to find the IRQ the TPM is using. This is for legacy x86 systems that - * do not have ACPI/etc. We typically expect the interrupt to be declared if - * present. - */ -static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - u8 original_int_vec; - int i, rc; - - rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality), - &original_int_vec); - if (rc < 0) - return; - - if (!original_int_vec) { - if (IS_ENABLED(CONFIG_X86)) - for (i = 3; i <= 15; i++) - if (!tpm_tis_probe_irq_single(chip, intmask, 0, - i)) - return; - } else if (!tpm_tis_probe_irq_single(chip, intmask, 0, - original_int_vec)) - return; -} - -static bool interrupts = true; -module_param(interrupts, bool, 0444); -MODULE_PARM_DESC(interrupts, "Enable interrupts"); - -static void tpm_tis_remove(struct tpm_chip *chip) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - u32 reg = TPM_INT_ENABLE(priv->locality); - u32 interrupt; - int rc; - - rc = tpm_tis_read32(priv, reg, &interrupt); - if (rc < 0) - interrupt = 0; - - tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt); - release_locality(chip, priv->locality, 1); -} - static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, acpi_handle acpi_dev_handle) { - u32 vendor, intfcaps, intmask; - u8 rid; - int rc, probe; - struct tpm_chip *chip; struct tpm_tis_tcg_phy *phy; + int irq = -1; phy = devm_kzalloc(dev, sizeof(struct tpm_tis_tcg_phy), GFP_KERNEL); if (phy == NULL) return -ENOMEM; - chip = tpmm_chip_alloc(dev, &tpm_tis); - if (IS_ERR(chip)) - return PTR_ERR(chip); - -#ifdef CONFIG_ACPI - chip->acpi_dev_handle = acpi_dev_handle; -#endif - phy->iobase = devm_ioremap_resource(dev, &tpm_info->res); if (IS_ERR(phy->iobase)) return PTR_ERR(phy->iobase); - phy->priv.phy_ops = &tpm_tcg; - - /* Maximum timeouts */ - chip->timeout_a = TIS_TIMEOUT_A_MAX; - chip->timeout_b = TIS_TIMEOUT_B_MAX; - chip->timeout_c = TIS_TIMEOUT_C_MAX; - chip->timeout_d = TIS_TIMEOUT_D_MAX; - - dev_set_drvdata(&chip->dev, &phy->priv); - - if (wait_startup(chip, 0) != 0) { - rc = -ENODEV; - goto out_err; - } - - /* Take control of the TPM's interrupt hardware and shut it off */ - rc = tpm_tis_read32(&phy->priv, TPM_INT_ENABLE(phy->priv.locality), - &intmask); - if (rc < 0) - goto out_err; - - intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | - TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; - intmask &= ~TPM_GLOBAL_INT_ENABLE; - tpm_tis_write32(&phy->priv, TPM_INT_ENABLE(phy->priv.locality), - intmask); - - if (request_locality(chip, 0) != 0) { - rc = -ENODEV; - goto out_err; - } - - rc = tpm2_probe(chip); - if (rc) - goto out_err; - - rc = tpm_tis_read32(&phy->priv, TPM_DID_VID(0), &vendor); - if (rc < 0) - goto out_err; - - phy->priv.manufacturer_id = vendor; - - rc = tpm_tis_read8(&phy->priv, TPM_RID(0), &rid); - if (rc < 0) - goto out_err; - - dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n", - (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2", - vendor >> 16, rid); - - if (!itpm) { - probe = probe_itpm(chip); - if (probe < 0) { - rc = -ENODEV; - goto out_err; - } - itpm = !!probe; - } + if (interrupts) + irq = tpm_info->irq; if (itpm) - dev_info(dev, "Intel iTPM workaround enabled\n"); - - /* Figure out the capabilities */ - rc = tpm_tis_read32(&phy->priv, TPM_INTF_CAPS(phy->priv.locality), - &intfcaps); - if (rc < 0) - goto out_err; - - dev_dbg(dev, "TPM interface capabilities (0x%x):\n", - intfcaps); - if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) - dev_dbg(dev, "\tBurst Count Static\n"); - if (intfcaps & TPM_INTF_CMD_READY_INT) - dev_dbg(dev, "\tCommand Ready Int Support\n"); - if (intfcaps & TPM_INTF_INT_EDGE_FALLING) - dev_dbg(dev, "\tInterrupt Edge Falling\n"); - if (intfcaps & TPM_INTF_INT_EDGE_RISING) - dev_dbg(dev, "\tInterrupt Edge Rising\n"); - if (intfcaps & TPM_INTF_INT_LEVEL_LOW) - dev_dbg(dev, "\tInterrupt Level Low\n"); - if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) - dev_dbg(dev, "\tInterrupt Level High\n"); - if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) - dev_dbg(dev, "\tLocality Change Int Support\n"); - if (intfcaps & TPM_INTF_STS_VALID_INT) - dev_dbg(dev, "\tSts Valid Int Support\n"); - if (intfcaps & TPM_INTF_DATA_AVAIL_INT) - dev_dbg(dev, "\tData Avail Int Support\n"); - - /* Very early on issue a command to the TPM in polling mode to make - * sure it works. May as well use that command to set the proper - * timeouts for the driver. - */ - if (tpm_get_timeouts(chip)) { - dev_err(dev, "Could not get TPM timeouts and durations\n"); - rc = -ENODEV; - goto out_err; - } + phy->priv.flags |= TPM_TIS_ITPM_POSSIBLE; - /* INTERRUPT Setup */ - init_waitqueue_head(&phy->priv.read_queue); - init_waitqueue_head(&phy->priv.int_queue); - if (interrupts && tpm_info->irq != -1) { - if (tpm_info->irq) { - tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, - tpm_info->irq); - if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) - dev_err(&chip->dev, FW_BUG - "TPM interrupt not working, polling instead\n"); - } else - tpm_tis_probe_irq(chip, intmask); - } - - if (chip->flags & TPM_CHIP_FLAG_TPM2) { - rc = tpm2_do_selftest(chip); - if (rc == TPM2_RC_INITIALIZE) { - dev_warn(dev, "Firmware has not started TPM\n"); - rc = tpm2_startup(chip, TPM2_SU_CLEAR); - if (!rc) - rc = tpm2_do_selftest(chip); - } - - if (rc) { - dev_err(dev, "TPM self test failed\n"); - if (rc > 0) - rc = -ENODEV; - goto out_err; - } - } else { - if (tpm_do_selftest(chip)) { - dev_err(dev, "TPM self test failed\n"); - rc = -ENODEV; - goto out_err; - } - } - - return tpm_chip_register(chip); -out_err: - tpm_tis_remove(chip); - return rc; -} - -#ifdef CONFIG_PM_SLEEP -static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) -{ - struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); - u32 intmask; - int rc; - - /* reenable interrupts that device may have lost or - BIOS/firmware may have disabled */ - rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), priv->irq); - if (rc < 0) - return; - - rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); - if (rc < 0) - return; - - intmask |= TPM_INTF_CMD_READY_INT - | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT - | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE; - - tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); -} - -static int tpm_tis_resume(struct device *dev) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - int ret; - - if (chip->flags & TPM_CHIP_FLAG_IRQ) - tpm_tis_reenable_interrupts(chip); - - ret = tpm_pm_resume(dev); - if (ret) - return ret; - - /* TPM 1.2 requires self-test on resume. This function actually returns - * an error code but for unknown reason it isn't handled. - */ - if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) - tpm_do_selftest(chip); - - return 0; + return tpm_tis_core_init(dev, &phy->priv, irq, &tpm_tcg, + acpi_dev_handle); } -#endif static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); @@ -1219,12 +363,6 @@ static struct platform_driver tis_drv = { }, }; -static bool force; -#ifdef CONFIG_X86 -module_param(force, bool, 0444); -MODULE_PARM_DESC(force, "Force device probe rather than using ACPI entry"); -#endif - static int tpm_tis_force_device(void) { struct platform_device *pdev; diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c new file mode 100644 index 0000000..03a06b3 --- /dev/null +++ b/drivers/char/tpm/tpm_tis_core.c @@ -0,0 +1,862 @@ +/* + * Copyright (C) 2005, 2006 IBM Corporation + * Copyright (C) 2014, 2015 Intel Corporation + * + * Authors: + * Leendert van Doorn + * Kylene Hall + * + * Maintained by: + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This device driver implements the TPM interface as defined in + * the TCG TPM Interface Spec version 1.2, revision 1.0. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tpm.h" +#include "tpm_tis_core.h" + +/* 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) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + unsigned long stop = jiffies + chip->timeout_a; + + do { + int rc; + u8 access; + + rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); + if (rc < 0) + return rc; + + if (access & 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) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + int rc; + u8 access; + + rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); + if (rc < 0) + return rc; + + if ((access & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) + return priv->locality = l; + + return -1; +} + +static void release_locality(struct tpm_chip *chip, int l, int force) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + int rc; + u8 access; + + rc = tpm_tis_read8(priv, TPM_ACCESS(l), &access); + if (rc < 0) + return; + + if (force || (access & + (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == + (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) + tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY); + +} + +static int request_locality(struct tpm_chip *chip, int l) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + unsigned long stop, timeout; + long rc; + + if (check_locality(chip, l) >= 0) + return l; + + rc = tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_REQUEST_USE); + if (rc < 0) + return rc; + + stop = jiffies + chip->timeout_a; + + if (chip->flags & TPM_CHIP_FLAG_IRQ) { +again: + timeout = stop - jiffies; + if ((long)timeout <= 0) + return -1; + rc = wait_event_interruptible_timeout(priv->int_queue, + (check_locality + (chip, l) >= 0), + timeout); + if (rc > 0) + return l; + if (rc == -ERESTARTSYS && freezing(current)) { + clear_thread_flag(TIF_SIGPENDING); + goto again; + } + } else { + /* wait for burstcount */ + do { + if (check_locality(chip, l) >= 0) + return l; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + } + return -1; +} + +static u8 tpm_tis_status(struct tpm_chip *chip) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + int rc; + u8 status; + + rc = tpm_tis_read8(priv, TPM_STS(priv->locality), &status); + if (rc < 0) + return 0; + + return status; +} + +static void tpm_tis_ready(struct tpm_chip *chip) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + + /* this causes the current command to be aborted */ + tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_COMMAND_READY); +} + +static int get_burstcount(struct tpm_chip *chip) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + unsigned long stop; + int burstcnt, rc; + u8 value; + + /* wait for burstcount */ + /* which timeout value, spec has 2 answers (c & d) */ + stop = jiffies + chip->timeout_d; + do { + rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 1, &value); + if (rc < 0) + return rc; + + burstcnt = value; + rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 2, &value); + if (rc < 0) + return rc; + + burstcnt += value << 8; + if (burstcnt) + return burstcnt; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + return -EBUSY; +} + +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + int size = 0, burstcnt, rc; + + while (size < count && + wait_for_tpm_stat(chip, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, + chip->timeout_c, + &priv->read_queue, true) == 0) { + burstcnt = min_t(int, get_burstcount(chip), count - size); + + rc = tpm_tis_read_bytes(priv, TPM_DATA_FIFO(priv->locality), + burstcnt, buf + size); + if (rc < 0) + return rc; + + size += burstcnt; + } + return size; +} + +static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + int size = 0; + int expected, status; + + if (count < TPM_HEADER_SIZE) { + size = -EIO; + goto out; + } + + size = recv_data(chip, buf, TPM_HEADER_SIZE); + /* read first 10 bytes, including tag, paramsize, and result */ + 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; + } + + wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, + &priv->int_queue, false); + status = tpm_tis_status(chip); + if (status & TPM_STS_DATA_AVAIL) { /* retry? */ + dev_err(&chip->dev, "Error left over data\n"); + size = -EIO; + goto out; + } + +out: + tpm_tis_ready(chip); + release_locality(chip, priv->locality, 0); + return size; +} + +/* + * If interrupts are used (signaled by an irq set in the vendor structure) + * tpm.c can skip polling for the data to be available as the interrupt is + * waited for here + */ +static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + int rc, status, burstcnt; + size_t count = 0; + bool itpm = priv->flags & TPM_TIS_ITPM_POSSIBLE; + + if (request_locality(chip, 0) < 0) + return -EBUSY; + + status = tpm_tis_status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + tpm_tis_ready(chip); + if (wait_for_tpm_stat + (chip, TPM_STS_COMMAND_READY, chip->timeout_b, + &priv->int_queue, false) < 0) { + rc = -ETIME; + goto out_err; + } + } + + while (count < len - 1) { + burstcnt = min_t(int, get_burstcount(chip), len - count - 1); + rc = tpm_tis_write_bytes(priv, TPM_DATA_FIFO(priv->locality), + burstcnt, buf + count); + if (rc < 0) + goto out_err; + + count += burstcnt; + + wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, + &priv->int_queue, false); + status = tpm_tis_status(chip); + if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) { + rc = -EIO; + goto out_err; + } + } + + /* write last byte */ + rc = tpm_tis_write8(priv, TPM_DATA_FIFO(priv->locality), buf[count]); + if (rc < 0) + goto out_err; + + wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, + &priv->int_queue, false); + status = tpm_tis_status(chip); + if (!itpm && (status & TPM_STS_DATA_EXPECT) != 0) { + rc = -EIO; + goto out_err; + } + + return 0; + +out_err: + tpm_tis_ready(chip); + release_locality(chip, priv->locality, 0); + return rc; +} + +static void disable_interrupts(struct tpm_chip *chip) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + u32 intmask; + int rc; + + rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); + if (rc < 0) + intmask = 0; + + intmask &= ~TPM_GLOBAL_INT_ENABLE; + rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); + + devm_free_irq(chip->dev.parent, priv->irq, chip); + priv->irq = 0; + chip->flags &= ~TPM_CHIP_FLAG_IRQ; +} + +/* + * If interrupts are used (signaled by an irq set in the vendor structure) + * tpm.c can skip polling for the data to be available as the interrupt is + * waited for here + */ +static int tpm_tis_send_main(struct tpm_chip *chip, u8 *buf, size_t len) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + int rc; + u32 ordinal; + unsigned long dur; + + rc = tpm_tis_send_data(chip, buf, len); + if (rc < 0) + return rc; + + /* go and do it */ + rc = tpm_tis_write8(priv, TPM_STS(priv->locality), TPM_STS_GO); + if (rc < 0) + goto out_err; + + if (chip->flags & TPM_CHIP_FLAG_IRQ) { + ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); + + if (chip->flags & TPM_CHIP_FLAG_TPM2) + dur = tpm2_calc_ordinal_duration(chip, ordinal); + else + dur = tpm_calc_ordinal_duration(chip, ordinal); + + if (wait_for_tpm_stat + (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, dur, + &priv->read_queue, false) < 0) { + rc = -ETIME; + goto out_err; + } + } + return len; +out_err: + tpm_tis_ready(chip); + release_locality(chip, priv->locality, 0); + return rc; +} + +static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len) +{ + int rc, irq; + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + + if (!(chip->flags & TPM_CHIP_FLAG_IRQ) || priv->irq_tested) + return tpm_tis_send_main(chip, buf, len); + + /* Verify receipt of the expected IRQ */ + irq = priv->irq; + priv->irq = 0; + chip->flags &= ~TPM_CHIP_FLAG_IRQ; + rc = tpm_tis_send_main(chip, buf, len); + priv->irq = irq; + chip->flags |= TPM_CHIP_FLAG_IRQ; + if (!priv->irq_tested) + msleep(1); + if (!priv->irq_tested) + disable_interrupts(chip); + priv->irq_tested = true; + return rc; +} + +struct tis_vendor_timeout_override { + u32 did_vid; + unsigned long timeout_us[4]; +}; + +static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = { + /* Atmel 3204 */ + { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000), + (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } }, +}; + +static bool tpm_tis_update_timeouts(struct tpm_chip *chip, + unsigned long *timeout_cap) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + int i, rc; + u32 did_vid; + + rc = tpm_tis_read32(priv, TPM_DID_VID(0), &did_vid); + if (rc < 0) + return rc; + + for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) { + if (vendor_timeout_overrides[i].did_vid != did_vid) + continue; + memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us, + sizeof(vendor_timeout_overrides[i].timeout_us)); + return true; + } + + return false; +} + +/* + * Early probing for iTPM with STS_DATA_EXPECT flaw. + * Try sending command without itpm flag set and if that + * fails, repeat with itpm flag set. + */ +static int probe_itpm(struct tpm_chip *chip) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + int rc = 0; + u8 cmd_getticks[] = { + 0x00, 0xc1, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x00, 0x00, 0xf1 + }; + size_t len = sizeof(cmd_getticks); + bool itpm; + u16 vendor; + + rc = tpm_tis_read16(priv, TPM_DID_VID(0), &vendor); + if (rc < 0) + return rc; + + /* probe only iTPMS */ + if (vendor != TPM_VID_INTEL) + return 0; + + itpm = false; + + rc = tpm_tis_send_data(chip, cmd_getticks, len); + if (rc == 0) + goto out; + + tpm_tis_ready(chip); + release_locality(chip, priv->locality, 0); + + itpm = true; + + rc = tpm_tis_send_data(chip, cmd_getticks, len); + if (rc == 0) { + dev_info(&chip->dev, "Detected an iTPM.\n"); + rc = 1; + } else + rc = -EFAULT; + +out: + tpm_tis_ready(chip); + release_locality(chip, priv->locality, 0); + + return rc; +} + +static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + + switch (priv->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 irqreturn_t tis_int_handler(int dummy, void *dev_id) +{ + struct tpm_chip *chip = dev_id; + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + u32 interrupt; + int i, rc; + + rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt); + if (rc < 0) + return IRQ_NONE; + + if (interrupt == 0) + return IRQ_NONE; + + priv->irq_tested = true; + if (interrupt & TPM_INTF_DATA_AVAIL_INT) + wake_up_interruptible(&priv->read_queue); + if (interrupt & TPM_INTF_LOCALITY_CHANGE_INT) + for (i = 0; i < 5; i++) + if (check_locality(chip, i) >= 0) + break; + if (interrupt & + (TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_STS_VALID_INT | + TPM_INTF_CMD_READY_INT)) + wake_up_interruptible(&priv->int_queue); + + /* Clear interrupts handled with TPM_EOI */ + rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), interrupt); + if (rc < 0) + return IRQ_NONE; + + tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &interrupt); + return IRQ_HANDLED; +} + +/* Register the IRQ and issue a command that will cause an interrupt. If an + * irq is seen then leave the chip setup for IRQ operation, otherwise reverse + * everything and leave in polling mode. Returns 0 on success. + */ +static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, + int flags, int irq) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + u8 original_int_vec; + int rc; + u32 int_status; + + if (devm_request_irq(chip->dev.parent, irq, tis_int_handler, flags, + dev_name(&chip->dev), chip) != 0) { + dev_info(&chip->dev, "Unable to request irq: %d for probe\n", + irq); + return -1; + } + priv->irq = irq; + + rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality), + &original_int_vec); + if (rc < 0) + return rc; + + rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), irq); + if (rc < 0) + return rc; + + rc = tpm_tis_read32(priv, TPM_INT_STATUS(priv->locality), &int_status); + if (rc < 0) + return rc; + + /* Clear all existing */ + rc = tpm_tis_write32(priv, TPM_INT_STATUS(priv->locality), int_status); + if (rc < 0) + return rc; + + /* Turn on */ + rc = tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), + intmask | TPM_GLOBAL_INT_ENABLE); + if (rc < 0) + return rc; + + priv->irq_tested = false; + + /* Generate an interrupt by having the core call through to + * tpm_tis_send + */ + if (chip->flags & TPM_CHIP_FLAG_TPM2) + tpm2_gen_interrupt(chip); + else + tpm_gen_interrupt(chip); + + /* tpm_tis_send will either confirm the interrupt is working or it + * will call disable_irq which undoes all of the above. + */ + if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) { + rc = tpm_tis_write8(priv, original_int_vec, + TPM_INT_VECTOR(priv->locality)); + if (rc < 0) + return rc; + + return 1; + } + + return 0; +} + +/* Try to find the IRQ the TPM is using. This is for legacy x86 systems that + * do not have ACPI/etc. We typically expect the interrupt to be declared if + * present. + */ +static void tpm_tis_probe_irq(struct tpm_chip *chip, u32 intmask) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + u8 original_int_vec; + int i, rc; + + rc = tpm_tis_read8(priv, TPM_INT_VECTOR(priv->locality), + &original_int_vec); + if (rc < 0) + return; + + if (!original_int_vec) { + if (IS_ENABLED(CONFIG_X86)) + for (i = 3; i <= 15; i++) + if (!tpm_tis_probe_irq_single(chip, intmask, 0, + i)) + return; + } else if (!tpm_tis_probe_irq_single(chip, intmask, 0, + original_int_vec)) + return; +} + +void tpm_tis_remove(struct tpm_chip *chip) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + u32 reg = TPM_INT_ENABLE(priv->locality); + u32 interrupt; + int rc; + + rc = tpm_tis_read32(priv, reg, &interrupt); + if (rc < 0) + interrupt = 0; + + tpm_tis_write32(priv, reg, ~TPM_GLOBAL_INT_ENABLE & interrupt); + release_locality(chip, priv->locality, 1); +} +EXPORT_SYMBOL_GPL(tpm_tis_remove); + +static const struct tpm_class_ops tpm_tis = { + .status = tpm_tis_status, + .recv = tpm_tis_recv, + .send = tpm_tis_send, + .cancel = tpm_tis_ready, + .update_timeouts = tpm_tis_update_timeouts, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = tpm_tis_req_canceled, +}; + +int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, + const struct tpm_tis_phy_ops *phy_ops, + acpi_handle acpi_dev_handle) +{ + u32 vendor, intfcaps, intmask; + u8 rid; + int rc, probe; + struct tpm_chip *chip; + + chip = tpmm_chip_alloc(dev, &tpm_tis); + if (IS_ERR(chip)) + return PTR_ERR(chip); + +#ifdef CONFIG_ACPI + chip->acpi_dev_handle = acpi_dev_handle; +#endif + + /* Maximum timeouts */ + chip->timeout_a = TIS_TIMEOUT_A_MAX; + chip->timeout_b = TIS_TIMEOUT_B_MAX; + chip->timeout_c = TIS_TIMEOUT_C_MAX; + chip->timeout_d = TIS_TIMEOUT_D_MAX; + priv->phy_ops = phy_ops; + dev_set_drvdata(&chip->dev, priv); + + if (wait_startup(chip, 0) != 0) { + rc = -ENODEV; + goto out_err; + } + + /* Take control of the TPM's interrupt hardware and shut it off */ + rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); + if (rc < 0) + goto out_err; + + intmask |= TPM_INTF_CMD_READY_INT | TPM_INTF_LOCALITY_CHANGE_INT | + TPM_INTF_DATA_AVAIL_INT | TPM_INTF_STS_VALID_INT; + intmask &= ~TPM_GLOBAL_INT_ENABLE; + tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); + + if (request_locality(chip, 0) != 0) { + rc = -ENODEV; + goto out_err; + } + + rc = tpm2_probe(chip); + if (rc) + goto out_err; + + rc = tpm_tis_read32(priv, TPM_DID_VID(0), &vendor); + if (rc < 0) + goto out_err; + + priv->manufacturer_id = vendor; + + rc = tpm_tis_read8(priv, TPM_RID(0), &rid); + if (rc < 0) + goto out_err; + + dev_info(dev, "%s TPM (device-id 0x%X, rev-id %d)\n", + (chip->flags & TPM_CHIP_FLAG_TPM2) ? "2.0" : "1.2", + vendor >> 16, rid); + + if (!(priv->flags & TPM_TIS_ITPM_POSSIBLE)) { + probe = probe_itpm(chip); + if (probe < 0) { + rc = -ENODEV; + goto out_err; + } + + if (!!probe) + priv->flags |= TPM_TIS_ITPM_POSSIBLE; + } + + /* Figure out the capabilities */ + rc = tpm_tis_read32(priv, TPM_INTF_CAPS(priv->locality), &intfcaps); + if (rc < 0) + goto out_err; + + dev_dbg(dev, "TPM interface capabilities (0x%x):\n", + intfcaps); + if (intfcaps & TPM_INTF_BURST_COUNT_STATIC) + dev_dbg(dev, "\tBurst Count Static\n"); + if (intfcaps & TPM_INTF_CMD_READY_INT) + dev_dbg(dev, "\tCommand Ready Int Support\n"); + if (intfcaps & TPM_INTF_INT_EDGE_FALLING) + dev_dbg(dev, "\tInterrupt Edge Falling\n"); + if (intfcaps & TPM_INTF_INT_EDGE_RISING) + dev_dbg(dev, "\tInterrupt Edge Rising\n"); + if (intfcaps & TPM_INTF_INT_LEVEL_LOW) + dev_dbg(dev, "\tInterrupt Level Low\n"); + if (intfcaps & TPM_INTF_INT_LEVEL_HIGH) + dev_dbg(dev, "\tInterrupt Level High\n"); + if (intfcaps & TPM_INTF_LOCALITY_CHANGE_INT) + dev_dbg(dev, "\tLocality Change Int Support\n"); + if (intfcaps & TPM_INTF_STS_VALID_INT) + dev_dbg(dev, "\tSts Valid Int Support\n"); + if (intfcaps & TPM_INTF_DATA_AVAIL_INT) + dev_dbg(dev, "\tData Avail Int Support\n"); + + /* Very early on issue a command to the TPM in polling mode to make + * sure it works. May as well use that command to set the proper + * timeouts for the driver. + */ + if (tpm_get_timeouts(chip)) { + dev_err(dev, "Could not get TPM timeouts and durations\n"); + rc = -ENODEV; + goto out_err; + } + + /* INTERRUPT Setup */ + init_waitqueue_head(&priv->read_queue); + init_waitqueue_head(&priv->int_queue); + if (irq != -1) { + if (irq) { + tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, + irq); + if (!(chip->flags & TPM_CHIP_FLAG_IRQ)) + dev_err(&chip->dev, FW_BUG + "TPM interrupt not working, polling instead\n"); + } else { + tpm_tis_probe_irq(chip, intmask); + } + } + + if (chip->flags & TPM_CHIP_FLAG_TPM2) { + rc = tpm2_do_selftest(chip); + if (rc == TPM2_RC_INITIALIZE) { + dev_warn(dev, "Firmware has not started TPM\n"); + rc = tpm2_startup(chip, TPM2_SU_CLEAR); + if (!rc) + rc = tpm2_do_selftest(chip); + } + + if (rc) { + dev_err(dev, "TPM self test failed\n"); + if (rc > 0) + rc = -ENODEV; + goto out_err; + } + } else { + if (tpm_do_selftest(chip)) { + dev_err(dev, "TPM self test failed\n"); + rc = -ENODEV; + goto out_err; + } + } + + return tpm_chip_register(chip); +out_err: + tpm_tis_remove(chip); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_tis_core_init); + +#ifdef CONFIG_PM_SLEEP +static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) +{ + struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); + u32 intmask; + int rc; + + /* reenable interrupts that device may have lost or + * BIOS/firmware may have disabled + */ + rc = tpm_tis_write8(priv, TPM_INT_VECTOR(priv->locality), priv->irq); + if (rc < 0) + return; + + rc = tpm_tis_read32(priv, TPM_INT_ENABLE(priv->locality), &intmask); + if (rc < 0) + return; + + intmask |= TPM_INTF_CMD_READY_INT + | TPM_INTF_LOCALITY_CHANGE_INT | TPM_INTF_DATA_AVAIL_INT + | TPM_INTF_STS_VALID_INT | TPM_GLOBAL_INT_ENABLE; + + tpm_tis_write32(priv, TPM_INT_ENABLE(priv->locality), intmask); +} + +int tpm_tis_resume(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + int ret; + + if (chip->flags & TPM_CHIP_FLAG_IRQ) + tpm_tis_reenable_interrupts(chip); + + ret = tpm_pm_resume(dev); + if (ret) + return ret; + + /* TPM 1.2 requires self-test on resume. This function actually returns + * an error code but for unknown reason it isn't handled. + */ + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) + tpm_do_selftest(chip); + + return 0; +} +EXPORT_SYMBOL_GPL(tpm_tis_resume); +#endif + +MODULE_AUTHOR("Leendert van Doorn (leendert@watson.ibm.com)"); +MODULE_DESCRIPTION("TPM Driver"); +MODULE_VERSION("2.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h index b922100..9191aab 100644 --- a/drivers/char/tpm/tpm_tis_core.h +++ b/drivers/char/tpm/tpm_tis_core.h @@ -25,11 +25,70 @@ #include "tpm.h" +enum tis_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum tis_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 tis_int_flags { + TPM_GLOBAL_INT_ENABLE = 0x80000000, + TPM_INTF_BURST_COUNT_STATIC = 0x100, + TPM_INTF_CMD_READY_INT = 0x080, + TPM_INTF_INT_EDGE_FALLING = 0x040, + TPM_INTF_INT_EDGE_RISING = 0x020, + TPM_INTF_INT_LEVEL_LOW = 0x010, + TPM_INTF_INT_LEVEL_HIGH = 0x008, + TPM_INTF_LOCALITY_CHANGE_INT = 0x004, + TPM_INTF_STS_VALID_INT = 0x002, + TPM_INTF_DATA_AVAIL_INT = 0x001, +}; + +enum tis_defaults { + TIS_MEM_LEN = 0x5000, + TIS_SHORT_TIMEOUT = 750, /* ms */ + TIS_LONG_TIMEOUT = 2000, /* 2 sec */ +}; + +/* Some timeout values are needed before it is known whether the chip is + * TPM 1.0 or TPM 2.0. + */ +#define TIS_TIMEOUT_A_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_A) +#define TIS_TIMEOUT_B_MAX max(TIS_LONG_TIMEOUT, TPM2_TIMEOUT_B) +#define TIS_TIMEOUT_C_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_C) +#define TIS_TIMEOUT_D_MAX max(TIS_SHORT_TIMEOUT, TPM2_TIMEOUT_D) + +#define TPM_ACCESS(l) (0x0000 | ((l) << 12)) +#define TPM_INT_ENABLE(l) (0x0008 | ((l) << 12)) +#define TPM_INT_VECTOR(l) (0x000C | ((l) << 12)) +#define TPM_INT_STATUS(l) (0x0010 | ((l) << 12)) +#define TPM_INTF_CAPS(l) (0x0014 | ((l) << 12)) +#define TPM_STS(l) (0x0018 | ((l) << 12)) +#define TPM_STS3(l) (0x001b | ((l) << 12)) +#define TPM_DATA_FIFO(l) (0x0024 | ((l) << 12)) + +#define TPM_DID_VID(l) (0x0F00 | ((l) << 12)) +#define TPM_RID(l) (0x0F04 | ((l) << 12)) + +enum tpm_tis_flags { + TPM_TIS_ITPM_POSSIBLE = BIT(0), +}; + struct tpm_tis_data { u16 manufacturer_id; int locality; int irq; bool irq_tested; + unsigned int flags; wait_queue_head_t int_queue; wait_queue_head_t read_queue; const struct tpm_tis_phy_ops *phy_ops; @@ -85,4 +144,13 @@ static inline int tpm_tis_write32(struct tpm_tis_data *data, u32 addr, return data->phy_ops->write32(data, addr, value); } +void tpm_tis_remove(struct tpm_chip *chip); +int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, + const struct tpm_tis_phy_ops *phy_ops, + acpi_handle acpi_dev_handle); + +#ifdef CONFIG_PM_SLEEP +int tpm_tis_resume(struct device *dev); +#endif + #endif -- cgit v0.10.2 From 0edbfea537d10c0de5505d0413368aad71027663 Mon Sep 17 00:00:00 2001 From: Christophe Ricard Date: Thu, 19 May 2016 00:35:53 +0200 Subject: tpm/tpm_tis_spi: Add support for spi phy Spi protocol standardized by the TCG is now supported by most of TPM vendors. It supports SPI Bit Protocol as describe in the TCG PTP specification (chapter 6.4.6 SPI Bit Protocol). Irq mode is not supported. This commit is based on the initial work by Peter Huewe. Signed-off-by: Peter Huewe Signed-off-by: Alexander Steffen Signed-off-by: Christophe Ricard Reviewed-by: Jarkko Sakkinen Tested-by: Jarkko Sakkinen Tested-by: Stefan Berger Reviewed-by: Stefan Berger Signed-off-by: Jarkko Sakkinen diff --git a/Documentation/devicetree/bindings/security/tpm/tpm_tis_spi.txt b/Documentation/devicetree/bindings/security/tpm/tpm_tis_spi.txt new file mode 100644 index 0000000..85741cd --- /dev/null +++ b/Documentation/devicetree/bindings/security/tpm/tpm_tis_spi.txt @@ -0,0 +1,24 @@ +Required properties: +- compatible: should be one of the following + "st,st33htpm-spi" + "infineon,slb9670" + "tcg,tpm_tis-spi" +- spi-max-frequency: Maximum SPI frequency (depends on TPMs). + +Optional SoC Specific Properties: +- pinctrl-names: Contains only one value - "default". +- pintctrl-0: Specifies the pin control groups used for this controller. + +Example (for ARM-based BeagleBoard xM with TPM_TIS on SPI4): + +&mcspi4 { + + status = "okay"; + + tpm_tis@0 { + + compatible = "tcg,tpm_tis-spi"; + + spi-max-frequency = <10000000>; + }; +}; diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 241ff4c..9faa0b1 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -41,6 +41,18 @@ config TCG_TIS within Linux. To compile this driver as a module, choose M here; the module will be called tpm_tis. +config TCG_TIS_SPI + tristate "TPM Interface Specification 1.3 Interface / TPM 2.0 FIFO Interface - (SPI)" + depends on SPI + select TCG_TIS_CORE + ---help--- + If you have a TPM security chip which is connected to a regular, + non-tcg SPI master (i.e. most embedded platforms) that is compliant with the + TCG TIS 1.3 TPM specification (TPM1.2) or the TCG PTP FIFO + specification (TPM2.0) 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_tis_spi. + config TCG_TIS_I2C_ATMEL tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)" depends on I2C diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 662221f..a385fb8 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -14,6 +14,7 @@ endif endif obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o obj-$(CONFIG_TCG_TIS) += tpm_tis.o +obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o obj-$(CONFIG_TCG_TIS_I2C_ATMEL) += tpm_i2c_atmel.o obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o obj-$(CONFIG_TCG_TIS_I2C_NUVOTON) += tpm_i2c_nuvoton.o diff --git a/drivers/char/tpm/tpm_tis_spi.c b/drivers/char/tpm/tpm_tis_spi.c new file mode 100644 index 0000000..dbaad9c --- /dev/null +++ b/drivers/char/tpm/tpm_tis_spi.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2015 Infineon Technologies AG + * Copyright (C) 2016 STMicroelectronics SAS + * + * Authors: + * Peter Huewe + * Christophe Ricard + * + * Maintained by: + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This device driver implements the TPM interface as defined in + * the TCG TPM Interface Spec version 1.3, revision 27 via _raw/native + * SPI access_. + * + * It is based on the original tpm_tis device driver from Leendert van + * Dorn and Kyleen Hall and Jarko Sakkinnen. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "tpm.h" +#include "tpm_tis_core.h" + +#define MAX_SPI_FRAMESIZE 64 + +struct tpm_tis_spi_phy { + struct tpm_tis_data priv; + struct spi_device *spi_device; + + u8 tx_buf[MAX_SPI_FRAMESIZE + 4]; + u8 rx_buf[MAX_SPI_FRAMESIZE + 4]; +}; + +static inline struct tpm_tis_spi_phy *to_tpm_tis_spi_phy(struct tpm_tis_data *data) +{ + return container_of(data, struct tpm_tis_spi_phy, priv); +} + +static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, u8 *result) +{ + struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); + int ret, i; + struct spi_message m; + struct spi_transfer spi_xfer = { + .tx_buf = phy->tx_buf, + .rx_buf = phy->rx_buf, + .len = 4, + }; + + if (len > MAX_SPI_FRAMESIZE) + return -ENOMEM; + + phy->tx_buf[0] = 0x80 | (len - 1); + phy->tx_buf[1] = 0xd4; + phy->tx_buf[2] = (addr >> 8) & 0xFF; + phy->tx_buf[3] = addr & 0xFF; + + spi_xfer.cs_change = 1; + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + + spi_bus_lock(phy->spi_device->master); + ret = spi_sync_locked(phy->spi_device, &m); + if (ret < 0) + goto exit; + + memset(phy->tx_buf, 0, len); + + /* According to TCG PTP specification, if there is no TPM present at + * all, then the design has a weak pull-up on MISO. If a TPM is not + * present, a pull-up on MISO means that the SB controller sees a 1, + * and will latch in 0xFF on the read. + */ + for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) { + spi_xfer.len = 1; + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + ret = spi_sync_locked(phy->spi_device, &m); + if (ret < 0) + goto exit; + } + + spi_xfer.cs_change = 0; + spi_xfer.len = len; + spi_xfer.rx_buf = result; + + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + ret = spi_sync_locked(phy->spi_device, &m); + +exit: + spi_bus_unlock(phy->spi_device->master); + return ret; +} + +static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr, + u16 len, u8 *value) +{ + struct tpm_tis_spi_phy *phy = to_tpm_tis_spi_phy(data); + int ret, i; + struct spi_message m; + struct spi_transfer spi_xfer = { + .tx_buf = phy->tx_buf, + .rx_buf = phy->rx_buf, + .len = 4, + }; + + if (len > MAX_SPI_FRAMESIZE) + return -ENOMEM; + + phy->tx_buf[0] = len - 1; + phy->tx_buf[1] = 0xd4; + phy->tx_buf[2] = (addr >> 8) & 0xFF; + phy->tx_buf[3] = addr & 0xFF; + + spi_xfer.cs_change = 1; + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + + spi_bus_lock(phy->spi_device->master); + ret = spi_sync_locked(phy->spi_device, &m); + if (ret < 0) + goto exit; + + memset(phy->tx_buf, 0, len); + + /* According to TCG PTP specification, if there is no TPM present at + * all, then the design has a weak pull-up on MISO. If a TPM is not + * present, a pull-up on MISO means that the SB controller sees a 1, + * and will latch in 0xFF on the read. + */ + for (i = 0; (phy->rx_buf[0] & 0x01) == 0 && i < TPM_RETRY; i++) { + spi_xfer.len = 1; + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + ret = spi_sync_locked(phy->spi_device, &m); + if (ret < 0) + goto exit; + } + + spi_xfer.len = len; + spi_xfer.tx_buf = value; + spi_xfer.cs_change = 0; + spi_xfer.tx_buf = value; + spi_message_init(&m); + spi_message_add_tail(&spi_xfer, &m); + ret = spi_sync_locked(phy->spi_device, &m); + +exit: + spi_bus_unlock(phy->spi_device->master); + return ret; +} + +static int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result) +{ + int rc; + + rc = data->phy_ops->read_bytes(data, addr, sizeof(u16), (u8 *)result); + if (!rc) + *result = le16_to_cpu(*result); + return rc; +} + +static int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result) +{ + int rc; + + rc = data->phy_ops->read_bytes(data, addr, sizeof(u32), (u8 *)result); + if (!rc) + *result = le32_to_cpu(*result); + return rc; +} + +static int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value) +{ + value = cpu_to_le32(value); + return data->phy_ops->write_bytes(data, addr, sizeof(u32), + (u8 *)&value); +} + +static const struct tpm_tis_phy_ops tpm_spi_phy_ops = { + .read_bytes = tpm_tis_spi_read_bytes, + .write_bytes = tpm_tis_spi_write_bytes, + .read16 = tpm_tis_spi_read16, + .read32 = tpm_tis_spi_read32, + .write32 = tpm_tis_spi_write32, +}; + +static int tpm_tis_spi_probe(struct spi_device *dev) +{ + struct tpm_tis_spi_phy *phy; + + phy = devm_kzalloc(&dev->dev, sizeof(struct tpm_tis_spi_phy), + GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->spi_device = dev; + + return tpm_tis_core_init(&dev->dev, &phy->priv, -1, &tpm_spi_phy_ops, + NULL); +} + +static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); + +static int tpm_tis_spi_remove(struct spi_device *dev) +{ + struct tpm_chip *chip = spi_get_drvdata(dev); + + tpm_chip_unregister(chip); + tpm_tis_remove(chip); + return 0; +} + +static const struct spi_device_id tpm_tis_spi_id[] = { + {"tpm_tis_spi", 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, tpm_tis_spi_id); + +static const struct of_device_id of_tis_spi_match[] = { + { .compatible = "st,st33htpm-spi", }, + { .compatible = "infineon,slb9670", }, + { .compatible = "tcg,tpm_tis-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_tis_spi_match); + +static const struct acpi_device_id acpi_tis_spi_match[] = { + {"SMO0768", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, acpi_tis_spi_match); + +static struct spi_driver tpm_tis_spi_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tpm_tis_spi", + .pm = &tpm_tis_pm, + .of_match_table = of_match_ptr(of_tis_spi_match), + .acpi_match_table = ACPI_PTR(acpi_tis_spi_match), + }, + .probe = tpm_tis_spi_probe, + .remove = tpm_tis_spi_remove, + .id_table = tpm_tis_spi_id, +}; +module_spi_driver(tpm_tis_spi_driver); + +MODULE_DESCRIPTION("TPM Driver for native SPI access"); +MODULE_LICENSE("GPL"); -- cgit v0.10.2 From 9da7e787e18471f890b6d3e2859417388595798a Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 17 Jun 2016 16:39:28 +0200 Subject: tpm_vtpm_proxy: fix address space of a user pointer in vtpmx_fops_ioctl() When running make C=2 M=drivers/char/tpm/ CC [M] drivers/char/tpm//tpm_crb.o CHECK drivers/char/tpm//tpm_vtpm_proxy.c drivers/char/tpm//tpm_vtpm_proxy.c:552:32: warning: incorrect type in assignment (different address spaces) drivers/char/tpm//tpm_vtpm_proxy.c:552:32: expected struct vtpm_proxy_new_dev *vtpm_new_dev_p drivers/char/tpm//tpm_vtpm_proxy.c:552:32: got void [noderef] *argp drivers/char/tpm//tpm_vtpm_proxy.c:553:51: warning: incorrect type in argument 2 (different address spaces) drivers/char/tpm//tpm_vtpm_proxy.c:553:51: expected void const [noderef] *from drivers/char/tpm//tpm_vtpm_proxy.c:553:51: got struct vtpm_proxy_new_dev *vtpm_new_dev_p drivers/char/tpm//tpm_vtpm_proxy.c:559:34: warning: incorrect type in argument 1 (different address spaces) drivers/char/tpm//tpm_vtpm_proxy.c:559:34: expected void [noderef] *to drivers/char/tpm//tpm_vtpm_proxy.c:559:34: got struct vtpm_proxy_new_dev *vtpm_new_dev_p The __user annotation was missing from the corresponding variable. Fixes: 794c38e01358 ("tpm: Proxy driver for supporting multiple emulated TPMs") Signed-off-by: Jarkko Sakkinen Tested-by: Stefan Berger diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index 4384042..86e27e8 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -541,7 +541,7 @@ static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl, unsigned long arg) { void __user *argp = (void __user *)arg; - struct vtpm_proxy_new_dev *vtpm_new_dev_p; + struct vtpm_proxy_new_dev __user *vtpm_new_dev_p; struct vtpm_proxy_new_dev vtpm_new_dev; struct file *file; -- cgit v0.10.2 From f786b752098216fedb73ba2905c8cce12358534a Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 17 Jun 2016 16:39:29 +0200 Subject: tpm_crb: fix address space of the return pointer in crb_map_res() When running make C=2 M=drivers/char/tpm/ CHECK drivers/char/tpm//tpm_crb.c drivers/char/tpm//tpm_crb.c:248:31: warning: incorrect type in return expression (different address spaces) drivers/char/tpm//tpm_crb.c:248:31: expected void [noderef] * drivers/char/tpm//tpm_crb.c:248:31: got void * CC: stable@vger.kernel.org Fixes: 1bd047be37d9 ("tpm_crb: Use devm_ioremap_resource") Signed-off-by: Jarkko Sakkinen Tested-by: Stefan Berger diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 1547636..1b8e1b5 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -245,7 +245,7 @@ static void __iomem *crb_map_res(struct device *dev, struct crb_priv *priv, /* Detect a 64 bit address on a 32 bit system */ if (start != new_res.start) - return ERR_PTR(-EINVAL); + return (void __iomem *) ERR_PTR(-EINVAL); if (!resource_contains(io_res, &new_res)) return devm_ioremap_resource(dev, &new_res); -- cgit v0.10.2 From 96a8f7f88d4e540e6342ed313c52f6977e4ccc54 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:02:45 -0400 Subject: netlabel: Mark rcu pointers with __rcu. This fixes sparse errors of the form: incompatible types in comparison expression (different address spaces) Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c index ada6742..d4d6640 100644 --- a/net/netlabel/netlabel_domainhash.c +++ b/net/netlabel/netlabel_domainhash.c @@ -55,8 +55,8 @@ struct netlbl_domhsh_tbl { static DEFINE_SPINLOCK(netlbl_domhsh_lock); #define netlbl_domhsh_rcu_deref(p) \ rcu_dereference_check(p, lockdep_is_held(&netlbl_domhsh_lock)) -static struct netlbl_domhsh_tbl *netlbl_domhsh; -static struct netlbl_dom_map *netlbl_domhsh_def; +static struct netlbl_domhsh_tbl __rcu *netlbl_domhsh; +static struct netlbl_dom_map __rcu *netlbl_domhsh_def; /* * Domain Hash Table Helper Functions diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index 9eaa9a1..ca590d2 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -116,8 +116,8 @@ struct netlbl_unlhsh_walk_arg { static DEFINE_SPINLOCK(netlbl_unlhsh_lock); #define netlbl_unlhsh_rcu_deref(p) \ rcu_dereference_check(p, lockdep_is_held(&netlbl_unlhsh_lock)) -static struct netlbl_unlhsh_tbl *netlbl_unlhsh; -static struct netlbl_unlhsh_iface *netlbl_unlhsh_def; +static struct netlbl_unlhsh_tbl __rcu *netlbl_unlhsh; +static struct netlbl_unlhsh_iface __rcu *netlbl_unlhsh_def; /* Accept unlabeled packets flag */ static u8 netlabel_unlabel_acceptflg; -- cgit v0.10.2 From 8f18e675c3335b5f113dbabc4afbab6da41ff61f Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:02:46 -0400 Subject: netlabel: Add an address family to domain hash entries. The reason is to allow different labelling protocols for different address families with the same domain. This requires the addition of an address family attribute in the netlink communication protocol. It is used in several messages: NLBL_MGMT_C_ADD and NLBL_MGMT_C_ADDDEF take it as an optional attribute for the unlabelled protocol. It may be one of AF_INET, AF_INET6 or AF_UNSPEC (to specify both address families). If it is missing, it defaults to AF_UNSPEC. NLBL_MGMT_C_LISTALL and NLBL_MGMT_C_LISTDEF return it as part of the enumeration of each item. Addtionally, it may be sent to LISTDEF to specify which address family to return. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c index d4d6640..3b3b304 100644 --- a/net/netlabel/netlabel_domainhash.c +++ b/net/netlabel/netlabel_domainhash.c @@ -56,7 +56,8 @@ static DEFINE_SPINLOCK(netlbl_domhsh_lock); #define netlbl_domhsh_rcu_deref(p) \ rcu_dereference_check(p, lockdep_is_held(&netlbl_domhsh_lock)) static struct netlbl_domhsh_tbl __rcu *netlbl_domhsh; -static struct netlbl_dom_map __rcu *netlbl_domhsh_def; +static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv4; +static struct netlbl_dom_map __rcu *netlbl_domhsh_def_ipv6; /* * Domain Hash Table Helper Functions @@ -126,18 +127,26 @@ static u32 netlbl_domhsh_hash(const char *key) return val & (netlbl_domhsh_rcu_deref(netlbl_domhsh)->size - 1); } +static bool netlbl_family_match(u16 f1, u16 f2) +{ + return (f1 == f2) || (f1 == AF_UNSPEC) || (f2 == AF_UNSPEC); +} + /** * netlbl_domhsh_search - Search for a domain entry * @domain: the domain + * @family: the address family * * Description: * Searches the domain hash table and returns a pointer to the hash table - * entry if found, otherwise NULL is returned. The caller is responsible for + * entry if found, otherwise NULL is returned. @family may be %AF_UNSPEC + * which matches any address family entries. The caller is responsible for * ensuring that the hash table is protected with either a RCU read lock or the * hash table lock. * */ -static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain) +static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain, + u16 family) { u32 bkt; struct list_head *bkt_list; @@ -147,7 +156,9 @@ static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain) bkt = netlbl_domhsh_hash(domain); bkt_list = &netlbl_domhsh_rcu_deref(netlbl_domhsh)->tbl[bkt]; list_for_each_entry_rcu(iter, bkt_list, list) - if (iter->valid && strcmp(iter->domain, domain) == 0) + if (iter->valid && + netlbl_family_match(iter->family, family) && + strcmp(iter->domain, domain) == 0) return iter; } @@ -157,28 +168,37 @@ static struct netlbl_dom_map *netlbl_domhsh_search(const char *domain) /** * netlbl_domhsh_search_def - Search for a domain entry * @domain: the domain - * @def: return default if no match is found + * @family: the address family * * Description: * Searches the domain hash table and returns a pointer to the hash table * entry if an exact match is found, if an exact match is not present in the * hash table then the default entry is returned if valid otherwise NULL is - * returned. The caller is responsible ensuring that the hash table is + * returned. @family may be %AF_UNSPEC which matches any address family + * entries. The caller is responsible ensuring that the hash table is * protected with either a RCU read lock or the hash table lock. * */ -static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain) +static struct netlbl_dom_map *netlbl_domhsh_search_def(const char *domain, + u16 family) { struct netlbl_dom_map *entry; - entry = netlbl_domhsh_search(domain); - if (entry == NULL) { - entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def); - if (entry != NULL && !entry->valid) - entry = NULL; + entry = netlbl_domhsh_search(domain, family); + if (entry != NULL) + return entry; + if (family == AF_INET || family == AF_UNSPEC) { + entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv4); + if (entry != NULL && entry->valid) + return entry; + } + if (family == AF_INET6 || family == AF_UNSPEC) { + entry = netlbl_domhsh_rcu_deref(netlbl_domhsh_def_ipv6); + if (entry != NULL && entry->valid) + return entry; } - return entry; + return NULL; } /** @@ -264,13 +284,19 @@ static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry) if (entry == NULL) return -EINVAL; + if (entry->family != AF_INET && entry->family != AF_INET6 && + (entry->family != AF_UNSPEC || + entry->def.type != NETLBL_NLTYPE_UNLABELED)) + return -EINVAL; + switch (entry->def.type) { case NETLBL_NLTYPE_UNLABELED: if (entry->def.cipso != NULL || entry->def.addrsel != NULL) return -EINVAL; break; case NETLBL_NLTYPE_CIPSOV4: - if (entry->def.cipso == NULL) + if (entry->family != AF_INET || + entry->def.cipso == NULL) return -EINVAL; break; case NETLBL_NLTYPE_ADDRSELECT: @@ -358,15 +384,18 @@ int __init netlbl_domhsh_init(u32 size) * * Description: * Adds a new entry to the domain hash table and handles any updates to the - * lower level protocol handler (i.e. CIPSO). Returns zero on success, - * negative on failure. + * lower level protocol handler (i.e. CIPSO). @entry->family may be set to + * %AF_UNSPEC which will add an entry that matches all address families. This + * is only useful for the unlabelled type and will only succeed if there is no + * existing entry for any address family with the same domain. Returns zero + * on success, negative on failure. * */ int netlbl_domhsh_add(struct netlbl_dom_map *entry, struct netlbl_audit *audit_info) { int ret_val = 0; - struct netlbl_dom_map *entry_old; + struct netlbl_dom_map *entry_old, *entry_b; struct netlbl_af4list *iter4; struct netlbl_af4list *tmp4; #if IS_ENABLED(CONFIG_IPV6) @@ -385,9 +414,10 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry, rcu_read_lock(); spin_lock(&netlbl_domhsh_lock); if (entry->domain != NULL) - entry_old = netlbl_domhsh_search(entry->domain); + entry_old = netlbl_domhsh_search(entry->domain, entry->family); else - entry_old = netlbl_domhsh_search_def(entry->domain); + entry_old = netlbl_domhsh_search_def(entry->domain, + entry->family); if (entry_old == NULL) { entry->valid = 1; @@ -397,7 +427,41 @@ int netlbl_domhsh_add(struct netlbl_dom_map *entry, &rcu_dereference(netlbl_domhsh)->tbl[bkt]); } else { INIT_LIST_HEAD(&entry->list); - rcu_assign_pointer(netlbl_domhsh_def, entry); + switch (entry->family) { + case AF_INET: + rcu_assign_pointer(netlbl_domhsh_def_ipv4, + entry); + break; + case AF_INET6: + rcu_assign_pointer(netlbl_domhsh_def_ipv6, + entry); + break; + case AF_UNSPEC: + if (entry->def.type != + NETLBL_NLTYPE_UNLABELED) { + ret_val = -EINVAL; + goto add_return; + } + entry_b = kzalloc(sizeof(*entry_b), GFP_ATOMIC); + if (entry_b == NULL) { + ret_val = -ENOMEM; + goto add_return; + } + entry_b->family = AF_INET6; + entry_b->def.type = NETLBL_NLTYPE_UNLABELED; + entry_b->valid = 1; + entry->family = AF_INET; + rcu_assign_pointer(netlbl_domhsh_def_ipv4, + entry); + rcu_assign_pointer(netlbl_domhsh_def_ipv6, + entry_b); + break; + default: + /* Already checked in + * netlbl_domhsh_validate(). */ + ret_val = -EINVAL; + goto add_return; + } } if (entry->def.type == NETLBL_NLTYPE_ADDRSELECT) { @@ -513,10 +577,12 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry, spin_lock(&netlbl_domhsh_lock); if (entry->valid) { entry->valid = 0; - if (entry != rcu_dereference(netlbl_domhsh_def)) - list_del_rcu(&entry->list); + if (entry == rcu_dereference(netlbl_domhsh_def_ipv4)) + RCU_INIT_POINTER(netlbl_domhsh_def_ipv4, NULL); + else if (entry == rcu_dereference(netlbl_domhsh_def_ipv6)) + RCU_INIT_POINTER(netlbl_domhsh_def_ipv6, NULL); else - RCU_INIT_POINTER(netlbl_domhsh_def, NULL); + list_del_rcu(&entry->list); } else ret_val = -ENOENT; spin_unlock(&netlbl_domhsh_lock); @@ -583,9 +649,9 @@ int netlbl_domhsh_remove_af4(const char *domain, rcu_read_lock(); if (domain) - entry_map = netlbl_domhsh_search(domain); + entry_map = netlbl_domhsh_search(domain, AF_INET); else - entry_map = netlbl_domhsh_search_def(domain); + entry_map = netlbl_domhsh_search_def(domain, AF_INET); if (entry_map == NULL || entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT) goto remove_af4_failure; @@ -625,25 +691,45 @@ remove_af4_failure: /** * netlbl_domhsh_remove - Removes an entry from the domain hash table * @domain: the domain to remove + * @family: address family * @audit_info: NetLabel audit information * * Description: * Removes an entry from the domain hash table and handles any updates to the - * lower level protocol handler (i.e. CIPSO). Returns zero on success, - * negative on failure. + * lower level protocol handler (i.e. CIPSO). @family may be %AF_UNSPEC which + * removes all address family entries. Returns zero on success, negative on + * failure. * */ -int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info) +int netlbl_domhsh_remove(const char *domain, u16 family, + struct netlbl_audit *audit_info) { - int ret_val; + int ret_val = -EINVAL; struct netlbl_dom_map *entry; rcu_read_lock(); - if (domain) - entry = netlbl_domhsh_search(domain); - else - entry = netlbl_domhsh_search_def(domain); - ret_val = netlbl_domhsh_remove_entry(entry, audit_info); + + if (family == AF_INET || family == AF_UNSPEC) { + if (domain) + entry = netlbl_domhsh_search(domain, AF_INET); + else + entry = netlbl_domhsh_search_def(domain, AF_INET); + ret_val = netlbl_domhsh_remove_entry(entry, audit_info); + if (ret_val && ret_val != -ENOENT) + goto done; + } + if (family == AF_INET6 || family == AF_UNSPEC) { + int ret_val2; + + if (domain) + entry = netlbl_domhsh_search(domain, AF_INET6); + else + entry = netlbl_domhsh_search_def(domain, AF_INET6); + ret_val2 = netlbl_domhsh_remove_entry(entry, audit_info); + if (ret_val2 != -ENOENT) + ret_val = ret_val2; + } +done: rcu_read_unlock(); return ret_val; @@ -651,32 +737,38 @@ int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info) /** * netlbl_domhsh_remove_default - Removes the default entry from the table + * @family: address family * @audit_info: NetLabel audit information * * Description: - * Removes/resets the default entry for the domain hash table and handles any - * updates to the lower level protocol handler (i.e. CIPSO). Returns zero on - * success, non-zero on failure. + * Removes/resets the default entry corresponding to @family from the domain + * hash table and handles any updates to the lower level protocol handler + * (i.e. CIPSO). @family may be %AF_UNSPEC which removes all address family + * entries. Returns zero on success, negative on failure. * */ -int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info) +int netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info) { - return netlbl_domhsh_remove(NULL, audit_info); + return netlbl_domhsh_remove(NULL, family, audit_info); } /** * netlbl_domhsh_getentry - Get an entry from the domain hash table * @domain: the domain name to search for + * @family: address family * * Description: * Look through the domain hash table searching for an entry to match @domain, - * return a pointer to a copy of the entry or NULL. The caller is responsible - * for ensuring that rcu_read_[un]lock() is called. + * with address family @family, return a pointer to a copy of the entry or + * NULL. The caller is responsible for ensuring that rcu_read_[un]lock() is + * called. * */ -struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain) +struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain, u16 family) { - return netlbl_domhsh_search_def(domain); + if (family == AF_UNSPEC) + return NULL; + return netlbl_domhsh_search_def(domain, family); } /** @@ -696,7 +788,7 @@ struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain, struct netlbl_dom_map *dom_iter; struct netlbl_af4list *addr_iter; - dom_iter = netlbl_domhsh_search_def(domain); + dom_iter = netlbl_domhsh_search_def(domain, AF_INET); if (dom_iter == NULL) return NULL; @@ -726,7 +818,7 @@ struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain, struct netlbl_dom_map *dom_iter; struct netlbl_af6list *addr_iter; - dom_iter = netlbl_domhsh_search_def(domain); + dom_iter = netlbl_domhsh_search_def(domain, AF_INET6); if (dom_iter == NULL) return NULL; diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h index 680caf4..56e45ae 100644 --- a/net/netlabel/netlabel_domainhash.h +++ b/net/netlabel/netlabel_domainhash.h @@ -70,6 +70,7 @@ struct netlbl_domaddr6_map { struct netlbl_dom_map { char *domain; + u16 family; struct netlbl_dommap_def def; u32 valid; @@ -91,9 +92,10 @@ int netlbl_domhsh_remove_af4(const char *domain, const struct in_addr *addr, const struct in_addr *mask, struct netlbl_audit *audit_info); -int netlbl_domhsh_remove(const char *domain, struct netlbl_audit *audit_info); -int netlbl_domhsh_remove_default(struct netlbl_audit *audit_info); -struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain); +int netlbl_domhsh_remove(const char *domain, u16 family, + struct netlbl_audit *audit_info); +int netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info); +struct netlbl_dom_map *netlbl_domhsh_getentry(const char *domain, u16 family); struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain, __be32 addr); #if IS_ENABLED(CONFIG_IPV6) diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 3c070f2..7e2a68f 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -72,7 +72,7 @@ int netlbl_cfg_map_del(const char *domain, struct netlbl_audit *audit_info) { if (addr == NULL && mask == NULL) { - return netlbl_domhsh_remove(domain, audit_info); + return netlbl_domhsh_remove(domain, family, audit_info); } else if (addr != NULL && mask != NULL) { switch (family) { case AF_INET: @@ -119,6 +119,7 @@ int netlbl_cfg_unlbl_map_add(const char *domain, if (entry->domain == NULL) goto cfg_unlbl_map_add_failure; } + entry->family = family; if (addr == NULL && mask == NULL) entry->def.type = NETLBL_NLTYPE_UNLABELED; @@ -345,6 +346,7 @@ int netlbl_cfg_cipsov4_map_add(u32 doi, entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (entry == NULL) goto out_entry; + entry->family = AF_INET; if (domain != NULL) { entry->domain = kstrdup(domain, GFP_ATOMIC); if (entry->domain == NULL) @@ -773,7 +775,7 @@ int netlbl_sock_setattr(struct sock *sk, struct netlbl_dom_map *dom_entry; rcu_read_lock(); - dom_entry = netlbl_domhsh_getentry(secattr->domain); + dom_entry = netlbl_domhsh_getentry(secattr->domain, family); if (dom_entry == NULL) { ret_val = -ENOENT; goto socket_setattr_return; diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index 13f777f..b2aeb5d 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -72,6 +72,7 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = { [NLBL_MGMT_A_PROTOCOL] = { .type = NLA_U32 }, [NLBL_MGMT_A_VERSION] = { .type = NLA_U32 }, [NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 }, + [NLBL_MGMT_A_FAMILY] = { .type = NLA_U16 }, }; /* @@ -119,6 +120,11 @@ static int netlbl_mgmt_add_common(struct genl_info *info, switch (entry->def.type) { case NETLBL_NLTYPE_UNLABELED: + if (info->attrs[NLBL_MGMT_A_FAMILY]) + entry->family = + nla_get_u16(info->attrs[NLBL_MGMT_A_FAMILY]); + else + entry->family = AF_UNSPEC; break; case NETLBL_NLTYPE_CIPSOV4: if (!info->attrs[NLBL_MGMT_A_CV4DOI]) @@ -128,12 +134,17 @@ static int netlbl_mgmt_add_common(struct genl_info *info, cipsov4 = cipso_v4_doi_getdef(tmp_val); if (cipsov4 == NULL) goto add_free_domain; + entry->family = AF_INET; entry->def.cipso = cipsov4; break; default: goto add_free_domain; } + if ((entry->family == AF_INET && info->attrs[NLBL_MGMT_A_IPV6ADDR]) || + (entry->family == AF_INET6 && info->attrs[NLBL_MGMT_A_IPV4ADDR])) + goto add_doi_put_def; + if (info->attrs[NLBL_MGMT_A_IPV4ADDR]) { struct in_addr *addr; struct in_addr *mask; @@ -178,6 +189,7 @@ static int netlbl_mgmt_add_common(struct genl_info *info, goto add_free_addrmap; } + entry->family = AF_INET; entry->def.type = NETLBL_NLTYPE_ADDRSELECT; entry->def.addrsel = addrmap; #if IS_ENABLED(CONFIG_IPV6) @@ -227,6 +239,7 @@ static int netlbl_mgmt_add_common(struct genl_info *info, goto add_free_addrmap; } + entry->family = AF_INET6; entry->def.type = NETLBL_NLTYPE_ADDRSELECT; entry->def.addrsel = addrmap; #endif /* IPv6 */ @@ -278,6 +291,10 @@ static int netlbl_mgmt_listentry(struct sk_buff *skb, return ret_val; } + ret_val = nla_put_u16(skb, NLBL_MGMT_A_FAMILY, entry->family); + if (ret_val != 0) + return ret_val; + switch (entry->def.type) { case NETLBL_NLTYPE_ADDRSELECT: nla_a = nla_nest_start(skb, NLBL_MGMT_A_SELECTORLIST); @@ -418,7 +435,7 @@ static int netlbl_mgmt_remove(struct sk_buff *skb, struct genl_info *info) netlbl_netlink_auditinfo(skb, &audit_info); domain = nla_data(info->attrs[NLBL_MGMT_A_DOMAIN]); - return netlbl_domhsh_remove(domain, &audit_info); + return netlbl_domhsh_remove(domain, AF_UNSPEC, &audit_info); } /** @@ -536,7 +553,7 @@ static int netlbl_mgmt_removedef(struct sk_buff *skb, struct genl_info *info) netlbl_netlink_auditinfo(skb, &audit_info); - return netlbl_domhsh_remove_default(&audit_info); + return netlbl_domhsh_remove_default(AF_UNSPEC, &audit_info); } /** @@ -556,6 +573,12 @@ static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) struct sk_buff *ans_skb = NULL; void *data; struct netlbl_dom_map *entry; + u16 family; + + if (info->attrs[NLBL_MGMT_A_FAMILY]) + family = nla_get_u16(info->attrs[NLBL_MGMT_A_FAMILY]); + else + family = AF_INET; ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (ans_skb == NULL) @@ -566,7 +589,7 @@ static int netlbl_mgmt_listdef(struct sk_buff *skb, struct genl_info *info) goto listdef_failure; rcu_read_lock(); - entry = netlbl_domhsh_getentry(NULL); + entry = netlbl_domhsh_getentry(NULL, family); if (entry == NULL) { ret_val = -ENOENT; goto listdef_failure_lock; diff --git a/net/netlabel/netlabel_mgmt.h b/net/netlabel/netlabel_mgmt.h index 8b6e1ab..f368dc0 100644 --- a/net/netlabel/netlabel_mgmt.h +++ b/net/netlabel/netlabel_mgmt.h @@ -58,7 +58,10 @@ * * NLBL_MGMT_A_CV4DOI * - * If using NETLBL_NLTYPE_UNLABELED no other attributes are required. + * If using NETLBL_NLTYPE_UNLABELED no other attributes are required, + * however the following attribute may optionally be sent: + * + * NLBL_MGMT_A_FAMILY * * o REMOVE: * Sent by an application to remove a domain mapping from the NetLabel @@ -77,6 +80,7 @@ * Required attributes: * * NLBL_MGMT_A_DOMAIN + * NLBL_MGMT_A_FAMILY * * If the IP address selectors are not used the following attribute is * required: @@ -108,7 +112,10 @@ * * NLBL_MGMT_A_CV4DOI * - * If using NETLBL_NLTYPE_UNLABELED no other attributes are required. + * If using NETLBL_NLTYPE_UNLABELED no other attributes are required, + * however the following attribute may optionally be sent: + * + * NLBL_MGMT_A_FAMILY * * o REMOVEDEF: * Sent by an application to remove the default domain mapping from the @@ -117,13 +124,17 @@ * o LISTDEF: * This message can be sent either from an application or by the kernel in * response to an application generated LISTDEF message. When sent by an - * application there is no payload. On success the kernel should send a - * response using the following format. + * application there may be an optional payload. * - * If the IP address selectors are not used the following attribute is + * NLBL_MGMT_A_FAMILY + * + * On success the kernel should send a response using the following format: + * + * If the IP address selectors are not used the following attributes are * required: * * NLBL_MGMT_A_PROTOCOL + * NLBL_MGMT_A_FAMILY * * If the IP address selectors are used then the following attritbute is * required: @@ -209,6 +220,9 @@ enum { /* (NLA_NESTED) * the selector list, there must be at least one * NLBL_MGMT_A_ADDRSELECTOR attribute */ + NLBL_MGMT_A_FAMILY, + /* (NLA_U16) + * The address family */ __NLBL_MGMT_A_MAX, }; #define NLBL_MGMT_A_MAX (__NLBL_MGMT_A_MAX - 1) diff --git a/net/netlabel/netlabel_unlabeled.c b/net/netlabel/netlabel_unlabeled.c index ca590d2..4528cff 100644 --- a/net/netlabel/netlabel_unlabeled.c +++ b/net/netlabel/netlabel_unlabeled.c @@ -1537,6 +1537,7 @@ int __init netlbl_unlabel_defconf(void) entry = kzalloc(sizeof(*entry), GFP_KERNEL); if (entry == NULL) return -ENOMEM; + entry->family = AF_UNSPEC; entry->def.type = NETLBL_NLTYPE_UNLABELED; ret_val = netlbl_domhsh_add_default(entry, &audit_info); if (ret_val != 0) -- cgit v0.10.2 From cb72d38211eacda2dd90b09540542b6582da614e Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:02:46 -0400 Subject: netlabel: Initial support for the CALIPSO netlink protocol. CALIPSO is a packet labelling protocol for IPv6 which is very similar to CIPSO. It is specified in RFC 5570. Much of the code is based on the current CIPSO code. This adds support for adding passthrough-type CALIPSO DOIs through the NLBL_CALIPSO_C_ADD command. It requires attributes: NLBL_CALIPSO_A_TYPE which must be CALIPSO_MAP_PASS. NLBL_CALIPSO_A_DOI. In passthrough mode the CALIPSO engine will map MLS secattr levels and categories directly to the packet label. At this stage, the major difference between this and the CIPSO code is that IPv6 may be compiled as a module. To allow for this the CALIPSO functions are registered at module init time. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/calipso.h b/include/net/calipso.h new file mode 100644 index 0000000..38dbb47 --- /dev/null +++ b/include/net/calipso.h @@ -0,0 +1,79 @@ +/* + * CALIPSO - Common Architecture Label IPv6 Security Option + * + * This is an implementation of the CALIPSO protocol as specified in + * RFC 5570. + * + * Authors: Paul Moore + * Huw Davies + * + */ + +/* + * (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * (c) Copyright Huw Davies , 2015 + * + * 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, see . + * + */ + +#ifndef _CALIPSO_H +#define _CALIPSO_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* known doi values */ +#define CALIPSO_DOI_UNKNOWN 0x00000000 + +/* doi mapping types */ +#define CALIPSO_MAP_UNKNOWN 0 +#define CALIPSO_MAP_PASS 2 + +/* + * CALIPSO DOI definitions + */ + +/* DOI definition struct */ +struct calipso_doi { + u32 doi; + u32 type; + + atomic_t refcount; + struct list_head list; + struct rcu_head rcu; +}; + +#ifdef CONFIG_NETLABEL +int __init calipso_init(void); +void calipso_exit(void); +#else +static inline int __init calipso_init(void) +{ + return 0; +} + +static inline void calipso_exit(void) +{ +} +#endif /* CONFIG_NETLABEL */ + +#endif /* _CALIPSO_H */ diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 7b5a300..6af1bb6 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -40,6 +40,7 @@ #include struct cipso_v4_doi; +struct calipso_doi; /* * NetLabel - A management interface for maintaining network packet label @@ -94,6 +95,8 @@ struct cipso_v4_doi; #define NETLBL_NLTYPE_UNLABELED_NAME "NLBL_UNLBL" #define NETLBL_NLTYPE_ADDRSELECT 6 #define NETLBL_NLTYPE_ADDRSELECT_NAME "NLBL_ADRSEL" +#define NETLBL_NLTYPE_CALIPSO 7 +#define NETLBL_NLTYPE_CALIPSO_NAME "NLBL_CALIPSO" /* * NetLabel - Kernel API for accessing the network packet label mappings. @@ -216,6 +219,23 @@ struct netlbl_lsm_secattr { } attr; }; +/** + * struct netlbl_calipso_ops - NetLabel CALIPSO operations + * @doi_add: add a CALIPSO DOI + * @doi_free: free a CALIPSO DOI + * + * Description: + * This structure is filled out by the CALIPSO engine and passed + * to the NetLabel core via a call to netlbl_calipso_ops_register(). + * It enables the CALIPSO engine (and hence IPv6) to be compiled + * as a module. + */ +struct netlbl_calipso_ops { + int (*doi_add)(struct calipso_doi *doi_def, + struct netlbl_audit *audit_info); + void (*doi_free)(struct calipso_doi *doi_def); +}; + /* * LSM security attribute operations (inline) */ @@ -598,4 +618,7 @@ static inline struct audit_buffer *netlbl_audit_start(int type, } #endif /* CONFIG_NETLABEL */ +const struct netlbl_calipso_ops * +netlbl_calipso_ops_register(const struct netlbl_calipso_ops *ops); + #endif /* _NETLABEL_H */ diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h index d820aa9..82e8aa5 100644 --- a/include/uapi/linux/audit.h +++ b/include/uapi/linux/audit.h @@ -130,6 +130,8 @@ #define AUDIT_MAC_IPSEC_EVENT 1415 /* Audit an IPSec event */ #define AUDIT_MAC_UNLBL_STCADD 1416 /* NetLabel: add a static label */ #define AUDIT_MAC_UNLBL_STCDEL 1417 /* NetLabel: del a static label */ +#define AUDIT_MAC_CALIPSO_ADD 1418 /* NetLabel: add CALIPSO DOI entry */ +#define AUDIT_MAC_CALIPSO_DEL 1419 /* NetLabel: del CALIPSO DOI entry */ #define AUDIT_FIRST_KERN_ANOM_MSG 1700 #define AUDIT_LAST_KERN_ANOM_MSG 1799 diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 2fbd90b..88ad447 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -21,6 +21,7 @@ ipv6-$(CONFIG_NETFILTER) += netfilter.o ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o ipv6-$(CONFIG_PROC_FS) += proc.o ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o +ipv6-$(CONFIG_NETLABEL) += calipso.o ipv6-objs += $(ipv6-y) diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index b11c37c..c241c18 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -60,6 +60,7 @@ #ifdef CONFIG_IPV6_TUNNEL #include #endif +#include #include #include @@ -970,6 +971,10 @@ static int __init inet6_init(void) if (err) goto pingv6_fail; + err = calipso_init(); + if (err) + goto calipso_fail; + #ifdef CONFIG_SYSCTL err = ipv6_sysctl_register(); if (err) @@ -980,8 +985,10 @@ out: #ifdef CONFIG_SYSCTL sysctl_fail: - pingv6_exit(); + calipso_exit(); #endif +calipso_fail: + pingv6_exit(); pingv6_fail: ipv6_packet_cleanup(); ipv6_packet_fail: diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c new file mode 100644 index 0000000..aa44310 --- /dev/null +++ b/net/ipv6/calipso.c @@ -0,0 +1,169 @@ +/* + * CALIPSO - Common Architecture Label IPv6 Security Option + * + * This is an implementation of the CALIPSO protocol as specified in + * RFC 5570. + * + * Authors: Paul Moore + * Huw Davies + * + */ + +/* (c) Copyright Hewlett-Packard Development Company, L.P., 2006, 2008 + * (c) Copyright Huw Davies , 2015 + * + * 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, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* List of available DOI definitions */ +static DEFINE_SPINLOCK(calipso_doi_list_lock); +static LIST_HEAD(calipso_doi_list); + +/* DOI List Functions + */ + +/** + * calipso_doi_search - Searches for a DOI definition + * @doi: the DOI to search for + * + * Description: + * Search the DOI definition list for a DOI definition with a DOI value that + * matches @doi. The caller is responsible for calling rcu_read_[un]lock(). + * Returns a pointer to the DOI definition on success and NULL on failure. + */ +static struct calipso_doi *calipso_doi_search(u32 doi) +{ + struct calipso_doi *iter; + + list_for_each_entry_rcu(iter, &calipso_doi_list, list) + if (iter->doi == doi && atomic_read(&iter->refcount)) + return iter; + return NULL; +} + +/** + * calipso_doi_add - Add a new DOI to the CALIPSO protocol engine + * @doi_def: the DOI structure + * @audit_info: NetLabel audit information + * + * Description: + * The caller defines a new DOI for use by the CALIPSO engine and calls this + * function to add it to the list of acceptable domains. The caller must + * ensure that the mapping table specified in @doi_def->map meets all of the + * requirements of the mapping type (see calipso.h for details). Returns + * zero on success and non-zero on failure. + * + */ +static int calipso_doi_add(struct calipso_doi *doi_def, + struct netlbl_audit *audit_info) +{ + int ret_val = -EINVAL; + u32 doi; + u32 doi_type; + struct audit_buffer *audit_buf; + + doi = doi_def->doi; + doi_type = doi_def->type; + + if (doi_def->doi == CALIPSO_DOI_UNKNOWN) + goto doi_add_return; + + atomic_set(&doi_def->refcount, 1); + + spin_lock(&calipso_doi_list_lock); + if (calipso_doi_search(doi_def->doi)) { + spin_unlock(&calipso_doi_list_lock); + ret_val = -EEXIST; + goto doi_add_return; + } + list_add_tail_rcu(&doi_def->list, &calipso_doi_list); + spin_unlock(&calipso_doi_list_lock); + ret_val = 0; + +doi_add_return: + audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_ADD, audit_info); + if (audit_buf) { + const char *type_str; + + switch (doi_type) { + case CALIPSO_MAP_PASS: + type_str = "pass"; + break; + default: + type_str = "(unknown)"; + } + audit_log_format(audit_buf, + " calipso_doi=%u calipso_type=%s res=%u", + doi, type_str, ret_val == 0 ? 1 : 0); + audit_log_end(audit_buf); + } + + return ret_val; +} + +/** + * calipso_doi_free - Frees a DOI definition + * @doi_def: the DOI definition + * + * Description: + * This function frees all of the memory associated with a DOI definition. + * + */ +static void calipso_doi_free(struct calipso_doi *doi_def) +{ + kfree(doi_def); +} + +static const struct netlbl_calipso_ops ops = { + .doi_add = calipso_doi_add, + .doi_free = calipso_doi_free, +}; + +/** + * calipso_init - Initialize the CALIPSO module + * + * Description: + * Initialize the CALIPSO module and prepare it for use. Returns zero on + * success and negative values on failure. + * + */ +int __init calipso_init(void) +{ + netlbl_calipso_ops_register(&ops); + return 0; +} + +void calipso_exit(void) +{ + netlbl_calipso_ops_register(NULL); +} diff --git a/net/netlabel/Makefile b/net/netlabel/Makefile index d2732fc..d341ede 100644 --- a/net/netlabel/Makefile +++ b/net/netlabel/Makefile @@ -12,4 +12,4 @@ obj-y += netlabel_mgmt.o # protocol modules obj-y += netlabel_unlabeled.o obj-y += netlabel_cipso_v4.o - +obj-$(subst m,y,$(CONFIG_IPV6)) += netlabel_calipso.o diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c new file mode 100644 index 0000000..8a113f9 --- /dev/null +++ b/net/netlabel/netlabel_calipso.c @@ -0,0 +1,216 @@ +/* + * NetLabel CALIPSO/IPv6 Support + * + * This file defines the CALIPSO/IPv6 functions for the NetLabel system. The + * NetLabel system manages static and dynamic label mappings for network + * protocols such as CIPSO and CALIPSO. + * + * Authors: Paul Moore + * Huw Davies + * + */ + +/* (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * (c) Copyright Huw Davies , 2015 + * + * 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, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "netlabel_user.h" +#include "netlabel_calipso.h" +#include "netlabel_mgmt.h" +#include "netlabel_domainhash.h" + +/* NetLabel Generic NETLINK CALIPSO family */ +static struct genl_family netlbl_calipso_gnl_family = { + .id = GENL_ID_GENERATE, + .hdrsize = 0, + .name = NETLBL_NLTYPE_CALIPSO_NAME, + .version = NETLBL_PROTO_VERSION, + .maxattr = NLBL_CALIPSO_A_MAX, +}; + +/* NetLabel Netlink attribute policy */ +static const struct nla_policy calipso_genl_policy[NLBL_CALIPSO_A_MAX + 1] = { + [NLBL_CALIPSO_A_DOI] = { .type = NLA_U32 }, + [NLBL_CALIPSO_A_MTYPE] = { .type = NLA_U32 }, +}; + +/* NetLabel Command Handlers + */ +/** + * netlbl_calipso_add_pass - Adds a CALIPSO pass DOI definition + * @info: the Generic NETLINK info block + * @audit_info: NetLabel audit information + * + * Description: + * Create a new CALIPSO_MAP_PASS DOI definition based on the given ADD message + * and add it to the CALIPSO engine. Return zero on success and non-zero on + * error. + * + */ +static int netlbl_calipso_add_pass(struct genl_info *info, + struct netlbl_audit *audit_info) +{ + int ret_val; + struct calipso_doi *doi_def = NULL; + + doi_def = kmalloc(sizeof(*doi_def), GFP_KERNEL); + if (!doi_def) + return -ENOMEM; + doi_def->type = CALIPSO_MAP_PASS; + doi_def->doi = nla_get_u32(info->attrs[NLBL_CALIPSO_A_DOI]); + ret_val = calipso_doi_add(doi_def, audit_info); + if (ret_val != 0) + calipso_doi_free(doi_def); + + return ret_val; +} + +/** + * netlbl_calipso_add - Handle an ADD message + * @skb: the NETLINK buffer + * @info: the Generic NETLINK info block + * + * Description: + * Create a new DOI definition based on the given ADD message and add it to the + * CALIPSO engine. Returns zero on success, negative values on failure. + * + */ +static int netlbl_calipso_add(struct sk_buff *skb, struct genl_info *info) + +{ + int ret_val = -EINVAL; + struct netlbl_audit audit_info; + + if (!info->attrs[NLBL_CALIPSO_A_DOI] || + !info->attrs[NLBL_CALIPSO_A_MTYPE]) + return -EINVAL; + + netlbl_netlink_auditinfo(skb, &audit_info); + switch (nla_get_u32(info->attrs[NLBL_CALIPSO_A_MTYPE])) { + case CALIPSO_MAP_PASS: + ret_val = netlbl_calipso_add_pass(info, &audit_info); + break; + } + if (ret_val == 0) + atomic_inc(&netlabel_mgmt_protocount); + + return ret_val; +} + +/* NetLabel Generic NETLINK Command Definitions + */ + +static const struct genl_ops netlbl_calipso_ops[] = { + { + .cmd = NLBL_CALIPSO_C_ADD, + .flags = GENL_ADMIN_PERM, + .policy = calipso_genl_policy, + .doit = netlbl_calipso_add, + .dumpit = NULL, + }, +}; + +/* NetLabel Generic NETLINK Protocol Functions + */ + +/** + * netlbl_calipso_genl_init - Register the CALIPSO NetLabel component + * + * Description: + * Register the CALIPSO packet NetLabel component with the Generic NETLINK + * mechanism. Returns zero on success, negative values on failure. + * + */ +int __init netlbl_calipso_genl_init(void) +{ + return genl_register_family_with_ops(&netlbl_calipso_gnl_family, + netlbl_calipso_ops); +} + +static const struct netlbl_calipso_ops *calipso_ops; + +/** + * netlbl_calipso_ops_register - Register the CALIPSO operations + * + * Description: + * Register the CALIPSO packet engine operations. + * + */ +const struct netlbl_calipso_ops * +netlbl_calipso_ops_register(const struct netlbl_calipso_ops *ops) +{ + return xchg(&calipso_ops, ops); +} +EXPORT_SYMBOL(netlbl_calipso_ops_register); + +static const struct netlbl_calipso_ops *netlbl_calipso_ops_get(void) +{ + return ACCESS_ONCE(calipso_ops); +} + +/** + * calipso_doi_add - Add a new DOI to the CALIPSO protocol engine + * @doi_def: the DOI structure + * @audit_info: NetLabel audit information + * + * Description: + * The caller defines a new DOI for use by the CALIPSO engine and calls this + * function to add it to the list of acceptable domains. The caller must + * ensure that the mapping table specified in @doi_def->map meets all of the + * requirements of the mapping type (see calipso.h for details). Returns + * zero on success and non-zero on failure. + * + */ +int calipso_doi_add(struct calipso_doi *doi_def, + struct netlbl_audit *audit_info) +{ + int ret_val = -ENOMSG; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->doi_add(doi_def, audit_info); + return ret_val; +} + +/** + * calipso_doi_free - Frees a DOI definition + * @doi_def: the DOI definition + * + * Description: + * This function frees all of the memory associated with a DOI definition. + * + */ +void calipso_doi_free(struct calipso_doi *doi_def) +{ + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ops->doi_free(doi_def); +} diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h new file mode 100644 index 0000000..f78790a --- /dev/null +++ b/net/netlabel/netlabel_calipso.h @@ -0,0 +1,90 @@ +/* + * NetLabel CALIPSO Support + * + * This file defines the CALIPSO functions for the NetLabel system. The + * NetLabel system manages static and dynamic label mappings for network + * protocols such as CIPSO and RIPSO. + * + * Authors: Paul Moore + * Huw Davies + * + */ + +/* (c) Copyright Hewlett-Packard Development Company, L.P., 2006 + * (c) Copyright Huw Davies , 2015 + * + * 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, see . + * + */ + +#ifndef _NETLABEL_CALIPSO +#define _NETLABEL_CALIPSO + +#include +#include + +/* The following NetLabel payloads are supported by the CALIPSO subsystem. + * + * o ADD: + * Sent by an application to add a new DOI mapping table. + * + * Required attributes: + * + * NLBL_CALIPSO_A_DOI + * NLBL_CALIPSO_A_MTYPE + * + * If using CALIPSO_MAP_PASS no additional attributes are required. + * + */ + +/* NetLabel CALIPSO commands */ +enum { + NLBL_CALIPSO_C_UNSPEC, + NLBL_CALIPSO_C_ADD, + NLBL_CALIPSO_C_REMOVE, + NLBL_CALIPSO_C_LIST, + NLBL_CALIPSO_C_LISTALL, + __NLBL_CALIPSO_C_MAX, +}; + +/* NetLabel CALIPSO attributes */ +enum { + NLBL_CALIPSO_A_UNSPEC, + NLBL_CALIPSO_A_DOI, + /* (NLA_U32) + * the DOI value */ + NLBL_CALIPSO_A_MTYPE, + /* (NLA_U32) + * the mapping table type (defined in the calipso.h header as + * CALIPSO_MAP_*) */ + __NLBL_CALIPSO_A_MAX, +}; + +#define NLBL_CALIPSO_A_MAX (__NLBL_CALIPSO_A_MAX - 1) + +/* NetLabel protocol functions */ +#if IS_ENABLED(CONFIG_IPV6) +int netlbl_calipso_genl_init(void); +#else +static inline int netlbl_calipso_genl_init(void) +{ + return 0; +} +#endif + +int calipso_doi_add(struct calipso_doi *doi_def, + struct netlbl_audit *audit_info); +void calipso_doi_free(struct calipso_doi *doi_def); + +#endif diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 7e2a68f..8d2e483 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -1170,6 +1170,7 @@ struct audit_buffer *netlbl_audit_start(int type, { return netlbl_audit_start_common(type, audit_info); } +EXPORT_SYMBOL(netlbl_audit_start); /* * Setup Functions diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index b2aeb5d..975b1e9 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -674,6 +674,15 @@ static int netlbl_mgmt_protocols(struct sk_buff *skb, goto protocols_return; protos_sent++; } +#if IS_ENABLED(CONFIG_IPV6) + if (protos_sent == 2) { + if (netlbl_mgmt_protocols_cb(skb, + cb, + NETLBL_NLTYPE_CALIPSO) < 0) + goto protocols_return; + protos_sent++; + } +#endif protocols_return: cb->args[0] = protos_sent; diff --git a/net/netlabel/netlabel_user.c b/net/netlabel/netlabel_user.c index adf8b79..58495f4 100644 --- a/net/netlabel/netlabel_user.c +++ b/net/netlabel/netlabel_user.c @@ -44,6 +44,7 @@ #include "netlabel_mgmt.h" #include "netlabel_unlabeled.h" #include "netlabel_cipso_v4.h" +#include "netlabel_calipso.h" #include "netlabel_user.h" /* @@ -71,6 +72,10 @@ int __init netlbl_netlink_init(void) if (ret_val != 0) return ret_val; + ret_val = netlbl_calipso_genl_init(); + if (ret_val != 0) + return ret_val; + return netlbl_unlabel_genl_init(); } -- cgit v0.10.2 From a5e34490c3160e09814403d040765b0ae0003121 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:02:47 -0400 Subject: netlabel: Add support for querying a CALIPSO DOI. Query a specified DOI through the NLBL_CALIPSO_C_LIST command. It requires the attribute: NLBL_CALIPSO_A_DOI. The reply will contain: NLBL_CALIPSO_A_MTYPE Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 6af1bb6..0f05b83 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -223,6 +223,8 @@ struct netlbl_lsm_secattr { * struct netlbl_calipso_ops - NetLabel CALIPSO operations * @doi_add: add a CALIPSO DOI * @doi_free: free a CALIPSO DOI + * @doi_getdef: returns a reference to a DOI + * @doi_putdef: releases a reference of a DOI * * Description: * This structure is filled out by the CALIPSO engine and passed @@ -234,6 +236,8 @@ struct netlbl_calipso_ops { int (*doi_add)(struct calipso_doi *doi_def, struct netlbl_audit *audit_info); void (*doi_free)(struct calipso_doi *doi_def); + struct calipso_doi *(*doi_getdef)(u32 doi); + void (*doi_putdef)(struct calipso_doi *doi_def); }; /* diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index aa44310..128cc69 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -144,9 +144,77 @@ static void calipso_doi_free(struct calipso_doi *doi_def) kfree(doi_def); } +/** + * calipso_doi_free_rcu - Frees a DOI definition via the RCU pointer + * @entry: the entry's RCU field + * + * Description: + * This function is designed to be used as a callback to the call_rcu() + * function so that the memory allocated to the DOI definition can be released + * safely. + * + */ +static void calipso_doi_free_rcu(struct rcu_head *entry) +{ + struct calipso_doi *doi_def; + + doi_def = container_of(entry, struct calipso_doi, rcu); + calipso_doi_free(doi_def); +} + +/** + * calipso_doi_getdef - Returns a reference to a valid DOI definition + * @doi: the DOI value + * + * Description: + * Searches for a valid DOI definition and if one is found it is returned to + * the caller. Otherwise NULL is returned. The caller must ensure that + * calipso_doi_putdef() is called when the caller is done. + * + */ +static struct calipso_doi *calipso_doi_getdef(u32 doi) +{ + struct calipso_doi *doi_def; + + rcu_read_lock(); + doi_def = calipso_doi_search(doi); + if (!doi_def) + goto doi_getdef_return; + if (!atomic_inc_not_zero(&doi_def->refcount)) + doi_def = NULL; + +doi_getdef_return: + rcu_read_unlock(); + return doi_def; +} + +/** + * calipso_doi_putdef - Releases a reference for the given DOI definition + * @doi_def: the DOI definition + * + * Description: + * Releases a DOI definition reference obtained from calipso_doi_getdef(). + * + */ +static void calipso_doi_putdef(struct calipso_doi *doi_def) +{ + if (!doi_def) + return; + + if (!atomic_dec_and_test(&doi_def->refcount)) + return; + spin_lock(&calipso_doi_list_lock); + list_del_rcu(&doi_def->list); + spin_unlock(&calipso_doi_list_lock); + + call_rcu(&doi_def->rcu, calipso_doi_free_rcu); +} + static const struct netlbl_calipso_ops ops = { .doi_add = calipso_doi_add, .doi_free = calipso_doi_free, + .doi_getdef = calipso_doi_getdef, + .doi_putdef = calipso_doi_putdef, }; /** diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index 8a113f9..6161170 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c @@ -124,6 +124,65 @@ static int netlbl_calipso_add(struct sk_buff *skb, struct genl_info *info) return ret_val; } +/** + * netlbl_calipso_list - Handle a LIST message + * @skb: the NETLINK buffer + * @info: the Generic NETLINK info block + * + * Description: + * Process a user generated LIST message and respond accordingly. + * Returns zero on success and negative values on error. + * + */ +static int netlbl_calipso_list(struct sk_buff *skb, struct genl_info *info) +{ + int ret_val; + struct sk_buff *ans_skb = NULL; + void *data; + u32 doi; + struct calipso_doi *doi_def; + + if (!info->attrs[NLBL_CALIPSO_A_DOI]) { + ret_val = -EINVAL; + goto list_failure; + } + + doi = nla_get_u32(info->attrs[NLBL_CALIPSO_A_DOI]); + + doi_def = calipso_doi_getdef(doi); + if (!doi_def) { + ret_val = -EINVAL; + goto list_failure; + } + + ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!ans_skb) { + ret_val = -ENOMEM; + goto list_failure_put; + } + data = genlmsg_put_reply(ans_skb, info, &netlbl_calipso_gnl_family, + 0, NLBL_CALIPSO_C_LIST); + if (!data) { + ret_val = -ENOMEM; + goto list_failure_put; + } + + ret_val = nla_put_u32(ans_skb, NLBL_CALIPSO_A_MTYPE, doi_def->type); + if (ret_val != 0) + goto list_failure_put; + + calipso_doi_putdef(doi_def); + + genlmsg_end(ans_skb, data); + return genlmsg_reply(ans_skb, info); + +list_failure_put: + calipso_doi_putdef(doi_def); +list_failure: + kfree_skb(ans_skb); + return ret_val; +} + /* NetLabel Generic NETLINK Command Definitions */ @@ -135,6 +194,13 @@ static const struct genl_ops netlbl_calipso_ops[] = { .doit = netlbl_calipso_add, .dumpit = NULL, }, + { + .cmd = NLBL_CALIPSO_C_LIST, + .flags = 0, + .policy = calipso_genl_policy, + .doit = netlbl_calipso_list, + .dumpit = NULL, + }, }; /* NetLabel Generic NETLINK Protocol Functions @@ -214,3 +280,39 @@ void calipso_doi_free(struct calipso_doi *doi_def) if (ops) ops->doi_free(doi_def); } + +/** + * calipso_doi_getdef - Returns a reference to a valid DOI definition + * @doi: the DOI value + * + * Description: + * Searches for a valid DOI definition and if one is found it is returned to + * the caller. Otherwise NULL is returned. The caller must ensure that + * calipso_doi_putdef() is called when the caller is done. + * + */ +struct calipso_doi *calipso_doi_getdef(u32 doi) +{ + struct calipso_doi *ret_val = NULL; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->doi_getdef(doi); + return ret_val; +} + +/** + * calipso_doi_putdef - Releases a reference for the given DOI definition + * @doi_def: the DOI definition + * + * Description: + * Releases a DOI definition reference obtained from calipso_doi_getdef(). + * + */ +void calipso_doi_putdef(struct calipso_doi *doi_def) +{ + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ops->doi_putdef(doi_def); +} diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h index f78790a..6da737a 100644 --- a/net/netlabel/netlabel_calipso.h +++ b/net/netlabel/netlabel_calipso.h @@ -46,6 +46,23 @@ * * If using CALIPSO_MAP_PASS no additional attributes are required. * + * o LIST: + * Sent by an application to list the details of a DOI definition. On + * success the kernel should send a response using the following format. + * + * Required attributes: + * + * NLBL_CALIPSO_A_DOI + * + * The valid response message format depends on the type of the DOI mapping, + * the defined formats are shown below. + * + * Required attributes: + * + * NLBL_CALIPSO_A_MTYPE + * + * If using CALIPSO_MAP_PASS no additional attributes are required. + * */ /* NetLabel CALIPSO commands */ @@ -86,5 +103,7 @@ static inline int netlbl_calipso_genl_init(void) int calipso_doi_add(struct calipso_doi *doi_def, struct netlbl_audit *audit_info); void calipso_doi_free(struct calipso_doi *doi_def); +struct calipso_doi *calipso_doi_getdef(u32 doi); +void calipso_doi_putdef(struct calipso_doi *doi_def); #endif -- cgit v0.10.2 From e1ce69df7e6e8cbdca78ae831ecf435b12b4c168 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:02:48 -0400 Subject: netlabel: Add support for enumerating the CALIPSO DOI list. Enumerate the DOI list through the NLBL_CALIPSO_C_LISTALL command. It takes no attributes. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 0f05b83..2653d3a 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -225,6 +225,7 @@ struct netlbl_lsm_secattr { * @doi_free: free a CALIPSO DOI * @doi_getdef: returns a reference to a DOI * @doi_putdef: releases a reference of a DOI + * @doi_walk: enumerate the DOI list * * Description: * This structure is filled out by the CALIPSO engine and passed @@ -238,6 +239,9 @@ struct netlbl_calipso_ops { void (*doi_free)(struct calipso_doi *doi_def); struct calipso_doi *(*doi_getdef)(u32 doi); void (*doi_putdef)(struct calipso_doi *doi_def); + int (*doi_walk)(u32 *skip_cnt, + int (*callback)(struct calipso_doi *doi_def, void *arg), + void *cb_arg); }; /* diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index 128cc69..fa17c7a 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -210,11 +210,52 @@ static void calipso_doi_putdef(struct calipso_doi *doi_def) call_rcu(&doi_def->rcu, calipso_doi_free_rcu); } +/** + * calipso_doi_walk - Iterate through the DOI definitions + * @skip_cnt: skip past this number of DOI definitions, updated + * @callback: callback for each DOI definition + * @cb_arg: argument for the callback function + * + * Description: + * Iterate over the DOI definition list, skipping the first @skip_cnt entries. + * For each entry call @callback, if @callback returns a negative value stop + * 'walking' through the list and return. Updates the value in @skip_cnt upon + * return. Returns zero on success, negative values on failure. + * + */ +static int calipso_doi_walk(u32 *skip_cnt, + int (*callback)(struct calipso_doi *doi_def, + void *arg), + void *cb_arg) +{ + int ret_val = -ENOENT; + u32 doi_cnt = 0; + struct calipso_doi *iter_doi; + + rcu_read_lock(); + list_for_each_entry_rcu(iter_doi, &calipso_doi_list, list) + if (atomic_read(&iter_doi->refcount) > 0) { + if (doi_cnt++ < *skip_cnt) + continue; + ret_val = callback(iter_doi, cb_arg); + if (ret_val < 0) { + doi_cnt--; + goto doi_walk_return; + } + } + +doi_walk_return: + rcu_read_unlock(); + *skip_cnt = doi_cnt; + return ret_val; +} + static const struct netlbl_calipso_ops ops = { .doi_add = calipso_doi_add, .doi_free = calipso_doi_free, .doi_getdef = calipso_doi_getdef, .doi_putdef = calipso_doi_putdef, + .doi_walk = calipso_doi_walk, }; /** diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index 6161170..2141145 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c @@ -46,6 +46,13 @@ #include "netlabel_mgmt.h" #include "netlabel_domainhash.h" +/* Argument struct for calipso_doi_walk() */ +struct netlbl_calipso_doiwalk_arg { + struct netlink_callback *nl_cb; + struct sk_buff *skb; + u32 seq; +}; + /* NetLabel Generic NETLINK CALIPSO family */ static struct genl_family netlbl_calipso_gnl_family = { .id = GENL_ID_GENERATE, @@ -183,6 +190,73 @@ list_failure: return ret_val; } +/** + * netlbl_calipso_listall_cb - calipso_doi_walk() callback for LISTALL + * @doi_def: the CALIPSO DOI definition + * @arg: the netlbl_calipso_doiwalk_arg structure + * + * Description: + * This function is designed to be used as a callback to the + * calipso_doi_walk() function for use in generating a response for a LISTALL + * message. Returns the size of the message on success, negative values on + * failure. + * + */ +static int netlbl_calipso_listall_cb(struct calipso_doi *doi_def, void *arg) +{ + int ret_val = -ENOMEM; + struct netlbl_calipso_doiwalk_arg *cb_arg = arg; + void *data; + + data = genlmsg_put(cb_arg->skb, NETLINK_CB(cb_arg->nl_cb->skb).portid, + cb_arg->seq, &netlbl_calipso_gnl_family, + NLM_F_MULTI, NLBL_CALIPSO_C_LISTALL); + if (!data) + goto listall_cb_failure; + + ret_val = nla_put_u32(cb_arg->skb, NLBL_CALIPSO_A_DOI, doi_def->doi); + if (ret_val != 0) + goto listall_cb_failure; + ret_val = nla_put_u32(cb_arg->skb, + NLBL_CALIPSO_A_MTYPE, + doi_def->type); + if (ret_val != 0) + goto listall_cb_failure; + + genlmsg_end(cb_arg->skb, data); + return 0; + +listall_cb_failure: + genlmsg_cancel(cb_arg->skb, data); + return ret_val; +} + +/** + * netlbl_calipso_listall - Handle a LISTALL message + * @skb: the NETLINK buffer + * @cb: the NETLINK callback + * + * Description: + * Process a user generated LISTALL message and respond accordingly. Returns + * zero on success and negative values on error. + * + */ +static int netlbl_calipso_listall(struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct netlbl_calipso_doiwalk_arg cb_arg; + u32 doi_skip = cb->args[0]; + + cb_arg.nl_cb = cb; + cb_arg.skb = skb; + cb_arg.seq = cb->nlh->nlmsg_seq; + + calipso_doi_walk(&doi_skip, netlbl_calipso_listall_cb, &cb_arg); + + cb->args[0] = doi_skip; + return skb->len; +} + /* NetLabel Generic NETLINK Command Definitions */ @@ -201,6 +275,13 @@ static const struct genl_ops netlbl_calipso_ops[] = { .doit = netlbl_calipso_list, .dumpit = NULL, }, + { + .cmd = NLBL_CALIPSO_C_LISTALL, + .flags = 0, + .policy = calipso_genl_policy, + .doit = NULL, + .dumpit = netlbl_calipso_listall, + }, }; /* NetLabel Generic NETLINK Protocol Functions @@ -316,3 +397,28 @@ void calipso_doi_putdef(struct calipso_doi *doi_def) if (ops) ops->doi_putdef(doi_def); } + +/** + * calipso_doi_walk - Iterate through the DOI definitions + * @skip_cnt: skip past this number of DOI definitions, updated + * @callback: callback for each DOI definition + * @cb_arg: argument for the callback function + * + * Description: + * Iterate over the DOI definition list, skipping the first @skip_cnt entries. + * For each entry call @callback, if @callback returns a negative value stop + * 'walking' through the list and return. Updates the value in @skip_cnt upon + * return. Returns zero on success, negative values on failure. + * + */ +int calipso_doi_walk(u32 *skip_cnt, + int (*callback)(struct calipso_doi *doi_def, void *arg), + void *cb_arg) +{ + int ret_val = -ENOMSG; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->doi_walk(skip_cnt, callback, cb_arg); + return ret_val; +} diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h index 6da737a..1f24174 100644 --- a/net/netlabel/netlabel_calipso.h +++ b/net/netlabel/netlabel_calipso.h @@ -63,6 +63,17 @@ * * If using CALIPSO_MAP_PASS no additional attributes are required. * + * o LISTALL: + * This message is sent by an application to list the valid DOIs on the + * system. When sent by an application there is no payload and the + * NLM_F_DUMP flag should be set. The kernel should respond with a series of + * the following messages. + * + * Required attributes: + * + * NLBL_CALIPSO_A_DOI + * NLBL_CALIPSO_A_MTYPE + * */ /* NetLabel CALIPSO commands */ @@ -105,5 +116,8 @@ int calipso_doi_add(struct calipso_doi *doi_def, void calipso_doi_free(struct calipso_doi *doi_def); struct calipso_doi *calipso_doi_getdef(u32 doi); void calipso_doi_putdef(struct calipso_doi *doi_def); +int calipso_doi_walk(u32 *skip_cnt, + int (*callback)(struct calipso_doi *doi_def, void *arg), + void *cb_arg); #endif -- cgit v0.10.2 From dc7de73f19962e824243985c046d6a2782d282fc Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:02:49 -0400 Subject: netlabel: Add support for creating a CALIPSO protocol domain mapping. This extends the NLBL_MGMT_C_ADD and NLBL_MGMT_C_ADDDEF commands to accept CALIPSO protocol DOIs. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c index 3b3b304..f1dee0d 100644 --- a/net/netlabel/netlabel_domainhash.c +++ b/net/netlabel/netlabel_domainhash.c @@ -37,10 +37,12 @@ #include #include #include +#include #include #include "netlabel_mgmt.h" #include "netlabel_addrlist.h" +#include "netlabel_calipso.h" #include "netlabel_domainhash.h" #include "netlabel_user.h" @@ -223,6 +225,7 @@ static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry, { struct audit_buffer *audit_buf; struct cipso_v4_doi *cipsov4 = NULL; + struct calipso_doi *calipso = NULL; u32 type; audit_buf = netlbl_audit_start_common(AUDIT_MAC_MAP_ADD, audit_info); @@ -241,12 +244,14 @@ static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry, struct netlbl_domaddr6_map *map6; map6 = netlbl_domhsh_addr6_entry(addr6); type = map6->def.type; + calipso = map6->def.calipso; netlbl_af6list_audit_addr(audit_buf, 0, NULL, &addr6->addr, &addr6->mask); #endif /* IPv6 */ } else { type = entry->def.type; cipsov4 = entry->def.cipso; + calipso = entry->def.calipso; } switch (type) { case NETLBL_NLTYPE_UNLABELED: @@ -258,6 +263,12 @@ static void netlbl_domhsh_audit_add(struct netlbl_dom_map *entry, " nlbl_protocol=cipsov4 cipso_doi=%u", cipsov4->doi); break; + case NETLBL_NLTYPE_CALIPSO: + BUG_ON(calipso == NULL); + audit_log_format(audit_buf, + " nlbl_protocol=calipso calipso_doi=%u", + calipso->doi); + break; } audit_log_format(audit_buf, " res=%u", result == 0 ? 1 : 0); audit_log_end(audit_buf); @@ -291,7 +302,8 @@ static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry) switch (entry->def.type) { case NETLBL_NLTYPE_UNLABELED: - if (entry->def.cipso != NULL || entry->def.addrsel != NULL) + if (entry->def.cipso != NULL || entry->def.calipso != NULL || + entry->def.addrsel != NULL) return -EINVAL; break; case NETLBL_NLTYPE_CIPSOV4: @@ -299,6 +311,11 @@ static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry) entry->def.cipso == NULL) return -EINVAL; break; + case NETLBL_NLTYPE_CALIPSO: + if (entry->family != AF_INET6 || + entry->def.calipso == NULL) + return -EINVAL; + break; case NETLBL_NLTYPE_ADDRSELECT: netlbl_af4list_foreach(iter4, &entry->def.addrsel->list4) { map4 = netlbl_domhsh_addr4_entry(iter4); @@ -320,6 +337,12 @@ static int netlbl_domhsh_validate(const struct netlbl_dom_map *entry) map6 = netlbl_domhsh_addr6_entry(iter6); switch (map6->def.type) { case NETLBL_NLTYPE_UNLABELED: + if (map6->def.calipso != NULL) + return -EINVAL; + break; + case NETLBL_NLTYPE_CALIPSO: + if (map6->def.calipso == NULL) + return -EINVAL; break; default: return -EINVAL; @@ -599,6 +622,10 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry, if (ret_val == 0) { struct netlbl_af4list *iter4; struct netlbl_domaddr4_map *map4; +#if IS_ENABLED(CONFIG_IPV6) + struct netlbl_af6list *iter6; + struct netlbl_domaddr6_map *map6; +#endif /* IPv6 */ switch (entry->def.type) { case NETLBL_NLTYPE_ADDRSELECT: @@ -607,12 +634,22 @@ int netlbl_domhsh_remove_entry(struct netlbl_dom_map *entry, map4 = netlbl_domhsh_addr4_entry(iter4); cipso_v4_doi_putdef(map4->def.cipso); } - /* no need to check the IPv6 list since we currently - * support only unlabeled protocols for IPv6 */ +#if IS_ENABLED(CONFIG_IPV6) + netlbl_af6list_foreach_rcu(iter6, + &entry->def.addrsel->list6) { + map6 = netlbl_domhsh_addr6_entry(iter6); + calipso_doi_putdef(map6->def.calipso); + } +#endif /* IPv6 */ break; case NETLBL_NLTYPE_CIPSOV4: cipso_v4_doi_putdef(entry->def.cipso); break; +#if IS_ENABLED(CONFIG_IPV6) + case NETLBL_NLTYPE_CALIPSO: + calipso_doi_putdef(entry->def.calipso); + break; +#endif /* IPv6 */ } call_rcu(&entry->rcu, netlbl_domhsh_free_entry); } diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h index 56e45ae..b7ddb6e 100644 --- a/net/netlabel/netlabel_domainhash.h +++ b/net/netlabel/netlabel_domainhash.h @@ -51,6 +51,7 @@ struct netlbl_dommap_def { union { struct netlbl_domaddr_map *addrsel; struct cipso_v4_doi *cipso; + struct calipso_doi *calipso; }; }; #define netlbl_domhsh_addr4_entry(iter) \ diff --git a/net/netlabel/netlabel_mgmt.c b/net/netlabel/netlabel_mgmt.c index 975b1e9..f85d0e0 100644 --- a/net/netlabel/netlabel_mgmt.c +++ b/net/netlabel/netlabel_mgmt.c @@ -41,8 +41,10 @@ #include #include #include +#include #include +#include "netlabel_calipso.h" #include "netlabel_domainhash.h" #include "netlabel_user.h" #include "netlabel_mgmt.h" @@ -73,6 +75,7 @@ static const struct nla_policy netlbl_mgmt_genl_policy[NLBL_MGMT_A_MAX + 1] = { [NLBL_MGMT_A_VERSION] = { .type = NLA_U32 }, [NLBL_MGMT_A_CV4DOI] = { .type = NLA_U32 }, [NLBL_MGMT_A_FAMILY] = { .type = NLA_U16 }, + [NLBL_MGMT_A_CLPDOI] = { .type = NLA_U32 }, }; /* @@ -96,6 +99,9 @@ static int netlbl_mgmt_add_common(struct genl_info *info, int ret_val = -EINVAL; struct netlbl_domaddr_map *addrmap = NULL; struct cipso_v4_doi *cipsov4 = NULL; +#if IS_ENABLED(CONFIG_IPV6) + struct calipso_doi *calipso = NULL; +#endif u32 tmp_val; struct netlbl_dom_map *entry = kzalloc(sizeof(*entry), GFP_KERNEL); @@ -137,6 +143,19 @@ static int netlbl_mgmt_add_common(struct genl_info *info, entry->family = AF_INET; entry->def.cipso = cipsov4; break; +#if IS_ENABLED(CONFIG_IPV6) + case NETLBL_NLTYPE_CALIPSO: + if (!info->attrs[NLBL_MGMT_A_CLPDOI]) + goto add_free_domain; + + tmp_val = nla_get_u32(info->attrs[NLBL_MGMT_A_CLPDOI]); + calipso = calipso_doi_getdef(tmp_val); + if (calipso == NULL) + goto add_free_domain; + entry->family = AF_INET6; + entry->def.calipso = calipso; + break; +#endif /* IPv6 */ default: goto add_free_domain; } @@ -232,6 +251,8 @@ static int netlbl_mgmt_add_common(struct genl_info *info, map->list.mask = *mask; map->list.valid = 1; map->def.type = entry->def.type; + if (calipso) + map->def.calipso = calipso; ret_val = netlbl_af6list_add(&map->list, &addrmap->list6); if (ret_val != 0) { @@ -255,6 +276,9 @@ add_free_addrmap: kfree(addrmap); add_doi_put_def: cipso_v4_doi_putdef(cipsov4); +#if IS_ENABLED(CONFIG_IPV6) + calipso_doi_putdef(calipso); +#endif add_free_domain: kfree(entry->domain); add_free_entry: @@ -357,6 +381,15 @@ static int netlbl_mgmt_listentry(struct sk_buff *skb, if (ret_val != 0) return ret_val; + switch (map6->def.type) { + case NETLBL_NLTYPE_CALIPSO: + ret_val = nla_put_u32(skb, NLBL_MGMT_A_CLPDOI, + map6->def.calipso->doi); + if (ret_val != 0) + return ret_val; + break; + } + nla_nest_end(skb, nla_b); } #endif /* IPv6 */ @@ -364,15 +397,25 @@ static int netlbl_mgmt_listentry(struct sk_buff *skb, nla_nest_end(skb, nla_a); break; case NETLBL_NLTYPE_UNLABELED: - ret_val = nla_put_u32(skb,NLBL_MGMT_A_PROTOCOL,entry->def.type); + ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, + entry->def.type); break; case NETLBL_NLTYPE_CIPSOV4: - ret_val = nla_put_u32(skb,NLBL_MGMT_A_PROTOCOL,entry->def.type); + ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, + entry->def.type); if (ret_val != 0) return ret_val; ret_val = nla_put_u32(skb, NLBL_MGMT_A_CV4DOI, entry->def.cipso->doi); break; + case NETLBL_NLTYPE_CALIPSO: + ret_val = nla_put_u32(skb, NLBL_MGMT_A_PROTOCOL, + entry->def.type); + if (ret_val != 0) + return ret_val; + ret_val = nla_put_u32(skb, NLBL_MGMT_A_CLPDOI, + entry->def.calipso->doi); + break; } return ret_val; diff --git a/net/netlabel/netlabel_mgmt.h b/net/netlabel/netlabel_mgmt.h index f368dc0..ea01e42 100644 --- a/net/netlabel/netlabel_mgmt.h +++ b/net/netlabel/netlabel_mgmt.h @@ -223,6 +223,9 @@ enum { NLBL_MGMT_A_FAMILY, /* (NLA_U16) * The address family */ + NLBL_MGMT_A_CLPDOI, + /* (NLA_U32) + * the CALIPSO DOI value */ __NLBL_MGMT_A_MAX, }; #define NLBL_MGMT_A_MAX (__NLBL_MGMT_A_MAX - 1) -- cgit v0.10.2 From d7cce01504a0ccb95b5007d846560cfccbc1947f Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:02:49 -0400 Subject: netlabel: Add support for removing a CALIPSO DOI. Remove a specified DOI through the NLBL_CALIPSO_C_REMOVE command. It requires the attribute: NLBL_CALIPSO_A_DOI. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 2653d3a..2c0513b 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -237,6 +237,7 @@ struct netlbl_calipso_ops { int (*doi_add)(struct calipso_doi *doi_def, struct netlbl_audit *audit_info); void (*doi_free)(struct calipso_doi *doi_def); + int (*doi_remove)(u32 doi, struct netlbl_audit *audit_info); struct calipso_doi *(*doi_getdef)(u32 doi); void (*doi_putdef)(struct calipso_doi *doi_def); int (*doi_walk)(u32 *skip_cnt, diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index fa17c7a..d7df7a4 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -163,6 +163,53 @@ static void calipso_doi_free_rcu(struct rcu_head *entry) } /** + * calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine + * @doi: the DOI value + * @audit_secid: the LSM secid to use in the audit message + * + * Description: + * Removes a DOI definition from the CALIPSO engine. The NetLabel routines will + * be called to release their own LSM domain mappings as well as our own + * domain list. Returns zero on success and negative values on failure. + * + */ +static int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info) +{ + int ret_val; + struct calipso_doi *doi_def; + struct audit_buffer *audit_buf; + + spin_lock(&calipso_doi_list_lock); + doi_def = calipso_doi_search(doi); + if (!doi_def) { + spin_unlock(&calipso_doi_list_lock); + ret_val = -ENOENT; + goto doi_remove_return; + } + if (!atomic_dec_and_test(&doi_def->refcount)) { + spin_unlock(&calipso_doi_list_lock); + ret_val = -EBUSY; + goto doi_remove_return; + } + list_del_rcu(&doi_def->list); + spin_unlock(&calipso_doi_list_lock); + + call_rcu(&doi_def->rcu, calipso_doi_free_rcu); + ret_val = 0; + +doi_remove_return: + audit_buf = netlbl_audit_start(AUDIT_MAC_CALIPSO_DEL, audit_info); + if (audit_buf) { + audit_log_format(audit_buf, + " calipso_doi=%u res=%u", + doi, ret_val == 0 ? 1 : 0); + audit_log_end(audit_buf); + } + + return ret_val; +} + +/** * calipso_doi_getdef - Returns a reference to a valid DOI definition * @doi: the DOI value * @@ -253,6 +300,7 @@ doi_walk_return: static const struct netlbl_calipso_ops ops = { .doi_add = calipso_doi_add, .doi_free = calipso_doi_free, + .doi_remove = calipso_doi_remove, .doi_getdef = calipso_doi_getdef, .doi_putdef = calipso_doi_putdef, .doi_walk = calipso_doi_walk, diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index 2141145..2857673 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c @@ -53,6 +53,12 @@ struct netlbl_calipso_doiwalk_arg { u32 seq; }; +/* Argument struct for netlbl_domhsh_walk() */ +struct netlbl_domhsh_walk_arg { + struct netlbl_audit *audit_info; + u32 doi; +}; + /* NetLabel Generic NETLINK CALIPSO family */ static struct genl_family netlbl_calipso_gnl_family = { .id = GENL_ID_GENERATE, @@ -257,6 +263,64 @@ static int netlbl_calipso_listall(struct sk_buff *skb, return skb->len; } +/** + * netlbl_calipso_remove_cb - netlbl_calipso_remove() callback for REMOVE + * @entry: LSM domain mapping entry + * @arg: the netlbl_domhsh_walk_arg structure + * + * Description: + * This function is intended for use by netlbl_calipso_remove() as the callback + * for the netlbl_domhsh_walk() function; it removes LSM domain map entries + * which are associated with the CALIPSO DOI specified in @arg. Returns zero on + * success, negative values on failure. + * + */ +static int netlbl_calipso_remove_cb(struct netlbl_dom_map *entry, void *arg) +{ + struct netlbl_domhsh_walk_arg *cb_arg = arg; + + if (entry->def.type == NETLBL_NLTYPE_CALIPSO && + entry->def.calipso->doi == cb_arg->doi) + return netlbl_domhsh_remove_entry(entry, cb_arg->audit_info); + + return 0; +} + +/** + * netlbl_calipso_remove - Handle a REMOVE message + * @skb: the NETLINK buffer + * @info: the Generic NETLINK info block + * + * Description: + * Process a user generated REMOVE message and respond accordingly. Returns + * zero on success, negative values on failure. + * + */ +static int netlbl_calipso_remove(struct sk_buff *skb, struct genl_info *info) +{ + int ret_val = -EINVAL; + struct netlbl_domhsh_walk_arg cb_arg; + struct netlbl_audit audit_info; + u32 skip_bkt = 0; + u32 skip_chain = 0; + + if (!info->attrs[NLBL_CALIPSO_A_DOI]) + return -EINVAL; + + netlbl_netlink_auditinfo(skb, &audit_info); + cb_arg.doi = nla_get_u32(info->attrs[NLBL_CALIPSO_A_DOI]); + cb_arg.audit_info = &audit_info; + ret_val = netlbl_domhsh_walk(&skip_bkt, &skip_chain, + netlbl_calipso_remove_cb, &cb_arg); + if (ret_val == 0 || ret_val == -ENOENT) { + ret_val = calipso_doi_remove(cb_arg.doi, &audit_info); + if (ret_val == 0) + atomic_dec(&netlabel_mgmt_protocount); + } + + return ret_val; +} + /* NetLabel Generic NETLINK Command Definitions */ @@ -269,6 +333,13 @@ static const struct genl_ops netlbl_calipso_ops[] = { .dumpit = NULL, }, { + .cmd = NLBL_CALIPSO_C_REMOVE, + .flags = GENL_ADMIN_PERM, + .policy = calipso_genl_policy, + .doit = netlbl_calipso_remove, + .dumpit = NULL, + }, + { .cmd = NLBL_CALIPSO_C_LIST, .flags = 0, .policy = calipso_genl_policy, @@ -363,6 +434,27 @@ void calipso_doi_free(struct calipso_doi *doi_def) } /** + * calipso_doi_remove - Remove an existing DOI from the CALIPSO protocol engine + * @doi: the DOI value + * @audit_secid: the LSM secid to use in the audit message + * + * Description: + * Removes a DOI definition from the CALIPSO engine. The NetLabel routines will + * be called to release their own LSM domain mappings as well as our own + * domain list. Returns zero on success and negative values on failure. + * + */ +int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info) +{ + int ret_val = -ENOMSG; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->doi_remove(doi, audit_info); + return ret_val; +} + +/** * calipso_doi_getdef - Returns a reference to a valid DOI definition * @doi: the DOI value * diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h index 1f24174..ed78554 100644 --- a/net/netlabel/netlabel_calipso.h +++ b/net/netlabel/netlabel_calipso.h @@ -46,6 +46,14 @@ * * If using CALIPSO_MAP_PASS no additional attributes are required. * + * o REMOVE: + * Sent by an application to remove a specific DOI mapping table from the + * CALIPSO system. + * + * Required attributes: + * + * NLBL_CALIPSO_A_DOI + * * o LIST: * Sent by an application to list the details of a DOI definition. On * success the kernel should send a response using the following format. @@ -114,6 +122,7 @@ static inline int netlbl_calipso_genl_init(void) int calipso_doi_add(struct calipso_doi *doi_def, struct netlbl_audit *audit_info); void calipso_doi_free(struct calipso_doi *doi_def); +int calipso_doi_remove(u32 doi, struct netlbl_audit *audit_info); struct calipso_doi *calipso_doi_getdef(u32 doi); void calipso_doi_putdef(struct calipso_doi *doi_def); int calipso_doi_walk(u32 *skip_cnt, -- cgit v0.10.2 From e67ae213c72f72be50561c060ae17e92426651da Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:02:50 -0400 Subject: ipv6: Add ipv6_renew_options_kern() that accepts a kernel mem pointer. The functionality is equivalent to ipv6_renew_options() except that the newopt pointer is in kernel, not user, memory The kernel memory implementation will be used by the CALIPSO network labelling engine, which needs to be able to set IPv6 hop-by-hop options. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/ipv6.h b/include/net/ipv6.h index d0aeb97..887313d 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -308,6 +308,12 @@ struct ipv6_txoptions *ipv6_renew_options(struct sock *sk, int newtype, struct ipv6_opt_hdr __user *newopt, int newoptlen); +struct ipv6_txoptions * +ipv6_renew_options_kern(struct sock *sk, + struct ipv6_txoptions *opt, + int newtype, + struct ipv6_opt_hdr *newopt, + int newoptlen); struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, struct ipv6_txoptions *opt); diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index ea7c4d6..d5fd3e7 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -758,6 +758,27 @@ static int ipv6_renew_option(void *ohdr, return 0; } +/** + * ipv6_renew_options - replace a specific ext hdr with a new one. + * + * @sk: sock from which to allocate memory + * @opt: original options + * @newtype: option type to replace in @opt + * @newopt: new option of type @newtype to replace (user-mem) + * @newoptlen: length of @newopt + * + * Returns a new set of options which is a copy of @opt with the + * option type @newtype replaced with @newopt. + * + * @opt may be NULL, in which case a new set of options is returned + * containing just @newopt. + * + * @newopt may be NULL, in which case the specified option type is + * not copied into the new set of options. + * + * The new set of options is allocated from the socket option memory + * buffer of @sk. + */ struct ipv6_txoptions * ipv6_renew_options(struct sock *sk, struct ipv6_txoptions *opt, int newtype, @@ -830,6 +851,34 @@ out: return ERR_PTR(err); } +/** + * ipv6_renew_options_kern - replace a specific ext hdr with a new one. + * + * @sk: sock from which to allocate memory + * @opt: original options + * @newtype: option type to replace in @opt + * @newopt: new option of type @newtype to replace (kernel-mem) + * @newoptlen: length of @newopt + * + * See ipv6_renew_options(). The difference is that @newopt is + * kernel memory, rather than user memory. + */ +struct ipv6_txoptions * +ipv6_renew_options_kern(struct sock *sk, struct ipv6_txoptions *opt, + int newtype, struct ipv6_opt_hdr *newopt, + int newoptlen) +{ + struct ipv6_txoptions *ret_val; + const mm_segment_t old_fs = get_fs(); + + set_fs(KERNEL_DS); + ret_val = ipv6_renew_options(sk, opt, newtype, + (struct ipv6_opt_hdr __user *)newopt, + newoptlen); + set_fs(old_fs); + return ret_val; +} + struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, struct ipv6_txoptions *opt) { -- cgit v0.10.2 From 3faa8f982f958961fda68b8d63e682fe77a032d4 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:02:51 -0400 Subject: netlabel: Move bitmap manipulation functions to the NetLabel core. This is to allow the CALIPSO labelling engine to use these. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 2c0513b..9fc2cab 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -434,6 +434,12 @@ int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, unsigned long bitmap, gfp_t flags); +/* Bitmap functions + */ +int netlbl_bitmap_walk(const unsigned char *bitmap, u32 bitmap_len, + u32 offset, u8 state); +void netlbl_bitmap_setbit(unsigned char *bitmap, u32 bit, u8 state); + /* * LSM protocol operations (NetLabel LSM/kernel API) */ diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index bdb2a07..d710d4e 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -135,76 +135,6 @@ int cipso_v4_rbm_strictvalid = 1; */ /** - * cipso_v4_bitmap_walk - Walk a bitmap looking for a bit - * @bitmap: the bitmap - * @bitmap_len: length in bits - * @offset: starting offset - * @state: if non-zero, look for a set (1) bit else look for a cleared (0) bit - * - * Description: - * Starting at @offset, walk the bitmap from left to right until either the - * desired bit is found or we reach the end. Return the bit offset, -1 if - * not found, or -2 if error. - */ -static int cipso_v4_bitmap_walk(const unsigned char *bitmap, - u32 bitmap_len, - u32 offset, - u8 state) -{ - u32 bit_spot; - u32 byte_offset; - unsigned char bitmask; - unsigned char byte; - - /* gcc always rounds to zero when doing integer division */ - byte_offset = offset / 8; - byte = bitmap[byte_offset]; - bit_spot = offset; - bitmask = 0x80 >> (offset % 8); - - while (bit_spot < bitmap_len) { - if ((state && (byte & bitmask) == bitmask) || - (state == 0 && (byte & bitmask) == 0)) - return bit_spot; - - bit_spot++; - bitmask >>= 1; - if (bitmask == 0) { - byte = bitmap[++byte_offset]; - bitmask = 0x80; - } - } - - return -1; -} - -/** - * cipso_v4_bitmap_setbit - Sets a single bit in a bitmap - * @bitmap: the bitmap - * @bit: the bit - * @state: if non-zero, set the bit (1) else clear the bit (0) - * - * Description: - * Set a single bit in the bitmask. Returns zero on success, negative values - * on error. - */ -static void cipso_v4_bitmap_setbit(unsigned char *bitmap, - u32 bit, - u8 state) -{ - u32 byte_spot; - u8 bitmask; - - /* gcc always rounds to zero when doing integer division */ - byte_spot = bit / 8; - bitmask = 0x80 >> (bit % 8); - if (state) - bitmap[byte_spot] |= bitmask; - else - bitmap[byte_spot] &= ~bitmask; -} - -/** * cipso_v4_cache_entry_free - Frees a cache entry * @entry: the entry to free * @@ -840,10 +770,10 @@ static int cipso_v4_map_cat_rbm_valid(const struct cipso_v4_doi *doi_def, cipso_cat_size = doi_def->map.std->cat.cipso_size; cipso_array = doi_def->map.std->cat.cipso; for (;;) { - cat = cipso_v4_bitmap_walk(bitmap, - bitmap_len_bits, - cat + 1, - 1); + cat = netlbl_bitmap_walk(bitmap, + bitmap_len_bits, + cat + 1, + 1); if (cat < 0) break; if (cat >= cipso_cat_size || @@ -909,7 +839,7 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, } if (net_spot >= net_clen_bits) return -ENOSPC; - cipso_v4_bitmap_setbit(net_cat, net_spot, 1); + netlbl_bitmap_setbit(net_cat, net_spot, 1); if (net_spot > net_spot_max) net_spot_max = net_spot; @@ -951,10 +881,10 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, } for (;;) { - net_spot = cipso_v4_bitmap_walk(net_cat, - net_clen_bits, - net_spot + 1, - 1); + net_spot = netlbl_bitmap_walk(net_cat, + net_clen_bits, + net_spot + 1, + 1); if (net_spot < 0) { if (net_spot == -2) return -EFAULT; diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 8d2e483..54f13a3 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -728,6 +728,76 @@ int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, return 0; } +/* Bitmap functions + */ + +/** + * netlbl_bitmap_walk - Walk a bitmap looking for a bit + * @bitmap: the bitmap + * @bitmap_len: length in bits + * @offset: starting offset + * @state: if non-zero, look for a set (1) bit else look for a cleared (0) bit + * + * Description: + * Starting at @offset, walk the bitmap from left to right until either the + * desired bit is found or we reach the end. Return the bit offset, -1 if + * not found, or -2 if error. + */ +int netlbl_bitmap_walk(const unsigned char *bitmap, u32 bitmap_len, + u32 offset, u8 state) +{ + u32 bit_spot; + u32 byte_offset; + unsigned char bitmask; + unsigned char byte; + + byte_offset = offset / 8; + byte = bitmap[byte_offset]; + bit_spot = offset; + bitmask = 0x80 >> (offset % 8); + + while (bit_spot < bitmap_len) { + if ((state && (byte & bitmask) == bitmask) || + (state == 0 && (byte & bitmask) == 0)) + return bit_spot; + + bit_spot++; + bitmask >>= 1; + if (bitmask == 0) { + byte = bitmap[++byte_offset]; + bitmask = 0x80; + } + } + + return -1; +} +EXPORT_SYMBOL(netlbl_bitmap_walk); + +/** + * netlbl_bitmap_setbit - Sets a single bit in a bitmap + * @bitmap: the bitmap + * @bit: the bit + * @state: if non-zero, set the bit (1) else clear the bit (0) + * + * Description: + * Set a single bit in the bitmask. Returns zero on success, negative values + * on error. + */ +void netlbl_bitmap_setbit(unsigned char *bitmap, u32 bit, u8 state) +{ + u32 byte_spot; + u8 bitmask; + + /* gcc always rounds to zero when doing integer division */ + byte_spot = bit / 8; + bitmask = 0x80 >> (bit % 8); + if (state) + bitmap[byte_spot] |= bitmask; + else + bitmap[byte_spot] &= ~bitmask; +} +EXPORT_SYMBOL(netlbl_bitmap_setbit); + /* * LSM Functions */ -- cgit v0.10.2 From ceba1832b1b2da0149c51de62a847c00bca1677a Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:02:51 -0400 Subject: calipso: Set the calipso socket label to match the secattr. CALIPSO is a hop-by-hop IPv6 option. A lot of this patch is based on the equivalent CISPO code. The main difference is due to manipulating the options in the hop-by-hop header. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 887313d..4e279a8 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -319,6 +319,8 @@ struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb, const struct inet6_skb_parm *opt); +struct ipv6_txoptions *ipv6_update_options(struct sock *sk, + struct ipv6_txoptions *opt); static inline bool ipv6_accept_ra(struct inet6_dev *idev) { diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 9fc2cab..918a604 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -226,6 +226,9 @@ struct netlbl_lsm_secattr { * @doi_getdef: returns a reference to a DOI * @doi_putdef: releases a reference of a DOI * @doi_walk: enumerate the DOI list + * @sock_getattr: retrieve the socket's attr + * @sock_setattr: set the socket's attr + * @sock_delattr: remove the socket's attr * * Description: * This structure is filled out by the CALIPSO engine and passed @@ -243,6 +246,12 @@ struct netlbl_calipso_ops { int (*doi_walk)(u32 *skip_cnt, int (*callback)(struct calipso_doi *doi_def, void *arg), void *cb_arg); + int (*sock_getattr)(struct sock *sk, + struct netlbl_lsm_secattr *secattr); + int (*sock_setattr)(struct sock *sk, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr); + void (*sock_delattr)(struct sock *sk); }; /* diff --git a/include/uapi/linux/in6.h b/include/uapi/linux/in6.h index 318a482..b39ea4f 100644 --- a/include/uapi/linux/in6.h +++ b/include/uapi/linux/in6.h @@ -143,6 +143,7 @@ struct in6_flowlabel_req { #define IPV6_TLV_PAD1 0 #define IPV6_TLV_PADN 1 #define IPV6_TLV_ROUTERALERT 5 +#define IPV6_TLV_CALIPSO 7 /* RFC 5570 */ #define IPV6_TLV_JUMBO 194 #define IPV6_TLV_HAO 201 /* home address option */ diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index d7df7a4..959db52 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -44,6 +44,24 @@ #include #include #include +#include + +/* Maximium size of the calipso option including + * the two-byte TLV header. + */ +#define CALIPSO_OPT_LEN_MAX (2 + 252) + +/* Size of the minimum calipso option including + * the two-byte TLV header. + */ +#define CALIPSO_HDR_LEN (2 + 8) + +/* Maximium size of the calipso option including + * the two-byte TLV header and upto 3 bytes of + * leading pad and 7 bytes of trailing pad. + */ +#define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7) + /* List of available DOI definitions */ static DEFINE_SPINLOCK(calipso_doi_list_lock); @@ -297,6 +315,580 @@ doi_walk_return: return ret_val; } +/** + * calipso_map_cat_hton - Perform a category mapping from host to network + * @doi_def: the DOI definition + * @secattr: the security attributes + * @net_cat: the zero'd out category bitmap in network/CALIPSO format + * @net_cat_len: the length of the CALIPSO bitmap in bytes + * + * Description: + * Perform a label mapping to translate a local MLS category bitmap to the + * correct CALIPSO bitmap using the given DOI definition. Returns the minimum + * size in bytes of the network bitmap on success, negative values otherwise. + * + */ +static int calipso_map_cat_hton(const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr, + unsigned char *net_cat, + u32 net_cat_len) +{ + int spot = -1; + u32 net_spot_max = 0; + u32 net_clen_bits = net_cat_len * 8; + + for (;;) { + spot = netlbl_catmap_walk(secattr->attr.mls.cat, + spot + 1); + if (spot < 0) + break; + if (spot >= net_clen_bits) + return -ENOSPC; + netlbl_bitmap_setbit(net_cat, spot, 1); + + if (spot > net_spot_max) + net_spot_max = spot; + } + + return (net_spot_max / 32 + 1) * 4; +} + +/** + * calipso_map_cat_ntoh - Perform a category mapping from network to host + * @doi_def: the DOI definition + * @net_cat: the category bitmap in network/CALIPSO format + * @net_cat_len: the length of the CALIPSO bitmap in bytes + * @secattr: the security attributes + * + * Description: + * Perform a label mapping to translate a CALIPSO bitmap to the correct local + * MLS category bitmap using the given DOI definition. Returns zero on + * success, negative values on failure. + * + */ +static int calipso_map_cat_ntoh(const struct calipso_doi *doi_def, + const unsigned char *net_cat, + u32 net_cat_len, + struct netlbl_lsm_secattr *secattr) +{ + int ret_val; + int spot = -1; + u32 net_clen_bits = net_cat_len * 8; + + for (;;) { + spot = netlbl_bitmap_walk(net_cat, + net_clen_bits, + spot + 1, + 1); + if (spot < 0) { + if (spot == -2) + return -EFAULT; + return 0; + } + + ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, + spot, + GFP_ATOMIC); + if (ret_val != 0) + return ret_val; + } + + return -EINVAL; +} + +/** + * calipso_pad_write - Writes pad bytes in TLV format + * @buf: the buffer + * @offset: offset from start of buffer to write padding + * @count: number of pad bytes to write + * + * Description: + * Write @count bytes of TLV padding into @buffer starting at offset @offset. + * @count should be less than 8 - see RFC 4942. + * + */ +static int calipso_pad_write(unsigned char *buf, unsigned int offset, + unsigned int count) +{ + if (WARN_ON_ONCE(count >= 8)) + return -EINVAL; + + switch (count) { + case 0: + break; + case 1: + buf[offset] = IPV6_TLV_PAD1; + break; + default: + buf[offset] = IPV6_TLV_PADN; + buf[offset + 1] = count - 2; + if (count > 2) + memset(buf + offset + 2, 0, count - 2); + break; + } + return 0; +} + +/** + * calipso_genopt - Generate a CALIPSO option + * @buf: the option buffer + * @start: offset from which to write + * @buf_len: the size of opt_buf + * @doi_def: the CALIPSO DOI to use + * @secattr: the security attributes + * + * Description: + * Generate a CALIPSO option using the DOI definition and security attributes + * passed to the function. This also generates upto three bytes of leading + * padding that ensures that the option is 4n + 2 aligned. It returns the + * number of bytes written (including any initial padding). + */ +static int calipso_genopt(unsigned char *buf, u32 start, u32 buf_len, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val; + u32 len, pad; + u16 crc; + static const unsigned char padding[4] = {2, 1, 0, 3}; + unsigned char *calipso; + + /* CALIPSO has 4n + 2 alignment */ + pad = padding[start & 3]; + if (buf_len <= start + pad + CALIPSO_HDR_LEN) + return -ENOSPC; + + if ((secattr->flags & NETLBL_SECATTR_MLS_LVL) == 0) + return -EPERM; + + len = CALIPSO_HDR_LEN; + + if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { + ret_val = calipso_map_cat_hton(doi_def, + secattr, + buf + start + pad + len, + buf_len - start - pad - len); + if (ret_val < 0) + return ret_val; + len += ret_val; + } + + calipso_pad_write(buf, start, pad); + calipso = buf + start + pad; + + calipso[0] = IPV6_TLV_CALIPSO; + calipso[1] = len - 2; + *(__be32 *)(calipso + 2) = htonl(doi_def->doi); + calipso[6] = (len - CALIPSO_HDR_LEN) / 4; + calipso[7] = secattr->attr.mls.lvl, + crc = ~crc_ccitt(0xffff, calipso, len); + calipso[8] = crc & 0xff; + calipso[9] = (crc >> 8) & 0xff; + return pad + len; +} + +/* Hop-by-hop hdr helper functions + */ + +/** + * calipso_opt_update - Replaces socket's hop options with a new set + * @sk: the socket + * @hop: new hop options + * + * Description: + * Replaces @sk's hop options with @hop. @hop may be NULL to leave + * the socket with no hop options. + * + */ +static int calipso_opt_update(struct sock *sk, struct ipv6_opt_hdr *hop) +{ + struct ipv6_txoptions *old = txopt_get(inet6_sk(sk)), *txopts; + + txopts = ipv6_renew_options_kern(sk, old, IPV6_HOPOPTS, + hop, hop ? ipv6_optlen(hop) : 0); + txopt_put(old); + if (IS_ERR(txopts)) + return PTR_ERR(txopts); + + txopts = ipv6_update_options(sk, txopts); + if (txopts) { + atomic_sub(txopts->tot_len, &sk->sk_omem_alloc); + txopt_put(txopts); + } + + return 0; +} + +/** + * calipso_tlv_len - Returns the length of the TLV + * @opt: the option header + * @offset: offset of the TLV within the header + * + * Description: + * Returns the length of the TLV option at offset @offset within + * the option header @opt. Checks that the entire TLV fits inside + * the option header, returns a negative value if this is not the case. + */ +static int calipso_tlv_len(struct ipv6_opt_hdr *opt, unsigned int offset) +{ + unsigned char *tlv = (unsigned char *)opt; + unsigned int opt_len = ipv6_optlen(opt), tlv_len; + + if (offset < sizeof(*opt) || offset >= opt_len) + return -EINVAL; + if (tlv[offset] == IPV6_TLV_PAD1) + return 1; + if (offset + 1 >= opt_len) + return -EINVAL; + tlv_len = tlv[offset + 1] + 2; + if (offset + tlv_len > opt_len) + return -EINVAL; + return tlv_len; +} + +/** + * calipso_opt_find - Finds the CALIPSO option in an IPv6 hop options header + * @hop: the hop options header + * @start: on return holds the offset of any leading padding + * @end: on return holds the offset of the first non-pad TLV after CALIPSO + * + * Description: + * Finds the space occupied by a CALIPSO option (including any leading and + * trailing padding). + * + * If a CALIPSO option exists set @start and @end to the + * offsets within @hop of the start of padding before the first + * CALIPSO option and the end of padding after the first CALIPSO + * option. In this case the function returns 0. + * + * In the absence of a CALIPSO option, @start and @end will be + * set to the start and end of any trailing padding in the header. + * This is useful when appending a new option, as the caller may want + * to overwrite some of this padding. In this case the function will + * return -ENOENT. + */ +static int calipso_opt_find(struct ipv6_opt_hdr *hop, unsigned int *start, + unsigned int *end) +{ + int ret_val = -ENOENT, tlv_len; + unsigned int opt_len, offset, offset_s = 0, offset_e = 0; + unsigned char *opt = (unsigned char *)hop; + + opt_len = ipv6_optlen(hop); + offset = sizeof(*hop); + + while (offset < opt_len) { + tlv_len = calipso_tlv_len(hop, offset); + if (tlv_len < 0) + return tlv_len; + + switch (opt[offset]) { + case IPV6_TLV_PAD1: + case IPV6_TLV_PADN: + if (offset_e) + offset_e = offset; + break; + case IPV6_TLV_CALIPSO: + ret_val = 0; + offset_e = offset; + break; + default: + if (offset_e == 0) + offset_s = offset; + else + goto out; + } + offset += tlv_len; + } + +out: + if (offset_s) + *start = offset_s + calipso_tlv_len(hop, offset_s); + else + *start = sizeof(*hop); + if (offset_e) + *end = offset_e + calipso_tlv_len(hop, offset_e); + else + *end = opt_len; + + return ret_val; +} + +/** + * calipso_opt_insert - Inserts a CALIPSO option into an IPv6 hop opt hdr + * @hop: the original hop options header + * @doi_def: the CALIPSO DOI to use + * @secattr: the specific security attributes of the socket + * + * Description: + * Creates a new hop options header based on @hop with a + * CALIPSO option added to it. If @hop already contains a CALIPSO + * option this is overwritten, otherwise the new option is appended + * after any existing options. If @hop is NULL then the new header + * will contain just the CALIPSO option and any needed padding. + * + */ +static struct ipv6_opt_hdr * +calipso_opt_insert(struct ipv6_opt_hdr *hop, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + unsigned int start, end, buf_len, pad, hop_len; + struct ipv6_opt_hdr *new; + int ret_val; + + if (hop) { + hop_len = ipv6_optlen(hop); + ret_val = calipso_opt_find(hop, &start, &end); + if (ret_val && ret_val != -ENOENT) + return ERR_PTR(ret_val); + } else { + hop_len = 0; + start = sizeof(*hop); + end = 0; + } + + buf_len = hop_len + start - end + CALIPSO_OPT_LEN_MAX_WITH_PAD; + new = kzalloc(buf_len, GFP_ATOMIC); + if (!new) + return ERR_PTR(-ENOMEM); + + if (start > sizeof(*hop)) + memcpy(new, hop, start); + ret_val = calipso_genopt((unsigned char *)new, start, buf_len, doi_def, + secattr); + if (ret_val < 0) + return ERR_PTR(ret_val); + + buf_len = start + ret_val; + /* At this point buf_len aligns to 4n, so (buf_len & 4) pads to 8n */ + pad = ((buf_len & 4) + (end & 7)) & 7; + calipso_pad_write((unsigned char *)new, buf_len, pad); + buf_len += pad; + + if (end != hop_len) { + memcpy((char *)new + buf_len, (char *)hop + end, hop_len - end); + buf_len += hop_len - end; + } + new->nexthdr = 0; + new->hdrlen = buf_len / 8 - 1; + + return new; +} + +/** + * calipso_opt_del - Removes the CALIPSO option from an option header + * @hop: the original header + * @new: the new header + * + * Description: + * Creates a new header based on @hop without any CALIPSO option. If @hop + * doesn't contain a CALIPSO option it returns -ENOENT. If @hop contains + * no other non-padding options, it returns zero with @new set to NULL. + * Otherwise it returns zero, creates a new header without the CALIPSO + * option (and removing as much padding as possible) and returns with + * @new set to that header. + * + */ +static int calipso_opt_del(struct ipv6_opt_hdr *hop, + struct ipv6_opt_hdr **new) +{ + int ret_val; + unsigned int start, end, delta, pad, hop_len; + + ret_val = calipso_opt_find(hop, &start, &end); + if (ret_val) + return ret_val; + + hop_len = ipv6_optlen(hop); + if (start == sizeof(*hop) && end == hop_len) { + /* There's no other option in the header so return NULL */ + *new = NULL; + return 0; + } + + delta = (end - start) & ~7; + *new = kzalloc(hop_len - delta, GFP_ATOMIC); + if (!*new) + return -ENOMEM; + + memcpy(*new, hop, start); + (*new)->hdrlen -= delta / 8; + pad = (end - start) & 7; + calipso_pad_write((unsigned char *)*new, start, pad); + if (end != hop_len) + memcpy((char *)*new + start + pad, (char *)hop + end, + hop_len - end); + + return 0; +} + +/** + * calipso_opt_getattr - Get the security attributes from a memory block + * @calipso: the CALIPSO option + * @secattr: the security attributes + * + * Description: + * Inspect @calipso and return the security attributes in @secattr. + * Returns zero on success and negative values on failure. + * + */ +static int calipso_opt_getattr(const unsigned char *calipso, + struct netlbl_lsm_secattr *secattr) +{ + int ret_val = -ENOMSG; + u32 doi, len = calipso[1], cat_len = calipso[6] * 4; + struct calipso_doi *doi_def; + + if (cat_len + 8 > len) + return -EINVAL; + + doi = get_unaligned_be32(calipso + 2); + rcu_read_lock(); + doi_def = calipso_doi_search(doi); + if (!doi_def) + goto getattr_return; + + secattr->attr.mls.lvl = calipso[7]; + secattr->flags |= NETLBL_SECATTR_MLS_LVL; + + if (cat_len) { + ret_val = calipso_map_cat_ntoh(doi_def, + calipso + 10, + cat_len, + secattr); + if (ret_val != 0) { + netlbl_catmap_free(secattr->attr.mls.cat); + goto getattr_return; + } + + secattr->flags |= NETLBL_SECATTR_MLS_CAT; + } + + secattr->type = NETLBL_NLTYPE_CALIPSO; + +getattr_return: + rcu_read_unlock(); + return ret_val; +} + +/* sock functions. + */ + +/** + * calipso_sock_getattr - Get the security attributes from a sock + * @sk: the sock + * @secattr: the security attributes + * + * Description: + * Query @sk to see if there is a CALIPSO option attached to the sock and if + * there is return the CALIPSO security attributes in @secattr. This function + * requires that @sk be locked, or privately held, but it does not do any + * locking itself. Returns zero on success and negative values on failure. + * + */ +static int calipso_sock_getattr(struct sock *sk, + struct netlbl_lsm_secattr *secattr) +{ + struct ipv6_opt_hdr *hop; + int opt_len, len, ret_val = -ENOMSG, offset; + unsigned char *opt; + struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); + + if (!txopts || !txopts->hopopt) + goto done; + + hop = txopts->hopopt; + opt = (unsigned char *)hop; + opt_len = ipv6_optlen(hop); + offset = sizeof(*hop); + while (offset < opt_len) { + len = calipso_tlv_len(hop, offset); + if (len < 0) { + ret_val = len; + goto done; + } + switch (opt[offset]) { + case IPV6_TLV_CALIPSO: + if (len < CALIPSO_HDR_LEN) + ret_val = -EINVAL; + else + ret_val = calipso_opt_getattr(&opt[offset], + secattr); + goto done; + default: + offset += len; + break; + } + } +done: + txopt_put(txopts); + return ret_val; +} + +/** + * calipso_sock_setattr - Add a CALIPSO option to a socket + * @sk: the socket + * @doi_def: the CALIPSO DOI to use + * @secattr: the specific security attributes of the socket + * + * Description: + * Set the CALIPSO option on the given socket using the DOI definition and + * security attributes passed to the function. This function requires + * exclusive access to @sk, which means it either needs to be in the + * process of being created or locked. Returns zero on success and negative + * values on failure. + * + */ +static int calipso_sock_setattr(struct sock *sk, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val; + struct ipv6_opt_hdr *old, *new; + struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); + + old = NULL; + if (txopts) + old = txopts->hopopt; + + new = calipso_opt_insert(old, doi_def, secattr); + txopt_put(txopts); + if (IS_ERR(new)) + return PTR_ERR(new); + + ret_val = calipso_opt_update(sk, new); + + kfree(new); + return ret_val; +} + +/** + * calipso_sock_delattr - Delete the CALIPSO option from a socket + * @sk: the socket + * + * Description: + * Removes the CALIPSO option from a socket, if present. + * + */ +static void calipso_sock_delattr(struct sock *sk) +{ + struct ipv6_opt_hdr *new_hop; + struct ipv6_txoptions *txopts = txopt_get(inet6_sk(sk)); + + if (!txopts || !txopts->hopopt) + goto done; + + if (calipso_opt_del(txopts->hopopt, &new_hop)) + goto done; + + calipso_opt_update(sk, new_hop); + kfree(new_hop); + +done: + txopt_put(txopts); +} + static const struct netlbl_calipso_ops ops = { .doi_add = calipso_doi_add, .doi_free = calipso_doi_free, @@ -304,6 +896,9 @@ static const struct netlbl_calipso_ops ops = { .doi_getdef = calipso_doi_getdef, .doi_putdef = calipso_doi_putdef, .doi_walk = calipso_doi_walk, + .sock_getattr = calipso_sock_getattr, + .sock_setattr = calipso_sock_setattr, + .sock_delattr = calipso_sock_delattr, }; /** diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 4449ad1..8a80d59 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -98,7 +98,6 @@ int ip6_ra_control(struct sock *sk, int sel) return 0; } -static struct ipv6_txoptions *ipv6_update_options(struct sock *sk, struct ipv6_txoptions *opt) { diff --git a/net/netlabel/Kconfig b/net/netlabel/Kconfig index 56958c8..d9eaa30 100644 --- a/net/netlabel/Kconfig +++ b/net/netlabel/Kconfig @@ -5,6 +5,7 @@ config NETLABEL bool "NetLabel subsystem support" depends on SECURITY + select CRC_CCITT if IPV6 default n ---help--- NetLabel provides support for explicit network packet labeling diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index 2857673..6f9c658 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c @@ -514,3 +514,67 @@ int calipso_doi_walk(u32 *skip_cnt, ret_val = ops->doi_walk(skip_cnt, callback, cb_arg); return ret_val; } + +/** + * calipso_sock_getattr - Get the security attributes from a sock + * @sk: the sock + * @secattr: the security attributes + * + * Description: + * Query @sk to see if there is a CALIPSO option attached to the sock and if + * there is return the CALIPSO security attributes in @secattr. This function + * requires that @sk be locked, or privately held, but it does not do any + * locking itself. Returns zero on success and negative values on failure. + * + */ +int calipso_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr) +{ + int ret_val = -ENOMSG; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->sock_getattr(sk, secattr); + return ret_val; +} + +/** + * calipso_sock_setattr - Add a CALIPSO option to a socket + * @sk: the socket + * @doi_def: the CALIPSO DOI to use + * @secattr: the specific security attributes of the socket + * + * Description: + * Set the CALIPSO option on the given socket using the DOI definition and + * security attributes passed to the function. This function requires + * exclusive access to @sk, which means it either needs to be in the + * process of being created or locked. Returns zero on success and negative + * values on failure. + * + */ +int calipso_sock_setattr(struct sock *sk, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val = -ENOMSG; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->sock_setattr(sk, doi_def, secattr); + return ret_val; +} + +/** + * calipso_sock_delattr - Delete the CALIPSO option from a socket + * @sk: the socket + * + * Description: + * Removes the CALIPSO option from a socket, if present. + * + */ +void calipso_sock_delattr(struct sock *sk) +{ + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ops->sock_delattr(sk); +} diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h index ed78554..49bc116 100644 --- a/net/netlabel/netlabel_calipso.h +++ b/net/netlabel/netlabel_calipso.h @@ -128,5 +128,10 @@ void calipso_doi_putdef(struct calipso_doi *doi_def); int calipso_doi_walk(u32 *skip_cnt, int (*callback)(struct calipso_doi *doi_def, void *arg), void *cb_arg); +int calipso_sock_getattr(struct sock *sk, struct netlbl_lsm_secattr *secattr); +int calipso_sock_setattr(struct sock *sk, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr); +void calipso_sock_delattr(struct sock *sk); #endif diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 54f13a3..00bab51 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -37,12 +37,14 @@ #include #include #include +#include #include #include #include "netlabel_domainhash.h" #include "netlabel_unlabeled.h" #include "netlabel_cipso_v4.h" +#include "netlabel_calipso.h" #include "netlabel_user.h" #include "netlabel_mgmt.h" #include "netlabel_addrlist.h" @@ -521,6 +523,7 @@ int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset) return -ENOENT; } +EXPORT_SYMBOL(netlbl_catmap_walk); /** * netlbl_catmap_walkrng - Find the end of a string of set bits @@ -656,6 +659,7 @@ int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, return 0; } +EXPORT_SYMBOL(netlbl_catmap_setbit); /** * netlbl_catmap_setrng - Set a range of bits in a LSM secattr catmap @@ -870,9 +874,21 @@ int netlbl_sock_setattr(struct sock *sk, break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - /* since we don't support any IPv6 labeling protocols right - * now we can optimize everything away until we do */ - ret_val = 0; + switch (dom_entry->def.type) { + case NETLBL_NLTYPE_ADDRSELECT: + ret_val = -EDESTADDRREQ; + break; + case NETLBL_NLTYPE_CALIPSO: + ret_val = calipso_sock_setattr(sk, + dom_entry->def.calipso, + secattr); + break; + case NETLBL_NLTYPE_UNLABELED: + ret_val = 0; + break; + default: + ret_val = -ENOENT; + } break; #endif /* IPv6 */ default: @@ -899,6 +915,11 @@ void netlbl_sock_delattr(struct sock *sk) case AF_INET: cipso_v4_sock_delattr(sk); break; +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + calipso_sock_delattr(sk); + break; +#endif /* IPv6 */ } } @@ -925,7 +946,7 @@ int netlbl_sock_getattr(struct sock *sk, break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - ret_val = -ENOMSG; + ret_val = calipso_sock_getattr(sk, secattr); break; #endif /* IPv6 */ default: @@ -953,6 +974,9 @@ int netlbl_conn_setattr(struct sock *sk, { int ret_val; struct sockaddr_in *addr4; +#if IS_ENABLED(CONFIG_IPV6) + struct sockaddr_in6 *addr6; +#endif struct netlbl_dommap_def *entry; rcu_read_lock(); @@ -973,7 +997,7 @@ int netlbl_conn_setattr(struct sock *sk, case NETLBL_NLTYPE_UNLABELED: /* just delete the protocols we support for right now * but we could remove other protocols if needed */ - cipso_v4_sock_delattr(sk); + netlbl_sock_delattr(sk); ret_val = 0; break; default: @@ -982,9 +1006,27 @@ int netlbl_conn_setattr(struct sock *sk, break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - /* since we don't support any IPv6 labeling protocols right - * now we can optimize everything away until we do */ - ret_val = 0; + addr6 = (struct sockaddr_in6 *)addr; + entry = netlbl_domhsh_getentry_af6(secattr->domain, + &addr6->sin6_addr); + if (entry == NULL) { + ret_val = -ENOENT; + goto conn_setattr_return; + } + switch (entry->type) { + case NETLBL_NLTYPE_CALIPSO: + ret_val = calipso_sock_setattr(sk, + entry->calipso, secattr); + break; + case NETLBL_NLTYPE_UNLABELED: + /* just delete the protocols we support for right now + * but we could remove other protocols if needed */ + netlbl_sock_delattr(sk); + ret_val = 0; + break; + default: + ret_val = -ENOENT; + } break; #endif /* IPv6 */ default: diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 1f989a5..5470f32 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -333,7 +333,7 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family) struct sk_security_struct *sksec = sk->sk_security; struct netlbl_lsm_secattr *secattr; - if (family != PF_INET) + if (family != PF_INET && family != PF_INET6) return 0; secattr = selinux_netlbl_sock_genattr(sk); -- cgit v0.10.2 From 1f440c99d3207d684a3ac48d6e528af548b5c915 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:05:27 -0400 Subject: netlabel: Prevent setsockopt() from changing the hop-by-hop option. If a socket has a netlabel in place then don't let setsockopt() alter the socket's IPv6 hop-by-hop option. This is in the same spirit as the existing check for IPv4. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 5470f32..2477a75 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -410,6 +410,21 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, } /** + * selinux_netlbl_option - Is this a NetLabel option + * @level: the socket level or protocol + * @optname: the socket option name + * + * Description: + * Returns true if @level and @optname refer to a NetLabel option. + * Helper for selinux_netlbl_socket_setsockopt(). + */ +static inline int selinux_netlbl_option(int level, int optname) +{ + return (level == IPPROTO_IP && optname == IP_OPTIONS) || + (level == IPPROTO_IPV6 && optname == IPV6_HOPOPTS); +} + +/** * selinux_netlbl_socket_setsockopt - Do not allow users to remove a NetLabel * @sock: the socket * @level: the socket level or protocol @@ -431,7 +446,7 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, struct sk_security_struct *sksec = sk->sk_security; struct netlbl_lsm_secattr secattr; - if (level == IPPROTO_IP && optname == IP_OPTIONS && + if (selinux_netlbl_option(level, optname) && (sksec->nlbl_state == NLBL_LABELED || sksec->nlbl_state == NLBL_CONNLABELED)) { netlbl_secattr_init(&secattr); -- cgit v0.10.2 From 56ac42bc94b18d45b6c484edeac33be86bfb3efa Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:05:28 -0400 Subject: ipv6: Allow request socks to contain IPv6 options. If set, these will take precedence over the parent's options during both sending and child creation. If they're not set, the parent's options (if any) will be used. This is to allow the security_inet_conn_request() hook to modify the IPv6 options in just the same way that it already may do for IPv4. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 012b1f9..236a810 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -97,7 +97,12 @@ struct inet_request_sock { u32 ir_mark; union { struct ip_options_rcu *opt; - struct sk_buff *pktopts; +#if IS_ENABLED(CONFIG_IPV6) + struct { + struct ipv6_txoptions *ipv6_opt; + struct sk_buff *pktopts; + }; +#endif }; }; diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 4663a01..3381748 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -216,14 +216,17 @@ static int dccp_v6_send_response(const struct sock *sk, struct request_sock *req skb = dccp_make_response(sk, dst, req); if (skb != NULL) { struct dccp_hdr *dh = dccp_hdr(skb); + struct ipv6_txoptions *opt; dh->dccph_checksum = dccp_v6_csum_finish(skb, &ireq->ir_v6_loc_addr, &ireq->ir_v6_rmt_addr); fl6.daddr = ireq->ir_v6_rmt_addr; rcu_read_lock(); - err = ip6_xmit(sk, skb, &fl6, rcu_dereference(np->opt), - np->tclass); + opt = ireq->ipv6_opt; + if (!opt) + opt = rcu_dereference(np->opt); + err = ip6_xmit(sk, skb, &fl6, opt, np->tclass); rcu_read_unlock(); err = net_xmit_eval(err); } @@ -236,6 +239,7 @@ done: static void dccp_v6_reqsk_destructor(struct request_sock *req) { dccp_feat_list_purge(&dccp_rsk(req)->dreq_featneg); + kfree(inet_rsk(req)->ipv6_opt); kfree_skb(inet_rsk(req)->pktopts); } @@ -494,7 +498,9 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk, * Yes, keeping reference count would be much more clever, but we make * one more one thing there: reattach optmem to newsk. */ - opt = rcu_dereference(np->opt); + opt = ireq->ipv6_opt; + if (!opt) + opt = rcu_dereference(np->opt); if (opt) { opt = ipv6_dup_options(newsk, opt); RCU_INIT_POINTER(newnp->opt, opt); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index e6e65f7..071174c 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6146,6 +6146,9 @@ struct request_sock *inet_reqsk_alloc(const struct request_sock_ops *ops, kmemcheck_annotate_bitfield(ireq, flags); ireq->opt = NULL; +#if IS_ENABLED(CONFIG_IPV6) + ireq->pktopts = NULL; +#endif atomic64_set(&ireq->ir_cookie, 0); ireq->ireq_state = TCP_NEW_SYN_RECV; write_pnet(&ireq->ireq_net, sock_net(sk_listener)); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 711d209..18daddc 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -443,6 +443,7 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, { struct inet_request_sock *ireq = inet_rsk(req); struct ipv6_pinfo *np = inet6_sk(sk); + struct ipv6_txoptions *opt; struct flowi6 *fl6 = &fl->u.ip6; struct sk_buff *skb; int err = -ENOMEM; @@ -463,8 +464,10 @@ static int tcp_v6_send_synack(const struct sock *sk, struct dst_entry *dst, fl6->flowlabel = ip6_flowlabel(ipv6_hdr(ireq->pktopts)); rcu_read_lock(); - err = ip6_xmit(sk, skb, fl6, rcu_dereference(np->opt), - np->tclass); + opt = ireq->ipv6_opt; + if (!opt) + opt = rcu_dereference(np->opt); + err = ip6_xmit(sk, skb, fl6, opt, np->tclass); rcu_read_unlock(); err = net_xmit_eval(err); } @@ -476,6 +479,7 @@ done: static void tcp_v6_reqsk_destructor(struct request_sock *req) { + kfree(inet_rsk(req)->ipv6_opt); kfree_skb(inet_rsk(req)->pktopts); } @@ -1107,7 +1111,9 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff * but we make one more one thing there: reattach optmem to newsk. */ - opt = rcu_dereference(np->opt); + opt = ireq->ipv6_opt; + if (!opt) + opt = rcu_dereference(np->opt); if (opt) { opt = ipv6_dup_options(newsk, opt); RCU_INIT_POINTER(newnp->opt, opt); -- cgit v0.10.2 From e1adea927080821ebfa7505bff752a4015955660 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:05:29 -0400 Subject: calipso: Allow request sockets to be relabelled by the lsm. Request sockets need to have a label that takes into account the incoming connection as well as their parent's label. This is used for the outgoing SYN-ACK and for their child full-socket. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 918a604..a2408c3 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -229,6 +229,8 @@ struct netlbl_lsm_secattr { * @sock_getattr: retrieve the socket's attr * @sock_setattr: set the socket's attr * @sock_delattr: remove the socket's attr + * @req_setattr: set the req socket's attr + * @req_delattr: remove the req socket's attr * * Description: * This structure is filled out by the CALIPSO engine and passed @@ -252,6 +254,10 @@ struct netlbl_calipso_ops { const struct calipso_doi *doi_def, const struct netlbl_lsm_secattr *secattr); void (*sock_delattr)(struct sock *sk); + int (*req_setattr)(struct request_sock *req, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr); + void (*req_delattr)(struct request_sock *req); }; /* diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index 959db52..067a564 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -889,6 +889,90 @@ done: txopt_put(txopts); } +/* request sock functions. + */ + +/** + * calipso_req_setattr - Add a CALIPSO option to a connection request socket + * @req: the connection request socket + * @doi_def: the CALIPSO DOI to use + * @secattr: the specific security attributes of the socket + * + * Description: + * Set the CALIPSO option on the given socket using the DOI definition and + * security attributes passed to the function. Returns zero on success and + * negative values on failure. + * + */ +static int calipso_req_setattr(struct request_sock *req, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + struct ipv6_txoptions *txopts; + struct inet_request_sock *req_inet = inet_rsk(req); + struct ipv6_opt_hdr *old, *new; + struct sock *sk = sk_to_full_sk(req_to_sk(req)); + + if (req_inet->ipv6_opt && req_inet->ipv6_opt->hopopt) + old = req_inet->ipv6_opt->hopopt; + else + old = NULL; + + new = calipso_opt_insert(old, doi_def, secattr); + if (IS_ERR(new)) + return PTR_ERR(new); + + txopts = ipv6_renew_options_kern(sk, req_inet->ipv6_opt, IPV6_HOPOPTS, + new, new ? ipv6_optlen(new) : 0); + + kfree(new); + + if (IS_ERR(txopts)) + return PTR_ERR(txopts); + + txopts = xchg(&req_inet->ipv6_opt, txopts); + if (txopts) { + atomic_sub(txopts->tot_len, &sk->sk_omem_alloc); + txopt_put(txopts); + } + + return 0; +} + +/** + * calipso_req_delattr - Delete the CALIPSO option from a request socket + * @reg: the request socket + * + * Description: + * Removes the CALIPSO option from a request socket, if present. + * + */ +static void calipso_req_delattr(struct request_sock *req) +{ + struct inet_request_sock *req_inet = inet_rsk(req); + struct ipv6_opt_hdr *new; + struct ipv6_txoptions *txopts; + struct sock *sk = sk_to_full_sk(req_to_sk(req)); + + if (!req_inet->ipv6_opt || !req_inet->ipv6_opt->hopopt) + return; + + if (calipso_opt_del(req_inet->ipv6_opt->hopopt, &new)) + return; /* Nothing to do */ + + txopts = ipv6_renew_options_kern(sk, req_inet->ipv6_opt, IPV6_HOPOPTS, + new, new ? ipv6_optlen(new) : 0); + + if (!IS_ERR(txopts)) { + txopts = xchg(&req_inet->ipv6_opt, txopts); + if (txopts) { + atomic_sub(txopts->tot_len, &sk->sk_omem_alloc); + txopt_put(txopts); + } + } + kfree(new); +} + static const struct netlbl_calipso_ops ops = { .doi_add = calipso_doi_add, .doi_free = calipso_doi_free, @@ -899,6 +983,8 @@ static const struct netlbl_calipso_ops ops = { .sock_getattr = calipso_sock_getattr, .sock_setattr = calipso_sock_setattr, .sock_delattr = calipso_sock_delattr, + .req_setattr = calipso_req_setattr, + .req_delattr = calipso_req_delattr, }; /** diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index 6f9c658..79519e3 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c @@ -578,3 +578,43 @@ void calipso_sock_delattr(struct sock *sk) if (ops) ops->sock_delattr(sk); } + +/** + * calipso_req_setattr - Add a CALIPSO option to a connection request socket + * @req: the connection request socket + * @doi_def: the CALIPSO DOI to use + * @secattr: the specific security attributes of the socket + * + * Description: + * Set the CALIPSO option on the given socket using the DOI definition and + * security attributes passed to the function. Returns zero on success and + * negative values on failure. + * + */ +int calipso_req_setattr(struct request_sock *req, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val = -ENOMSG; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->req_setattr(req, doi_def, secattr); + return ret_val; +} + +/** + * calipso_req_delattr - Delete the CALIPSO option from a request socket + * @reg: the request socket + * + * Description: + * Removes the CALIPSO option from a request socket, if present. + * + */ +void calipso_req_delattr(struct request_sock *req) +{ + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ops->req_delattr(req); +} diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h index 49bc116..1372fdd 100644 --- a/net/netlabel/netlabel_calipso.h +++ b/net/netlabel/netlabel_calipso.h @@ -133,5 +133,9 @@ int calipso_sock_setattr(struct sock *sk, const struct calipso_doi *doi_def, const struct netlbl_lsm_secattr *secattr); void calipso_sock_delattr(struct sock *sk); +int calipso_req_setattr(struct request_sock *req, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr); +void calipso_req_delattr(struct request_sock *req); #endif diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 00bab51..9b725f7 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -1053,12 +1053,13 @@ int netlbl_req_setattr(struct request_sock *req, { int ret_val; struct netlbl_dommap_def *entry; + struct inet_request_sock *ireq = inet_rsk(req); rcu_read_lock(); switch (req->rsk_ops->family) { case AF_INET: entry = netlbl_domhsh_getentry_af4(secattr->domain, - inet_rsk(req)->ir_rmt_addr); + ireq->ir_rmt_addr); if (entry == NULL) { ret_val = -ENOENT; goto req_setattr_return; @@ -1069,9 +1070,7 @@ int netlbl_req_setattr(struct request_sock *req, entry->cipso, secattr); break; case NETLBL_NLTYPE_UNLABELED: - /* just delete the protocols we support for right now - * but we could remove other protocols if needed */ - cipso_v4_req_delattr(req); + netlbl_req_delattr(req); ret_val = 0; break; default: @@ -1080,9 +1079,24 @@ int netlbl_req_setattr(struct request_sock *req, break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - /* since we don't support any IPv6 labeling protocols right - * now we can optimize everything away until we do */ - ret_val = 0; + entry = netlbl_domhsh_getentry_af6(secattr->domain, + &ireq->ir_v6_rmt_addr); + if (entry == NULL) { + ret_val = -ENOENT; + goto req_setattr_return; + } + switch (entry->type) { + case NETLBL_NLTYPE_CALIPSO: + ret_val = calipso_req_setattr(req, + entry->calipso, secattr); + break; + case NETLBL_NLTYPE_UNLABELED: + netlbl_req_delattr(req); + ret_val = 0; + break; + default: + ret_val = -ENOENT; + } break; #endif /* IPv6 */ default: @@ -1108,6 +1122,11 @@ void netlbl_req_delattr(struct request_sock *req) case AF_INET: cipso_v4_req_delattr(req); break; +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + calipso_req_delattr(req); + break; +#endif /* IPv6 */ } } diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 2477a75..ca220c3 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -284,7 +284,7 @@ int selinux_netlbl_inet_conn_request(struct request_sock *req, u16 family) int rc; struct netlbl_lsm_secattr secattr; - if (family != PF_INET) + if (family != PF_INET && family != PF_INET6) return 0; netlbl_secattr_init(&secattr); -- cgit v0.10.2 From 0868383b822e4d8ebde980c7aac973a6aa81a3ec Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:06:15 -0400 Subject: ipv6: constify the skb pointer of ipv6_find_tlv(). Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 4e279a8..24a5ebe 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -945,7 +945,7 @@ enum { int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, int target, unsigned short *fragoff, int *fragflg); -int ipv6_find_tlv(struct sk_buff *skb, int offset, int type); +int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type); struct in6_addr *fl6_update_dst(struct flowi6 *fl6, const struct ipv6_txoptions *opt, diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index 9508a20..305e2ed 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c @@ -112,7 +112,7 @@ int ipv6_skip_exthdr(const struct sk_buff *skb, int start, u8 *nexthdrp, } EXPORT_SYMBOL(ipv6_skip_exthdr); -int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) +int ipv6_find_tlv(const struct sk_buff *skb, int offset, int type) { const unsigned char *nh = skb_network_header(skb); int packet_len = skb_tail_pointer(skb) - skb_network_header(skb); -- cgit v0.10.2 From 2917f57b6bc15cc6787496ee5f2fdf17f0e9b7d3 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:06:15 -0400 Subject: calipso: Allow the lsm to label the skbuff directly. In some cases, the lsm needs to add the label to the skbuff directly. A NF_INET_LOCAL_OUT IPv6 hook is added to selinux to match the IPv4 behaviour. This allows selinux to label the skbuffs that it requires. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/netlabel.h b/include/net/netlabel.h index a2408c3..e0e4ce8 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -231,6 +231,10 @@ struct netlbl_lsm_secattr { * @sock_delattr: remove the socket's attr * @req_setattr: set the req socket's attr * @req_delattr: remove the req socket's attr + * @opt_getattr: retrieve attr from memory block + * @skbuff_optptr: find option in packet + * @skbuff_setattr: set the skbuff's attr + * @skbuff_delattr: remove the skbuff's attr * * Description: * This structure is filled out by the CALIPSO engine and passed @@ -258,6 +262,13 @@ struct netlbl_calipso_ops { const struct calipso_doi *doi_def, const struct netlbl_lsm_secattr *secattr); void (*req_delattr)(struct request_sock *req); + int (*opt_getattr)(const unsigned char *calipso, + struct netlbl_lsm_secattr *secattr); + unsigned char *(*skbuff_optptr)(const struct sk_buff *skb); + int (*skbuff_setattr)(struct sk_buff *skb, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr); + int (*skbuff_delattr)(struct sk_buff *skb); }; /* diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index 067a564..fa371a8 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -62,6 +62,11 @@ */ #define CALIPSO_OPT_LEN_MAX_WITH_PAD (3 + CALIPSO_OPT_LEN_MAX + 7) + /* Maximium size of u32 aligned buffer required to hold calipso + * option. Max of 3 initial pad bytes starting from buffer + 3. + * i.e. the worst case is when the previous tlv finishes on 4n + 3. + */ +#define CALIPSO_MAX_BUFFER (6 + CALIPSO_OPT_LEN_MAX) /* List of available DOI definitions */ static DEFINE_SPINLOCK(calipso_doi_list_lock); @@ -973,6 +978,162 @@ static void calipso_req_delattr(struct request_sock *req) kfree(new); } +/* skbuff functions. + */ + +/** + * calipso_skbuff_optptr - Find the CALIPSO option in the packet + * @skb: the packet + * + * Description: + * Parse the packet's IP header looking for a CALIPSO option. Returns a pointer + * to the start of the CALIPSO option on success, NULL if one if not found. + * + */ +static unsigned char *calipso_skbuff_optptr(const struct sk_buff *skb) +{ + const struct ipv6hdr *ip6_hdr = ipv6_hdr(skb); + int offset; + + if (ip6_hdr->nexthdr != NEXTHDR_HOP) + return NULL; + + offset = ipv6_find_tlv(skb, sizeof(*ip6_hdr), IPV6_TLV_CALIPSO); + if (offset >= 0) + return (unsigned char *)ip6_hdr + offset; + + return NULL; +} + +/** + * calipso_skbuff_setattr - Set the CALIPSO option on a packet + * @skb: the packet + * @doi_def: the CALIPSO DOI to use + * @secattr: the security attributes + * + * Description: + * Set the CALIPSO option on the given packet based on the security attributes. + * Returns a pointer to the IP header on success and NULL on failure. + * + */ +static int calipso_skbuff_setattr(struct sk_buff *skb, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val; + struct ipv6hdr *ip6_hdr; + struct ipv6_opt_hdr *hop; + unsigned char buf[CALIPSO_MAX_BUFFER]; + int len_delta, new_end, pad; + unsigned int start, end; + + ip6_hdr = ipv6_hdr(skb); + if (ip6_hdr->nexthdr == NEXTHDR_HOP) { + hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1); + ret_val = calipso_opt_find(hop, &start, &end); + if (ret_val && ret_val != -ENOENT) + return ret_val; + } else { + start = 0; + end = 0; + } + + memset(buf, 0, sizeof(buf)); + ret_val = calipso_genopt(buf, start & 3, sizeof(buf), doi_def, secattr); + if (ret_val < 0) + return ret_val; + + new_end = start + ret_val; + /* At this point new_end aligns to 4n, so (new_end & 4) pads to 8n */ + pad = ((new_end & 4) + (end & 7)) & 7; + len_delta = new_end - (int)end + pad; + ret_val = skb_cow(skb, skb_headroom(skb) + len_delta); + if (ret_val < 0) + return ret_val; + + if (len_delta) { + if (len_delta > 0) + skb_push(skb, len_delta); + else + skb_pull(skb, -len_delta); + memmove((char *)ip6_hdr - len_delta, ip6_hdr, + sizeof(*ip6_hdr) + start); + skb_reset_network_header(skb); + ip6_hdr = ipv6_hdr(skb); + } + + hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1); + if (start == 0) { + struct ipv6_opt_hdr *new_hop = (struct ipv6_opt_hdr *)buf; + + new_hop->nexthdr = ip6_hdr->nexthdr; + new_hop->hdrlen = len_delta / 8 - 1; + ip6_hdr->nexthdr = NEXTHDR_HOP; + } else { + hop->hdrlen += len_delta / 8; + } + memcpy((char *)hop + start, buf + (start & 3), new_end - start); + calipso_pad_write((unsigned char *)hop, new_end, pad); + + return 0; +} + +/** + * calipso_skbuff_delattr - Delete any CALIPSO options from a packet + * @skb: the packet + * + * Description: + * Removes any and all CALIPSO options from the given packet. Returns zero on + * success, negative values on failure. + * + */ +static int calipso_skbuff_delattr(struct sk_buff *skb) +{ + int ret_val; + struct ipv6hdr *ip6_hdr; + struct ipv6_opt_hdr *old_hop; + u32 old_hop_len, start = 0, end = 0, delta, size, pad; + + if (!calipso_skbuff_optptr(skb)) + return 0; + + /* since we are changing the packet we should make a copy */ + ret_val = skb_cow(skb, skb_headroom(skb)); + if (ret_val < 0) + return ret_val; + + ip6_hdr = ipv6_hdr(skb); + old_hop = (struct ipv6_opt_hdr *)(ip6_hdr + 1); + old_hop_len = ipv6_optlen(old_hop); + + ret_val = calipso_opt_find(old_hop, &start, &end); + if (ret_val) + return ret_val; + + if (start == sizeof(*old_hop) && end == old_hop_len) { + /* There's no other option in the header so we delete + * the whole thing. */ + delta = old_hop_len; + size = sizeof(*ip6_hdr); + ip6_hdr->nexthdr = old_hop->nexthdr; + } else { + delta = (end - start) & ~7; + if (delta) + old_hop->hdrlen -= delta / 8; + pad = (end - start) & 7; + size = sizeof(*ip6_hdr) + start + pad; + calipso_pad_write((unsigned char *)old_hop, start, pad); + } + + if (delta) { + skb_pull(skb, delta); + memmove((char *)ip6_hdr + delta, ip6_hdr, size); + skb_reset_network_header(skb); + } + + return 0; +} + static const struct netlbl_calipso_ops ops = { .doi_add = calipso_doi_add, .doi_free = calipso_doi_free, @@ -985,6 +1146,10 @@ static const struct netlbl_calipso_ops ops = { .sock_delattr = calipso_sock_delattr, .req_setattr = calipso_req_setattr, .req_delattr = calipso_req_delattr, + .opt_getattr = calipso_opt_getattr, + .skbuff_optptr = calipso_skbuff_optptr, + .skbuff_setattr = calipso_skbuff_setattr, + .skbuff_delattr = calipso_skbuff_delattr, }; /** diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index 79519e3..0d02c26 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c @@ -618,3 +618,85 @@ void calipso_req_delattr(struct request_sock *req) if (ops) ops->req_delattr(req); } + +/** + * calipso_optptr - Find the CALIPSO option in the packet + * @skb: the packet + * + * Description: + * Parse the packet's IP header looking for a CALIPSO option. Returns a pointer + * to the start of the CALIPSO option on success, NULL if one if not found. + * + */ +unsigned char *calipso_optptr(const struct sk_buff *skb) +{ + unsigned char *ret_val = NULL; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->skbuff_optptr(skb); + return ret_val; +} + +/** + * calipso_getattr - Get the security attributes from a memory block. + * @calipso: the CALIPSO option + * @secattr: the security attributes + * + * Description: + * Inspect @calipso and return the security attributes in @secattr. + * Returns zero on success and negative values on failure. + * + */ +int calipso_getattr(const unsigned char *calipso, + struct netlbl_lsm_secattr *secattr) +{ + int ret_val = -ENOMSG; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->opt_getattr(calipso, secattr); + return ret_val; +} + +/** + * calipso_skbuff_setattr - Set the CALIPSO option on a packet + * @skb: the packet + * @doi_def: the CALIPSO DOI to use + * @secattr: the security attributes + * + * Description: + * Set the CALIPSO option on the given packet based on the security attributes. + * Returns a pointer to the IP header on success and NULL on failure. + * + */ +int calipso_skbuff_setattr(struct sk_buff *skb, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val = -ENOMSG; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->skbuff_setattr(skb, doi_def, secattr); + return ret_val; +} + +/** + * calipso_skbuff_delattr - Delete any CALIPSO options from a packet + * @skb: the packet + * + * Description: + * Removes any and all CALIPSO options from the given packet. Returns zero on + * success, negative values on failure. + * + */ +int calipso_skbuff_delattr(struct sk_buff *skb) +{ + int ret_val = -ENOMSG; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->skbuff_delattr(skb); + return ret_val; +} diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h index 1372fdd..66ba92e 100644 --- a/net/netlabel/netlabel_calipso.h +++ b/net/netlabel/netlabel_calipso.h @@ -137,5 +137,12 @@ int calipso_req_setattr(struct request_sock *req, const struct calipso_doi *doi_def, const struct netlbl_lsm_secattr *secattr); void calipso_req_delattr(struct request_sock *req); +unsigned char *calipso_optptr(const struct sk_buff *skb); +int calipso_getattr(const unsigned char *calipso, + struct netlbl_lsm_secattr *secattr); +int calipso_skbuff_setattr(struct sk_buff *skb, + const struct calipso_doi *doi_def, + const struct netlbl_lsm_secattr *secattr); +int calipso_skbuff_delattr(struct sk_buff *skb); #endif diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 9b725f7..c50a1c5 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -1147,13 +1147,17 @@ int netlbl_skbuff_setattr(struct sk_buff *skb, { int ret_val; struct iphdr *hdr4; +#if IS_ENABLED(CONFIG_IPV6) + struct ipv6hdr *hdr6; +#endif struct netlbl_dommap_def *entry; rcu_read_lock(); switch (family) { case AF_INET: hdr4 = ip_hdr(skb); - entry = netlbl_domhsh_getentry_af4(secattr->domain,hdr4->daddr); + entry = netlbl_domhsh_getentry_af4(secattr->domain, + hdr4->daddr); if (entry == NULL) { ret_val = -ENOENT; goto skbuff_setattr_return; @@ -1174,9 +1178,26 @@ int netlbl_skbuff_setattr(struct sk_buff *skb, break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: - /* since we don't support any IPv6 labeling protocols right - * now we can optimize everything away until we do */ - ret_val = 0; + hdr6 = ipv6_hdr(skb); + entry = netlbl_domhsh_getentry_af6(secattr->domain, + &hdr6->daddr); + if (entry == NULL) { + ret_val = -ENOENT; + goto skbuff_setattr_return; + } + switch (entry->type) { + case NETLBL_NLTYPE_CALIPSO: + ret_val = calipso_skbuff_setattr(skb, entry->calipso, + secattr); + break; + case NETLBL_NLTYPE_UNLABELED: + /* just delete the protocols we support for right now + * but we could remove other protocols if needed */ + ret_val = calipso_skbuff_delattr(skb); + break; + default: + ret_val = -ENOENT; + } break; #endif /* IPv6 */ default: @@ -1215,6 +1236,9 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb, break; #if IS_ENABLED(CONFIG_IPV6) case AF_INET6: + ptr = calipso_optptr(skb); + if (ptr && calipso_getattr(ptr, secattr) == 0) + return 0; break; #endif /* IPv6 */ } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index a00ab81..cb7c5c8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5063,6 +5063,15 @@ static unsigned int selinux_ipv4_output(void *priv, return selinux_ip_output(skb, PF_INET); } +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) +static unsigned int selinux_ipv6_output(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ + return selinux_ip_output(skb, PF_INET6); +} +#endif /* IPV6 */ + static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, int ifindex, u16 family) @@ -6297,6 +6306,12 @@ static struct nf_hook_ops selinux_nf_ops[] = { .hooknum = NF_INET_FORWARD, .priority = NF_IP6_PRI_SELINUX_FIRST, }, + { + .hook = selinux_ipv6_output, + .pf = NFPROTO_IPV6, + .hooknum = NF_INET_LOCAL_OUT, + .priority = NF_IP6_PRI_SELINUX_FIRST, + }, #endif /* IPV6 */ }; -- cgit v0.10.2 From a04e71f631fa3d2fd2aa0404c11484739d1e9073 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:06:16 -0400 Subject: netlabel: Pass a family parameter to netlbl_skbuff_err(). This makes it possible to route the error to the appropriate labelling engine. CALIPSO is far less verbose than CIPSO when encountering a bogus packet, so there is no need for a CALIPSO error handler. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/netlabel.h b/include/net/netlabel.h index e0e4ce8..d8a46a8 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -488,7 +488,7 @@ int netlbl_skbuff_setattr(struct sk_buff *skb, int netlbl_skbuff_getattr(const struct sk_buff *skb, u16 family, struct netlbl_lsm_secattr *secattr); -void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway); +void netlbl_skbuff_err(struct sk_buff *skb, u16 family, int error, int gateway); /* * LSM label mapping cache operations diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index c50a1c5..a42ce3c 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -1249,6 +1249,7 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb, /** * netlbl_skbuff_err - Handle a LSM error on a sk_buff * @skb: the packet + * @family: the family * @error: the error code * @gateway: true if host is acting as a gateway, false otherwise * @@ -1258,10 +1259,14 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb, * according to the packet's labeling protocol. * */ -void netlbl_skbuff_err(struct sk_buff *skb, int error, int gateway) +void netlbl_skbuff_err(struct sk_buff *skb, u16 family, int error, int gateway) { - if (cipso_v4_optptr(skb)) - cipso_v4_error(skb, error, gateway); + switch (family) { + case AF_INET: + if (cipso_v4_optptr(skb)) + cipso_v4_error(skb, error, gateway); + break; + } } /** diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index cb7c5c8..51eafe5 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4603,13 +4603,13 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) err = selinux_inet_sys_rcv_skb(sock_net(sk), skb->skb_iif, addrp, family, peer_sid, &ad); if (err) { - selinux_netlbl_err(skb, err, 0); + selinux_netlbl_err(skb, family, err, 0); return err; } err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, PEER__RECV, &ad); if (err) { - selinux_netlbl_err(skb, err, 0); + selinux_netlbl_err(skb, family, err, 0); return err; } } @@ -4977,7 +4977,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex, addrp, family, peer_sid, &ad); if (err) { - selinux_netlbl_err(skb, err, 1); + selinux_netlbl_err(skb, family, err, 1); return NF_DROP; } } diff --git a/security/selinux/include/netlabel.h b/security/selinux/include/netlabel.h index 8c59b8f..75686d5 100644 --- a/security/selinux/include/netlabel.h +++ b/security/selinux/include/netlabel.h @@ -40,7 +40,8 @@ #ifdef CONFIG_NETLABEL void selinux_netlbl_cache_invalidate(void); -void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway); +void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, + int gateway); void selinux_netlbl_sk_security_free(struct sk_security_struct *sksec); void selinux_netlbl_sk_security_reset(struct sk_security_struct *sksec); @@ -72,6 +73,7 @@ static inline void selinux_netlbl_cache_invalidate(void) } static inline void selinux_netlbl_err(struct sk_buff *skb, + u16 family, int error, int gateway) { diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index ca220c3..dfca50d 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -151,9 +151,9 @@ void selinux_netlbl_cache_invalidate(void) * present on the packet, NetLabel is smart enough to only act when it should. * */ -void selinux_netlbl_err(struct sk_buff *skb, int error, int gateway) +void selinux_netlbl_err(struct sk_buff *skb, u16 family, int error, int gateway) { - netlbl_skbuff_err(skb, error, gateway); + netlbl_skbuff_err(skb, family, error, gateway); } /** @@ -405,7 +405,7 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, return 0; if (nlbl_sid != SECINITSID_UNLABELED) - netlbl_skbuff_err(skb, rc, 0); + netlbl_skbuff_err(skb, family, rc, 0); return rc; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 11f7901..292fdea 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3992,7 +3992,7 @@ access_check: rc = smk_bu_note("IPv4 delivery", skp, ssp->smk_in, MAY_WRITE, rc); if (rc != 0) - netlbl_skbuff_err(skb, rc, 0); + netlbl_skbuff_err(skb, sk->sk_family, rc, 0); break; #if IS_ENABLED(CONFIG_IPV6) case PF_INET6: -- cgit v0.10.2 From 2e532b702834c07f614caf4489feb691e713232a Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:06:17 -0400 Subject: calipso: Add validation of CALIPSO option. Lengths, checksum and the DOI are checked. Checking of the level and categories are left for the socket layer. CRC validation is performed in the calipso module to avoid unconditionally linking crc_ccitt() into ipv6. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/calipso.h b/include/net/calipso.h index 38dbb47..85404e2 100644 --- a/include/net/calipso.h +++ b/include/net/calipso.h @@ -65,6 +65,7 @@ struct calipso_doi { #ifdef CONFIG_NETLABEL int __init calipso_init(void); void calipso_exit(void); +bool calipso_validate(const struct sk_buff *skb, const unsigned char *option); #else static inline int __init calipso_init(void) { @@ -74,6 +75,11 @@ static inline int __init calipso_init(void) static inline void calipso_exit(void) { } +static inline bool calipso_validate(const struct sk_buff *skb, + const unsigned char *option) +{ + return true; +} #endif /* CONFIG_NETLABEL */ #endif /* _CALIPSO_H */ diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index fa371a8..ea80450 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -321,6 +321,47 @@ doi_walk_return: } /** + * calipso_validate - Validate a CALIPSO option + * @skb: the packet + * @option: the start of the option + * + * Description: + * This routine is called to validate a CALIPSO option. + * If the option is valid then %true is returned, otherwise + * %false is returned. + * + * The caller should have already checked that the length of the + * option (including the TLV header) is >= 10 and that the catmap + * length is consistent with the option length. + * + * We leave checks on the level and categories to the socket layer. + */ +bool calipso_validate(const struct sk_buff *skb, const unsigned char *option) +{ + struct calipso_doi *doi_def; + bool ret_val; + u16 crc, len = option[1] + 2; + static const u8 zero[2]; + + /* The original CRC runs over the option including the TLV header + * with the CRC-16 field (at offset 8) zeroed out. */ + crc = crc_ccitt(0xffff, option, 8); + crc = crc_ccitt(crc, zero, sizeof(zero)); + if (len > 10) + crc = crc_ccitt(crc, option + 10, len - 10); + crc = ~crc; + if (option[8] != (crc & 0xff) || option[9] != ((crc >> 8) & 0xff)) + return false; + + rcu_read_lock(); + doi_def = calipso_doi_search(get_unaligned_be32(option + 2)); + ret_val = !!doi_def; + rcu_read_unlock(); + + return ret_val; +} + +/** * calipso_map_cat_hton - Perform a category mapping from host to network * @doi_def: the DOI definition * @secattr: the security attributes diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index d5fd3e7..0f69cab 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -43,6 +43,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_IPV6_MIP6) #include #endif @@ -603,6 +604,28 @@ drop: return false; } +/* CALIPSO RFC 5570 */ + +static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff) +{ + const unsigned char *nh = skb_network_header(skb); + + if (nh[optoff + 1] < 8) + goto drop; + + if (nh[optoff + 6] * 4 + 8 > nh[optoff + 1]) + goto drop; + + if (!calipso_validate(skb, nh + optoff)) + goto drop; + + return true; + +drop: + kfree_skb(skb); + return false; +} + static const struct tlvtype_proc tlvprochopopt_lst[] = { { .type = IPV6_TLV_ROUTERALERT, @@ -612,6 +635,10 @@ static const struct tlvtype_proc tlvprochopopt_lst[] = { .type = IPV6_TLV_JUMBO, .func = ipv6_hop_jumbo, }, + { + .type = IPV6_TLV_CALIPSO, + .func = ipv6_hop_calipso, + }, { -1, } }; -- cgit v0.10.2 From 4fee5242bf41d9ad641d4c1b821e36eb7ba37fbf Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:06:17 -0400 Subject: calipso: Add a label cache. This works in exactly the same way as the CIPSO label cache. The idea is to allow the lsm to cache the result of a secattr lookup so that it doesn't need to perform the lookup for every skbuff. It introduces two sysctl controls: calipso_cache_enable - enables/disables the cache. calipso_cache_bucket_size - sets the size of a cache bucket. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/calipso.h b/include/net/calipso.h index 85404e2..b1b30cd 100644 --- a/include/net/calipso.h +++ b/include/net/calipso.h @@ -62,6 +62,12 @@ struct calipso_doi { struct rcu_head rcu; }; +/* + * Sysctl Variables + */ +extern int calipso_cache_enabled; +extern int calipso_cache_bucketsize; + #ifdef CONFIG_NETLABEL int __init calipso_init(void); void calipso_exit(void); diff --git a/include/net/netlabel.h b/include/net/netlabel.h index d8a46a8..a306bc7 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -235,6 +235,8 @@ struct netlbl_lsm_secattr { * @skbuff_optptr: find option in packet * @skbuff_setattr: set the skbuff's attr * @skbuff_delattr: remove the skbuff's attr + * @cache_invalidate: invalidate cache + * @cache_add: add cache entry * * Description: * This structure is filled out by the CALIPSO engine and passed @@ -269,6 +271,9 @@ struct netlbl_calipso_ops { const struct calipso_doi *doi_def, const struct netlbl_lsm_secattr *secattr); int (*skbuff_delattr)(struct sk_buff *skb); + void (*cache_invalidate)(void); + int (*cache_add)(const unsigned char *calipso_ptr, + const struct netlbl_lsm_secattr *secattr); }; /* @@ -494,7 +499,7 @@ void netlbl_skbuff_err(struct sk_buff *skb, u16 family, int error, int gateway); * LSM label mapping cache operations */ void netlbl_cache_invalidate(void); -int netlbl_cache_add(const struct sk_buff *skb, +int netlbl_cache_add(const struct sk_buff *skb, u16 family, const struct netlbl_lsm_secattr *secattr); /* @@ -647,7 +652,7 @@ static inline void netlbl_cache_invalidate(void) { return; } -static inline int netlbl_cache_add(const struct sk_buff *skb, +static inline int netlbl_cache_add(const struct sk_buff *skb, u16 family, const struct netlbl_lsm_secattr *secattr) { return 0; diff --git a/net/ipv6/calipso.c b/net/ipv6/calipso.c index ea80450..c53b92c 100644 --- a/net/ipv6/calipso.c +++ b/net/ipv6/calipso.c @@ -72,6 +72,255 @@ static DEFINE_SPINLOCK(calipso_doi_list_lock); static LIST_HEAD(calipso_doi_list); +/* Label mapping cache */ +int calipso_cache_enabled = 1; +int calipso_cache_bucketsize = 10; +#define CALIPSO_CACHE_BUCKETBITS 7 +#define CALIPSO_CACHE_BUCKETS BIT(CALIPSO_CACHE_BUCKETBITS) +#define CALIPSO_CACHE_REORDERLIMIT 10 +struct calipso_map_cache_bkt { + spinlock_t lock; + u32 size; + struct list_head list; +}; + +struct calipso_map_cache_entry { + u32 hash; + unsigned char *key; + size_t key_len; + + struct netlbl_lsm_cache *lsm_data; + + u32 activity; + struct list_head list; +}; + +static struct calipso_map_cache_bkt *calipso_cache; + +/* Label Mapping Cache Functions + */ + +/** + * calipso_cache_entry_free - Frees a cache entry + * @entry: the entry to free + * + * Description: + * This function frees the memory associated with a cache entry including the + * LSM cache data if there are no longer any users, i.e. reference count == 0. + * + */ +static void calipso_cache_entry_free(struct calipso_map_cache_entry *entry) +{ + if (entry->lsm_data) + netlbl_secattr_cache_free(entry->lsm_data); + kfree(entry->key); + kfree(entry); +} + +/** + * calipso_map_cache_hash - Hashing function for the CALIPSO cache + * @key: the hash key + * @key_len: the length of the key in bytes + * + * Description: + * The CALIPSO tag hashing function. Returns a 32-bit hash value. + * + */ +static u32 calipso_map_cache_hash(const unsigned char *key, u32 key_len) +{ + return jhash(key, key_len, 0); +} + +/** + * calipso_cache_init - Initialize the CALIPSO cache + * + * Description: + * Initializes the CALIPSO label mapping cache, this function should be called + * before any of the other functions defined in this file. Returns zero on + * success, negative values on error. + * + */ +static int __init calipso_cache_init(void) +{ + u32 iter; + + calipso_cache = kcalloc(CALIPSO_CACHE_BUCKETS, + sizeof(struct calipso_map_cache_bkt), + GFP_KERNEL); + if (!calipso_cache) + return -ENOMEM; + + for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) { + spin_lock_init(&calipso_cache[iter].lock); + calipso_cache[iter].size = 0; + INIT_LIST_HEAD(&calipso_cache[iter].list); + } + + return 0; +} + +/** + * calipso_cache_invalidate - Invalidates the current CALIPSO cache + * + * Description: + * Invalidates and frees any entries in the CALIPSO cache. Returns zero on + * success and negative values on failure. + * + */ +static void calipso_cache_invalidate(void) +{ + struct calipso_map_cache_entry *entry, *tmp_entry; + u32 iter; + + for (iter = 0; iter < CALIPSO_CACHE_BUCKETS; iter++) { + spin_lock_bh(&calipso_cache[iter].lock); + list_for_each_entry_safe(entry, + tmp_entry, + &calipso_cache[iter].list, list) { + list_del(&entry->list); + calipso_cache_entry_free(entry); + } + calipso_cache[iter].size = 0; + spin_unlock_bh(&calipso_cache[iter].lock); + } +} + +/** + * calipso_cache_check - Check the CALIPSO cache for a label mapping + * @key: the buffer to check + * @key_len: buffer length in bytes + * @secattr: the security attribute struct to use + * + * Description: + * This function checks the cache to see if a label mapping already exists for + * the given key. If there is a match then the cache is adjusted and the + * @secattr struct is populated with the correct LSM security attributes. The + * cache is adjusted in the following manner if the entry is not already the + * first in the cache bucket: + * + * 1. The cache entry's activity counter is incremented + * 2. The previous (higher ranking) entry's activity counter is decremented + * 3. If the difference between the two activity counters is geater than + * CALIPSO_CACHE_REORDERLIMIT the two entries are swapped + * + * Returns zero on success, -ENOENT for a cache miss, and other negative values + * on error. + * + */ +static int calipso_cache_check(const unsigned char *key, + u32 key_len, + struct netlbl_lsm_secattr *secattr) +{ + u32 bkt; + struct calipso_map_cache_entry *entry; + struct calipso_map_cache_entry *prev_entry = NULL; + u32 hash; + + if (!calipso_cache_enabled) + return -ENOENT; + + hash = calipso_map_cache_hash(key, key_len); + bkt = hash & (CALIPSO_CACHE_BUCKETS - 1); + spin_lock_bh(&calipso_cache[bkt].lock); + list_for_each_entry(entry, &calipso_cache[bkt].list, list) { + if (entry->hash == hash && + entry->key_len == key_len && + memcmp(entry->key, key, key_len) == 0) { + entry->activity += 1; + atomic_inc(&entry->lsm_data->refcount); + secattr->cache = entry->lsm_data; + secattr->flags |= NETLBL_SECATTR_CACHE; + secattr->type = NETLBL_NLTYPE_CALIPSO; + if (!prev_entry) { + spin_unlock_bh(&calipso_cache[bkt].lock); + return 0; + } + + if (prev_entry->activity > 0) + prev_entry->activity -= 1; + if (entry->activity > prev_entry->activity && + entry->activity - prev_entry->activity > + CALIPSO_CACHE_REORDERLIMIT) { + __list_del(entry->list.prev, entry->list.next); + __list_add(&entry->list, + prev_entry->list.prev, + &prev_entry->list); + } + + spin_unlock_bh(&calipso_cache[bkt].lock); + return 0; + } + prev_entry = entry; + } + spin_unlock_bh(&calipso_cache[bkt].lock); + + return -ENOENT; +} + +/** + * calipso_cache_add - Add an entry to the CALIPSO cache + * @calipso_ptr: the CALIPSO option + * @secattr: the packet's security attributes + * + * Description: + * Add a new entry into the CALIPSO label mapping cache. Add the new entry to + * head of the cache bucket's list, if the cache bucket is out of room remove + * the last entry in the list first. It is important to note that there is + * currently no checking for duplicate keys. Returns zero on success, + * negative values on failure. The key stored starts at calipso_ptr + 2, + * i.e. the type and length bytes are not stored, this corresponds to + * calipso_ptr[1] bytes of data. + * + */ +static int calipso_cache_add(const unsigned char *calipso_ptr, + const struct netlbl_lsm_secattr *secattr) +{ + int ret_val = -EPERM; + u32 bkt; + struct calipso_map_cache_entry *entry = NULL; + struct calipso_map_cache_entry *old_entry = NULL; + u32 calipso_ptr_len; + + if (!calipso_cache_enabled || calipso_cache_bucketsize <= 0) + return 0; + + calipso_ptr_len = calipso_ptr[1]; + + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return -ENOMEM; + entry->key = kmemdup(calipso_ptr + 2, calipso_ptr_len, GFP_ATOMIC); + if (!entry->key) { + ret_val = -ENOMEM; + goto cache_add_failure; + } + entry->key_len = calipso_ptr_len; + entry->hash = calipso_map_cache_hash(calipso_ptr, calipso_ptr_len); + atomic_inc(&secattr->cache->refcount); + entry->lsm_data = secattr->cache; + + bkt = entry->hash & (CALIPSO_CACHE_BUCKETS - 1); + spin_lock_bh(&calipso_cache[bkt].lock); + if (calipso_cache[bkt].size < calipso_cache_bucketsize) { + list_add(&entry->list, &calipso_cache[bkt].list); + calipso_cache[bkt].size += 1; + } else { + old_entry = list_entry(calipso_cache[bkt].list.prev, + struct calipso_map_cache_entry, list); + list_del(&old_entry->list); + list_add(&entry->list, &calipso_cache[bkt].list); + calipso_cache_entry_free(old_entry); + } + spin_unlock_bh(&calipso_cache[bkt].lock); + + return 0; + +cache_add_failure: + if (entry) + calipso_cache_entry_free(entry); + return ret_val; +} + /* DOI List Functions */ @@ -789,6 +1038,9 @@ static int calipso_opt_getattr(const unsigned char *calipso, if (cat_len + 8 > len) return -EINVAL; + if (calipso_cache_check(calipso + 2, calipso[1], secattr) == 0) + return 0; + doi = get_unaligned_be32(calipso + 2); rcu_read_lock(); doi_def = calipso_doi_search(doi); @@ -1191,6 +1443,8 @@ static const struct netlbl_calipso_ops ops = { .skbuff_optptr = calipso_skbuff_optptr, .skbuff_setattr = calipso_skbuff_setattr, .skbuff_delattr = calipso_skbuff_delattr, + .cache_invalidate = calipso_cache_invalidate, + .cache_add = calipso_cache_add }; /** @@ -1203,11 +1457,17 @@ static const struct netlbl_calipso_ops ops = { */ int __init calipso_init(void) { - netlbl_calipso_ops_register(&ops); - return 0; + int ret_val; + + ret_val = calipso_cache_init(); + if (!ret_val) + netlbl_calipso_ops_register(&ops); + return ret_val; } void calipso_exit(void) { netlbl_calipso_ops_register(NULL); + calipso_cache_invalidate(); + kfree(calipso_cache); } diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 45243bb..69c50e7 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -15,6 +15,9 @@ #include #include #include +#ifdef CONFIG_NETLABEL +#include +#endif static int one = 1; static int auto_flowlabels_min; @@ -106,6 +109,22 @@ static struct ctl_table ipv6_rotable[] = { .proc_handler = proc_dointvec_minmax, .extra1 = &one }, +#ifdef CONFIG_NETLABEL + { + .procname = "calipso_cache_enable", + .data = &calipso_cache_enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, + { + .procname = "calipso_cache_bucket_size", + .data = &calipso_cache_bucketsize, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, +#endif /* CONFIG_NETLABEL */ { } }; diff --git a/net/netlabel/netlabel_calipso.c b/net/netlabel/netlabel_calipso.c index 0d02c26..2ec93c5 100644 --- a/net/netlabel/netlabel_calipso.c +++ b/net/netlabel/netlabel_calipso.c @@ -700,3 +700,41 @@ int calipso_skbuff_delattr(struct sk_buff *skb) ret_val = ops->skbuff_delattr(skb); return ret_val; } + +/** + * calipso_cache_invalidate - Invalidates the current CALIPSO cache + * + * Description: + * Invalidates and frees any entries in the CALIPSO cache. Returns zero on + * success and negative values on failure. + * + */ +void calipso_cache_invalidate(void) +{ + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ops->cache_invalidate(); +} + +/** + * calipso_cache_add - Add an entry to the CALIPSO cache + * @calipso_ptr: the CALIPSO option + * @secattr: the packet's security attributes + * + * Description: + * Add a new entry into the CALIPSO label mapping cache. + * Returns zero on success, negative values on failure. + * + */ +int calipso_cache_add(const unsigned char *calipso_ptr, + const struct netlbl_lsm_secattr *secattr) + +{ + int ret_val = -ENOMSG; + const struct netlbl_calipso_ops *ops = netlbl_calipso_ops_get(); + + if (ops) + ret_val = ops->cache_add(calipso_ptr, secattr); + return ret_val; +} diff --git a/net/netlabel/netlabel_calipso.h b/net/netlabel/netlabel_calipso.h index 66ba92e..9fd291c 100644 --- a/net/netlabel/netlabel_calipso.h +++ b/net/netlabel/netlabel_calipso.h @@ -144,5 +144,8 @@ int calipso_skbuff_setattr(struct sk_buff *skb, const struct calipso_doi *doi_def, const struct netlbl_lsm_secattr *secattr); int calipso_skbuff_delattr(struct sk_buff *skb); +void calipso_cache_invalidate(void); +int calipso_cache_add(const unsigned char *calipso_ptr, + const struct netlbl_lsm_secattr *secattr); #endif diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index a42ce3c..fbad718 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -1281,11 +1281,15 @@ void netlbl_skbuff_err(struct sk_buff *skb, u16 family, int error, int gateway) void netlbl_cache_invalidate(void) { cipso_v4_cache_invalidate(); +#if IS_ENABLED(CONFIG_IPV6) + calipso_cache_invalidate(); +#endif /* IPv6 */ } /** * netlbl_cache_add - Add an entry to a NetLabel protocol cache * @skb: the packet + * @family: the family * @secattr: the packet's security attributes * * Description: @@ -1294,7 +1298,7 @@ void netlbl_cache_invalidate(void) * values on error. * */ -int netlbl_cache_add(const struct sk_buff *skb, +int netlbl_cache_add(const struct sk_buff *skb, u16 family, const struct netlbl_lsm_secattr *secattr) { unsigned char *ptr; @@ -1302,10 +1306,20 @@ int netlbl_cache_add(const struct sk_buff *skb, if ((secattr->flags & NETLBL_SECATTR_CACHE) == 0) return -ENOMSG; - ptr = cipso_v4_optptr(skb); - if (ptr) - return cipso_v4_cache_add(ptr, secattr); - + switch (family) { + case AF_INET: + ptr = cipso_v4_optptr(skb); + if (ptr) + return cipso_v4_cache_add(ptr, secattr); + break; +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + ptr = calipso_optptr(skb); + if (ptr) + return calipso_cache_add(ptr, secattr); + break; +#endif /* IPv6 */ + } return -ENOMSG; } diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index dfca50d..aaba667 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -54,6 +54,7 @@ * */ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb, + u16 family, struct netlbl_lsm_secattr *secattr, u32 *sid) { @@ -63,7 +64,7 @@ static int selinux_netlbl_sidlookup_cached(struct sk_buff *skb, if (rc == 0 && (secattr->flags & NETLBL_SECATTR_CACHEABLE) && (secattr->flags & NETLBL_SECATTR_CACHE)) - netlbl_cache_add(skb, secattr); + netlbl_cache_add(skb, family, secattr); return rc; } @@ -214,7 +215,8 @@ int selinux_netlbl_skbuff_getsid(struct sk_buff *skb, netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) - rc = selinux_netlbl_sidlookup_cached(skb, &secattr, sid); + rc = selinux_netlbl_sidlookup_cached(skb, family, + &secattr, sid); else *sid = SECSID_NULL; *type = secattr.type; @@ -382,7 +384,8 @@ int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec, netlbl_secattr_init(&secattr); rc = netlbl_skbuff_getattr(skb, family, &secattr); if (rc == 0 && secattr.flags != NETLBL_SECATTR_NONE) - rc = selinux_netlbl_sidlookup_cached(skb, &secattr, &nlbl_sid); + rc = selinux_netlbl_sidlookup_cached(skb, family, + &secattr, &nlbl_sid); else nlbl_sid = SECINITSID_UNLABELED; netlbl_secattr_destroy(&secattr); -- cgit v0.10.2 From 3f09354ac84c6904787189d85fb306bf60f714b8 Mon Sep 17 00:00:00 2001 From: Huw Davies Date: Mon, 27 Jun 2016 15:06:18 -0400 Subject: netlabel: Implement CALIPSO config functions for SMACK. SMACK uses similar functions to control CIPSO, these are the equivalent functions for CALIPSO and follow exactly the same semantics. int netlbl_cfg_calipso_add(struct calipso_doi *doi_def, struct netlbl_audit *audit_info) Adds a CALIPSO doi. void netlbl_cfg_calipso_del(u32 doi, struct netlbl_audit *audit_info) Removes a CALIPSO doi. int netlbl_cfg_calipso_map_add(u32 doi, const char *domain, const struct in6_addr *addr, const struct in6_addr *mask, struct netlbl_audit *audit_info) Creates a mapping between a domain and a CALIPSO doi. If addr and mask are non-NULL this creates an address-selector type mapping. This also extends netlbl_cfg_map_del() to remove IPv6 address-selector mappings. Signed-off-by: Huw Davies Signed-off-by: Paul Moore diff --git a/include/net/netlabel.h b/include/net/netlabel.h index a306bc7..efe9806 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -445,6 +445,14 @@ int netlbl_cfg_cipsov4_map_add(u32 doi, const struct in_addr *addr, const struct in_addr *mask, struct netlbl_audit *audit_info); +int netlbl_cfg_calipso_add(struct calipso_doi *doi_def, + struct netlbl_audit *audit_info); +void netlbl_cfg_calipso_del(u32 doi, struct netlbl_audit *audit_info); +int netlbl_cfg_calipso_map_add(u32 doi, + const char *domain, + const struct in6_addr *addr, + const struct in6_addr *mask, + struct netlbl_audit *audit_info); /* * LSM security attribute operations */ @@ -561,6 +569,24 @@ static inline int netlbl_cfg_cipsov4_map_add(u32 doi, { return -ENOSYS; } +static inline int netlbl_cfg_calipso_add(struct calipso_doi *doi_def, + struct netlbl_audit *audit_info) +{ + return -ENOSYS; +} +static inline void netlbl_cfg_calipso_del(u32 doi, + struct netlbl_audit *audit_info) +{ + return; +} +static inline int netlbl_cfg_calipso_map_add(u32 doi, + const char *domain, + const struct in6_addr *addr, + const struct in6_addr *mask, + struct netlbl_audit *audit_info) +{ + return -ENOSYS; +} static inline int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset) { diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c index f1dee0d..41d0e95 100644 --- a/net/netlabel/netlabel_domainhash.c +++ b/net/netlabel/netlabel_domainhash.c @@ -725,6 +725,72 @@ remove_af4_failure: return -ENOENT; } +#if IS_ENABLED(CONFIG_IPV6) +/** + * netlbl_domhsh_remove_af6 - Removes an address selector entry + * @domain: the domain + * @addr: IPv6 address + * @mask: IPv6 address mask + * @audit_info: NetLabel audit information + * + * Description: + * Removes an individual address selector from a domain mapping and potentially + * the entire mapping if it is empty. Returns zero on success, negative values + * on failure. + * + */ +int netlbl_domhsh_remove_af6(const char *domain, + const struct in6_addr *addr, + const struct in6_addr *mask, + struct netlbl_audit *audit_info) +{ + struct netlbl_dom_map *entry_map; + struct netlbl_af6list *entry_addr; + struct netlbl_af4list *iter4; + struct netlbl_af6list *iter6; + struct netlbl_domaddr6_map *entry; + + rcu_read_lock(); + + if (domain) + entry_map = netlbl_domhsh_search(domain, AF_INET6); + else + entry_map = netlbl_domhsh_search_def(domain, AF_INET6); + if (entry_map == NULL || + entry_map->def.type != NETLBL_NLTYPE_ADDRSELECT) + goto remove_af6_failure; + + spin_lock(&netlbl_domhsh_lock); + entry_addr = netlbl_af6list_remove(addr, mask, + &entry_map->def.addrsel->list6); + spin_unlock(&netlbl_domhsh_lock); + + if (entry_addr == NULL) + goto remove_af6_failure; + netlbl_af4list_foreach_rcu(iter4, &entry_map->def.addrsel->list4) + goto remove_af6_single_addr; + netlbl_af6list_foreach_rcu(iter6, &entry_map->def.addrsel->list6) + goto remove_af6_single_addr; + /* the domain mapping is empty so remove it from the mapping table */ + netlbl_domhsh_remove_entry(entry_map, audit_info); + +remove_af6_single_addr: + rcu_read_unlock(); + /* yick, we can't use call_rcu here because we don't have a rcu head + * pointer but hopefully this should be a rare case so the pause + * shouldn't be a problem */ + synchronize_rcu(); + entry = netlbl_domhsh_addr6_entry(entry_addr); + calipso_doi_putdef(entry->def.calipso); + kfree(entry); + return 0; + +remove_af6_failure: + rcu_read_unlock(); + return -ENOENT; +} +#endif /* IPv6 */ + /** * netlbl_domhsh_remove - Removes an entry from the domain hash table * @domain: the domain to remove diff --git a/net/netlabel/netlabel_domainhash.h b/net/netlabel/netlabel_domainhash.h index b7ddb6e..1f92477 100644 --- a/net/netlabel/netlabel_domainhash.h +++ b/net/netlabel/netlabel_domainhash.h @@ -93,6 +93,10 @@ int netlbl_domhsh_remove_af4(const char *domain, const struct in_addr *addr, const struct in_addr *mask, struct netlbl_audit *audit_info); +int netlbl_domhsh_remove_af6(const char *domain, + const struct in6_addr *addr, + const struct in6_addr *mask, + struct netlbl_audit *audit_info); int netlbl_domhsh_remove(const char *domain, u16 family, struct netlbl_audit *audit_info); int netlbl_domhsh_remove_default(u16 family, struct netlbl_audit *audit_info); @@ -102,6 +106,10 @@ struct netlbl_dommap_def *netlbl_domhsh_getentry_af4(const char *domain, #if IS_ENABLED(CONFIG_IPV6) struct netlbl_dommap_def *netlbl_domhsh_getentry_af6(const char *domain, const struct in6_addr *addr); +int netlbl_domhsh_remove_af6(const char *domain, + const struct in6_addr *addr, + const struct in6_addr *mask, + struct netlbl_audit *audit_info); #endif /* IPv6 */ int netlbl_domhsh_walk(u32 *skip_bkt, diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index fbad718..28c56b9 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -80,6 +80,11 @@ int netlbl_cfg_map_del(const char *domain, case AF_INET: return netlbl_domhsh_remove_af4(domain, addr, mask, audit_info); +#if IS_ENABLED(CONFIG_IPV6) + case AF_INET6: + return netlbl_domhsh_remove_af6(domain, addr, mask, + audit_info); +#endif /* IPv6 */ default: return -EPFNOSUPPORT; } @@ -403,6 +408,139 @@ out_entry: return ret_val; } +/** + * netlbl_cfg_calipso_add - Add a new CALIPSO DOI definition + * @doi_def: CALIPSO DOI definition + * @audit_info: NetLabel audit information + * + * Description: + * Add a new CALIPSO DOI definition as defined by @doi_def. Returns zero on + * success and negative values on failure. + * + */ +int netlbl_cfg_calipso_add(struct calipso_doi *doi_def, + struct netlbl_audit *audit_info) +{ +#if IS_ENABLED(CONFIG_IPV6) + return calipso_doi_add(doi_def, audit_info); +#else /* IPv6 */ + return -ENOSYS; +#endif /* IPv6 */ +} + +/** + * netlbl_cfg_calipso_del - Remove an existing CALIPSO DOI definition + * @doi: CALIPSO DOI + * @audit_info: NetLabel audit information + * + * Description: + * Remove an existing CALIPSO DOI definition matching @doi. Returns zero on + * success and negative values on failure. + * + */ +void netlbl_cfg_calipso_del(u32 doi, struct netlbl_audit *audit_info) +{ +#if IS_ENABLED(CONFIG_IPV6) + calipso_doi_remove(doi, audit_info); +#endif /* IPv6 */ +} + +/** + * netlbl_cfg_calipso_map_add - Add a new CALIPSO DOI mapping + * @doi: the CALIPSO DOI + * @domain: the domain mapping to add + * @addr: IP address + * @mask: IP address mask + * @audit_info: NetLabel audit information + * + * Description: + * Add a new NetLabel/LSM domain mapping for the given CALIPSO DOI to the + * NetLabel subsystem. A @domain value of NULL adds a new default domain + * mapping. Returns zero on success, negative values on failure. + * + */ +int netlbl_cfg_calipso_map_add(u32 doi, + const char *domain, + const struct in6_addr *addr, + const struct in6_addr *mask, + struct netlbl_audit *audit_info) +{ +#if IS_ENABLED(CONFIG_IPV6) + int ret_val = -ENOMEM; + struct calipso_doi *doi_def; + struct netlbl_dom_map *entry; + struct netlbl_domaddr_map *addrmap = NULL; + struct netlbl_domaddr6_map *addrinfo = NULL; + + doi_def = calipso_doi_getdef(doi); + if (doi_def == NULL) + return -ENOENT; + + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) + goto out_entry; + entry->family = AF_INET6; + if (domain != NULL) { + entry->domain = kstrdup(domain, GFP_ATOMIC); + if (entry->domain == NULL) + goto out_domain; + } + + if (addr == NULL && mask == NULL) { + entry->def.calipso = doi_def; + entry->def.type = NETLBL_NLTYPE_CALIPSO; + } else if (addr != NULL && mask != NULL) { + addrmap = kzalloc(sizeof(*addrmap), GFP_ATOMIC); + if (addrmap == NULL) + goto out_addrmap; + INIT_LIST_HEAD(&addrmap->list4); + INIT_LIST_HEAD(&addrmap->list6); + + addrinfo = kzalloc(sizeof(*addrinfo), GFP_ATOMIC); + if (addrinfo == NULL) + goto out_addrinfo; + addrinfo->def.calipso = doi_def; + addrinfo->def.type = NETLBL_NLTYPE_CALIPSO; + addrinfo->list.addr = *addr; + addrinfo->list.addr.s6_addr32[0] &= mask->s6_addr32[0]; + addrinfo->list.addr.s6_addr32[1] &= mask->s6_addr32[1]; + addrinfo->list.addr.s6_addr32[2] &= mask->s6_addr32[2]; + addrinfo->list.addr.s6_addr32[3] &= mask->s6_addr32[3]; + addrinfo->list.mask = *mask; + addrinfo->list.valid = 1; + ret_val = netlbl_af6list_add(&addrinfo->list, &addrmap->list6); + if (ret_val != 0) + goto cfg_calipso_map_add_failure; + + entry->def.addrsel = addrmap; + entry->def.type = NETLBL_NLTYPE_ADDRSELECT; + } else { + ret_val = -EINVAL; + goto out_addrmap; + } + + ret_val = netlbl_domhsh_add(entry, audit_info); + if (ret_val != 0) + goto cfg_calipso_map_add_failure; + + return 0; + +cfg_calipso_map_add_failure: + kfree(addrinfo); +out_addrinfo: + kfree(addrmap); +out_addrmap: + kfree(entry->domain); +out_domain: + kfree(entry); +out_entry: + calipso_doi_putdef(doi_def); + return ret_val; +#else /* IPv6 */ + return -ENOSYS; +#endif /* IPv6 */ +} + /* * Security Attribute Functions */ -- cgit v0.10.2 From 96d450bbeccda6f32c70bbb9ee54057f68733cad Mon Sep 17 00:00:00 2001 From: Eric Richter Date: Wed, 1 Jun 2016 13:14:00 -0500 Subject: integrity: add measured_pcrs field to integrity cache To keep track of which measurements have been extended to which PCRs, this patch defines a new integrity_iint_cache field named measured_pcrs. This field is a bitmask of the PCRs measured. Each bit corresponds to a PCR index. For example, bit 10 corresponds to PCR 10. Signed-off-by: Eric Richter Signed-off-by: Mimi Zohar diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 345b759..c710d22 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -79,6 +79,7 @@ static void iint_free(struct integrity_iint_cache *iint) iint->ima_bprm_status = INTEGRITY_UNKNOWN; iint->ima_read_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; + iint->measured_pcrs = 0; kmem_cache_free(iint_cache, iint); } @@ -159,6 +160,7 @@ static void init_once(void *foo) iint->ima_bprm_status = INTEGRITY_UNKNOWN; iint->ima_read_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; + iint->measured_pcrs = 0; } static int __init integrity_iintcache_init(void) diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 90bc57d..24520b4 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -103,6 +103,7 @@ struct integrity_iint_cache { struct inode *inode; /* back pointer to inode in question */ u64 version; /* track inode changes */ unsigned long flags; + unsigned long measured_pcrs; enum integrity_status ima_file_status:4; enum integrity_status ima_mmap_status:4; enum integrity_status ima_bprm_status:4; -- cgit v0.10.2 From 0260643ce8047d2a58f76222d09f161149622465 Mon Sep 17 00:00:00 2001 From: Eric Richter Date: Wed, 1 Jun 2016 13:14:01 -0500 Subject: ima: add policy support for extending different pcrs This patch defines a new IMA measurement policy rule option "pcr=", which allows extending different PCRs on a per rule basis. For example, the system independent files could extend the default IMA Kconfig specified PCR, while the system dependent files could extend a different PCR. The following is an example of this usage with an SELinux policy; the rule would extend PCR 11 with system configuration files: measure func=FILE_CHECK mask=MAY_READ obj_type=system_conf_t pcr=11 Changelog v3: - FIELD_SIZEOF returns bytes, not bits. Fixed INVALID_PCR Signed-off-by: Eric Richter Signed-off-by: Mimi Zohar diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 0f887a5..3d35fbe 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -32,6 +32,7 @@ #define IMA_FSUUID 0x0020 #define IMA_INMASK 0x0040 #define IMA_EUID 0x0080 +#define IMA_PCR 0x0100 #define UNKNOWN 0 #define MEASURE 0x0001 /* same as IMA_MEASURE */ @@ -40,6 +41,9 @@ #define DONT_APPRAISE 0x0008 #define AUDIT 0x0040 +#define INVALID_PCR(a) (((a) < 0) || \ + (a) >= (FIELD_SIZEOF(struct integrity_iint_cache, measured_pcrs) * 8)) + int ima_policy_flag; static int temp_ima_appraise; @@ -60,6 +64,7 @@ struct ima_rule_entry { u8 fsuuid[16]; kuid_t uid; kuid_t fowner; + int pcr; struct { void *rule; /* LSM file metadata specific */ void *args_p; /* audit value */ @@ -478,7 +483,8 @@ enum { Opt_subj_user, Opt_subj_role, Opt_subj_type, Opt_func, Opt_mask, Opt_fsmagic, Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner, - Opt_appraise_type, Opt_permit_directio + Opt_appraise_type, Opt_permit_directio, + Opt_pcr }; static match_table_t policy_tokens = { @@ -502,6 +508,7 @@ static match_table_t policy_tokens = { {Opt_fowner, "fowner=%s"}, {Opt_appraise_type, "appraise_type=%s"}, {Opt_permit_directio, "permit_directio"}, + {Opt_pcr, "pcr=%s"}, {Opt_err, NULL} }; @@ -774,6 +781,20 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) case Opt_permit_directio: entry->flags |= IMA_PERMIT_DIRECTIO; break; + case Opt_pcr: + if (entry->action != MEASURE) { + result = -EINVAL; + break; + } + ima_log_string(ab, "pcr", args[0].from); + + result = kstrtoint(args[0].from, 10, &entry->pcr); + if (result || INVALID_PCR(entry->pcr)) + result = -EINVAL; + else + entry->flags |= IMA_PCR; + + break; case Opt_err: ima_log_string(ab, "UNKNOWN", p); result = -EINVAL; @@ -1011,6 +1032,12 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); } + if (entry->flags & IMA_PCR) { + snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr); + seq_printf(m, pt(Opt_pcr), tbuf); + seq_puts(m, " "); + } + if (entry->flags & IMA_FSUUID) { seq_printf(m, "fsuuid=%pU", entry->fsuuid); seq_puts(m, " "); -- cgit v0.10.2 From 725de7fabb9fe4ca388c780ad4644352f2f06ccc Mon Sep 17 00:00:00 2001 From: Eric Richter Date: Wed, 1 Jun 2016 13:14:02 -0500 Subject: ima: extend ima_get_action() to return the policy pcr Different policy rules may extend different PCRs. This patch retrieves the specific PCR for the matched rule. Subsequent patches will include the rule specific PCR in the measurement list and extend the appropriate PCR. Signed-off-by: Eric Richter Signed-off-by: Mimi Zohar diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index d3a939b..3c8e71e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -154,7 +154,8 @@ enum ima_hooks { }; /* LIM API function definitions */ -int ima_get_action(struct inode *inode, int mask, enum ima_hooks func); +int ima_get_action(struct inode *inode, int mask, + enum ima_hooks func, int *pcr); int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func); int ima_collect_measurement(struct integrity_iint_cache *iint, struct file *file, void *buf, loff_t size, @@ -174,7 +175,7 @@ const char *ima_d_path(const struct path *path, char **pathbuf); /* IMA policy related functions */ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, - int flags); + int flags, int *pcr); void ima_init_policy(void); void ima_update_policy(void); void ima_update_policy_flag(void); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 5a2218f..225b9ce 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -157,6 +157,7 @@ err_out: * @inode: pointer to inode to measure * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) * @func: caller identifier + * @pcr: pointer filled in if matched measure policy sets pcr= * * The policy is defined in terms of keypairs: * subj=, obj=, type=, func=, mask=, fsmagic= @@ -168,13 +169,13 @@ err_out: * Returns IMA_MEASURE, IMA_APPRAISE mask. * */ -int ima_get_action(struct inode *inode, int mask, enum ima_hooks func) +int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr) { int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE; flags &= ima_policy_flag; - return ima_match_policy(inode, func, mask, flags); + return ima_match_policy(inode, func, mask, flags, pcr); } /* diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 1bcbc12..fe8e923 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -41,7 +41,7 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func) if (!ima_appraise) return 0; - return ima_match_policy(inode, func, mask, IMA_APPRAISE); + return ima_match_policy(inode, func, mask, IMA_APPRAISE, NULL); } static int ima_fix_xattr(struct dentry *dentry, diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 68b26c3..58b08b2 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -162,6 +162,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size, char *pathbuf = NULL; const char *pathname = NULL; int rc = -ENOMEM, action, must_appraise; + int pcr = CONFIG_IMA_MEASURE_PCR_IDX; struct evm_ima_xattr_data *xattr_value = NULL; int xattr_len = 0; bool violation_check; @@ -174,7 +175,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size, * bitmask based on the appraise/audit/measurement policy. * Included is the appraise submask. */ - action = ima_get_action(inode, mask, func); + action = ima_get_action(inode, mask, func, &pcr); violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) && (ima_policy_flag & IMA_MEASURE)); if (!action && !violation_check) diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3d35fbe..aed47b7 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -324,6 +324,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) * @inode: pointer to an inode for which the policy decision is being made * @func: IMA hook identifier * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC) + * @pcr: set the pcr to extend * * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type) * conditions. @@ -333,7 +334,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) * than writes so ima_match_policy() is classical RCU candidate. */ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, - int flags) + int flags, int *pcr) { struct ima_rule_entry *entry; int action = 0, actmask = flags | (flags << 1); @@ -358,6 +359,9 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, else actmask &= ~(entry->action | entry->action >> 1); + if ((pcr) && (entry->flags & IMA_PCR)) + *pcr = entry->pcr; + if (!actmask) break; } -- cgit v0.10.2 From 14b1da85bbe9a59c5e01123a06dea4c4758a6db9 Mon Sep 17 00:00:00 2001 From: Eric Richter Date: Wed, 1 Jun 2016 13:14:03 -0500 Subject: ima: include pcr for each measurement log entry The IMA measurement list entries include the Kconfig defined PCR value. This patch defines a new ima_template_entry field for including the PCR as specified in the policy rule. Signed-off-by: Eric Richter Signed-off-by: Mimi Zohar diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 3c8e71e..db25f54 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -88,6 +88,7 @@ struct ima_template_desc { }; struct ima_template_entry { + int pcr; u8 digest[TPM_DIGEST_SIZE]; /* sha1 or md5 measurement hash */ struct ima_template_desc *template_desc; /* template descriptor */ u32 template_data_len; @@ -163,13 +164,14 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len); + int xattr_len, int pcr); void ima_audit_measurement(struct integrity_iint_cache *iint, const unsigned char *filename); int ima_alloc_init_template(struct ima_event_data *event_data, struct ima_template_entry **entry); int ima_store_template(struct ima_template_entry *entry, int violation, - struct inode *inode, const unsigned char *filename); + struct inode *inode, + const unsigned char *filename, int pcr); void ima_free_template_entry(struct ima_template_entry *entry); const char *ima_d_path(const struct path *path, char **pathbuf); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 225b9ce..8363ba3 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -87,7 +87,7 @@ out: */ int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, - const unsigned char *filename) + const unsigned char *filename, int pcr) { static const char op[] = "add_template_measure"; static const char audit_cause[] = "hashing_error"; @@ -114,6 +114,7 @@ int ima_store_template(struct ima_template_entry *entry, } memcpy(entry->digest, hash.hdr.digest, hash.hdr.length); } + entry->pcr = pcr; result = ima_add_template_entry(entry, violation, op, inode, filename); return result; } @@ -144,7 +145,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename, result = -ENOMEM; goto err_out; } - result = ima_store_template(entry, violation, inode, filename); + result = ima_store_template(entry, violation, inode, + filename, CONFIG_IMA_MEASURE_PCR_IDX); if (result < 0) ima_free_template_entry(entry); err_out: @@ -253,7 +255,7 @@ out: void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, - int xattr_len) + int xattr_len, int pcr) { static const char op[] = "add_template_measure"; static const char audit_cause[] = "ENOMEM"; @@ -274,7 +276,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, return; } - result = ima_store_template(entry, violation, inode, filename); + result = ima_store_template(entry, violation, inode, filename, pcr); if (!result || result == -EEXIST) iint->flags |= IMA_MEASURED; if (result < 0) diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 5d679a6..32912bd 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -79,7 +79,8 @@ static int __init ima_add_boot_aggregate(void) } result = ima_store_template(entry, violation, NULL, - boot_aggregate_name); + boot_aggregate_name, + CONFIG_IMA_MEASURE_PCR_IDX); if (result < 0) { ima_free_template_entry(entry); audit_cause = "store_entry"; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 58b08b2..3627afd 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -239,7 +239,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size, if (action & IMA_MEASURE) ima_store_measurement(iint, file, pathname, - xattr_value, xattr_len); + xattr_value, xattr_len, pcr); if (action & IMA_APPRAISE_SUBMASK) rc = ima_appraise_measurement(func, iint, file, pathname, xattr_value, xattr_len, opened); -- cgit v0.10.2 From 5f6f027b50d8ed9f1ba4447aa5aed3a94b601fe8 Mon Sep 17 00:00:00 2001 From: Eric Richter Date: Wed, 1 Jun 2016 13:14:04 -0500 Subject: ima: change ima_measurements_show() to display the entry specific pcr IMA assumes that the same default Kconfig PCR is extended for each entry. This patch replaces the default configured PCR with the policy defined PCR. Signed-off-by: Eric Richter Signed-off-by: Mimi Zohar diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 60d011a..c07a384 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -123,7 +123,6 @@ static int ima_measurements_show(struct seq_file *m, void *v) struct ima_template_entry *e; char *template_name; int namelen; - u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX; bool is_ima_template = false; int i; @@ -137,10 +136,10 @@ static int ima_measurements_show(struct seq_file *m, void *v) /* * 1st: PCRIndex - * PCR used is always the same (config option) in - * little-endian format + * PCR used defaults to the same (config option) in + * little-endian format, unless set in policy */ - ima_putc(m, &pcr, sizeof(pcr)); + ima_putc(m, &e->pcr, sizeof(e->pcr)); /* 2nd: template digest */ ima_putc(m, e->digest, TPM_DIGEST_SIZE); @@ -219,7 +218,7 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v) e->template_desc->name : e->template_desc->fmt; /* 1st: PCR used (config option) */ - seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX); + seq_printf(m, "%2d ", e->pcr); /* 2nd: SHA1 template hash */ ima_print_digest(m, e->digest, TPM_DIGEST_SIZE); -- cgit v0.10.2 From 67696f6d79923cdc0084b73b4bbe52e6749a43a4 Mon Sep 17 00:00:00 2001 From: Eric Richter Date: Wed, 1 Jun 2016 13:14:05 -0500 Subject: ima: redefine duplicate template entries Template entry duplicates are prevented from being added to the measurement list by checking a hash table that contains the template entry digests. However, the PCR value is not included in this comparison, so duplicate template entry digests with differing PCRs may be dropped. This patch redefines duplicate template entries as template entries with the same digest and same PCR values. Reported-by: Mimi Zohar Signed-off-by: Eric Richter Signed-off-by: Mimi Zohar diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 552705d..04a9ac1 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -44,7 +44,8 @@ struct ima_h_table ima_htable = { static DEFINE_MUTEX(ima_extend_list_mutex); /* lookup up the digest value in the hash table, and return the entry */ -static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value) +static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value, + int pcr) { struct ima_queue_entry *qe, *ret = NULL; unsigned int key; @@ -54,7 +55,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value) rcu_read_lock(); hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) { rc = memcmp(qe->entry->digest, digest_value, TPM_DIGEST_SIZE); - if (rc == 0) { + if ((rc == 0) && (qe->entry->pcr == pcr)) { ret = qe; break; } @@ -118,7 +119,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, mutex_lock(&ima_extend_list_mutex); if (!violation) { memcpy(digest, entry->digest, sizeof(digest)); - if (ima_lookup_digest_entry(digest)) { + if (ima_lookup_digest_entry(digest, entry->pcr)) { audit_cause = "hash_exists"; result = -EEXIST; goto out; -- cgit v0.10.2 From a422638d492a35316e3fd9bb31bfc9769b249bca Mon Sep 17 00:00:00 2001 From: Eric Richter Date: Wed, 1 Jun 2016 13:14:06 -0500 Subject: ima: change integrity cache to store measured pcr IMA avoids re-measuring files by storing the current state as a flag in the integrity cache. It will then skip adding a new measurement log entry if the cache reports the file as already measured. If a policy measures an already measured file to a new PCR, the measurement will not be added to the list. This patch implements a new bitfield for specifying which PCR the file was measured into, rather than if it was measured. Signed-off-by: Eric Richter Signed-off-by: Mimi Zohar diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 8363ba3..9df26a2 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -266,7 +266,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, xattr_len, NULL}; int violation = 0; - if (iint->flags & IMA_MEASURED) + if (iint->measured_pcrs & (0x1 << pcr)) return; result = ima_alloc_init_template(&event_data, &entry); @@ -277,8 +277,10 @@ void ima_store_measurement(struct integrity_iint_cache *iint, } result = ima_store_template(entry, violation, inode, filename, pcr); - if (!result || result == -EEXIST) + if (!result || result == -EEXIST) { iint->flags |= IMA_MEASURED; + iint->measured_pcrs |= (0x1 << pcr); + } if (result < 0) ima_free_template_entry(entry); } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index fe8e923..4b9b4a4 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -370,6 +370,7 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig) return; iint->flags &= ~IMA_DONE_MASK; + iint->measured_pcrs = 0; if (digsig) iint->flags |= IMA_DIGSIG; return; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 3627afd..596ef61 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -125,6 +125,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, if ((iint->version != inode->i_version) || (iint->flags & IMA_NEW_FILE)) { iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); + iint->measured_pcrs = 0; if (iint->flags & IMA_APPRAISE) ima_update_xattr(iint, file); } @@ -210,7 +211,11 @@ static int process_measurement(struct file *file, char *buf, loff_t size, */ iint->flags |= action; action &= IMA_DO_MASK; - action &= ~((iint->flags & IMA_DONE_MASK) >> 1); + action &= ~((iint->flags & (IMA_DONE_MASK ^ IMA_MEASURED)) >> 1); + + /* If target pcr is already measured, unset IMA_MEASURE action */ + if ((action & IMA_MEASURE) && (iint->measured_pcrs & (0x1 << pcr))) + action ^= IMA_MEASURE; /* Nothing to do, just return existing appraised status */ if (!action) { -- cgit v0.10.2 From 544e1cea03e6674e3c12a3b8e8cc507c3dbeaf0c Mon Sep 17 00:00:00 2001 From: Eric Richter Date: Wed, 1 Jun 2016 13:14:07 -0500 Subject: ima: extend the measurement entry specific pcr Extend the PCR supplied as a parameter, instead of assuming that the measurement entry uses the default configured PCR. Signed-off-by: Eric Richter Signed-off-by: Mimi Zohar diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 04a9ac1..32f6ac0 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -90,14 +90,14 @@ static int ima_add_digest_entry(struct ima_template_entry *entry) return 0; } -static int ima_pcr_extend(const u8 *hash) +static int ima_pcr_extend(const u8 *hash, int pcr) { int result = 0; if (!ima_used_chip) return result; - result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash); + result = tpm_pcr_extend(TPM_ANY_NUM, pcr, hash); if (result != 0) pr_err("Error Communicating to TPM chip, result: %d\n", result); return result; @@ -136,7 +136,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, if (violation) /* invalidate pcr */ memset(digest, 0xff, sizeof(digest)); - tpmresult = ima_pcr_extend(digest); + tpmresult = ima_pcr_extend(digest, entry->pcr); if (tpmresult != 0) { snprintf(tpm_audit_cause, AUDIT_CAUSE_LEN_MAX, "TPM_error(%d)", tpmresult); -- cgit v0.10.2 From f6041c1d8a0825e41219a0443d365318d238d7b3 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Tue, 5 Jul 2016 23:53:19 -0700 Subject: samples/seccomp: Add standalone config option Add a separate Kconfig option for SAMPLES_SECCOMP. Main reason for this is that, just like other samples, it's forced to be a module. Without this, since the sample is a target only controlled by CONFIG_SECCOMP_FILTER, the samples will be built before include files are put in place properly. For example, from an arm64 allmodconfig built with "make -sk -j 32" (without specific target), the following happens: samples/seccomp/bpf-fancy.c:13:27: fatal error: linux/seccomp.h: No such file or directory samples/seccomp/bpf-helper.h:20:50: fatal error: linux/seccomp.h: No such file or directory samples/seccomp/dropper.c:20:27: fatal error: linux/seccomp.h: No such file or directory samples/seccomp/bpf-direct.c:21:27: fatal error: linux/seccomp.h: No such file or directory So, just stick to the same format as other samples. Signed-off-by: Olof Johansson Signed-off-by: Kees Cook diff --git a/samples/Kconfig b/samples/Kconfig index 559a58b..ccc50be 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -85,4 +85,11 @@ config SAMPLE_CONNECTOR with it. See also Documentation/connector/connector.txt +config SAMPLE_SECCOMP + tristate "Build seccomp sample code -- loadable modules only" + depends on SECCOMP_FILTER && m + help + Build samples of seccomp filters using various methods of + BPF filter construction. + endif # SAMPLES diff --git a/samples/seccomp/Makefile b/samples/seccomp/Makefile index 1b4e4b8..ae7ff6f 100644 --- a/samples/seccomp/Makefile +++ b/samples/seccomp/Makefile @@ -1,7 +1,7 @@ # kbuild trick to avoid linker error. Can be omitted if a module is built. obj- := dummy.o -hostprogs-$(CONFIG_SECCOMP_FILTER) := bpf-fancy dropper bpf-direct +hostprogs-$(CONFIG_SAMPLE_SECCOMP) := bpf-fancy dropper bpf-direct HOSTCFLAGS_bpf-fancy.o += -I$(objtree)/usr/include HOSTCFLAGS_bpf-fancy.o += -idirafter $(objtree)/include -- cgit v0.10.2 From ece38248cfca9d1b0ba183c923f0acd2e0bf6316 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Thu, 7 Jul 2016 12:39:11 -0700 Subject: MAINTAINERS - Location of the Smack repository The Smack working tree has moved from gitorious to github. Signed-off-by: Casey Schaufler diff --git a/MAINTAINERS b/MAINTAINERS index 7227c79..a296727 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10530,7 +10530,7 @@ SMACK SECURITY MODULE M: Casey Schaufler L: linux-security-module@vger.kernel.org W: http://schaufler-ca.com -T: git git://git.gitorious.org/smack-next/kernel.git +T: git git://github.com/cschaufler/smack-next S: Maintained F: Documentation/security/Smack.txt F: security/smack/ -- cgit v0.10.2 From dcda617a0c5160c73e0aa02813c871339ea08004 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Apr 2016 16:55:10 -0700 Subject: apparmor: fix refcount bug in profile replacement Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 705c287..222052f 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1189,12 +1189,12 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) aa_get_profile(newest); aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); - } else - aa_put_profile(newest); + } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); __list_add_profile(&parent->base.profiles, ent->new); + aa_put_profile(newest); } else { /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, -- cgit v0.10.2 From ec34fa24a934f4c8fd68f39b84abf34c42e5b06a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Apr 2016 16:57:19 -0700 Subject: apparmor: fix replacement bug that adds new child to old parent When set atomic replacement is used and the parent is updated before the child, and the child did not exist in the old parent so there is no direct replacement then the new child is incorrectly added to the old parent. This results in the new parent not having the child(ren) that it should and the old parent when being destroyed asserting the following error. AppArmor: policy_destroy: internal error, policy '' still contains profiles Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 222052f..c92a9f6 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1193,7 +1193,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, aa_get_profile(ent->new)); - __list_add_profile(&parent->base.profiles, ent->new); + __list_add_profile(&newest->base.profiles, ent->new); aa_put_profile(newest); } else { /* aafs interface uses replacedby */ -- cgit v0.10.2 From b6b1b81b3afba922505b57f4c812bba022f7c4a9 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 8 Jun 2014 11:20:54 -0700 Subject: apparmor: fix uninitialized lsm_audit member BugLink: http://bugs.launchpad.net/bugs/1268727 The task field in the lsm_audit struct needs to be initialized if a change_hat fails, otherwise the following oops will occur BUG: unable to handle kernel paging request at 0000002fbead7d08 IP: [] _raw_spin_lock+0xe/0x50 PGD 1e3f35067 PUD 0 Oops: 0002 [#1] SMP Modules linked in: pppox crc_ccitt p8023 p8022 psnap llc ax25 btrfs raid6_pq xor xfs libcrc32c dm_multipath scsi_dh kvm_amd dcdbas kvm microcode amd64_edac_mod joydev edac_core psmouse edac_mce_amd serio_raw k10temp sp5100_tco i2c_piix4 ipmi_si ipmi_msghandler acpi_power_meter mac_hid lp parport hid_generic usbhid hid pata_acpi mpt2sas ahci raid_class pata_atiixp bnx2 libahci scsi_transport_sas [last unloaded: tipc] CPU: 2 PID: 699 Comm: changehat_twice Tainted: GF O 3.13.0-7-generic #25-Ubuntu Hardware name: Dell Inc. PowerEdge R415/08WNM9, BIOS 1.8.6 12/06/2011 task: ffff8802135c6000 ti: ffff880212986000 task.ti: ffff880212986000 RIP: 0010:[] [] _raw_spin_lock+0xe/0x50 RSP: 0018:ffff880212987b68 EFLAGS: 00010006 RAX: 0000000000020000 RBX: 0000002fbead7500 RCX: 0000000000000000 RDX: 0000000000000292 RSI: ffff880212987ba8 RDI: 0000002fbead7d08 RBP: ffff880212987b68 R08: 0000000000000246 R09: ffff880216e572a0 R10: ffffffff815fd677 R11: ffffea0008469580 R12: ffffffff8130966f R13: ffff880212987ba8 R14: 0000002fbead7d08 R15: ffff8800d8c6b830 FS: 00002b5e6c84e7c0(0000) GS:ffff880216e40000(0000) knlGS:0000000055731700 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000002fbead7d08 CR3: 000000021270f000 CR4: 00000000000006e0 Stack: ffff880212987b98 ffffffff81075f17 ffffffff8130966f 0000000000000009 0000000000000000 0000000000000000 ffff880212987bd0 ffffffff81075f7c 0000000000000292 ffff880212987c08 ffff8800d8c6b800 0000000000000026 Call Trace: [] __lock_task_sighand+0x47/0x80 [] ? apparmor_cred_prepare+0x2f/0x50 [] do_send_sig_info+0x2c/0x80 [] send_sig_info+0x1e/0x30 [] aa_audit+0x13d/0x190 [] aa_audit_file+0xbc/0x130 [] ? apparmor_cred_prepare+0x2f/0x50 [] aa_change_hat+0x202/0x530 [] aa_setprocattr_changehat+0x116/0x1d0 [] apparmor_setprocattr+0x25d/0x300 [] security_setprocattr+0x16/0x20 [] proc_pid_attr_write+0x107/0x130 [] vfs_write+0xb4/0x1f0 [] SyS_write+0x49/0xa0 [] tracesys+0xe1/0xe6 Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index 89c7865..3a7f1da 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -200,7 +200,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, if (sa->aad->type == AUDIT_APPARMOR_KILL) (void)send_sig_info(SIGKILL, NULL, - sa->u.tsk ? sa->u.tsk : current); + sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? + sa->u.tsk : current); if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) return complain_error(sa->aad->error); diff --git a/security/apparmor/file.c b/security/apparmor/file.c index d186674..4d2af4b 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -110,7 +110,8 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - sa.type = LSM_AUDIT_DATA_NONE; + sa.type = LSM_AUDIT_DATA_TASK; + sa.u.tsk = NULL; sa.aad = &aad; aad.op = op, aad.fs.request = request; -- cgit v0.10.2 From 9049a7922124d843a2cd26a02b1d00a17596ec0c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:03 -0700 Subject: apparmor: exec should not be returning ENOENT when it denies The current behavior is confusing as it causes exec failures to report the executable is missing instead of identifying that apparmor caused the failure. Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index dc0027b..67a7418 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -433,7 +433,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) new_profile = aa_get_newest_profile(ns->unconfined); info = "ux fallback"; } else { - error = -ENOENT; + error = -EACCES; info = "profile not found"; /* remove MAY_EXEC to audit as failure */ perms.allow &= ~MAY_EXEC; -- cgit v0.10.2 From d671e890205a663429da74e1972e652bea4d73ab Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:01:56 -0700 Subject: apparmor: fix update the mtime of the profile file on replacement Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index ad4fa49..45a6199 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -379,6 +379,8 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old, for (i = 0; i < AAFS_PROF_SIZEOF; i++) { new->dents[i] = old->dents[i]; + if (new->dents[i]) + new->dents[i]->d_inode->i_mtime = CURRENT_TIME; old->dents[i] = NULL; } } -- cgit v0.10.2 From f2e561d190da7ff5ee265fa460e2d7f753dddfda Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:08 -0700 Subject: apparmor: fix disconnected bind mnts reconnection Bind mounts can fail to be properly reconnected when PATH_CONNECT is specified. Ensure that when PATH_CONNECT is specified the path has a root. BugLink: http://bugs.launchpad.net/bugs/1319984 Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/path.c b/security/apparmor/path.c index edddc02..f261678 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -141,7 +141,10 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, error = -EACCES; if (*res == '/') *name = res + 1; - } + } else if (*res != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + } out: -- cgit v0.10.2 From bd35db8b8ca6e27fc17a9057ef78e1ddfc0de351 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 25 Jul 2014 04:02:10 -0700 Subject: apparmor: internal paths should be treated as disconnected Internal mounts are not mounted anywhere and as such should be treated as disconnected paths. Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/path.c b/security/apparmor/path.c index f261678..a8fc7d0 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -25,7 +25,6 @@ #include "include/path.h" #include "include/policy.h" - /* modified from dcache.c */ static int prepend(char **buffer, int buflen, const char *str, int namelen) { @@ -39,6 +38,38 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) +/* If the path is not connected to the expected root, + * check if it is a sysctl and handle specially else remove any + * leading / that __d_path may have returned. + * Unless + * specifically directed to connect the path, + * OR + * if in a chroot and doing chroot relative paths and the path + * resolves to the namespace root (would be connected outside + * of chroot) and specifically directed to connect paths to + * namespace root. + */ +static int disconnect(const struct path *path, char *buf, char **name, + int flags) +{ + int error = 0; + + if (!(flags & PATH_CONNECT_PATH) && + !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && + our_mnt(path->mnt))) { + /* disconnected path, don't return pathname starting + * with '/' + */ + error = -EACCES; + if (**name == '/') + *name = *name + 1; + } else if (**name != '/') + /* CONNECT_PATH with missing root */ + error = prepend(name, *name - buf, "/", 1); + + return error; +} + /** * d_namespace_path - lookup a name associated with a given path * @path: path to lookup (NOT NULL) @@ -74,7 +105,8 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, * control instead of hard coded /proc */ return prepend(name, *name - buf, "/proc", 5); - } + } else + return disconnect(path, buf, name, flags); return 0; } @@ -120,32 +152,8 @@ static int d_namespace_path(const struct path *path, char *buf, int buflen, goto out; } - /* If the path is not connected to the expected root, - * check if it is a sysctl and handle specially else remove any - * leading / that __d_path may have returned. - * Unless - * specifically directed to connect the path, - * OR - * if in a chroot and doing chroot relative paths and the path - * resolves to the namespace root (would be connected outside - * of chroot) and specifically directed to connect paths to - * namespace root. - */ - if (!connected) { - if (!(flags & PATH_CONNECT_PATH) && - !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && - our_mnt(path->mnt))) { - /* disconnected path, don't return pathname starting - * with '/' - */ - error = -EACCES; - if (*res == '/') - *name = res + 1; - } else if (*res != '/') - /* CONNECT_PATH with missing root */ - error = prepend(name, *name - buf, "/", 1); - - } + if (!connected) + error = disconnect(path, buf, name, flags); out: return error; -- cgit v0.10.2 From 6059f71f1e94486a51cef90e872add11fa7b5775 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 24 Oct 2014 09:16:14 -0700 Subject: apparmor: add parameter to control whether policy hashing is used Signed-off-by: John Johansen Acked-by: Tyler Hicks Acked-by: Seth Arnold diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index 232469b..be5e941 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -31,13 +31,26 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE If you are unsure how to answer this question, answer 1. config SECURITY_APPARMOR_HASH - bool "SHA1 hash of loaded profiles" + bool "Enable introspection of sha1 hashes for loaded profiles" depends on SECURITY_APPARMOR select CRYPTO select CRYPTO_SHA1 default y help - This option selects whether sha1 hashing is done against loaded - profiles and exported for inspection to user space via the apparmor - filesystem. + This option selects whether introspection of loaded policy + is available to userspace via the apparmor filesystem. + +config SECURITY_APPARMOR_HASH_DEFAULT + bool "Enable policy hash introspection by default" + depends on SECURITY_APPARMOR_HASH + default y + + help + This option selects whether sha1 hashing of loaded policy + is enabled by default. The generation of sha1 hashes for + loaded policy provide system administrators a quick way + to verify that policy in the kernel matches what is expected, + however it can slow down policy load on some devices. In + these cases policy hashing can be disabled by default and + enabled only if needed. diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index e4ea626..5d721e9 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -37,6 +37,7 @@ extern enum audit_mode aa_g_audit; extern bool aa_g_audit_header; extern bool aa_g_debug; +extern bool aa_g_hash_policy; extern bool aa_g_lock_policy; extern bool aa_g_logsyscall; extern bool aa_g_paranoid_load; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 2660fbc..656b97c 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -669,6 +669,10 @@ enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE; module_param_call(mode, param_set_mode, param_get_mode, &aa_g_profile_mode, S_IRUSR | S_IWUSR); +/* whether policy verification hashing is enabled */ +bool aa_g_hash_policy = CONFIG_SECURITY_APPARMOR_HASH_DEFAULT; +module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); + /* Debug mode */ bool aa_g_debug; module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index a689f10..a55fb2f 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -775,8 +775,9 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) if (error) goto fail_profile; - error = aa_calc_profile_hash(profile, e.version, start, - e.pos - start); + if (aa_g_hash_policy) + error = aa_calc_profile_hash(profile, e.version, start, + e.pos - start); if (error) goto fail_profile; -- cgit v0.10.2 From f351841f8d41072e741e45299070d421a5833a4a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 13:59:02 -0700 Subject: apparmor: fix put() parent ref after updating the active ref Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index c92a9f6..455c9f8 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1187,8 +1187,8 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* parent replaced in this atomic set? */ if (newest != parent) { aa_get_profile(newest); - aa_put_profile(parent); rcu_assign_pointer(ent->new->parent, newest); + aa_put_profile(parent); } /* aafs interface uses replacedby */ rcu_assign_pointer(ent->new->replacedby->profile, -- cgit v0.10.2 From bf15cf0c641be8e57d45f110a9d91464f5bb461a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 14:16:50 -0700 Subject: apparmor: fix log failures for all profiles in a set currently only the profile that is causing the failure is logged. This makes it more confusing than necessary about which profiles loaded and which didn't. So make sure to log success and failure messages for all profiles in the set being loaded. Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 455c9f8..db31bc5 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1067,7 +1067,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, */ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) { - const char *ns_name, *name = NULL, *info = NULL; + const char *ns_name, *info = NULL; struct aa_namespace *ns = NULL; struct aa_load_ent *ent, *tmp; int op = OP_PROF_REPL; @@ -1082,18 +1082,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) /* released below */ ns = aa_prepare_namespace(ns_name); if (!ns) { - info = "failed to prepare namespace"; - error = -ENOMEM; - name = ns_name; - goto fail; + error = audit_policy(op, GFP_KERNEL, ns_name, + "failed to prepare namespace", -ENOMEM); + goto free; } mutex_lock(&ns->lock); /* setup parent and ns info */ list_for_each_entry(ent, &lh, list) { struct aa_policy *policy; - - name = ent->new->base.hname; error = __lookup_replace(ns, ent->new->base.hname, noreplace, &ent->old, &info); if (error) @@ -1121,7 +1118,6 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) if (!p) { error = -ENOENT; info = "parent does not exist"; - name = ent->new->base.hname; goto fail_lock; } rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); @@ -1214,9 +1210,22 @@ out: fail_lock: mutex_unlock(&ns->lock); -fail: - error = audit_policy(op, GFP_KERNEL, name, info, error); + /* audit cause of failure */ + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error); + /* audit status that rest of profiles in the atomic set failed too */ + info = "valid profile in failed atomic policy load"; + list_for_each_entry(tmp, &lh, list) { + if (tmp == ent) { + info = "unchecked profile in failed atomic policy load"; + /* skip entry that caused failure */ + continue; + } + op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error); + } +free: list_for_each_entry_safe(ent, tmp, &lh, list) { list_del_init(&ent->list); aa_load_ent_free(ent); -- cgit v0.10.2 From 7ee6da25dcce27b6023a8673fdf8be98dcf7cacf Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 16 Apr 2016 14:19:38 -0700 Subject: apparmor: fix audit full profile hname on successful load Currently logging of a successful profile load only logs the basename of the profile. This can result in confusion when a child profile has the same name as the another profile in the set. Logging the hname will ensure there is no confusion. Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index db31bc5..ca402d0 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -1159,7 +1159,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) list_del_init(&ent->list); op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; - audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); + audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); if (ent->old) { __replace_profile(ent->old, ent->new, 1); -- cgit v0.10.2 From f7da2de01127b58d93cebeab165136d0998e7b1a Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 20 Apr 2016 14:18:18 -0700 Subject: apparmor: ensure the target profile name is always audited The target profile name was not being correctly audited in a few cases because the target variable was not being set and gotos passed the code to set it at apply: Since it is always based on new_profile just drop the target var and conditionally report based on new_profile. Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 67a7418..fc3036b 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -346,7 +346,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) file_inode(bprm->file)->i_uid, file_inode(bprm->file)->i_mode }; - const char *name = NULL, *target = NULL, *info = NULL; + const char *name = NULL, *info = NULL; int error = 0; if (bprm->cred_prepared) @@ -399,6 +399,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (cxt->onexec) { struct file_perms cp; info = "change_profile onexec"; + new_profile = aa_get_newest_profile(cxt->onexec); if (!(perms.allow & AA_MAY_ONEXEC)) goto audit; @@ -413,7 +414,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (!(cp.allow & AA_MAY_ONEXEC)) goto audit; - new_profile = aa_get_newest_profile(cxt->onexec); goto apply; } @@ -445,10 +445,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (!new_profile) { error = -ENOMEM; info = "could not create null profile"; - } else { + } else error = -EACCES; - target = new_profile->base.hname; - } perms.xindex |= AA_X_UNSAFE; } else /* fail exec */ @@ -459,7 +457,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) * fail the exec. */ if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) { - aa_put_profile(new_profile); error = -EPERM; goto cleanup; } @@ -474,10 +471,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { error = may_change_ptraced_domain(new_profile); - if (error) { - aa_put_profile(new_profile); + if (error) goto audit; - } } /* Determine if secure exec is needed. @@ -498,7 +493,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) bprm->unsafe |= AA_SECURE_X_NEEDED; } apply: - target = new_profile->base.hname; /* when transitioning profiles clear unsafe personality bits */ bprm->per_clear |= PER_CLEAR_ON_SETID; @@ -506,15 +500,19 @@ x_clear: aa_put_profile(cxt->profile); /* transfer new profile reference will be released when cxt is freed */ cxt->profile = new_profile; + new_profile = NULL; /* clear out all temporary/transitional state from the context */ aa_clear_task_cxt_trans(cxt); audit: error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, - name, target, cond.uid, info, error); + name, + new_profile ? new_profile->base.hname : NULL, + cond.uid, info, error); cleanup: + aa_put_profile(new_profile); aa_put_profile(profile); kfree(buffer); -- cgit v0.10.2 From 23ca7b640b4a55f8747301b6bd984dd05545f6a7 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 17 Mar 2016 12:02:54 -0700 Subject: apparmor: check that xindex is in trans_table bounds Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index a55fb2f..951ae46 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -676,7 +676,7 @@ static bool verify_xindex(int xindex, int table_size) int index, xtype; xtype = xindex & AA_X_TYPE_MASK; index = xindex & AA_X_INDEX_MASK; - if (xtype == AA_X_TABLE && index > table_size) + if (xtype == AA_X_TABLE && index >= table_size) return 0; return 1; } -- cgit v0.10.2 From 0b938a2e2cf0b0a2c8bac9769111545aff0fee97 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 18 Nov 2015 11:41:05 -0800 Subject: apparmor: fix ref count leak when profile sha1 hash is read Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 45a6199..0d8dd71 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -331,6 +331,7 @@ static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) seq_printf(seq, "%.2x", profile->hash[i]); seq_puts(seq, "\n"); } + aa_put_profile(profile); return 0; } -- cgit v0.10.2 From de7c4cc947f9f56f61520ee7edaf380434a98c8d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 Dec 2015 18:09:10 -0800 Subject: apparmor: fix refcount race when finding a child profile When finding a child profile via an rcu critical section, the profile may be put and scheduled for deletion after the child is found but before its refcount is incremented. Protect against this by repeating the lookup if the profiles refcount is 0 and is one its way to deletion. Signed-off-by: John Johansen Acked-by: Seth Arnold diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index ca402d0..7807125 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -766,7 +766,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) struct aa_profile *profile; rcu_read_lock(); - profile = aa_get_profile(__find_child(&parent->base.profiles, name)); + do { + profile = __find_child(&parent->base.profiles, name); + } while (profile && !aa_get_profile_not0(profile)); rcu_read_unlock(); /* refcount released by caller */ -- cgit v0.10.2 From 38dbd7d8be36b5e68c96a24b406f3653180c1c03 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 16 Nov 2015 21:46:33 +0800 Subject: apparmor: use list_next_entry instead of list_entry_next list_next_entry has been defined in list.h, so I replace list_entry_next with it. Signed-off-by: Geliang Tang Acked-by: Serge Hallyn Signed-off-by: John Johansen diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 0d8dd71..729e595 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -553,8 +553,6 @@ fail2: } -#define list_entry_next(pos, member) \ - list_entry(pos->member.next, typeof(*pos), member) #define list_entry_is_head(pos, head, member) (&pos->member == (head)) /** @@ -585,7 +583,7 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root, parent = ns->parent; while (ns != root) { mutex_unlock(&ns->lock); - next = list_entry_next(ns, base.list); + next = list_next_entry(ns, base.list); if (!list_entry_is_head(next, &parent->sub_ns, base.list)) { mutex_lock(&next->lock); return next; @@ -639,7 +637,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) parent = rcu_dereference_protected(p->parent, mutex_is_locked(&p->ns->lock)); while (parent) { - p = list_entry_next(p, base.list); + p = list_next_entry(p, base.list); if (!list_entry_is_head(p, &parent->base.profiles, base.list)) return p; p = parent; @@ -648,7 +646,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) } /* is next another profile in the namespace */ - p = list_entry_next(p, base.list); + p = list_next_entry(p, base.list); if (!list_entry_is_head(p, &ns->base.profiles, base.list)) return p; -- cgit v0.10.2 From ff118479a76dbece9ae1c65c7c6a3ebe9cfa73e0 Mon Sep 17 00:00:00 2001 From: Jeff Mahoney Date: Fri, 6 Nov 2015 15:17:30 -0500 Subject: apparmor: allow SYS_CAP_RESOURCE to be sufficient to prlimit another task While using AppArmor, SYS_CAP_RESOURCE is insufficient to call prlimit on another task. The only other example of a AppArmor mediating access to another, already running, task (ignoring fork+exec) is ptrace. The AppArmor model for ptrace is that one of the following must be true: 1) The tracer is unconfined 2) The tracer is in complain mode 3) The tracer and tracee are confined by the same profile 4) The tracer is confined but has SYS_CAP_PTRACE 1), 2, and 3) are already true for setrlimit. We can match the ptrace model just by allowing CAP_SYS_RESOURCE. We still test the values of the rlimit since it can always be overridden using a value that means unlimited for a particular resource. Signed-off-by: Jeff Mahoney Signed-off-by: John Johansen diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 748bf0c..67a6072 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -101,9 +101,11 @@ int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, /* TODO: extend resource control to handle other (non current) * profiles. AppArmor rules currently have the implicit assumption * that the task is setting the resource of a task confined with - * the same profile. + * the same profile or that the task setting the resource of another + * task has CAP_SYS_RESOURCE. */ - if (profile != task_profile || + if ((profile != task_profile && + aa_capable(profile, CAP_SYS_RESOURCE, 1)) || (profile->rlimits.mask & (1 << resource) && new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) error = -EACCES; -- cgit v0.10.2 From 15756178c6a65b261a080e21af4766f59cafc112 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 2 Jun 2016 02:37:02 -0700 Subject: apparmor: add missing id bounds check on dfa verification Signed-off-by: John Johansen diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h index 001c43a..a1c04fe 100644 --- a/security/apparmor/include/match.h +++ b/security/apparmor/include/match.h @@ -62,6 +62,7 @@ struct table_set_header { #define YYTD_ID_ACCEPT2 6 #define YYTD_ID_NXT 7 #define YYTD_ID_TSIZE 8 +#define YYTD_ID_MAX 8 #define YYTD_DATA8 1 #define YYTD_DATA16 2 diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 727eb42..f9f57c6 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -47,6 +47,8 @@ static struct table_header *unpack_table(char *blob, size_t bsize) * it every time we use td_id as an index */ th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; + if (th.td_id > YYTD_ID_MAX) + goto out; th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); blob += sizeof(struct table_header); -- cgit v0.10.2 From 3197f5adf539a3ee6331f433a51483f8c842f890 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 15 Jun 2016 09:57:55 +0300 Subject: apparmor: don't check for vmalloc_addr if kvzalloc() failed Signed-off-by: John Johansen diff --git a/security/apparmor/match.c b/security/apparmor/match.c index f9f57c6..32b72eb 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -75,14 +75,14 @@ static struct table_header *unpack_table(char *blob, size_t bsize) u32, be32_to_cpu); else goto fail; + /* if table was vmalloced make sure the page tables are synced + * before it is used, as it goes live to all cpus. + */ + if (is_vmalloc_addr(table)) + vm_unmap_aliases(); } out: - /* if table was vmalloced make sure the page tables are synced - * before it is used, as it goes live to all cpus. - */ - if (is_vmalloc_addr(table)) - vm_unmap_aliases(); return table; fail: kvfree(table); -- cgit v0.10.2 From 5f20fdfed16bc599a325a145bf0123a8e1c9beea Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 15 Jun 2016 10:00:55 +0300 Subject: apparmor: fix oops in profile_unpack() when policy_db is not present BugLink: http://bugs.launchpad.net/bugs/1592547 If unpack_dfa() returns NULL due to the dfa not being present, profile_unpack() is not checking if the dfa is not present (NULL). Signed-off-by: John Johansen diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 951ae46..b9b1c66 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -583,6 +583,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) error = PTR_ERR(profile->policy.dfa); profile->policy.dfa = NULL; goto fail; + } else if (!profile->policy.dfa) { + error = -EPROTO; + goto fail; } if (!unpack_u32(e, &profile->policy.start[0], "start")) /* default start state */ -- cgit v0.10.2 From 58acf9d911c8831156634a44d0b022d683e1e50c Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 22 Jun 2016 18:01:08 -0700 Subject: apparmor: fix module parameters can be changed after policy is locked the policy_lock parameter is a one way switch that prevents policy from being further modified. Unfortunately some of the module parameters can effectively modify policy by turning off enforcement. split policy_admin_capable into a view check and a full admin check, and update the admin check to test the policy_lock parameter. Signed-off-by: John Johansen diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index c28b0f2..52275f0 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -403,6 +403,8 @@ static inline int AUDIT_MODE(struct aa_profile *profile) return profile->audit; } +bool policy_view_capable(void); +bool policy_admin_capable(void); bool aa_may_manage_policy(int op); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 656b97c..300f833 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -730,51 +730,49 @@ __setup("apparmor=", apparmor_enabled_setup); /* set global flag turning off the ability to load policy */ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; - if (aa_g_lock_policy) - return -EACCES; return param_set_bool(val, kp); } static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aabool(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; return param_set_bool(val, kp); } static int param_get_aabool(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aauint(const char *val, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; return param_set_uint(val, kp); } static int param_get_aauint(char *buffer, const struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; return param_get_uint(buffer, kp); } static int param_get_audit(char *buffer, struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_view_capable()) return -EPERM; if (!apparmor_enabled) @@ -786,7 +784,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) static int param_set_audit(const char *val, struct kernel_param *kp) { int i; - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) @@ -807,7 +805,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) static int param_get_mode(char *buffer, struct kernel_param *kp) { - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) @@ -819,7 +817,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) static int param_set_mode(const char *val, struct kernel_param *kp) { int i; - if (!capable(CAP_MAC_ADMIN)) + if (!policy_admin_capable()) return -EPERM; if (!apparmor_enabled) diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 7807125..179e68d 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -918,6 +918,22 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, &sa, NULL); } +bool policy_view_capable(void) +{ + struct user_namespace *user_ns = current_user_ns(); + bool response = false; + + if (ns_capable(user_ns, CAP_MAC_ADMIN)) + response = true; + + return response; +} + +bool policy_admin_capable(void) +{ + return policy_view_capable() && !aa_g_lock_policy; +} + /** * aa_may_manage_policy - can the current task manage policy * @op: the policy manipulation operation being done @@ -932,7 +948,7 @@ bool aa_may_manage_policy(int op) return 0; } - if (!capable(CAP_MAC_ADMIN)) { + if (!policy_admin_capable()) { audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); return 0; } -- cgit v0.10.2 From f4ee2def2d70692ccff0d55353df4ee594fd0017 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 10 Jun 2016 23:34:26 +0200 Subject: apparmor: do not expose kernel stack Do not copy uninitalized fields th.td_hilen, th.td_data. Signed-off-by: Heinrich Schuchardt Signed-off-by: John Johansen diff --git a/security/apparmor/match.c b/security/apparmor/match.c index 32b72eb..3f900fc 100644 --- a/security/apparmor/match.c +++ b/security/apparmor/match.c @@ -63,7 +63,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize) table = kvzalloc(tsize); if (table) { - *table = th; + table->td_id = th.td_id; + table->td_flags = th.td_flags; + table->td_lolen = th.td_lolen; if (th.td_flags == YYTD_DATA8) UNPACK_ARRAY(table->td_data, blob, th.td_lolen, u8, byte_to_byte); -- cgit v0.10.2 From e89b8081327ac9efbf273e790b8677e64fd0361a Mon Sep 17 00:00:00 2001 From: Vegard Nossum Date: Thu, 7 Jul 2016 13:41:11 -0700 Subject: apparmor: fix oops, validate buffer size in apparmor_setprocattr() When proc_pid_attr_write() was changed to use memdup_user apparmor's (interface violating) assumption that the setprocattr buffer was always a single page was violated. The size test is not strictly speaking needed as proc_pid_attr_write() will reject anything larger, but for the sake of robustness we can keep it in. SMACK and SELinux look safe to me, but somebody else should probably have a look just in case. Based on original patch from Vegard Nossum modified for the case that apparmor provides null termination. Fixes: bb646cdb12e75d82258c2f2e7746d5952d3e321a Reported-by: Vegard Nossum Cc: Al Viro Cc: John Johansen Cc: Paul Moore Cc: Stephen Smalley Cc: Eric Paris Cc: Casey Schaufler Cc: stable@kernel.org Signed-off-by: John Johansen Reviewed-by: Tyler Hicks Signed-off-by: James Morris diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 300f833..c6921a0 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -500,34 +500,34 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - char *command, *args = value; + char *command, *largs = NULL, *args = value; size_t arg_size; int error; if (size == 0) return -EINVAL; - /* args points to a PAGE_SIZE buffer, AppArmor requires that - * the buffer must be null terminated or have size <= PAGE_SIZE -1 - * so that AppArmor can null terminate them - */ - if (args[size - 1] != '\0') { - if (size == PAGE_SIZE) - return -EINVAL; - args[size] = '\0'; - } - /* task can only write its own attributes */ if (current != task) return -EACCES; - args = value; + /* AppArmor requires that the buffer must be null terminated atm */ + if (args[size - 1] != '\0') { + /* null terminate */ + largs = args = kmalloc(size + 1, GFP_KERNEL); + if (!args) + return -ENOMEM; + memcpy(args, value, size); + args[size] = '\0'; + } + + error = -EINVAL; args = strim(args); command = strsep(&args, " "); if (!args) - return -EINVAL; + goto out; args = skip_spaces(args); if (!*args) - return -EINVAL; + goto out; arg_size = size - (args - (char *) value); if (strcmp(name, "current") == 0) { @@ -553,10 +553,12 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, goto fail; } else /* only support the "current" and "exec" process attributes */ - return -EINVAL; + goto fail; if (!error) error = size; +out: + kfree(largs); return error; fail: @@ -565,9 +567,9 @@ fail: aad.profile = aa_current_profile(); aad.op = OP_SETPROCATTR; aad.info = name; - aad.error = -EINVAL; + aad.error = error = -EINVAL; aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); - return -EINVAL; + goto out; } static int apparmor_task_setrlimit(struct task_struct *task, -- cgit v0.10.2 From d4d03f74a73f3b8b2801d4d02011b6b69778cbcc Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sat, 9 Jul 2016 23:46:33 -0700 Subject: apparmor: fix arg_size computation for when setprocattr is null terminated Signed-off-by: John Johansen diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index c6921a0..3be30c7 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -529,7 +529,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, if (!*args) goto out; - arg_size = size - (args - (char *) value); + arg_size = size - (args - (largs ? largs : (char *) value)); if (strcmp(name, "current") == 0) { if (strcmp(command, "changehat") == 0) { error = aa_setprocattr_changehat(args, arg_size, -- cgit v0.10.2 From 79b591c091b31291fecd9551db0a67fdc5901d50 Mon Sep 17 00:00:00 2001 From: "apronin@chromium.org" Date: Thu, 14 Jul 2016 17:29:40 -0700 Subject: tpm_tis_core: convert max timeouts from msec to jiffies tpm_tis_core was missing conversion from msec when assigning max timeouts from constants. Fixes: aec04cbdf723 ("tpm: TPM 2.0 FIFO Interface") Signed-off-by: Andrey Pronin Reviewed-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 03a06b3..ace95b3 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -671,10 +671,10 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, #endif /* Maximum timeouts */ - chip->timeout_a = TIS_TIMEOUT_A_MAX; - chip->timeout_b = TIS_TIMEOUT_B_MAX; - chip->timeout_c = TIS_TIMEOUT_C_MAX; - chip->timeout_d = TIS_TIMEOUT_D_MAX; + chip->timeout_a = msecs_to_jiffies(TIS_TIMEOUT_A_MAX); + chip->timeout_b = msecs_to_jiffies(TIS_TIMEOUT_B_MAX); + chip->timeout_c = msecs_to_jiffies(TIS_TIMEOUT_C_MAX); + chip->timeout_d = msecs_to_jiffies(TIS_TIMEOUT_D_MAX); priv->phy_ops = phy_ops; dev_set_drvdata(&chip->dev, priv); -- cgit v0.10.2 From 1b0612b04090e416828c0dd5ed197b0913d834a0 Mon Sep 17 00:00:00 2001 From: "apronin@chromium.org" Date: Thu, 14 Jul 2016 18:07:18 -0700 Subject: tpm: fix byte-order for the value read by tpm2_get_tpm_pt The result must be converted from BE byte order, which is used by the TPM2 protocol. This has not popped out because tpm2_get_tpm_pt() has been only used for probing. Fixes: 7a1d7e6dd76a ("tpm: TPM 2.0 baseline support") Change-Id: I7d71cd379b1a3b7659d20a1b6008216762596590 Signed-off-by: Andrey Pronin Reviewed-by: Jason Gunthorpe Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index a1673dc..a88b31e 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -703,7 +703,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, rc = tpm_transmit_cmd(chip, &cmd, sizeof(cmd), desc); if (!rc) - *value = cmd.params.get_tpm_pt_out.value; + *value = be32_to_cpu(cmd.params.get_tpm_pt_out.value); return rc; } -- cgit v0.10.2 From 9754d45e997000ad4021bc4606cc266bb38d876f Mon Sep 17 00:00:00 2001 From: Andrey Pronin Date: Thu, 30 Jun 2016 10:25:43 -0700 Subject: tpm: read burstcount from TPM_STS in one 32-bit transaction Some chips incorrectly support partial reads from TPM_STS register at non-zero offsets. Read the entire 32-bits register instead of making two 8-bit reads to support such devices and reduce the number of bus transactions when obtaining the burstcount from TPM_STS. Fixes: 27084efee0c3 ("tpm: driver for next generation TPM chips") Signed-off-by: Andrey Pronin Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index ace95b3..fb8c3de 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -157,22 +157,17 @@ static int get_burstcount(struct tpm_chip *chip) struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); unsigned long stop; int burstcnt, rc; - u8 value; + u32 value; /* wait for burstcount */ /* which timeout value, spec has 2 answers (c & d) */ stop = jiffies + chip->timeout_d; do { - rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 1, &value); + rc = tpm_tis_read32(priv, TPM_STS(priv->locality), &value); if (rc < 0) return rc; - burstcnt = value; - rc = tpm_tis_read8(priv, TPM_STS(priv->locality) + 2, &value); - if (rc < 0) - return rc; - - burstcnt += value << 8; + burstcnt = (value >> 8) & 0xFFFF; if (burstcnt) return burstcnt; msleep(TPM_TIMEOUT); -- cgit v0.10.2 From fa7539b27f4a9d0bfaede2f7547c2322ac68f1f2 Mon Sep 17 00:00:00 2001 From: Andrew Zamansky Date: Wed, 29 Jun 2016 12:58:49 +0300 Subject: tpm2_i2c_nuvoton: add irq validity check In 570a3609 IRQ path is incorrectly always exercised while it should be exercised only when there is an IRQ number allocated. This commit reverts the old behavior. [jarkko.sakkinen@linux.intel.com: updated description] Fixes: 570a36097f3 ("tpm: drop 'irq' from struct tpm_vendor_specific") Signed-off-by: Andrew Zamansky Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 4e32094..8266982 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -559,10 +559,8 @@ static int i2c_nuvoton_probe(struct i2c_client *client, * TPM_INTF_INT_LEVEL_LOW | TPM_INTF_DATA_AVAIL_INT * The IRQ should be set in the i2c_board_info (which is done * automatically in of_i2c_register_devices, for device tree users */ - chip->flags |= TPM_CHIP_FLAG_IRQ; priv->irq = client->irq; - - if (chip->flags & TPM_CHIP_FLAG_IRQ) { + if (client->irq) { dev_dbg(dev, "%s() priv->irq\n", __func__); rc = devm_request_irq(dev, client->irq, i2c_nuvoton_int_handler, @@ -572,9 +570,9 @@ static int i2c_nuvoton_probe(struct i2c_client *client, if (rc) { dev_err(dev, "%s() Unable to request irq: %d for use\n", __func__, priv->irq); - chip->flags &= ~TPM_CHIP_FLAG_IRQ; priv->irq = 0; } else { + chip->flags |= TPM_CHIP_FLAG_IRQ; /* Clear any pending interrupt */ i2c_nuvoton_ready(chip); /* - wait for TPM_STS==0xA0 (stsValid, commandReady) */ -- cgit v0.10.2 From 2b88cd96eb85d2f07873e6050e531df45d8a0cc8 Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Sun, 12 Jun 2016 15:05:29 +0100 Subject: tpm: use devm_add_action_or_reset If devm_add_action() fails we are explicitly calling put_device() to free the resources allocated. Lets use the helper devm_add_action_or_reset() and return directly in case of error, as we know that the cleanup function has been already called by the helper if there was any error. Signed-off-by: Sudip Mukherjee Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 1965dc7..5a2f043 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -213,11 +213,11 @@ struct tpm_chip *tpmm_chip_alloc(struct device *pdev, if (IS_ERR(chip)) return chip; - rc = devm_add_action(pdev, (void (*)(void *)) put_device, &chip->dev); - if (rc) { - put_device(&chip->dev); + rc = devm_add_action_or_reset(pdev, + (void (*)(void *)) put_device, + &chip->dev); + if (rc) return ERR_PTR(rc); - } dev_set_drvdata(pdev, chip); -- cgit v0.10.2 From cae8b441fc20812d7260dc3b45e05ee98fcfff1b Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 12 Jul 2016 11:41:49 -0600 Subject: tpm: Factor out common startup code The TCG standard startup sequence (get timeouts, tpm startup, etc) for TPM and TPM2 chips is being open coded in many drivers, move it into the core code. tpm_tis and tpm_crb are used as the basis for the core code implementation and the easy drivers are converted. In the process several small drivers bugs relating to error handling this flow are fixed. For now the flag TPM_OPS_AUTO_STARTUP is optional to allow a staged driver roll out, but ultimately all drivers should use this flow and the flag removed. Some drivers still do not implement the startup sequence at all and will need to be tested with it enabled. Signed-off-by: Jason Gunthorpe Tested-by: Andrew Zamansky Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c index a7c99a2..c2ee304 100644 --- a/drivers/char/tpm/st33zp24/st33zp24.c +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -505,6 +505,7 @@ static bool st33zp24_req_canceled(struct tpm_chip *chip, u8 status) } static const struct tpm_class_ops st33zp24_tpm = { + .flags = TPM_OPS_AUTO_STARTUP, .send = st33zp24_send, .recv = st33zp24_recv, .cancel = st33zp24_cancel, @@ -592,9 +593,6 @@ int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, tpm_gen_interrupt(chip); } - tpm_get_timeouts(chip); - tpm_do_selftest(chip); - return tpm_chip_register(chip); _tpm_clean_answer: dev_info(&chip->dev, "TPM initialization fail\n"); diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 5a2f043..e595013 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -354,6 +354,15 @@ int tpm_chip_register(struct tpm_chip *chip) { int rc; + if (chip->ops->flags & TPM_OPS_AUTO_STARTUP) { + if (chip->flags & TPM_CHIP_FLAG_TPM2) + rc = tpm2_auto_startup(chip); + else + rc = tpm1_auto_startup(chip); + if (rc) + return rc; + } + rc = tpm1_chip_register(chip); if (rc) return rc; diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 5e3c1b6..1abe2d7 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -843,6 +843,33 @@ int tpm_do_selftest(struct tpm_chip *chip) } EXPORT_SYMBOL_GPL(tpm_do_selftest); +/** + * tpm1_auto_startup - Perform the standard automatic TPM initialization + * sequence + * @chip: TPM chip to use + * + * Returns 0 on success, < 0 in case of fatal error. + */ +int tpm1_auto_startup(struct tpm_chip *chip) +{ + int rc; + + rc = tpm_get_timeouts(chip); + if (rc) + goto out; + rc = tpm_do_selftest(chip); + if (rc) { + dev_err(&chip->dev, "TPM self test failed\n"); + goto out; + } + + return rc; +out: + if (rc > 0) + rc = -ENODEV; + return rc; +} + int tpm_send(u32 chip_num, void *cmd, size_t buflen) { struct tpm_chip *chip; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 8890df2..3e32d5b 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -484,6 +484,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, void *cmd, int len, const char *desc); extern int tpm_get_timeouts(struct tpm_chip *); extern void tpm_gen_interrupt(struct tpm_chip *); +int tpm1_auto_startup(struct tpm_chip *chip); extern int tpm_do_selftest(struct tpm_chip *); extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32); extern int tpm_pm_suspend(struct device *); @@ -526,10 +527,9 @@ int tpm2_unseal_trusted(struct tpm_chip *chip, ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc); -extern int tpm2_startup(struct tpm_chip *chip, u16 startup_type); +int tpm2_auto_startup(struct tpm_chip *chip); extern void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type); extern unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *, u32); -extern int tpm2_do_selftest(struct tpm_chip *chip); extern int tpm2_gen_interrupt(struct tpm_chip *chip); extern int tpm2_probe(struct tpm_chip *chip); #endif diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index a88b31e..08c7e23 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -728,7 +728,7 @@ static const struct tpm_input_header tpm2_startup_header = { * returned it remarks a POSIX error code. If a positive number is returned * it remarks a TPM error. */ -int tpm2_startup(struct tpm_chip *chip, u16 startup_type) +static int tpm2_startup(struct tpm_chip *chip, u16 startup_type) { struct tpm2_cmd cmd; @@ -738,7 +738,6 @@ int tpm2_startup(struct tpm_chip *chip, u16 startup_type) return tpm_transmit_cmd(chip, &cmd, sizeof(cmd), "attempting to start the TPM"); } -EXPORT_SYMBOL_GPL(tpm2_startup); #define TPM2_SHUTDOWN_IN_SIZE \ (sizeof(struct tpm_input_header) + \ @@ -854,7 +853,7 @@ static int tpm2_start_selftest(struct tpm_chip *chip, bool full) * returned it remarks a POSIX error code. If a positive number is returned * it remarks a TPM error. */ -int tpm2_do_selftest(struct tpm_chip *chip) +static int tpm2_do_selftest(struct tpm_chip *chip) { int rc; unsigned int loops; @@ -894,7 +893,6 @@ int tpm2_do_selftest(struct tpm_chip *chip) return rc; } -EXPORT_SYMBOL_GPL(tpm2_do_selftest); /** * tpm2_gen_interrupt() - generate an interrupt @@ -942,3 +940,43 @@ int tpm2_probe(struct tpm_chip *chip) return 0; } EXPORT_SYMBOL_GPL(tpm2_probe); + +/** + * tpm2_auto_startup - Perform the standard automatic TPM initialization + * sequence + * @chip: TPM chip to use + * + * Returns 0 on success, < 0 in case of fatal error. + */ +int tpm2_auto_startup(struct tpm_chip *chip) +{ + int rc; + + rc = tpm_get_timeouts(chip); + if (rc) + goto out; + + rc = tpm2_do_selftest(chip); + if (rc != TPM2_RC_INITIALIZE) { + dev_err(&chip->dev, "TPM self test failed\n"); + goto out; + } + + if (rc == TPM2_RC_INITIALIZE) { + rc = tpm2_startup(chip, TPM2_SU_CLEAR); + if (rc) + goto out; + + rc = tpm2_do_selftest(chip); + if (rc) { + dev_err(&chip->dev, "TPM self test failed\n"); + goto out; + } + } + + return rc; +out: + if (rc > 0) + rc = -ENODEV; + return rc; +} diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 1b8e1b5..018c3825 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -188,6 +188,7 @@ static bool crb_req_canceled(struct tpm_chip *chip, u8 status) } static const struct tpm_class_ops tpm_crb = { + .flags = TPM_OPS_AUTO_STARTUP, .status = crb_status, .recv = crb_recv, .send = crb_send, @@ -200,7 +201,6 @@ static const struct tpm_class_ops tpm_crb = { static int crb_init(struct acpi_device *device, struct crb_priv *priv) { struct tpm_chip *chip; - int rc; chip = tpmm_chip_alloc(&device->dev, &tpm_crb); if (IS_ERR(chip)) @@ -210,14 +210,6 @@ static int crb_init(struct acpi_device *device, struct crb_priv *priv) chip->acpi_dev_handle = device->handle; chip->flags = TPM_CHIP_FLAG_TPM2; - rc = tpm_get_timeouts(chip); - if (rc) - return rc; - - rc = tpm2_do_selftest(chip); - if (rc) - return rc; - return tpm_chip_register(chip); } diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index c37aa72..95ce2e9 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -141,6 +141,7 @@ static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status) } static const struct tpm_class_ops i2c_atmel = { + .flags = TPM_OPS_AUTO_STARTUP, .status = i2c_atmel_read_status, .recv = i2c_atmel_recv, .send = i2c_atmel_send, @@ -179,11 +180,6 @@ static int i2c_atmel_probe(struct i2c_client *client, /* There is no known way to probe for this device, and all version * information seems to be read via TPM commands. Thus we rely on the * TPM startup process in the common code to detect the device. */ - if (tpm_get_timeouts(chip)) - return -ENODEV; - - if (tpm_do_selftest(chip)) - return -ENODEV; return tpm_chip_register(chip); } diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index a426b6f..62ee44e 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -567,6 +567,7 @@ static bool tpm_tis_i2c_req_canceled(struct tpm_chip *chip, u8 status) } static const struct tpm_class_ops tpm_tis_i2c = { + .flags = TPM_OPS_AUTO_STARTUP, .status = tpm_tis_i2c_status, .recv = tpm_tis_i2c_recv, .send = tpm_tis_i2c_send, @@ -619,9 +620,6 @@ static int tpm_tis_i2c_init(struct device *dev) tpm_dev.chip = chip; - tpm_get_timeouts(chip); - tpm_do_selftest(chip); - return tpm_chip_register(chip); out_release: release_locality(chip, tpm_dev.locality, 1); diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 8266982..6e404e0 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -461,6 +461,7 @@ static bool i2c_nuvoton_req_canceled(struct tpm_chip *chip, u8 status) } static const struct tpm_class_ops tpm_i2c = { + .flags = TPM_OPS_AUTO_STARTUP, .status = i2c_nuvoton_read_status, .recv = i2c_nuvoton_recv, .send = i2c_nuvoton_send, @@ -607,12 +608,6 @@ static int i2c_nuvoton_probe(struct i2c_client *client, } } - if (tpm_get_timeouts(chip)) - return -ENODEV; - - if (tpm_do_selftest(chip)) - return -ENODEV; - return tpm_chip_register(chip); } diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index fb8c3de..d66f51b 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -638,6 +638,7 @@ void tpm_tis_remove(struct tpm_chip *chip) EXPORT_SYMBOL_GPL(tpm_tis_remove); static const struct tpm_class_ops tpm_tis = { + .flags = TPM_OPS_AUTO_STARTUP, .status = tpm_tis_status, .recv = tpm_tis_recv, .send = tpm_tis_send, @@ -773,29 +774,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, } } - if (chip->flags & TPM_CHIP_FLAG_TPM2) { - rc = tpm2_do_selftest(chip); - if (rc == TPM2_RC_INITIALIZE) { - dev_warn(dev, "Firmware has not started TPM\n"); - rc = tpm2_startup(chip, TPM2_SU_CLEAR); - if (!rc) - rc = tpm2_do_selftest(chip); - } - - if (rc) { - dev_err(dev, "TPM self test failed\n"); - if (rc > 0) - rc = -ENODEV; - goto out_err; - } - } else { - if (tpm_do_selftest(chip)) { - dev_err(dev, "TPM self test failed\n"); - rc = -ENODEV; - goto out_err; - } - } - return tpm_chip_register(chip); out_err: tpm_tis_remove(chip); diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c index 86e27e8..9a94033 100644 --- a/drivers/char/tpm/tpm_vtpm_proxy.c +++ b/drivers/char/tpm/tpm_vtpm_proxy.c @@ -346,6 +346,7 @@ static bool vtpm_proxy_tpm_req_canceled(struct tpm_chip *chip, u8 status) } static const struct tpm_class_ops vtpm_proxy_tpm_ops = { + .flags = TPM_OPS_AUTO_STARTUP, .recv = vtpm_proxy_tpm_op_recv, .send = vtpm_proxy_tpm_op_send, .cancel = vtpm_proxy_tpm_op_cancel, @@ -366,14 +367,6 @@ static void vtpm_proxy_work(struct work_struct *work) work); int rc; - if (proxy_dev->flags & VTPM_PROXY_FLAG_TPM2) - rc = tpm2_startup(proxy_dev->chip, TPM2_SU_CLEAR); - else - rc = tpm_get_timeouts(proxy_dev->chip); - - if (rc) - goto err; - rc = tpm_chip_register(proxy_dev->chip); if (rc) goto err; diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 706e63e..da158f0 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -33,7 +33,12 @@ struct tpm_chip; struct trusted_key_payload; struct trusted_key_options; +enum TPM_OPS_FLAGS { + TPM_OPS_AUTO_STARTUP = BIT(0), +}; + struct tpm_class_ops { + unsigned int flags; const u8 req_complete_mask; const u8 req_complete_val; bool (*req_canceled)(struct tpm_chip *chip, u8 status); -- cgit v0.10.2 From 82cc1a49b6358394938e759dc4c22b2be773bbad Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 12 Jul 2016 11:41:50 -0600 Subject: tpm: Add TPM 2.0 support to the Nuvoton i2c driver (NPCT6xx family) The command flow is exactly the same, the core simply needs to be told to enable TPM2 mode when the compatible string indicates a TPM2. Signed-off-by: Andrew Azmansky Tested-by: Andrew Zamansky Signed-off-by: Jason Gunthorpe Acked-by: Rob Herring Reviewed-by: Jarkko Sakkinen Signed-off-by: Jarkko Sakkinen diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt index 53987449..d4f6542 100644 --- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt +++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt @@ -62,6 +62,7 @@ national,lm80 Serial Interface ACPI-Compatible Microprocessor System Hardware M national,lm85 Temperature sensor with integrated fan control national,lm92 ±0.33°C Accurate, 12-Bit + Sign Temperature Sensor and Thermal Window Comparator with Two-Wire Interface nuvoton,npct501 i2c trusted platform module (TPM) +nuvoton,npct601 i2c trusted platform module (TPM2) nxp,pca9556 Octal SMBus and I2C registered interface nxp,pca9557 8-bit I2C-bus and SMBus I/O port with reset nxp,pcf8563 Real-time clock/calendar diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 6e404e0..e3a9155 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -1,5 +1,5 @@ -/****************************************************************************** - * Nuvoton TPM I2C Device Driver Interface for WPCT301/NPCT501, + /****************************************************************************** + * Nuvoton TPM I2C Device Driver Interface for WPCT301/NPCT501/NPCT6XX, * based on the TCG TPM Interface Spec version 1.2. * Specifications at www.trustedcomputinggroup.org * @@ -31,6 +31,7 @@ #include #include #include +#include #include "tpm.h" /* I2C interface offsets */ @@ -52,7 +53,8 @@ #define TPM_I2C_RETRY_DELAY_SHORT 2 /* msec */ #define TPM_I2C_RETRY_DELAY_LONG 10 /* msec */ -#define I2C_DRIVER_NAME "tpm_i2c_nuvoton" +#define OF_IS_TPM2 ((void *)1) +#define I2C_IS_TPM2 1 struct priv_data { int irq; @@ -165,7 +167,7 @@ static int i2c_nuvoton_get_burstcount(struct i2c_client *client, } /* - * WPCT301/NPCT501 SINT# supports only dataAvail + * WPCT301/NPCT501/NPCT6XX SINT# supports only dataAvail * any call to this function which is not waiting for dataAvail will * set queue to NULL to avoid waiting for interrupt */ @@ -545,6 +547,16 @@ static int i2c_nuvoton_probe(struct i2c_client *client, if (!priv) return -ENOMEM; + if (dev->of_node) { + const struct of_device_id *of_id; + + of_id = of_match_device(dev->driver->of_match_table, dev); + if (of_id && of_id->data == OF_IS_TPM2) + chip->flags |= TPM_CHIP_FLAG_TPM2; + } else + if (id->driver_data == I2C_IS_TPM2) + chip->flags |= TPM_CHIP_FLAG_TPM2; + init_waitqueue_head(&priv->read_queue); /* Default timeouts */ @@ -620,7 +632,8 @@ static int i2c_nuvoton_remove(struct i2c_client *client) } static const struct i2c_device_id i2c_nuvoton_id[] = { - {I2C_DRIVER_NAME, 0}, + {"tpm_i2c_nuvoton"}, + {"tpm2_i2c_nuvoton", .driver_data = I2C_IS_TPM2}, {} }; MODULE_DEVICE_TABLE(i2c, i2c_nuvoton_id); @@ -629,6 +642,7 @@ MODULE_DEVICE_TABLE(i2c, i2c_nuvoton_id); static const struct of_device_id i2c_nuvoton_of_match[] = { {.compatible = "nuvoton,npct501"}, {.compatible = "winbond,wpct301"}, + {.compatible = "nuvoton,npct601", .data = OF_IS_TPM2}, {}, }; MODULE_DEVICE_TABLE(of, i2c_nuvoton_of_match); @@ -641,7 +655,7 @@ static struct i2c_driver i2c_nuvoton_driver = { .probe = i2c_nuvoton_probe, .remove = i2c_nuvoton_remove, .driver = { - .name = I2C_DRIVER_NAME, + .name = "tpm_i2c_nuvoton", .pm = &i2c_nuvoton_pm_ops, .of_match_table = of_match_ptr(i2c_nuvoton_of_match), }, -- cgit v0.10.2 From 7616ac70d1bb4f2e9d25c1a82d283f3368a7b632 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 25 Jul 2016 10:59:07 -0700 Subject: apparmor: fix SECURITY_APPARMOR_HASH_DEFAULT parameter handling The newly added Kconfig option could never work and just causes a build error when disabled: security/apparmor/lsm.c:675:25: error: 'CONFIG_SECURITY_APPARMOR_HASH_DEFAULT' undeclared here (not in a function) bool aa_g_hash_policy = CONFIG_SECURITY_APPARMOR_HASH_DEFAULT; The problem is that the macro undefined in this case, and we need to use the IS_ENABLED() helper to turn it into a boolean constant. Another minor problem with the original patch is that the option is even offered in sysfs when SECURITY_APPARMOR_HASH is not enabled, so this also hides the option in that case. Signed-off-by: Arnd Bergmann Fixes: 6059f71f1e94 ("apparmor: add parameter to control whether policy hashing is used") Signed-off-by: John Johansen Signed-off-by: James Morris diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c index 532471d..b75dab0 100644 --- a/security/apparmor/crypto.c +++ b/security/apparmor/crypto.c @@ -39,6 +39,9 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, int error = -ENOMEM; u32 le32_version = cpu_to_le32(version); + if (!aa_g_hash_policy) + return 0; + if (!apparmor_tfm) return 0; diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 3be30c7..41b8cb1 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -671,9 +671,11 @@ enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE; module_param_call(mode, param_set_mode, param_get_mode, &aa_g_profile_mode, S_IRUSR | S_IWUSR); +#ifdef CONFIG_SECURITY_APPARMOR_HASH /* whether policy verification hashing is enabled */ -bool aa_g_hash_policy = CONFIG_SECURITY_APPARMOR_HASH_DEFAULT; +bool aa_g_hash_policy = IS_ENABLED(CONFIG_SECURITY_APPARMOR_HASH_DEFAULT); module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); +#endif /* Debug mode */ bool aa_g_debug; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index b9b1c66..1381206 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -778,8 +778,7 @@ int aa_unpack(void *udata, size_t size, struct list_head *lh, const char **ns) if (error) goto fail_profile; - if (aa_g_hash_policy) - error = aa_calc_profile_hash(profile, e.version, start, + error = aa_calc_profile_hash(profile, e.version, start, e.pos - start); if (error) goto fail_profile; -- cgit v0.10.2