diff options
Diffstat (limited to 'security')
-rw-r--r-- | security/Kconfig | 3 | ||||
-rw-r--r-- | security/Makefile | 2 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 1 | ||||
-rw-r--r-- | security/selinux/hooks.c | 146 | ||||
-rw-r--r-- | security/selinux/include/xfrm.h | 9 | ||||
-rw-r--r-- | security/selinux/netlabel.c | 6 | ||||
-rw-r--r-- | security/selinux/ss/policydb.c | 22 | ||||
-rw-r--r-- | security/selinux/xfrm.c | 53 | ||||
-rw-r--r-- | security/tlk_driver/Kconfig | 14 | ||||
-rw-r--r-- | security/tlk_driver/Makefile | 30 | ||||
-rw-r--r-- | security/tlk_driver/ote_asm.S | 74 | ||||
-rw-r--r-- | security/tlk_driver/ote_comms.c | 547 | ||||
-rw-r--r-- | security/tlk_driver/ote_device.c | 769 | ||||
-rw-r--r-- | security/tlk_driver/ote_fs.c | 175 | ||||
-rw-r--r-- | security/tlk_driver/ote_log.c | 204 | ||||
-rw-r--r-- | security/tlk_driver/ote_protocol.h | 341 | ||||
-rw-r--r-- | security/tlk_driver/ote_types.h | 79 |
17 files changed, 2421 insertions, 54 deletions
diff --git a/security/Kconfig b/security/Kconfig index e9c6ac724fef..788af6d871e1 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -103,7 +103,7 @@ config INTEL_TXT config LSM_MMAP_MIN_ADDR int "Low address space for LSM to protect from user allocation" depends on SECURITY && SECURITY_SELINUX - default 32768 if ARM + default 32768 if ARM || (ARM64 && COMPAT) default 65536 help This is the portion of low virtual memory which should be protected @@ -121,6 +121,7 @@ source security/selinux/Kconfig source security/smack/Kconfig source security/tomoyo/Kconfig source security/apparmor/Kconfig +source security/tlk_driver/Kconfig source security/yama/Kconfig source security/integrity/Kconfig diff --git a/security/Makefile b/security/Makefile index c26c81e92571..60fa993aefcb 100644 --- a/security/Makefile +++ b/security/Makefile @@ -8,6 +8,7 @@ subdir-$(CONFIG_SECURITY_SMACK) += smack subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor subdir-$(CONFIG_SECURITY_YAMA) += yama +subdir-$(CONFIG_TRUSTED_LITTLE_KERNEL) += tlk_driver # always enable default capabilities obj-y += commoncap.o @@ -24,6 +25,7 @@ obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o obj-$(CONFIG_SECURITY_YAMA) += yama/built-in.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o +obj-$(CONFIG_TRUSTED_LITTLE_KERNEL) += tlk_driver/built-in.o # Object integrity file lists subdir-$(CONFIG_INTEGRITY) += integrity diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 399433ad614e..a9c3d3cd1990 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -73,7 +73,6 @@ static struct ima_rule_entry default_rules[] = { {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, - {.action = DONT_MEASURE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 44087388010c..1a9ab5419fff 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -53,6 +53,7 @@ #include <net/ip.h> /* for local_port_range[] */ #include <net/sock.h> #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ +#include <net/inet_connection_sock.h> #include <net/net_namespace.h> #include <net/netlabel.h> #include <linux/uaccess.h> @@ -3800,7 +3801,7 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) u32 nlbl_sid; u32 nlbl_type; - selinux_skb_xfrm_sid(skb, &xfrm_sid); + selinux_xfrm_skb_sid(skb, &xfrm_sid); selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); err = security_net_peersid_resolve(nlbl_sid, nlbl_type, xfrm_sid, sid); @@ -3814,6 +3815,30 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) return 0; } +/** + * selinux_conn_sid - Determine the child socket label for a connection + * @sk_sid: the parent socket's SID + * @skb_sid: the packet's SID + * @conn_sid: the resulting connection SID + * + * If @skb_sid is valid then the user:role:type information from @sk_sid is + * combined with the MLS information from @skb_sid in order to create + * @conn_sid. If @skb_sid is not valid then then @conn_sid is simply a copy + * of @sk_sid. Returns zero on success, negative values on failure. + * + */ +static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid) +{ + int err = 0; + + if (skb_sid != SECSID_NULL) + err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid); + else + *conn_sid = sk_sid; + + return err; +} + /* socket security operations */ static int socket_sockcreate_sid(const struct task_security_struct *tsec, @@ -4281,8 +4306,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) } err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER, PEER__RECV, &ad); - if (err) + if (err) { selinux_netlbl_err(skb, err, 0); + return err; + } } if (secmark_active) { @@ -4420,7 +4447,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct sk_security_struct *sksec = sk->sk_security; int err; u16 family = sk->sk_family; - u32 newsid; + u32 connsid; u32 peersid; /* handle mapped IPv4 packets arriving via IPv6 sockets */ @@ -4430,16 +4457,11 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, err = selinux_skb_peerlbl_sid(skb, family, &peersid); if (err) return err; - if (peersid == SECSID_NULL) { - req->secid = sksec->sid; - req->peer_secid = SECSID_NULL; - } else { - err = security_sid_mls_copy(sksec->sid, peersid, &newsid); - if (err) - return err; - req->secid = newsid; - req->peer_secid = peersid; - } + err = selinux_conn_sid(sksec->sid, peersid, &connsid); + if (err) + return err; + req->secid = connsid; + req->peer_secid = peersid; return selinux_netlbl_inet_conn_request(req, family); } @@ -4699,6 +4721,7 @@ static unsigned int selinux_ipv6_forward(unsigned int hooknum, static unsigned int selinux_ip_output(struct sk_buff *skb, u16 family) { + struct sock *sk; u32 sid; if (!netlbl_enabled()) @@ -4707,8 +4730,27 @@ static unsigned int selinux_ip_output(struct sk_buff *skb, /* we do this in the LOCAL_OUT path and not the POST_ROUTING path * because we want to make sure we apply the necessary labeling * before IPsec is applied so we can leverage AH protection */ - if (skb->sk) { - struct sk_security_struct *sksec = skb->sk->sk_security; + sk = skb->sk; + if (sk) { + struct sk_security_struct *sksec; + + if (sk->sk_state == TCP_LISTEN) + /* if the socket is the listening state then this + * packet is a SYN-ACK packet which means it needs to + * be labeled based on the connection/request_sock and + * not the parent socket. unfortunately, we can't + * lookup the request_sock yet as it isn't queued on + * the parent socket until after the SYN-ACK is sent. + * the "solution" is to simply pass the packet as-is + * as any IP option based labeling should be copied + * from the initial connection request (in the IP + * layer). it is far from ideal, but until we get a + * security label in the packet itself this is the + * best we can do. */ + return NF_ACCEPT; + + /* standard practice, label using the parent socket */ + sksec = sk->sk_security; sid = sksec->sid; } else sid = SECINITSID_KERNEL; @@ -4778,27 +4820,37 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, * as fast and as clean as possible. */ if (!selinux_policycap_netpeer) return selinux_ip_postroute_compat(skb, ifindex, family); + + secmark_active = selinux_secmark_enabled(); + peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); + if (!secmark_active && !peerlbl_active) + return NF_ACCEPT; + + sk = skb->sk; + #ifdef CONFIG_XFRM /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec * packet transformation so allow the packet to pass without any checks * since we'll have another chance to perform access control checks * when the packet is on it's final way out. * NOTE: there appear to be some IPv6 multicast cases where skb->dst - * is NULL, in this case go ahead and apply access control. */ - if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL) + * is NULL, in this case go ahead and apply access control. + * is NULL, in this case go ahead and apply access control. + * NOTE: if this is a local socket (skb->sk != NULL) that is in the + * TCP listening state we cannot wait until the XFRM processing + * is done as we will miss out on the SA label if we do; + * unfortunately, this means more work, but it is only once per + * connection. */ + if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL && + !(sk != NULL && sk->sk_state == TCP_LISTEN)) return NF_ACCEPT; #endif - secmark_active = selinux_secmark_enabled(); - peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); - if (!secmark_active && !peerlbl_active) - return NF_ACCEPT; - /* if the packet is being forwarded then get the peer label from the - * packet itself; otherwise check to see if it is from a local - * application or the kernel, if from an application get the peer label - * from the sending socket, otherwise use the kernel's sid */ - sk = skb->sk; if (sk == NULL) { + /* Without an associated socket the packet is either coming + * from the kernel or it is being forwarded; check the packet + * to determine which and if the packet is being forwarded + * query the packet directly to determine the security label. */ if (skb->skb_iif) { secmark_perm = PACKET__FORWARD_OUT; if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) @@ -4807,7 +4859,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, secmark_perm = PACKET__SEND; peer_sid = SECINITSID_KERNEL; } + } else if (sk->sk_state == TCP_LISTEN) { + /* Locally generated packet but the associated socket is in the + * listening state which means this is a SYN-ACK packet. In + * this particular case the correct security label is assigned + * to the connection/request_sock but unfortunately we can't + * query the request_sock as it isn't queued on the parent + * socket until after the SYN-ACK packet is sent; the only + * viable choice is to regenerate the label like we do in + * selinux_inet_conn_request(). See also selinux_ip_output() + * for similar problems. */ + u32 skb_sid; + struct sk_security_struct *sksec = sk->sk_security; + if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) + return NF_DROP; + /* At this point, if the returned skb peerlbl is SECSID_NULL + * and the packet has been through at least one XFRM + * transformation then we must be dealing with the "final" + * form of labeled IPsec packet; since we've already applied + * all of our access controls on this packet we can safely + * pass the packet. */ + if (skb_sid == SECSID_NULL) { + switch (family) { + case PF_INET: + if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) + return NF_ACCEPT; + break; + case PF_INET6: + if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) + return NF_ACCEPT; + default: + return NF_DROP_ERR(-ECONNREFUSED); + } + } + if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid)) + return NF_DROP; + secmark_perm = PACKET__SEND; } else { + /* Locally generated packet, fetch the security label from the + * associated socket. */ struct sk_security_struct *sksec = sk->sk_security; peer_sid = sksec->sid; secmark_perm = PACKET__SEND; @@ -5471,11 +5561,11 @@ static int selinux_setprocattr(struct task_struct *p, /* Check for ptracing, and update the task SID if ok. Otherwise, leave SID unchanged and fail. */ ptsid = 0; - task_lock(p); + rcu_read_lock(); tracer = ptrace_parent(p); if (tracer) ptsid = task_sid(tracer); - task_unlock(p); + rcu_read_unlock(); if (tracer) { error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS, diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 65f67cb0aefb..3ffdadc9960f 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -47,6 +47,7 @@ int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb, int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, struct common_audit_data *ad, u8 proto); int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall); +int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid); static inline void selinux_xfrm_notify_policyload(void) { @@ -80,12 +81,12 @@ static inline int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int static inline void selinux_xfrm_notify_policyload(void) { } -#endif -static inline void selinux_skb_xfrm_sid(struct sk_buff *skb, u32 *sid) +static inline int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid) { - int err = selinux_xfrm_decode_session(skb, sid, 0); - BUG_ON(err); + *sid = SECSID_NULL; + return 0; } +#endif #endif /* _SELINUX_XFRM_H_ */ diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index da4b8b233280..6235d052338b 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -442,8 +442,7 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr) sksec->nlbl_state != NLBL_CONNLABELED) return 0; - local_bh_disable(); - bh_lock_sock_nested(sk); + lock_sock(sk); /* connected sockets are allowed to disconnect when the address family * is set to AF_UNSPEC, if that is what is happening we want to reset @@ -464,7 +463,6 @@ int selinux_netlbl_socket_connect(struct sock *sk, struct sockaddr *addr) sksec->nlbl_state = NLBL_CONNLABELED; socket_connect_return: - bh_unlock_sock(sk); - local_bh_enable(); + release_sock(sk); return rc; } diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 9cd9b7c661ec..bcdca73033f3 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1941,7 +1941,19 @@ static int filename_trans_read(struct policydb *p, void *fp) if (rc) goto out; - hashtab_insert(p->filename_trans, ft, otype); + rc = hashtab_insert(p->filename_trans, ft, otype); + if (rc) { + /* + * Do not return -EEXIST to the caller, or the system + * will not boot. + */ + if (rc != -EEXIST) + goto out; + /* But free memory to avoid memory leak. */ + kfree(ft); + kfree(name); + kfree(otype); + } } hash_eval(p->filename_trans, "filenametr"); return 0; @@ -3246,10 +3258,10 @@ static int filename_write_helper(void *key, void *data, void *ptr) if (rc) return rc; - buf[0] = ft->stype; - buf[1] = ft->ttype; - buf[2] = ft->tclass; - buf[3] = otype->otype; + buf[0] = cpu_to_le32(ft->stype); + buf[1] = cpu_to_le32(ft->ttype); + buf[2] = cpu_to_le32(ft->tclass); + buf[3] = cpu_to_le32(otype->otype); rc = put_entry(buf, sizeof(u32), 4, fp); if (rc) diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index d03081886214..78504a18958a 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -152,21 +152,13 @@ int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy * return rc; } -/* - * LSM hook implementation that checks and/or returns the xfrm sid for the - * incoming packet. - */ - -int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) +static int selinux_xfrm_skb_sid_ingress(struct sk_buff *skb, + u32 *sid, int ckall) { - struct sec_path *sp; + struct sec_path *sp = skb->sp; *sid = SECSID_NULL; - if (skb == NULL) - return 0; - - sp = skb->sp; if (sp) { int i, sid_set = 0; @@ -190,6 +182,45 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) return 0; } +static u32 selinux_xfrm_skb_sid_egress(struct sk_buff *skb) +{ + struct dst_entry *dst = skb_dst(skb); + struct xfrm_state *x; + + if (dst == NULL) + return SECSID_NULL; + x = dst->xfrm; + if (x == NULL || !selinux_authorizable_xfrm(x)) + return SECSID_NULL; + + return x->security->ctx_sid; +} + +/* + * LSM hook implementation that checks and/or returns the xfrm sid for the + * incoming packet. + */ + +int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall) +{ + if (skb == NULL) { + *sid = SECSID_NULL; + return 0; + } + return selinux_xfrm_skb_sid_ingress(skb, sid, ckall); +} + +int selinux_xfrm_skb_sid(struct sk_buff *skb, u32 *sid) +{ + int rc; + + rc = selinux_xfrm_skb_sid_ingress(skb, sid, 0); + if (rc == 0 && *sid == SECSID_NULL) + *sid = selinux_xfrm_skb_sid_egress(skb); + + return rc; +} + /* * Security blob allocation for xfrm_policy and xfrm_state * CTX does not have a meaningful value on input diff --git a/security/tlk_driver/Kconfig b/security/tlk_driver/Kconfig new file mode 100644 index 000000000000..5199be43dd20 --- /dev/null +++ b/security/tlk_driver/Kconfig @@ -0,0 +1,14 @@ +config TRUSTED_LITTLE_KERNEL + bool "Enable Open Trusted Execution driver" + select TEGRA_USE_SECURE_KERNEL + help + This option adds kernel support for communication with the + Trusted LK secure OS monitor/runtime support. + If you are unsure how to answer this question, answer N. + +config OTE_ENABLE_LOGGER + bool "Enable TLK logs in linux kmsg" + depends on TRUSTED_LITTLE_KERNEL + help + This option adds support in the kernel driver to read the logs + from the secure world and make them available as a part of kmsg. diff --git a/security/tlk_driver/Makefile b/security/tlk_driver/Makefile new file mode 100644 index 000000000000..9ab4168f7e11 --- /dev/null +++ b/security/tlk_driver/Makefile @@ -0,0 +1,30 @@ +# +# Copyright (c) 2013-2014, NVIDIA Corporation. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +# more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +tlk_driver-objs += ote_device.o +tlk_driver-objs += ote_comms.o +tlk_driver-objs += ote_fs.o +tlk_driver-objs += ote_asm.o +tlk_driver-objs += ote_log.o + +ifeq ($(CONFIG_ARM),y) +plus_sec := $(call as-instr,.arch_extension sec,+sec) +AFLAGS_ote_asm.o :=-Wa,-march=armv7-a$(plus_sec) +endif + +obj-$(CONFIG_TRUSTED_LITTLE_KERNEL) += tlk_driver.o diff --git a/security/tlk_driver/ote_asm.S b/security/tlk_driver/ote_asm.S new file mode 100644 index 000000000000..2c5563ba7ed8 --- /dev/null +++ b/security/tlk_driver/ote_asm.S @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2014, NVIDIA Corporation. All rights reserved. + * + * 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. + * + * 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. + */ + +#include <linux/linkage.h> +#include <linux/init.h> + +#ifdef CONFIG_ARM64 + +ENTRY(tlk_irq_handler) + mov x0, #0x5 + movk x0, #0x3200, lsl #16 // TE_SMC_NS_IRQ_DONE + smc #0 + ret +ENDPROC(tlk_irq_handler) + +/* uint32_t tlk_generic_smc(uint32_t arg0, uint32_t arg1, uint32_t arg2) */ +ENTRY(_tlk_generic_smc) + smc #0 + ret +ENDPROC(_tlk_generic_smc) + + /* allows MAX_EXT_SMC_ARGS (r0-r11) to be passed in registers */ + +/* uint32_t tlk_extended_smc(uint32_t *regs) */ +ENTRY(_tlk_extended_smc) + /* + * Allows MAX_EXT_SMC_ARGS (r0-r11) to be passed in registers + * (for aarch64, these are scratch, so no need to save them) + */ + mov x12, x0 + ldp x0, x1, [x12], #16 + ldp x2, x3, [x12], #16 + ldp x4, x5, [x12], #16 + ldp x6, x7, [x12], #16 + ldp x8, x9, [x12], #16 + ldp x10, x11, [x12], #16 + smc #0 + ret +ENDPROC(_tlk_extended_smc) + +#else + +ENTRY(tlk_irq_handler) + movw r0, #0x5 + movt r0, #0x3200 @ TE_SMC_NS_IRQ_DONE + mov r1, #0 + mov r2, #0 + smc #0 +ENDPROC(tlk_irq_handler) + +ENTRY(_tlk_generic_smc) + smc #0 + mov pc, lr +ENDPROC(_tlk_generic_smc) + +ENTRY(_tlk_extended_smc) + stmfd sp!, {r4-r12} @ save reg state + mov r12, r0 @ reg ptr to r12 + ldmia r12, {r0-r11} @ load arg regs + smc #0 + ldmfd sp!, {r4-r12} @ restore saved regs +ENDPROC(_tlk_extended_smc) + +#endif diff --git a/security/tlk_driver/ote_comms.c b/security/tlk_driver/ote_comms.c new file mode 100644 index 000000000000..11d43b53079c --- /dev/null +++ b/security/tlk_driver/ote_comms.c @@ -0,0 +1,547 @@ +/* + * Copyright (c) 2012-2014 NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/atomic.h> +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/printk.h> +#include <linux/ioctl.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/pagemap.h> +#include <asm/smp_plat.h> + +#include "ote_protocol.h" + +bool verbose_smc; +core_param(verbose_smc, verbose_smc, bool, 0644); + +#define SET_RESULT(req, r, ro) { req->result = r; req->result_origin = ro; } + +static struct te_shmem_desc *te_add_shmem_desc(void *buffer, size_t size, + struct tlk_context *context) +{ + struct te_shmem_desc *shmem_desc = NULL; + shmem_desc = kzalloc(sizeof(struct te_shmem_desc), GFP_KERNEL); + if (shmem_desc) { + INIT_LIST_HEAD(&(shmem_desc->list)); + shmem_desc->buffer = buffer; + shmem_desc->size = size; + list_add_tail(&shmem_desc->list, &(context->shmem_alloc_list)); + } + + return shmem_desc; +} + +static int te_pin_mem_buffers(void *buffer, size_t size, + struct tlk_context *context) +{ + struct te_shmem_desc *shmem_desc = NULL; + int ret = 0; + + shmem_desc = te_add_shmem_desc(buffer, size, context); + if (!shmem_desc) { + pr_err("%s: te_add_shmem_desc Failed\n", __func__); + ret = OTE_ERROR_OUT_OF_MEMORY; + goto error; + } + + return OTE_SUCCESS; +error: + return ret; +} + +static int te_setup_temp_buffers(struct te_request *request, + struct tlk_context *context) +{ + uint32_t i; + int ret = OTE_SUCCESS; + struct te_oper_param *params = request->params; + + for (i = 0; i < request->params_size; i++) { + switch (params[i].type) { + case TE_PARAM_TYPE_NONE: + case TE_PARAM_TYPE_INT_RO: + case TE_PARAM_TYPE_INT_RW: + break; + case TE_PARAM_TYPE_MEM_RO: + case TE_PARAM_TYPE_MEM_RW: + ret = te_pin_mem_buffers( + params[i].u.Mem.base, + params[i].u.Mem.len, + context); + if (ret < 0) { + pr_err("%s failed with err (%d)\n", + __func__, ret); + ret = OTE_ERROR_BAD_PARAMETERS; + break; + } + break; + default: + pr_err("%s: OTE_ERROR_BAD_PARAMETERS\n", __func__); + ret = OTE_ERROR_BAD_PARAMETERS; + break; + } + } + return ret; +} + +static int te_setup_temp_buffers_compat(struct te_request_compat *request, + struct tlk_context *context) +{ + uint32_t i; + int ret = OTE_SUCCESS; + struct te_oper_param_compat *params; + + params = (struct te_oper_param_compat *)(uintptr_t)request->params; + for (i = 0; i < request->params_size; i++) { + switch (params[i].type) { + case TE_PARAM_TYPE_NONE: + case TE_PARAM_TYPE_INT_RO: + case TE_PARAM_TYPE_INT_RW: + break; + case TE_PARAM_TYPE_MEM_RO: + case TE_PARAM_TYPE_MEM_RW: + ret = te_pin_mem_buffers( + (void *)(uintptr_t)params[i].u.Mem.base, + params[i].u.Mem.len, + context); + if (ret < 0) { + pr_err("%s failed with err (%d)\n", + __func__, ret); + ret = OTE_ERROR_BAD_PARAMETERS; + break; + } + break; + default: + pr_err("%s: OTE_ERROR_BAD_PARAMETERS\n", __func__); + ret = OTE_ERROR_BAD_PARAMETERS; + break; + } + } + return ret; +} + +static void te_del_shmem_desc(void *buffer, struct tlk_context *context) +{ + struct te_shmem_desc *shmem_desc, *tmp_shmem_desc; + + list_for_each_entry_safe(shmem_desc, tmp_shmem_desc, + &(context->shmem_alloc_list), list) { + if (shmem_desc->buffer == buffer) { + list_del(&shmem_desc->list); + kfree(shmem_desc); + } + } +} + +/* + * Deregister previously initialized shared memory + */ +void te_unregister_memory(void *buffer, + struct tlk_context *context) +{ + if (!(list_empty(&(context->shmem_alloc_list)))) + te_del_shmem_desc(buffer, context); + else + pr_err("No buffers to unpin\n"); +} + +static void te_unpin_temp_buffers(struct te_request *request, + struct tlk_context *context) +{ + uint32_t i; + struct te_oper_param *params = request->params; + + for (i = 0; i < request->params_size; i++) { + switch (params[i].type) { + case TE_PARAM_TYPE_NONE: + case TE_PARAM_TYPE_INT_RO: + case TE_PARAM_TYPE_INT_RW: + break; + case TE_PARAM_TYPE_MEM_RO: + case TE_PARAM_TYPE_MEM_RW: + te_unregister_memory(params[i].u.Mem.base, context); + break; + default: + pr_err("%s: OTE_ERROR_BAD_PARAMETERS\n", __func__); + break; + } + } +} + +static void te_unpin_temp_buffers_compat(struct te_request_compat *request, + struct tlk_context *context) +{ + uint32_t i; + struct te_oper_param_compat *params; + + params = (struct te_oper_param_compat *)(uintptr_t)request->params; + for (i = 0; i < request->params_size; i++) { + switch (params[i].type) { + case TE_PARAM_TYPE_NONE: + case TE_PARAM_TYPE_INT_RO: + case TE_PARAM_TYPE_INT_RW: + break; + case TE_PARAM_TYPE_MEM_RO: + case TE_PARAM_TYPE_MEM_RW: + te_unregister_memory( + (void *)(uintptr_t)params[i].u.Mem.base, + context); + break; + default: + pr_err("%s: OTE_ERROR_BAD_PARAMETERS\n", __func__); + break; + } + } +} + +#ifdef CONFIG_SMP +cpumask_t saved_cpu_mask; +static void switch_cpumask_to_cpu0(void) +{ + long ret; + cpumask_t local_cpu_mask = CPU_MASK_NONE; + + cpu_set(0, local_cpu_mask); + cpumask_copy(&saved_cpu_mask, tsk_cpus_allowed(current)); + ret = sched_setaffinity(0, &local_cpu_mask); + if (ret) + pr_err("sched_setaffinity #1 -> 0x%lX", ret); +} + +static void restore_cpumask(void) +{ + long ret = sched_setaffinity(0, &saved_cpu_mask); + if (ret) + pr_err("sched_setaffinity #2 -> 0x%lX", ret); +} +#else +static inline void switch_cpumask_to_cpu0(void) {}; +static inline void restore_cpumask(void) {}; +#endif + +uint32_t tlk_generic_smc(uint32_t arg0, uintptr_t arg1, uintptr_t arg2) +{ + uint32_t retval; + + switch_cpumask_to_cpu0(); + + retval = _tlk_generic_smc(arg0, arg1, arg2); + while (retval == TE_ERROR_PREEMPT_BY_IRQ || + retval == TE_ERROR_PREEMPT_BY_FS) { + if (retval == TE_ERROR_PREEMPT_BY_IRQ) { + retval = _tlk_generic_smc((60 << 24), 0, 0); + } else { + tlk_ss_op(); + retval = _tlk_generic_smc(TE_SMC_SS_REQ_COMPLETE, 0, 0); + } + } + + restore_cpumask(); + + /* Print TLK logs if any */ + ote_print_logs(); + + return retval; +} + +uint32_t tlk_extended_smc(uintptr_t *regs) +{ + uint32_t retval; + + switch_cpumask_to_cpu0(); + + retval = _tlk_extended_smc(regs); + while (retval == 0xFFFFFFFD) + retval = _tlk_generic_smc((60 << 24), 0, 0); + + restore_cpumask(); + + /* Print TLK logs if any */ + ote_print_logs(); + + return retval; +} + +/* + * Do an SMC call + */ +static void do_smc(struct te_request *request, struct tlk_device *dev) +{ + uint32_t smc_args; + uint32_t smc_params = 0; + + if (dev->req_param_buf) { + smc_args = (char *)request - dev->req_param_buf; + if (request->params) + smc_params = (char *)request->params - + dev->req_param_buf; + } else { + smc_args = (uint32_t)virt_to_phys(request); + if (request->params) + smc_params = (uint32_t)virt_to_phys(request->params); + } + + tlk_generic_smc(request->type, smc_args, smc_params); +} + +/* + * Do an SMC call + */ +static void do_smc_compat(struct te_request_compat *request, + struct tlk_device *dev) +{ + uint32_t smc_args; + uint32_t smc_params = 0; + + smc_args = (char *)request - dev->req_param_buf; + if (request->params) { + smc_params = + (char *)(uintptr_t)request->params - dev->req_param_buf; + } + + tlk_generic_smc(request->type, smc_args, smc_params); +} + +struct tlk_smc_work_args { + uint32_t arg0; + uint32_t arg1; + uint32_t arg2; +}; + +static long tlk_generic_smc_on_cpu0(void *args) +{ + struct tlk_smc_work_args *work; + int cpu = cpu_logical_map(smp_processor_id()); + uint32_t retval; + + BUG_ON(cpu != 0); + + work = (struct tlk_smc_work_args *)args; + retval = _tlk_generic_smc(work->arg0, work->arg1, work->arg2); + while (retval == 0xFFFFFFFD) + retval = _tlk_generic_smc((60 << 24), 0, 0); + return retval; +} + +/* + * VPR programming SMC + * + * This routine is called both from normal threads and worker threads. + * The worker threads are per-cpu and have PF_NO_SETAFFINITY set, so + * any calls to sched_setaffinity will fail. + * + * If it's a worker thread on CPU0, just invoke the SMC directly. If + * it's running on a non-CPU0, use work_on_cpu() to schedule the SMC + * on CPU0. + */ +int te_set_vpr_params(void *vpr_base, size_t vpr_size) +{ + uint32_t retval; + + /* Share the same lock used when request is send from user side */ + mutex_lock(&smc_lock); + + if (current->flags & + (PF_WQ_WORKER | PF_NO_SETAFFINITY | PF_KTHREAD)) { + struct tlk_smc_work_args work_args; + int cpu = cpu_logical_map(smp_processor_id()); + + work_args.arg0 = TE_SMC_PROGRAM_VPR; + work_args.arg1 = (uint32_t)vpr_base; + work_args.arg2 = vpr_size; + + /* workers don't change CPU. depending on the CPU, execute + * directly or sched work */ + if (cpu == 0 && (current->flags & PF_WQ_WORKER)) + retval = tlk_generic_smc_on_cpu0(&work_args); + else + retval = work_on_cpu(0, + tlk_generic_smc_on_cpu0, &work_args); + } else { + retval = tlk_generic_smc(TE_SMC_PROGRAM_VPR, + (uintptr_t)vpr_base, vpr_size); + } + + mutex_unlock(&smc_lock); + + if (retval != OTE_SUCCESS) { + pr_err("te_set_vpr_params failed err (0x%x)\n", retval); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(te_set_vpr_params); + +/* + * Open session SMC (supporting client-based te_open_session() calls) + */ +void te_open_session(struct te_opensession *cmd, + struct te_request *request, + struct tlk_context *context) +{ + int ret; + + ret = te_setup_temp_buffers(request, context); + if (ret != OTE_SUCCESS) { + pr_err("te_setup_temp_buffers failed err (0x%x)\n", ret); + SET_RESULT(request, ret, OTE_RESULT_ORIGIN_API); + return; + } + + memcpy(&request->dest_uuid, + &cmd->dest_uuid, + sizeof(struct te_service_id)); + + pr_info("OPEN_CLIENT_SESSION: 0x%x 0x%x 0x%x 0x%x\n", + request->dest_uuid[0], + request->dest_uuid[1], + request->dest_uuid[2], + request->dest_uuid[3]); + + request->type = TE_SMC_OPEN_SESSION; + + do_smc(request, context->dev); + + te_unpin_temp_buffers(request, context); +} + +/* + * Close session SMC (supporting client-based te_close_session() calls) + */ +void te_close_session(struct te_closesession *cmd, + struct te_request *request, + struct tlk_context *context) +{ + request->session_id = cmd->session_id; + request->type = TE_SMC_CLOSE_SESSION; + + do_smc(request, context->dev); + if (request->result) + pr_info("Error closing session: %08x\n", request->result); +} + +/* + * Launch operation SMC (supporting client-based te_launch_operation() calls) + */ +void te_launch_operation(struct te_launchop *cmd, + struct te_request *request, + struct tlk_context *context) +{ + int ret; + + ret = te_setup_temp_buffers(request, context); + if (ret != OTE_SUCCESS) { + pr_err("te_setup_temp_buffers failed err (0x%x)\n", ret); + SET_RESULT(request, ret, OTE_RESULT_ORIGIN_API); + return; + } + + request->session_id = cmd->session_id; + request->command_id = cmd->operation.command; + request->type = TE_SMC_LAUNCH_OPERATION; + + do_smc(request, context->dev); + + te_unpin_temp_buffers(request, context); +} + +/* + * Open session SMC (supporting client-based te_open_session() calls) + */ +void te_open_session_compat(struct te_opensession_compat *cmd, + struct te_request_compat *request, + struct tlk_context *context) +{ + int ret; + + ret = te_setup_temp_buffers_compat(request, context); + if (ret != OTE_SUCCESS) { + pr_err("te_setup_temp_buffers failed err (0x%x)\n", ret); + SET_RESULT(request, ret, OTE_RESULT_ORIGIN_API); + return; + } + + memcpy(&request->dest_uuid, + &cmd->dest_uuid, + sizeof(struct te_service_id)); + + pr_info("OPEN_CLIENT_SESSION_COMPAT: 0x%x 0x%x 0x%x 0x%x\n", + request->dest_uuid[0], + request->dest_uuid[1], + request->dest_uuid[2], + request->dest_uuid[3]); + + request->type = TE_SMC_OPEN_SESSION; + + do_smc_compat(request, context->dev); + + te_unpin_temp_buffers_compat(request, context); +} + +/* + * Close session SMC (supporting client-based te_close_session() calls) + */ +void te_close_session_compat(struct te_closesession_compat *cmd, + struct te_request_compat *request, + struct tlk_context *context) +{ + request->session_id = cmd->session_id; + request->type = TE_SMC_CLOSE_SESSION; + + do_smc_compat(request, context->dev); + if (request->result) + pr_info("Error closing session: %08x\n", request->result); +} + +/* + * Launch operation SMC (supporting client-based te_launch_operation() calls) + */ +void te_launch_operation_compat(struct te_launchop_compat *cmd, + struct te_request_compat *request, + struct tlk_context *context) +{ + int ret; + + ret = te_setup_temp_buffers_compat(request, context); + if (ret != OTE_SUCCESS) { + pr_err("te_setup_temp_buffers failed err (0x%x)\n", ret); + SET_RESULT(request, ret, OTE_RESULT_ORIGIN_API); + return; + } + + request->session_id = cmd->session_id; + request->command_id = cmd->operation.command; + request->type = TE_SMC_LAUNCH_OPERATION; + + do_smc_compat(request, context->dev); + + te_unpin_temp_buffers_compat(request, context); +} + +static int __init tlk_register_irq_handler(void) +{ + tlk_generic_smc(TE_SMC_REGISTER_IRQ_HANDLER, + (uintptr_t)tlk_irq_handler, 0); + return 0; +} + +arch_initcall(tlk_register_irq_handler); diff --git a/security/tlk_driver/ote_device.c b/security/tlk_driver/ote_device.c new file mode 100644 index 000000000000..32a2b65bf36e --- /dev/null +++ b/security/tlk_driver/ote_device.c @@ -0,0 +1,769 @@ +/* + * Copyright (c) 2013-2014 NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/atomic.h> +#include <linux/uaccess.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/printk.h> +#include <linux/ioctl.h> +#include <linux/miscdevice.h> +#include <linux/mm.h> +#include <asm/cacheflush.h> +#include <asm/outercache.h> +#include <linux/list.h> +#include <linux/dma-mapping.h> + +#include "ote_protocol.h" + +#define SET_ANSWER(a, r, ro) { a.result = r; a.result_origin = ro; } + +struct tlk_device tlk_dev; +DEFINE_MUTEX(smc_lock); + +static int te_create_free_cmd_list(struct tlk_device *dev) +{ + int cmd_desc_count, ret = 0; + struct te_cmd_req_desc *req_desc; + struct te_cmd_req_desc_compat *req_desc_compat; + int bitmap_size; + bool use_reqbuf; + + /* + * Check if new shared req/param register SMC is supported. + * + * If it is, TLK can map in the shared req/param buffers and do_smc + * only needs to send the offsets within each (with cache coherency + * being maintained by HW through an NS mapping). + * + * If the SMC support is not yet present, then fallback to the old + * mode of writing to an uncached buffer to maintain coherency (and + * phys addresses are passed in do_smc). + */ + dev->req_param_buf = NULL; + use_reqbuf = !tlk_generic_smc(TE_SMC_REGISTER_REQ_BUF, 0, 0); + + if (use_reqbuf) { + dev->req_param_buf = kmalloc((2 * PAGE_SIZE), GFP_KERNEL); + + /* requests in the first page, params in the second */ + dev->req_addr = (struct te_request *) dev->req_param_buf; + dev->param_addr = (struct te_oper_param *) + (dev->req_param_buf + PAGE_SIZE); + + tlk_generic_smc(TE_SMC_REGISTER_REQ_BUF, + (uintptr_t)dev->req_addr, (2 * PAGE_SIZE)); + } else { + dev->req_addr = dma_alloc_coherent(NULL, PAGE_SIZE, + &dev->req_addr_phys, GFP_KERNEL); + dev->param_addr = dma_alloc_coherent(NULL, PAGE_SIZE, + &dev->param_addr_phys, GFP_KERNEL); + } + + if (!dev->req_addr || !dev->param_addr || !dev->req_param_buf) { + ret = -ENOMEM; + goto error; + } + + /* requests in the first page, params in the second */ + dev->req_addr_compat = (struct te_request_compat *) + dev->req_param_buf; + dev->param_addr_compat = (struct te_oper_param_compat *) + (dev->req_param_buf + PAGE_SIZE); + + /* alloc param bitmap allocator */ + bitmap_size = BITS_TO_LONGS(TE_PARAM_MAX) * sizeof(long); + dev->param_bitmap = kzalloc(bitmap_size, GFP_KERNEL); + + for (cmd_desc_count = 0; + cmd_desc_count < TE_CMD_DESC_MAX; cmd_desc_count++) { + + req_desc = kzalloc(sizeof(struct te_cmd_req_desc), GFP_KERNEL); + if (req_desc == NULL) { + pr_err("Failed to allocate cmd req descriptor\n"); + ret = -ENOMEM; + goto error; + } + req_desc->req_addr = dev->req_addr + cmd_desc_count; + INIT_LIST_HEAD(&(req_desc->list)); + + /* Add the cmd param descriptor to free list */ + list_add_tail(&req_desc->list, &(dev->free_cmd_list)); + } + + for (cmd_desc_count = 0; + cmd_desc_count < TE_CMD_DESC_MAX_COMPAT; cmd_desc_count++) { + + req_desc_compat = kzalloc(sizeof(struct te_cmd_req_desc_compat), + GFP_KERNEL); + if (req_desc_compat == NULL) { + pr_err("Failed to allocate cmd req descriptor\n"); + ret = -ENOMEM; + goto error; + } + req_desc_compat->req_addr = + dev->req_addr_compat + cmd_desc_count; + INIT_LIST_HEAD(&(req_desc_compat->list)); + + /* Add the cmd param descriptor to free list */ + list_add_tail(&req_desc_compat->list, &(dev->free_cmd_list)); + } + +error: + return ret; +} + +static struct te_oper_param *te_get_free_params(struct tlk_device *dev, + unsigned int nparams) +{ + struct te_oper_param *params = NULL; + int idx, nbits; + + if (nparams) { + nbits = get_count_order(nparams); + idx = bitmap_find_free_region(dev->param_bitmap, + TE_PARAM_MAX, nbits); + if (idx >= 0) + params = dev->param_addr + idx; + } + return params; +} + +static void te_put_free_params(struct tlk_device *dev, + struct te_oper_param *params, uint32_t nparams) +{ + int idx, nbits; + + idx = (params - dev->param_addr); + nbits = get_count_order(nparams); + bitmap_release_region(dev->param_bitmap, idx, nbits); +} + +static struct te_oper_param_compat * + te_get_free_params_compat(struct tlk_device *dev, unsigned int nparams) +{ + struct te_oper_param_compat *params = NULL; + int idx, nbits; + + if (nparams) { + nbits = get_count_order(nparams); + idx = bitmap_find_free_region(dev->param_bitmap, + TE_PARAM_MAX, nbits); + if (idx >= 0) + params = dev->param_addr_compat + idx; + } + return params; +} + +static void te_put_free_params_compat(struct tlk_device *dev, + struct te_oper_param_compat *params, uint32_t nparams) +{ + int idx, nbits; + + idx = (params - dev->param_addr_compat); + nbits = get_count_order(nparams); + bitmap_release_region(dev->param_bitmap, idx, nbits); +} + +static struct te_cmd_req_desc *te_get_free_cmd_desc(struct tlk_device *dev) +{ + struct te_cmd_req_desc *cmd_desc = NULL; + + if (!(list_empty(&(dev->free_cmd_list)))) { + cmd_desc = list_first_entry(&(dev->free_cmd_list), + struct te_cmd_req_desc, list); + list_del(&(cmd_desc->list)); + list_add_tail(&cmd_desc->list, &(dev->used_cmd_list)); + } + return cmd_desc; +} + +static void te_put_used_cmd_desc(struct tlk_device *dev, + struct te_cmd_req_desc *cmd_desc) +{ + struct te_cmd_req_desc *param_desc, *tmp_param_desc; + + if (cmd_desc) { + list_for_each_entry_safe(param_desc, tmp_param_desc, + &(dev->used_cmd_list), list) { + if (cmd_desc->req_addr == param_desc->req_addr) { + list_del(¶m_desc->list); + list_add_tail(¶m_desc->list, + &(dev->free_cmd_list)); + } + } + } +} + +static struct te_cmd_req_desc_compat * +te_get_free_cmd_desc_compat(struct tlk_device *dev) +{ + struct te_cmd_req_desc_compat *cmd_desc = NULL; + + if (!(list_empty(&(dev->free_cmd_list)))) { + cmd_desc = list_first_entry(&(dev->free_cmd_list), + struct te_cmd_req_desc_compat, list); + list_del(&(cmd_desc->list)); + list_add_tail(&cmd_desc->list, &(dev->used_cmd_list)); + } + return cmd_desc; +} + +static void te_put_used_cmd_desc_compat(struct tlk_device *dev, + struct te_cmd_req_desc_compat *cmd_desc) +{ + struct te_cmd_req_desc_compat *param_desc, *tmp_param_desc; + + if (cmd_desc) { + list_for_each_entry_safe(param_desc, tmp_param_desc, + &(dev->used_cmd_list), list) { + if (cmd_desc->req_addr == param_desc->req_addr) { + list_del(¶m_desc->list); + list_add_tail(¶m_desc->list, + &(dev->free_cmd_list)); + } + } + } +} + +static void __attribute__((unused)) te_print_cmd_list( + struct tlk_device *dev, int used_list) +{ + struct te_cmd_req_desc *param_desc; + + if (!used_list) { + pr_info("Printing free cmd list\n"); + if (!(list_empty(&(dev->free_cmd_list)))) { + list_for_each_entry(param_desc, &(dev->free_cmd_list), + list) + pr_info("Phys addr for cmd req desc (%p)\n", + param_desc->req_addr); + } + } else { + pr_info("Printing used cmd list\n"); + if (!(list_empty(&(dev->used_cmd_list)))) { + list_for_each_entry(param_desc, &(dev->used_cmd_list), + list) + pr_info("Phys addr for cmd req desc (%p)\n", + param_desc->req_addr); + } + } +} + +static int tlk_device_open(struct inode *inode, struct file *file) +{ + struct tlk_context *context; + int ret = 0; + + context = kzalloc(sizeof(struct tlk_context), GFP_KERNEL); + if (!context) { + ret = -ENOMEM; + goto error; + } + context->dev = &tlk_dev; + INIT_LIST_HEAD(&(context->shmem_alloc_list)); + + file->private_data = context; + return 0; +error: + return ret; +} + +static int tlk_device_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + file->private_data = NULL; + return 0; +} + +static int copy_params_from_user(struct te_request *req, + struct te_operation *operation) +{ + struct te_oper_param *param_array; + struct te_oper_param *user_param; + uint32_t i; + + if (operation->list_count == 0) + return 0; + + param_array = req->params; + if (param_array == NULL) { + pr_err("param_array empty\n"); + return 1; + } + + user_param = operation->list_head; + for (i = 0; i < operation->list_count && user_param != NULL; i++) { + if (copy_from_user(param_array + i, user_param, + sizeof(struct te_oper_param))) { + pr_err("Failed to copy operation parameter:%d, %p, " \ + "list_count: %d\n", + i, user_param, operation->list_count); + return 1; + } + user_param = param_array[i].next_ptr_user; + } + return 0; +} + +static int copy_params_to_user(struct te_request *req, + struct te_operation *operation) +{ + struct te_oper_param *param_array; + struct te_oper_param *user_param; + uint32_t i; + + if (operation->list_count == 0) + return 0; + + param_array = req->params; + if (param_array == NULL) { + pr_err("param_array empty\n"); + return 1; + } + + user_param = operation->list_head; + for (i = 0; i < req->params_size; i++) { + if (copy_to_user(user_param, param_array + i, + sizeof(struct te_oper_param))) { + pr_err("Failed to copy back parameter:%d %p\n", i, + user_param); + return 1; + } + user_param = param_array[i].next_ptr_user; + } + return 0; +} + +static long te_handle_trustedapp_ioctl(struct file *file, + unsigned int ioctl_num, unsigned long ioctl_param) +{ + long err = 0; + union te_cmd cmd; + void *ptr_user_answer = NULL; + struct te_operation *operation = NULL; + struct te_oper_param *params = NULL; + struct te_answer answer; + struct te_request *request; + + struct te_cmd_req_desc *cmd_desc = NULL; + struct tlk_context *context = file->private_data; + struct tlk_device *dev = context->dev; + + if (copy_from_user(&cmd, (void __user *)ioctl_param, + sizeof(union te_cmd))) { + pr_err("Failed to copy command request\n"); + err = -EFAULT; + goto error; + } + + memset(&answer, 0, sizeof(struct te_answer)); + + switch (ioctl_num) { + case TE_IOCTL_OPEN_CLIENT_SESSION: + operation = &cmd.opensession.operation; + ptr_user_answer = (void *)cmd.opensession.answer; + + cmd_desc = te_get_free_cmd_desc(dev); + params = te_get_free_params(dev, operation->list_count); + + if (!cmd_desc || (operation->list_count && !params)) { + SET_ANSWER(answer, + OTE_ERROR_OUT_OF_MEMORY, + OTE_RESULT_ORIGIN_COMMS); + pr_err("failed to get cmd_desc/params\n"); + goto error; + } + + request = cmd_desc->req_addr; + memset(request, 0, sizeof(struct te_request)); + + request->params = params; + request->params_size = operation->list_count; + + if (copy_params_from_user(request, operation)) { + err = -EFAULT; + pr_info("failed to copy params from user\n"); + goto error; + } + + te_open_session(&cmd.opensession, request, context); + + SET_ANSWER(answer, request->result, request->result_origin); + answer.session_id = request->session_id; + break; + + case TE_IOCTL_CLOSE_CLIENT_SESSION: + ptr_user_answer = (void *)cmd.closesession.answer; + cmd_desc = te_get_free_cmd_desc(dev); + if (!cmd_desc) { + SET_ANSWER(answer, + OTE_ERROR_OUT_OF_MEMORY, + OTE_RESULT_ORIGIN_COMMS); + pr_err("failed to get cmd_desc\n"); + goto error; + } + + request = cmd_desc->req_addr; + memset(request, 0, sizeof(struct te_request)); + + /* close session cannot fail */ + te_close_session(&cmd.closesession, request, context); + break; + + case TE_IOCTL_LAUNCH_OPERATION: + operation = &cmd.launchop.operation; + ptr_user_answer = (void *)cmd.launchop.answer; + + cmd_desc = te_get_free_cmd_desc(dev); + params = te_get_free_params(dev, operation->list_count); + + if (!cmd_desc || (operation->list_count && !params)) { + SET_ANSWER(answer, + OTE_ERROR_OUT_OF_MEMORY, + OTE_RESULT_ORIGIN_COMMS); + pr_err("failed to get cmd_desc/params\n"); + goto error; + } + + request = cmd_desc->req_addr; + memset(request, 0, sizeof(struct te_request)); + + request->params = params; + request->params_size = operation->list_count; + + if (copy_params_from_user(request, operation)) { + err = -EFAULT; + pr_info("failed to copy params from user\n"); + goto error; + } + + te_launch_operation(&cmd.launchop, request, context); + + SET_ANSWER(answer, request->result, request->result_origin); + break; + + default: + pr_err("Invalid IOCTL Cmd\n"); + err = -EINVAL; + goto error; + } + if (ptr_user_answer && !err) { + if (copy_to_user(ptr_user_answer, &answer, + sizeof(struct te_answer))) { + pr_err("Failed to copy answer\n"); + err = -EFAULT; + } + } + if (request->params && !err) { + if (copy_params_to_user(request, operation)) { + pr_err("Failed to copy return params\n"); + err = -EFAULT; + } + } + +error: + if (cmd_desc) + te_put_used_cmd_desc(dev, cmd_desc); + if (params) + te_put_free_params(dev, params, operation->list_count); + return err; +} + +static int copy_params_from_user_compat(struct te_request_compat *req, + struct te_operation_compat *operation) +{ + struct te_oper_param_compat *param_array; + struct te_oper_param_compat *user_param; + uint32_t i; + + if (operation->list_count == 0) + return 0; + + param_array = (struct te_oper_param_compat *)(uintptr_t)req->params; + if (param_array == NULL) { + pr_err("param_array empty\n"); + return 1; + } + + user_param = (struct te_oper_param_compat *)(uintptr_t) + operation->list_head; + for (i = 0; i < operation->list_count && user_param != NULL; i++) { + if (copy_from_user(param_array + i, user_param, + sizeof(struct te_oper_param_compat))) { + pr_err("Failed to copy operation parameter:%d, %p, " \ + "list_count: %d\n", + i, user_param, operation->list_count); + return 1; + } + user_param = (struct te_oper_param_compat *)(uintptr_t) + param_array[i].next_ptr_user; + } + return 0; +} + +static int copy_params_to_user_compat(struct te_request_compat *req, + struct te_operation_compat *operation) +{ + struct te_oper_param_compat *param_array; + struct te_oper_param_compat *user_param; + uint32_t i; + + if (operation->list_count == 0) + return 0; + + param_array = + (struct te_oper_param_compat *)(uintptr_t)req->params; + if (param_array == NULL) { + pr_err("param_array empty\n"); + return 1; + } + + user_param = + (struct te_oper_param_compat *)(uintptr_t)operation->list_head; + for (i = 0; i < req->params_size; i++) { + if (copy_to_user(user_param, param_array + i, + sizeof(struct te_oper_param_compat))) { + pr_err("Failed to copy back parameter:%d %p\n", i, + user_param); + return 1; + } + user_param = (struct te_oper_param_compat *)(uintptr_t) + param_array[i].next_ptr_user; + } + return 0; +} + +static long te_handle_trustedapp_ioctl_compat(struct file *file, + unsigned int ioctl_num, unsigned long ioctl_param) +{ + long err = 0; + union te_cmd_compat cmd_compat; + struct te_operation_compat *operation = NULL; + struct te_oper_param_compat *params = NULL; + struct te_request_compat *request; + void __user *ptr_user_answer = NULL; + struct te_answer answer; + struct te_cmd_req_desc_compat *cmd_desc = NULL; + struct tlk_context *context = file->private_data; + struct tlk_device *dev = context->dev; + + if (copy_from_user(&cmd_compat, (void __user *)ioctl_param, + sizeof(union te_cmd_compat))) { + pr_err("Failed to copy command request\n"); + err = -EFAULT; + goto error; + } + + memset(&answer, 0, sizeof(struct te_answer)); + + switch (ioctl_num) { + case TE_IOCTL_OPEN_CLIENT_SESSION_COMPAT: + operation = &cmd_compat.opensession.operation; + ptr_user_answer = (void *)(uintptr_t) + cmd_compat.opensession.answer; + + cmd_desc = te_get_free_cmd_desc_compat(dev); + params = te_get_free_params_compat(dev, operation->list_count); + + if (!cmd_desc || (operation->list_count && !params)) { + SET_ANSWER(answer, + OTE_ERROR_OUT_OF_MEMORY, + OTE_RESULT_ORIGIN_COMMS); + pr_err("failed to get cmd_desc/params\n"); + goto error; + } + + request = cmd_desc->req_addr; + memset(request, 0, sizeof(struct te_request_compat)); + + request->params = (uintptr_t)params; + request->params_size = operation->list_count; + + if (copy_params_from_user_compat(request, operation)) { + err = -EFAULT; + pr_info("failed to copy params from user\n"); + goto error; + } + + te_open_session_compat(&cmd_compat.opensession, + request, context); + + SET_ANSWER(answer, request->result, request->result_origin); + answer.session_id = request->session_id; + break; + + case TE_IOCTL_CLOSE_CLIENT_SESSION_COMPAT: + ptr_user_answer = (void *)(uintptr_t) + cmd_compat.closesession.answer; + cmd_desc = te_get_free_cmd_desc_compat(dev); + if (!cmd_desc) { + SET_ANSWER(answer, + OTE_ERROR_OUT_OF_MEMORY, + OTE_RESULT_ORIGIN_COMMS); + pr_err("failed to get cmd_desc\n"); + goto error; + } + + request = cmd_desc->req_addr; + memset(request, 0, sizeof(struct te_request_compat)); + + /* close session cannot fail */ + te_close_session_compat(&cmd_compat.closesession, + request, context); + break; + + case TE_IOCTL_LAUNCH_OPERATION_COMPAT: + operation = &cmd_compat.launchop.operation; + ptr_user_answer = (void *)(uintptr_t)cmd_compat.launchop.answer; + + cmd_desc = te_get_free_cmd_desc_compat(dev); + params = te_get_free_params_compat(dev, operation->list_count); + + if (!cmd_desc || (operation->list_count && !params)) { + SET_ANSWER(answer, + OTE_ERROR_OUT_OF_MEMORY, + OTE_RESULT_ORIGIN_COMMS); + pr_err("failed to get cmd_desc/params\n"); + goto error; + } + + request = cmd_desc->req_addr; + memset(request, 0, sizeof(struct te_request_compat)); + + request->params = (uintptr_t)params; + request->params_size = operation->list_count; + + if (copy_params_from_user_compat(request, operation)) { + err = -EFAULT; + pr_info("failed to copy params from user\n"); + goto error; + } + + te_launch_operation_compat(&cmd_compat.launchop, + request, context); + + SET_ANSWER(answer, request->result, request->result_origin); + break; + + default: + pr_err("Invalid IOCTL Cmd\n"); + err = -EINVAL; + goto error; + } + if (ptr_user_answer && !err) { + if (copy_to_user(ptr_user_answer, &answer, + sizeof(struct te_answer))) { + pr_err("Failed to copy answer\n"); + err = -EFAULT; + } + } + if (request->params && !err) { + if (copy_params_to_user_compat(request, operation)) { + pr_err("Failed to copy return params\n"); + err = -EFAULT; + } + } + +error: + if (cmd_desc) + te_put_used_cmd_desc_compat(dev, cmd_desc); + if (params) + te_put_free_params_compat(dev, params, operation->list_count); + return err; +} + +static long tlk_device_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + int err; + + switch (ioctl_num) { + case TE_IOCTL_OPEN_CLIENT_SESSION: + case TE_IOCTL_CLOSE_CLIENT_SESSION: + case TE_IOCTL_LAUNCH_OPERATION: + mutex_lock(&smc_lock); + err = te_handle_trustedapp_ioctl(file, ioctl_num, ioctl_param); + mutex_unlock(&smc_lock); + break; + + case TE_IOCTL_OPEN_CLIENT_SESSION_COMPAT: + case TE_IOCTL_CLOSE_CLIENT_SESSION_COMPAT: + case TE_IOCTL_LAUNCH_OPERATION_COMPAT: + mutex_lock(&smc_lock); + err = te_handle_trustedapp_ioctl_compat(file, ioctl_num, + ioctl_param); + mutex_unlock(&smc_lock); + break; + + case TE_IOCTL_SS_NEW_REQ_LEGACY: + case TE_IOCTL_SS_REQ_COMPLETE_LEGACY: + err = te_handle_ss_ioctl_legacy(file, ioctl_num, ioctl_param); + break; + + case TE_IOCTL_SS_NEW_REQ: + case TE_IOCTL_SS_REQ_COMPLETE: + err = te_handle_ss_ioctl(file, ioctl_num, ioctl_param); + break; + + default: + pr_err("%s: Invalid IOCTL (0x%x) id 0x%x max 0x%lx\n", + __func__, ioctl_num, _IOC_NR(ioctl_num), + (unsigned long)TE_IOCTL_MAX_NR); + err = -EINVAL; + break; + } + + return err; +} + +/* + * tlk_driver function definitions. + */ +static const struct file_operations tlk_device_fops = { + .owner = THIS_MODULE, + .open = tlk_device_open, + .release = tlk_device_release, + .unlocked_ioctl = tlk_device_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = tlk_device_ioctl, +#endif +}; + +struct miscdevice tlk_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tlk_device", + .fops = &tlk_device_fops, +}; + +static int __init tlk_init(void) +{ + int ret; + + INIT_LIST_HEAD(&(tlk_dev.used_cmd_list)); + INIT_LIST_HEAD(&(tlk_dev.free_cmd_list)); + + ret = te_create_free_cmd_list(&tlk_dev); + if (ret != 0) + return ret; + + return misc_register(&tlk_misc_device); +} + +module_init(tlk_init); diff --git a/security/tlk_driver/ote_fs.c b/security/tlk_driver/ote_fs.c new file mode 100644 index 000000000000..f58bdd64aecb --- /dev/null +++ b/security/tlk_driver/ote_fs.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2013-2014 NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/slab.h> +#include <linux/syscalls.h> +#include <linux/list.h> +#include <linux/completion.h> +#include <linux/workqueue.h> +#include <linux/bitops.h> +#include <linux/uaccess.h> +#include <linux/dma-mapping.h> + +#include "ote_protocol.h" + +static DECLARE_COMPLETION(req_ready); +static DECLARE_COMPLETION(req_complete); + +static struct te_ss_op_legacy *ss_op_shmem_legacy; +static struct te_ss_op *ss_op_shmem; +static uint32_t ss_op_size; + +static void indicate_ss_op_complete(void) +{ + tlk_generic_smc(TE_SMC_SS_REQ_COMPLETE, 0, 0); +} + +int te_handle_ss_ioctl_legacy(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + switch (ioctl_num) { + case TE_IOCTL_SS_NEW_REQ_LEGACY: + /* wait for a new request */ + if (wait_for_completion_interruptible(&req_ready)) + return -ENODATA; + + /* transfer pending request to daemon's buffer */ + if (copy_to_user((void __user *)ioctl_param, ss_op_shmem_legacy, + ss_op_size)) { + pr_err("copy_to_user failed for new request\n"); + return -EFAULT; + } + break; + + case TE_IOCTL_SS_REQ_COMPLETE_LEGACY: /* request complete */ + if (copy_from_user(ss_op_shmem_legacy, + (void __user *)ioctl_param, ss_op_size)) { + pr_err("copy_from_user failed for request\n"); + return -EFAULT; + } + + /* signal the producer */ + complete(&req_complete); + break; + } + + return 0; +} + +void tlk_ss_op_legacy(uint32_t size) +{ + /* store size of request */ + ss_op_size = size; + + /* signal consumer */ + complete(&req_ready); + + /* wait for the consumer's signal */ + wait_for_completion(&req_complete); + + /* signal completion to the secure world */ + indicate_ss_op_complete(); +} + +static int __init tlk_ss_init_legacy(void) +{ + dma_addr_t ss_op_shmem_dma; + + /* allocate shared memory buffer */ + ss_op_shmem_legacy = dma_alloc_coherent(NULL, + sizeof(struct te_ss_op_legacy), &ss_op_shmem_dma, GFP_KERNEL); + if (!ss_op_shmem_legacy) { + pr_err("%s: no memory available for fs operations\n", __func__); + return -ENOMEM; + } + + tlk_generic_smc(TE_SMC_SS_REGISTER_HANDLER_LEGACY, + (uintptr_t)tlk_ss_op_legacy, (uintptr_t)ss_op_shmem_legacy); + + return 0; +} + +arch_initcall(tlk_ss_init_legacy); + +int te_handle_ss_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + switch (ioctl_num) { + case TE_IOCTL_SS_NEW_REQ: + /* wait for a new request */ + if (wait_for_completion_interruptible(&req_ready)) + return -ENODATA; + + /* transfer pending request to daemon's buffer */ + if (copy_to_user((void __user *)ioctl_param, ss_op_shmem->data, + ss_op_shmem->req_size)) { + pr_err("copy_to_user failed for new request\n"); + return -EFAULT; + } + break; + + case TE_IOCTL_SS_REQ_COMPLETE: /* request complete */ + if (copy_from_user(ss_op_shmem->data, + (void __user *)ioctl_param, ss_op_shmem->req_size)) { + pr_err("copy_from_user failed for request\n"); + return -EFAULT; + } + + /* signal the producer */ + complete(&req_complete); + break; + } + + return 0; +} + +void tlk_ss_op(void) +{ + /* signal consumer */ + complete(&req_ready); + + /* wait for the consumer's signal */ + wait_for_completion(&req_complete); +} + +static int __init tlk_ss_init(void) +{ + dma_addr_t ss_op_shmem_dma; + int32_t ret; + + /* allocate shared memory buffer */ + ss_op_shmem = dma_alloc_coherent(NULL, sizeof(struct te_ss_op), + &ss_op_shmem_dma, GFP_KERNEL); + if (!ss_op_shmem) { + pr_err("%s: no memory available for fs operations\n", __func__); + return -ENOMEM; + } + + ret = tlk_generic_smc(TE_SMC_SS_REGISTER_HANDLER, + (uintptr_t)ss_op_shmem, 0); + if (ret != 0) { + dma_free_coherent(NULL, sizeof(struct te_ss_op), + (void *)ss_op_shmem, ss_op_shmem_dma); + ss_op_shmem = NULL; + return -ENOTSUPP; + } + + return 0; +} + +arch_initcall(tlk_ss_init); diff --git a/security/tlk_driver/ote_log.c b/security/tlk_driver/ote_log.c new file mode 100644 index 000000000000..0e8f6b290f30 --- /dev/null +++ b/security/tlk_driver/ote_log.c @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2013-2014 NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/slab.h> +#include <linux/syscalls.h> +#include <linux/list.h> +#include <linux/completion.h> +#include <linux/workqueue.h> +#include <linux/bitops.h> +#include <linux/uaccess.h> + +#include <asm/page.h> +#include <linux/dma-mapping.h> +#include <linux/string.h> + +#include "ote_protocol.h" + +#define LOGBUF_SIZE 8192 + +struct circular_buffer { + uint32_t size; /* Indicates the total size of the buffer */ + uint32_t start; /* Starting point of valid data in buffer */ + uint32_t end; /* First character which is empty (can be written to) */ + uint32_t overflow; /* Indicator whether buffer has overwritten itself */ + char *buf; +}; + +#if defined(CONFIG_OTE_ENABLE_LOGGER) + +static int ote_logging_enabled; +struct circular_buffer *cb; + +/* + * Initialize the shared buffer for TLK logging. + * The shared buffer is allocated in DMA memory to get uncached memory + * since TLK directly writes to the physical address of the shared buffer. + * The structure is declared in DMA memory too since it's members will + * also be updated by the TLK directly to their physical addresses. + */ +static int circ_buf_init(struct circular_buffer **cbptr) +{ + + dma_addr_t tp; + + *cbptr = (struct circular_buffer *) dma_alloc_coherent(NULL, + sizeof(struct circular_buffer), &tp, GFP_KERNEL); + if (!*cbptr) { + pr_err("%s: no memory avaiable for circular buffer struct\n", + __func__); + return -ENOMEM; + } + memset(*cbptr, 0, sizeof(struct circular_buffer)); + + (*cbptr)->start = 0; + (*cbptr)->end = 0; + (*cbptr)->size = LOGBUF_SIZE; + + (*cbptr)->buf = (char *) dma_alloc_coherent(NULL, LOGBUF_SIZE, + &tp, GFP_KERNEL); + if (!(*cbptr)->buf) { + pr_err("%s: no memory avaiable for shared buffer\n", + __func__); + /* Frees the memory allocated using dma_alloc_coherent */ + dma_free_coherent(NULL, + sizeof(struct circular_buffer), cbptr, tp); + return -ENOMEM; + } + memset((*cbptr)->buf, 0, LOGBUF_SIZE); + + (*cbptr)->overflow = 0; + + return 0; +} + +/* + * Copy the contents of the circular buffer into a char buffer in order. + * This helps to treat the buffer like a string and use it to tokenize it + * into lines, tag and display it. + */ +static int circ_buf_copy(struct circular_buffer *cb, char *text) +{ + if (cb->end == cb->start) + return 0; + + if (cb->end > cb->start) { + if (abs(cb->end - cb->start) > LOGBUF_SIZE) { + pr_err("%s: cbuf pointers corrupted\n", __func__); + return -EINVAL; + } + + memcpy(text, cb->buf + cb->start, cb->end - cb->start); + + } else if (cb->start > cb->end) { + if (abs(cb->end - cb->start) > LOGBUF_SIZE) { + pr_err("%s: cbuf pointers corrupted\n", __func__); + return -EINVAL; + } + + memcpy(text, cb->buf + cb->start, cb->size - cb->start); + memcpy(text + cb->size - cb->start, cb->buf, cb->end); + + } + + return 0; +} + +/* + * Function which prints TLK logs. + * Tokenizes the TLK logs into lines, tags each line + * and prints it out to kmsg file. + */ +void ote_print_logs(void) +{ + char *text = NULL; + char *temp = NULL; + char *buffer = NULL; + + if (!ote_logging_enabled) + return; + + buffer = kzalloc(LOGBUF_SIZE, GFP_KERNEL); + BUG_ON(!buffer); + + /* This detects if the buffer proved to be too small to hold the data. + * If buffer is not large enough, it overwrites it's oldest data, + * This warning serves to alert the user to possibly use a bigger buffer + */ + if (cb->overflow == 1) { + pr_info("\n[TLK] **WARNING** TLK buffer overwritten.\n\n"); + cb->overflow = 0; + } + + if (circ_buf_copy(cb, buffer) != 0) { + kfree(buffer); + return; + } + cb->buf[cb->end] = '\0'; + + /* In case no delimiter was found, + * the token is taken to be the entire string *stringp, + * and *stringp is made NULL. + */ + text = buffer; + temp = strsep(&text, "\n"); + while (temp != NULL) { + if (strnlen(temp, LOGBUF_SIZE)) + pr_info("[TLK] %s\n", temp); + temp = strsep(&text, "\n"); + } + + /* Indicate that buffer is empty */ + cb->start = cb->end; + kfree(buffer); +} +#else +void ote_print_logs(void) {} +#endif + +/* + * Call function to initialize circular buffer. + * An SMC is made to send the virtual address of the structure to + * the secure OS. + */ +static int __init ote_logger_init(void) +{ + uintptr_t smc_args[MAX_EXT_SMC_ARGS]; + +#if defined(CONFIG_OTE_ENABLE_LOGGER) + if (circ_buf_init(&cb) != 0) + return -1; + + smc_args[0] = TE_SMC_INIT_LOGGER; + smc_args[1] = (uintptr_t)cb; + + /* enable logging only if secure firmware supports it */ + if (!tlk_generic_smc(smc_args[0], smc_args[1], 0)) + ote_logging_enabled = 1; + + ote_print_logs(); +#else + smc_args[0] = TE_SMC_INIT_LOGGER; + smc_args[1] = 0; + tlk_generic_smc(smc_args[0], smc_args[1], 0); +#endif + + return 0; +} + +arch_initcall(ote_logger_init); diff --git a/security/tlk_driver/ote_protocol.h b/security/tlk_driver/ote_protocol.h new file mode 100644 index 000000000000..608ee8981e7c --- /dev/null +++ b/security/tlk_driver/ote_protocol.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2013-2014 NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __OTE_PROTOCOL_H__ +#define __OTE_PROTOCOL_H__ + +#include "ote_types.h" + +#define TE_IOCTL_MAGIC_NUMBER ('t') +#define TE_IOCTL_OPEN_CLIENT_SESSION \ + _IOWR(TE_IOCTL_MAGIC_NUMBER, 0x10, union te_cmd) +#define TE_IOCTL_CLOSE_CLIENT_SESSION \ + _IOWR(TE_IOCTL_MAGIC_NUMBER, 0x11, union te_cmd) +#define TE_IOCTL_LAUNCH_OPERATION \ + _IOWR(TE_IOCTL_MAGIC_NUMBER, 0x14, union te_cmd) + +/* ioctls using new structs (eventually to replace current ioctls) */ +#define TE_IOCTL_OPEN_CLIENT_SESSION_COMPAT \ + _IOWR(TE_IOCTL_MAGIC_NUMBER, 0x10, union te_cmd_compat) +#define TE_IOCTL_CLOSE_CLIENT_SESSION_COMPAT \ + _IOWR(TE_IOCTL_MAGIC_NUMBER, 0x11, union te_cmd_compat) +#define TE_IOCTL_LAUNCH_OPERATION_COMPAT \ + _IOWR(TE_IOCTL_MAGIC_NUMBER, 0x14, union te_cmd_compat) + +#define TE_IOCTL_SS_NEW_REQ_LEGACY \ + _IOR(TE_IOCTL_MAGIC_NUMBER, 0x20, struct te_ss_op_legacy) +#define TE_IOCTL_SS_REQ_COMPLETE_LEGACY \ + _IOWR(TE_IOCTL_MAGIC_NUMBER, 0x21, struct te_ss_op_legacy) + +/* ioctls using new SS structs (eventually to replace current SS ioctls) */ +#define TE_IOCTL_SS_NEW_REQ \ + _IOR(TE_IOCTL_MAGIC_NUMBER, 0x20, struct te_ss_op) +#define TE_IOCTL_SS_REQ_COMPLETE \ + _IOWR(TE_IOCTL_MAGIC_NUMBER, 0x21, struct te_ss_op) + +#define TE_IOCTL_MIN_NR _IOC_NR(TE_IOCTL_OPEN_CLIENT_SESSION) +#define TE_IOCTL_MAX_NR _IOC_NR(TE_IOCTL_SS_REQ_COMPLETE) + +/* shared buffer is 2 pages: 1st are requests, 2nd are params */ +#define TE_CMD_DESC_MAX (PAGE_SIZE / sizeof(struct te_request)) +#define TE_PARAM_MAX (PAGE_SIZE / sizeof(struct te_oper_param)) + +#define TE_CMD_DESC_MAX_COMPAT \ + (PAGE_SIZE / sizeof(struct te_request_compat)) +#define TE_PARAM_MAX_COMPAT \ + (PAGE_SIZE / sizeof(struct te_oper_param_compat)) + +#define MAX_EXT_SMC_ARGS 12 + +extern struct mutex smc_lock; +extern struct tlk_device tlk_dev; + +uint32_t _tlk_generic_smc(uint32_t arg0, uintptr_t arg1, uintptr_t arg2); +uint32_t tlk_generic_smc(uint32_t arg0, uintptr_t arg1, uintptr_t arg2); +uint32_t _tlk_extended_smc(uintptr_t *args); +uint32_t tlk_extended_smc(uintptr_t *args); +void tlk_irq_handler(void); + +/* errors returned by secure world in reponse to SMC calls */ +enum { + TE_ERROR_PREEMPT_BY_IRQ = 0xFFFFFFFD, + TE_ERROR_PREEMPT_BY_FS = 0xFFFFFFFE, +}; + +struct tlk_device { + struct te_request *req_addr; + dma_addr_t req_addr_phys; + struct te_oper_param *param_addr; + dma_addr_t param_addr_phys; + + struct te_request_compat *req_addr_compat; + struct te_oper_param_compat *param_addr_compat; + + char *req_param_buf; + + unsigned long *param_bitmap; + + struct list_head used_cmd_list; + struct list_head free_cmd_list; +}; + +struct te_cmd_req_desc { + struct te_request *req_addr; + struct list_head list; +}; + +struct te_cmd_req_desc_compat { + struct te_request_compat *req_addr; + struct list_head list; +}; + +struct te_shmem_desc { + struct list_head list; + void *buffer; + size_t size; + unsigned int mem_type; +}; + +struct tlk_context { + struct tlk_device *dev; + struct list_head shmem_alloc_list; +}; + +enum { + /* Trusted Application Calls */ + TE_SMC_OPEN_SESSION = 0x30000001, + TE_SMC_CLOSE_SESSION = 0x30000002, + TE_SMC_LAUNCH_OPERATION = 0x30000003, + + /* Trusted OS calls */ + TE_SMC_REGISTER_REQ_BUF = 0x32000002, + TE_SMC_REGISTER_IRQ_HANDLER = 0x32000004, + TE_SMC_NS_IRQ_DONE = 0x32000005, + TE_SMC_INIT_LOGGER = 0x32000007, + TE_SMC_SS_REGISTER_HANDLER_LEGACY = 0x32000008, + TE_SMC_SS_REQ_COMPLETE = 0x32000009, + TE_SMC_SS_REGISTER_HANDLER = 0x32000010, + + /* SIP (SOC specific) calls. */ + TE_SMC_PROGRAM_VPR = 0x82000003, +}; + +enum { + TE_PARAM_TYPE_NONE = 0, + TE_PARAM_TYPE_INT_RO = 1, + TE_PARAM_TYPE_INT_RW = 2, + TE_PARAM_TYPE_MEM_RO = 3, + TE_PARAM_TYPE_MEM_RW = 4, +}; + +struct te_oper_param { + uint32_t index; + uint32_t type; + union { + struct { + uint32_t val; + } Int; + struct { + void *base; + uint32_t len; + } Mem; + } u; + void *next_ptr_user; +}; + +struct te_oper_param_compat { + uint32_t index; + uint32_t type; + union { + struct { + uint32_t val; + } Int; + struct { + uint64_t base; + uint32_t len; + } Mem; + } u; + uint64_t next_ptr_user; +}; + +struct te_operation { + uint32_t command; + struct te_oper_param *list_head; + /* Maintain a pointer to tail of list to easily add new param node */ + struct te_oper_param *list_tail; + uint32_t list_count; + uint32_t status; + uint32_t iterface_side; +}; + +struct te_service_id { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_hi_and_version; + uint8_t clock_seq_and_node[8]; +}; + +/* + * OpenSession + */ +struct te_opensession { + struct te_service_id dest_uuid; + struct te_operation operation; + uint32_t answer; +}; + +/* + * CloseSession + */ +struct te_closesession { + uint32_t session_id; + uint32_t answer; +}; + +/* + * LaunchOperation + */ +struct te_launchop { + uint32_t session_id; + struct te_operation operation; + uint32_t answer; +}; + +union te_cmd { + struct te_opensession opensession; + struct te_closesession closesession; + struct te_launchop launchop; +}; + +/* + * Compat versions of the original structs (eventually to replace + * the old structs, once the lib/TLK kernel changes are in). + */ +struct te_operation_compat { + uint32_t command; + uint32_t status; + uint64_t list_head; + uint64_t list_tail; + uint32_t list_count; + uint32_t interface_side; +}; + +/* + * OpenSession + */ +struct te_opensession_compat { + struct te_service_id dest_uuid; + struct te_operation_compat operation; + uint64_t answer; +}; + +/* + * CloseSession + */ +struct te_closesession_compat { + uint32_t session_id; + uint64_t answer; +}; + +/* + * LaunchOperation + */ +struct te_launchop_compat { + uint32_t session_id; + struct te_operation_compat operation; + uint64_t answer; +}; + +union te_cmd_compat { + struct te_opensession_compat opensession; + struct te_closesession_compat closesession; + struct te_launchop_compat launchop; +}; + +struct te_request { + uint32_t type; + uint32_t session_id; + uint32_t command_id; + struct te_oper_param *params; + uint32_t params_size; + uint32_t dest_uuid[4]; + uint32_t result; + uint32_t result_origin; +}; + +struct te_request_compat { + uint32_t type; + uint32_t session_id; + uint32_t command_id; + uint64_t params; + uint32_t params_size; + uint32_t dest_uuid[4]; + uint32_t result; + uint32_t result_origin; +}; + +struct te_answer { + uint32_t result; + uint32_t session_id; + uint32_t result_origin; +}; + +void te_open_session(struct te_opensession *cmd, + struct te_request *request, + struct tlk_context *context); + +void te_close_session(struct te_closesession *cmd, + struct te_request *request, + struct tlk_context *context); + +void te_launch_operation(struct te_launchop *cmd, + struct te_request *request, + struct tlk_context *context); + +void te_open_session_compat(struct te_opensession_compat *cmd, + struct te_request_compat *request, + struct tlk_context *context); + +void te_close_session_compat(struct te_closesession_compat *cmd, + struct te_request_compat *request, + struct tlk_context *context); + +void te_launch_operation_compat(struct te_launchop_compat *cmd, + struct te_request_compat *request, + struct tlk_context *context); + +#define SS_OP_MAX_DATA_SIZE 0x1000 +struct te_ss_op { + uint32_t req_size; + uint8_t data[SS_OP_MAX_DATA_SIZE]; +}; + +struct te_ss_op_legacy { + uint8_t data[SS_OP_MAX_DATA_SIZE]; +}; + +int te_handle_ss_ioctl_legacy(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param); +int te_handle_ss_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param); +int te_handle_fs_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param); +void ote_print_logs(void); +void tlk_ss_op(void); + +#endif diff --git a/security/tlk_driver/ote_types.h b/security/tlk_driver/ote_types.h new file mode 100644 index 000000000000..593400f7a03a --- /dev/null +++ b/security/tlk_driver/ote_types.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2013 NVIDIA Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __OTE_TYPES_H__ +#define __OTE_TYPES_H__ + +/* + * Return Codes + */ +enum { + /* Success */ + OTE_SUCCESS = 0x00000000, + OTE_ERROR_NO_ERROR = OTE_SUCCESS, + /* Non-specific cause */ + OTE_ERROR_GENERIC = 0xFFFF0000, + /* Access priviledge not sufficient */ + OTE_ERROR_ACCESS_DENIED = 0xFFFF0001, + /* The operation was cancelled */ + OTE_ERROR_CANCEL = 0xFFFF0002, + /* Concurrent accesses conflict */ + OTE_ERROR_ACCESS_CONFLICT = 0xFFFF0003, + /* Too much data for req was passed */ + OTE_ERROR_EXCESS_DATA = 0xFFFF0004, + /* Input data was of invalid format */ + OTE_ERROR_BAD_FORMAT = 0xFFFF0005, + /* Input parameters were invalid */ + OTE_ERROR_BAD_PARAMETERS = 0xFFFF0006, + /* Oper invalid in current state */ + OTE_ERROR_BAD_STATE = 0xFFFF0007, + /* The req data item not found */ + OTE_ERROR_ITEM_NOT_FOUND = 0xFFFF0008, + /* The req oper not implemented */ + OTE_ERROR_NOT_IMPLEMENTED = 0xFFFF0009, + /* The req oper not supported */ + OTE_ERROR_NOT_SUPPORTED = 0xFFFF000A, + /* Expected data was missing */ + OTE_ERROR_NO_DATA = 0xFFFF000B, + /* System ran out of resources */ + OTE_ERROR_OUT_OF_MEMORY = 0xFFFF000C, + /* The system is busy */ + OTE_ERROR_BUSY = 0xFFFF000D, + /* Communication failed */ + OTE_ERROR_COMMUNICATION = 0xFFFF000E, + /* A security fault was detected */ + OTE_ERROR_SECURITY = 0xFFFF000F, + /* The supplied buffer is too short */ + OTE_ERROR_SHORT_BUFFER = 0xFFFF0010, +}; + +/* + * Return Code origins + */ +enum { + /* Originated from OTE Client API */ + OTE_RESULT_ORIGIN_API = 1, + /* Originated from Underlying Communication Stack */ + OTE_RESULT_ORIGIN_COMMS = 2, + /* Originated from Common OTE Code */ + OTE_RESULT_ORIGIN_KERNEL = 3, + /* Originated from Trusted APP Code */ + OTE_RESULT_ORIGIN_TRUSTED_APP = 4, +}; + +#endif |