From nobody Wed Dec 4 19:03:26 2024 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 702BA170A37; Tue, 3 Dec 2024 04:20:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199627; cv=none; b=pdZd5JrPrEXmpkreMIS+iMZ39L4dacCNlCvWkH0+dh/o1fXGHkICeat2ut4gpt2+T00zldEw7ccCbRBh6MNiD3jP6UDO/vP+ZNxx9fcQBlxFfXlzU+ms7VdktI/ygw4s5nKdALqeEf704q2uMjp9rl4H7fCn+jVALwkGwGsXor4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199627; c=relaxed/simple; bh=NaklzNLjoztZc/VckNx5Z1xWz1aiEX0434y7jjpSTy4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=UEheEpci6hUktz9r1K2M4MdqnazhrPA+gkVEHTm+0NedKrHRDctClsO7c7FbFuQJ4yrO9azlVsA7tO0j6HjlkWJk/ses3g+GnlSPZNjQgczRI2QZLeOvjZ6a7zPSj4lFit96S9LMTQDo2Lh2PtX6kY2y5RESOlHIi4zzX1vc3Xo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=TiAtX6dA; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="TiAtX6dA" Received: from pps.filterd (m0279864.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4B2Kqtq9004838; Tue, 3 Dec 2024 04:20:06 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= B3NG/6UaKiTJE9LkNUapBa8sgFcq09AmmIzHNtGPBuo=; b=TiAtX6dAiJ6jbvHQ hXy9qvwjjOEIT6cndpwIjx00X0ufXEdCJVRFcrShOCGX1ogNoDxp9veXe1JsRDh7 hM7xC1x/X+MdhA70ltqd17C+Xs/He9/GN6A1uU6aYv6ukmiL9uwdWsgF5pwE0ZyV Il5hPwt0kj3Ap65Voe6RZqsmRLtm9KcmzAnAwvkC+l+s/lJhyUOIUamdzi4DVE1E K193tDE0fIuB13NzDbgGcdhZsIc2elA1bUSNlIMa5OfiCEoXK2BQJotxAn1/PLoO J6AtRoFWEoAK199+R/Hk83/9n4XYR8HNoDPyK4vS3LtOuADRLorUShXSlSIggBD6 Sd34Cw== Received: from nasanppmta04.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 437uvjxmq5-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 03 Dec 2024 04:20:06 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA04.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 4B34K5dr004313 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 3 Dec 2024 04:20:05 GMT Received: from hu-azarrabi-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 2 Dec 2024 20:20:04 -0800 From: Amirreza Zarrabi Date: Mon, 2 Dec 2024 20:19:17 -0800 Subject: [PATCH 01/10] tee: allow a driver to allocate a tee_device without a pool Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-1-f502ef01e016@quicinc.com> References: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> In-Reply-To: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> To: Jens Wiklander , Sumit Garg , Bjorn Andersson , "Konrad Dybcio" , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Bartosz Golaszewski" , Srinivas Kandagatla CC: , , , , , Amirreza Zarrabi X-Mailer: b4 0.13.0 X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-GUID: virVfcAtZj4Z5ipCoOrxlBAv700pc5Vh X-Proofpoint-ORIG-GUID: virVfcAtZj4Z5ipCoOrxlBAv700pc5Vh X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.60.29 definitions=2024-09-06_09,2024-09-06_01,2024-09-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 mlxscore=0 malwarescore=0 suspectscore=0 mlxlogscore=999 impostorscore=0 adultscore=0 phishscore=0 bulkscore=0 priorityscore=1501 clxscore=1011 spamscore=0 lowpriorityscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2411120000 definitions=main-2412030034 A TEE driver doesn't always need to provide a pool if it doesn't support memory sharing ioctls and can allocate memory for TEE messages in some other way. Although this is mentioned in the documentation for tee_device_alloc(), it is not handled correctly. Signed-off-by: Amirreza Zarrabi --- drivers/tee/tee_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index d113679b1e2d..24edce4cdbaa 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -888,7 +888,7 @@ struct tee_device *tee_device_alloc(const struct tee_de= sc *teedesc, =20 if (!teedesc || !teedesc->name || !teedesc->ops || !teedesc->ops->get_version || !teedesc->ops->open || - !teedesc->ops->release || !pool) + !teedesc->ops->release) return ERR_PTR(-EINVAL); =20 teedev =3D kzalloc(sizeof(*teedev), GFP_KERNEL); --=20 2.34.1 From nobody Wed Dec 4 19:03:26 2024 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9C3221865EE; Tue, 3 Dec 2024 04:20:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199628; cv=none; b=fYeYtF99d1aXg2HTwKB7tTLJpY7bXp05O8b48JgVe8wbIQmEfnsvCtV9j68iFyJIhEmKxe+MaZqnDsuvqdS2U8GgcacWeJsYdjH8bUzNkFNcYhxICUYE8lF4F+oM5swQNSTXxqCNKbPT+Jc5kXbLdKjFOFlGwVFDefkV1Lu+s8w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199628; c=relaxed/simple; bh=3i/3N33GOjM+TRUYdEPkKA9tAtATAOGKoiWjEBrayg8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=NvayCcacdbDY/orGQujt+DiBW8BBoi36iNvSNPP5PMT+87Me0ghR7J+4qprSrp6/9b21ekuwWDtzgadQlUlbAl+9OaQ6JRRrmUncjMBTYuxOpSQ07CqQRM6bUxmBTtgdl/34tYiN7Cxe0p5lGPFz6UsL7RB2C/AfQkc537F4kXo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=QOT3yHy5; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="QOT3yHy5" Received: from pps.filterd (m0279862.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4B2NLL5e026307; Tue, 3 Dec 2024 04:20:06 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= 9aNXv5On+dFmkzecaFA/cMCc+Ygz0XIJ35BiM+VNrVo=; b=QOT3yHy5ozh21wrn b6SbcKOyrkV8e4V5L+rsigvzlIz2QBE738GxLJu+ZIoJqm/p1bKnNovLCnTN7y27 /U6qjBjynM7xH2k/unT4HHXRQfLAjSn719xSdo9sAq+mQYzMer/P0GFp26M6nEbK L2ltCH8s4DftGykIZyERWl7EA0A94yUkjG1iU6FvTNrA8RF3YNl6B9Ok+WPbrUbc +RQvGa+9f9HLPm6tJVHgWlY/z/8stnRclCqRDdu/9lctN2+DYVc11z7eM+YOOScO gy7v5dUDmSa1O7r++UQPh4J7S8qzCcxU9OLwtN0eAj7Xjar83iC2gP1NZPMcrXrW uosePg== Received: from nasanppmta01.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 437ufe6p2v-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 03 Dec 2024 04:20:06 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA01.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 4B34K5t0031186 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 3 Dec 2024 04:20:05 GMT Received: from hu-azarrabi-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 2 Dec 2024 20:20:05 -0800 From: Amirreza Zarrabi Date: Mon, 2 Dec 2024 20:19:18 -0800 Subject: [PATCH 02/10] tee: add TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-2-f502ef01e016@quicinc.com> References: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> In-Reply-To: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> To: Jens Wiklander , Sumit Garg , Bjorn Andersson , "Konrad Dybcio" , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Bartosz Golaszewski" , Srinivas Kandagatla CC: , , , , , Amirreza Zarrabi X-Mailer: b4 0.13.0 X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: 8XTfu1P-uI3ZOeF2M7W4crw0o68Sbb-N X-Proofpoint-GUID: 8XTfu1P-uI3ZOeF2M7W4crw0o68Sbb-N X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.60.29 definitions=2024-09-06_09,2024-09-06_01,2024-09-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 suspectscore=0 spamscore=0 impostorscore=0 lowpriorityscore=0 priorityscore=1501 mlxlogscore=999 clxscore=1011 adultscore=0 bulkscore=0 mlxscore=0 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2411120000 definitions=main-2412030034 For drivers that can transfer data to the TEE without needing shared memory from client, it is necessary to receive the user address directly, bypassing any processing by the TEE subsystem. Introduce TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT/OUTPUT/INOUT to represent userspace buffers. Signed-off-by: Amirreza Zarrabi --- drivers/tee/tee_core.c | 26 ++++++++++++++++++++++++++ include/linux/tee_drv.h | 6 ++++++ include/uapi/linux/tee.h | 22 ++++++++++++++++------ 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 24edce4cdbaa..942ff5b359b2 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -381,6 +381,16 @@ static int params_from_user(struct tee_context *ctx, s= truct tee_param *params, params[n].u.value.b =3D ip.b; params[n].u.value.c =3D ip.c; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INOUT: + params[n].u.membuf.uaddr =3D u64_to_user_ptr(ip.a); + params[n].u.membuf.size =3D ip.b; + + if (!access_ok(params[n].u.membuf.uaddr, params[n].u.membuf.size)) + return -EFAULT; + + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: @@ -449,6 +459,11 @@ static int params_to_user(struct tee_ioctl_param __use= r *uparams, put_user(p->u.value.c, &up->c)) return -EFAULT; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INOUT: + if (put_user((u64)p->u.membuf.size, &up->b)) + return -EFAULT; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: if (put_user((u64)p->u.memref.size, &up->b)) @@ -649,6 +664,12 @@ static int params_to_supp(struct tee_context *ctx, ip.b =3D p->u.value.b; ip.c =3D p->u.value.c; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INOUT: + ip.a =3D (u64)p->u.membuf.uaddr; + ip.b =3D p->u.membuf.size; + ip.c =3D 0; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: @@ -751,6 +772,11 @@ static int params_from_supp(struct tee_param *params, = size_t num_params, p->u.value.b =3D ip.b; p->u.value.c =3D ip.c; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INOUT: + p->u.membuf.uaddr =3D u64_to_user_ptr(ip.a); + p->u.membuf.size =3D ip.b; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: /* diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index a54c203000ed..b66e611fece4 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -82,6 +82,11 @@ struct tee_param_memref { struct tee_shm *shm; }; =20 +struct tee_param_membuf { + void * __user uaddr; + size_t size; +}; + struct tee_param_value { u64 a; u64 b; @@ -92,6 +97,7 @@ struct tee_param { u64 attr; union { struct tee_param_memref memref; + struct tee_param_membuf membuf; struct tee_param_value value; } u; }; diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index d0430bee8292..fae68386968a 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -151,6 +151,13 @@ struct tee_ioctl_buf_data { #define TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT 6 #define TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT 7 /* input and output */ =20 +/* + * These defines memory buffer parameters. + */ +#define TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT 8 +#define TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT 9 +#define TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INOUT 10 /* input and output */ + /* * Mask for the type part of the attribute, leaves room for more types */ @@ -186,14 +193,17 @@ struct tee_ioctl_buf_data { /** * struct tee_ioctl_param - parameter * @attr: attributes - * @a: if a memref, offset into the shared memory object, else a value par= ameter - * @b: if a memref, size of the buffer, else a value parameter + * @a: if a memref, offset into the shared memory object, + * else if a membuf, address into the user buffer, + * else a value parameter + * @b: if a memref or membuf, size of the buffer, else a value parameter * @c: if a memref, shared memory identifier, else a value parameter * - * @attr & TEE_PARAM_ATTR_TYPE_MASK indicates if memref or value is used in - * the union. TEE_PARAM_ATTR_TYPE_VALUE_* indicates value and - * TEE_PARAM_ATTR_TYPE_MEMREF_* indicates memref. TEE_PARAM_ATTR_TYPE_NONE - * indicates that none of the members are used. + * @attr & TEE_PARAM_ATTR_TYPE_MASK indicates if memref, membuf, or value = is + * used in the union. TEE_PARAM_ATTR_TYPE_VALUE_* indicates value, + * TEE_PARAM_ATTR_TYPE_MEMREF_* indicates memref, and TEE_PARAM_ATTR_TYPE_= MEMBUF_* + * indicates membuf. TEE_PARAM_ATTR_TYPE_NONE indicates that none of the m= embers + * are used. * * Shared memory is allocated with TEE_IOC_SHM_ALLOC which returns an * identifier representing the shared memory object. A memref can reference --=20 2.34.1 From nobody Wed Dec 4 19:03:26 2024 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9C286166F29; Tue, 3 Dec 2024 04:20:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199626; cv=none; b=J1kJ/2dweyYbUJoV27DpSfSLHx3L1lGSi6MaanCLzDYplsha3uEcm9Tpb7MSGD4YfT+V0/ITFiriCFnrp5/t6eFS9vvnjH3jb6A5vVrky6rhxZZrH2giNOUcGEfKgBkmMn7sPhOIfCdd6mugsVjVfUXH2+ckMiEmuHoi1KsZC2Q= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199626; c=relaxed/simple; bh=OLZ1v2QzrSrSkIZI04gALvSBD1D2VCLpxJp0bOz2UMA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=dwFvFhatLp4v27iglxBCMFGhaNR2wu6EpXvVYwvoCoMyXD4QV1CtAaKydZg6PoUb27hlPU8HAbFq4aZtHmoV4nCoARCYEJCg1BNfqxF/gwgJjhLyp44hFPJCUsHepbaaNgReDBmFQ78FY7fdsT+/xUTIjMuRGIBI+xYQkdFL3/M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=PIW7d5y3; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="PIW7d5y3" Received: from pps.filterd (m0279866.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4B34BYKD031795; Tue, 3 Dec 2024 04:20:07 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= DzDawwrp1oDEx6fSIHvQDRbS0UQEe6+wD+e+JOken8I=; b=PIW7d5y3UI8Mq9ff aAJ9PKRUvUarWS4XBhtMp94UliIsgJtx2+m7o238DgSFAxBTmo1IdDb7SaC6eFG0 ddeVm0HIfHBUaKh9btmqcHrSV+ADI26mg8uOhMCekQ4OqB0P7zM9Mb7kvchRSaVy tI+CQMCq8zizi+Ho9/r/LWTxvah/4xXUUDdqSUTuCHnwbDtb/BfgmbWwD4l/N8vf oTdQR4OAY49Z4f2QYgxLc/2X7Cf/RIYlzVN0eBu2Mw+USKS3XYR2+sQkgyMEcRTD EcCE806C7r64M58SO6cpNIn7IVlAX4kIQe69G8RxdKXYN0H2U171eIKZXB74jFK3 9uAtxg== Received: from nasanppmta02.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 439trbg0j5-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 03 Dec 2024 04:20:06 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA02.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 4B34K6HG016645 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 3 Dec 2024 04:20:06 GMT Received: from hu-azarrabi-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 2 Dec 2024 20:20:05 -0800 From: Amirreza Zarrabi Date: Mon, 2 Dec 2024 20:19:19 -0800 Subject: [PATCH 03/10] tee: add TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-3-f502ef01e016@quicinc.com> References: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> In-Reply-To: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> To: Jens Wiklander , Sumit Garg , Bjorn Andersson , "Konrad Dybcio" , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Bartosz Golaszewski" , Srinivas Kandagatla CC: , , , , , Amirreza Zarrabi X-Mailer: b4 0.13.0 X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-GUID: Fc7wX2G3Y5JT37CwiZHAyyPe7hIxauVQ X-Proofpoint-ORIG-GUID: Fc7wX2G3Y5JT37CwiZHAyyPe7hIxauVQ X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.60.29 definitions=2024-09-06_09,2024-09-06_01,2024-09-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 mlxlogscore=999 mlxscore=0 suspectscore=0 spamscore=0 malwarescore=0 adultscore=0 priorityscore=1501 clxscore=1015 phishscore=0 lowpriorityscore=0 impostorscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2411120000 definitions=main-2412030034 The TEE subsystem allows session-based access to trusted services, requiring a session to be established to receive a service. This is not suitable for an environment that represents services as objects. An object supports various operations that a client can invoke, potentially generating a result or a new object that can be invoked independently of the original object. Add TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT/OUTPUT/INOUT to represent an object. Objects may reside in either TEE or userspace. To invoke an object in TEE, introduce a new ioctl. Use the existing SUPPL_RECV and SUPPL_SEND to invoke an object in userspace. Signed-off-by: Amirreza Zarrabi --- drivers/tee/tee_core.c | 85 ++++++++++++++++++++++++++++++++++++++++++++= ++++ include/linux/tee_core.h | 4 +++ include/linux/tee_drv.h | 6 ++++ include/uapi/linux/tee.h | 41 +++++++++++++++++++---- 4 files changed, 130 insertions(+), 6 deletions(-) diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 942ff5b359b2..037bd17159e2 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -374,6 +374,7 @@ static int params_from_user(struct tee_context *ctx, st= ruct tee_param *params, switch (ip.attr & TEE_IOCTL_PARAM_ATTR_TYPE_MASK) { case TEE_IOCTL_PARAM_ATTR_TYPE_NONE: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: break; case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT: @@ -391,6 +392,11 @@ static int params_from_user(struct tee_context *ctx, s= truct tee_param *params, return -EFAULT; =20 break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT: + params[n].u.objref.id =3D ip.a; + params[n].u.objref.flags =3D ip.b; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: @@ -464,6 +470,12 @@ static int params_to_user(struct tee_ioctl_param __use= r *uparams, if (put_user((u64)p->u.membuf.size, &up->b)) return -EFAULT; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT: + if (put_user(p->u.objref.id, &up->a) || + put_user(p->u.objref.flags, &up->b)) + return -EFAULT; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: if (put_user((u64)p->u.memref.size, &up->b)) @@ -616,6 +628,66 @@ static int tee_ioctl_invoke(struct tee_context *ctx, return rc; } =20 +static int tee_ioctl_object_invoke(struct tee_context *ctx, + struct tee_ioctl_buf_data __user *ubuf) +{ + int rc; + size_t n; + struct tee_ioctl_buf_data buf; + struct tee_ioctl_object_invoke_arg __user *uarg; + struct tee_ioctl_object_invoke_arg arg; + struct tee_ioctl_param __user *uparams =3D NULL; + struct tee_param *params =3D NULL; + + if (!ctx->teedev->desc->ops->object_invoke_func) + return -EINVAL; + + if (copy_from_user(&buf, ubuf, sizeof(buf))) + return -EFAULT; + + if (buf.buf_len > TEE_MAX_ARG_SIZE || + buf.buf_len < sizeof(struct tee_ioctl_object_invoke_arg)) + return -EINVAL; + + uarg =3D u64_to_user_ptr(buf.buf_ptr); + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) !=3D buf.buf_len) + return -EINVAL; + + if (arg.num_params) { + params =3D kcalloc(arg.num_params, sizeof(struct tee_param), + GFP_KERNEL); + if (!params) + return -ENOMEM; + uparams =3D uarg->params; + rc =3D params_from_user(ctx, params, arg.num_params, uparams); + if (rc) + goto out; + } + + rc =3D ctx->teedev->desc->ops->object_invoke_func(ctx, &arg, params); + if (rc) + goto out; + + if (put_user(arg.ret, &uarg->ret)) { + rc =3D -EFAULT; + goto out; + } + rc =3D params_to_user(uparams, arg.num_params, params); +out: + if (params) { + /* Decrease ref count for all valid shared memory pointers */ + for (n =3D 0; n < arg.num_params; n++) + if (tee_param_is_memref(params + n) && + params[n].u.memref.shm) + tee_shm_put(params[n].u.memref.shm); + kfree(params); + } + return rc; +} + static int tee_ioctl_cancel(struct tee_context *ctx, struct tee_ioctl_cancel_arg __user *uarg) { @@ -670,6 +742,12 @@ static int params_to_supp(struct tee_context *ctx, ip.b =3D p->u.membuf.size; ip.c =3D 0; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT: + ip.a =3D p->u.objref.id; + ip.b =3D p->u.objref.flags; + ip.c =3D 0; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: @@ -777,6 +855,11 @@ static int params_from_supp(struct tee_param *params, = size_t num_params, p->u.membuf.uaddr =3D u64_to_user_ptr(ip.a); p->u.membuf.size =3D ip.b; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT: + p->u.objref.id =3D ip.a; + p->u.objref.flags =3D ip.b; + break; case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT: case TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT: /* @@ -857,6 +940,8 @@ static long tee_ioctl(struct file *filp, unsigned int c= md, unsigned long arg) return tee_ioctl_open_session(ctx, uarg); case TEE_IOC_INVOKE: return tee_ioctl_invoke(ctx, uarg); + case TEE_IOC_OBJECT_INVOKE: + return tee_ioctl_object_invoke(ctx, uarg); case TEE_IOC_CANCEL: return tee_ioctl_cancel(ctx, uarg); case TEE_IOC_CLOSE_SESSION: diff --git a/include/linux/tee_core.h b/include/linux/tee_core.h index a38494d6b5f4..6311dedd2b83 100644 --- a/include/linux/tee_core.h +++ b/include/linux/tee_core.h @@ -71,6 +71,7 @@ struct tee_device { * @close_session: close a session * @system_session: declare session as a system session * @invoke_func: invoke a trusted function + * @object_invoke_func: invoke an object * @cancel_req: request cancel of an ongoing invoke or open * @supp_recv: called for supplicant to get a command * @supp_send: called for supplicant to send a response @@ -90,6 +91,9 @@ struct tee_driver_ops { int (*invoke_func)(struct tee_context *ctx, struct tee_ioctl_invoke_arg *arg, struct tee_param *param); + int (*object_invoke_func)(struct tee_context *ctx, + struct tee_ioctl_object_invoke_arg *arg, + struct tee_param *param); int (*cancel_req)(struct tee_context *ctx, u32 cancel_id, u32 session); int (*supp_recv)(struct tee_context *ctx, u32 *func, u32 *num_params, struct tee_param *param); diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index b66e611fece4..a556e6bc0c53 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -87,6 +87,11 @@ struct tee_param_membuf { size_t size; }; =20 +struct tee_param_objref { + u64 id; + u64 flags; +}; + struct tee_param_value { u64 a; u64 b; @@ -97,6 +102,7 @@ struct tee_param { u64 attr; union { struct tee_param_memref memref; + struct tee_param_objref objref; struct tee_param_membuf membuf; struct tee_param_value value; } u; diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index fae68386968a..5d33a8009efb 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -48,8 +48,10 @@ #define TEE_GEN_CAP_PRIVILEGED (1 << 1)/* Privileged device (for supplican= t) */ #define TEE_GEN_CAP_REG_MEM (1 << 2)/* Supports registering shared memory = */ #define TEE_GEN_CAP_MEMREF_NULL (1 << 3)/* NULL MemRef support */ +#define TEE_GEN_CAP_OBJREF (1 << 4)/* Supports generic object reference */ =20 -#define TEE_MEMREF_NULL (__u64)(-1) /* NULL MemRef Buffer */ +#define TEE_MEMREF_NULL ((__u64)(-1)) /* NULL MemRef Buffer */ +#define TEE_OBJREF_NULL ((__u64)(-1)) /* NULL ObjRef Object */ =20 /* * TEE Implementation ID @@ -158,6 +160,13 @@ struct tee_ioctl_buf_data { #define TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT 9 #define TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INOUT 10 /* input and output */ =20 +/* + * These defines object reference parameters. + */ +#define TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT 11 +#define TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT 12 +#define TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT 13 + /* * Mask for the type part of the attribute, leaves room for more types */ @@ -195,15 +204,16 @@ struct tee_ioctl_buf_data { * @attr: attributes * @a: if a memref, offset into the shared memory object, * else if a membuf, address into the user buffer, - * else a value parameter - * @b: if a memref or membuf, size of the buffer, else a value parameter + * else if an objref, object identifier, else a value parameter + * @b: if a memref or membuf, size of the buffer, + * else if objref, a flag for object, else a value parameter * @c: if a memref, shared memory identifier, else a value parameter * * @attr & TEE_PARAM_ATTR_TYPE_MASK indicates if memref, membuf, or value = is * used in the union. TEE_PARAM_ATTR_TYPE_VALUE_* indicates value, - * TEE_PARAM_ATTR_TYPE_MEMREF_* indicates memref, and TEE_PARAM_ATTR_TYPE_= MEMBUF_* - * indicates membuf. TEE_PARAM_ATTR_TYPE_NONE indicates that none of the m= embers - * are used. + * TEE_PARAM_ATTR_TYPE_MEMREF_* indicates memref, TEE_PARAM_ATTR_TYPE_MEMB= UF_* + * indicates membuf, and TEE_PARAM_ATTR_TYPE_OBJREF_* indicates objref. + * TEE_PARAM_ATTR_TYPE_NONE indicates that none of the members are used. * * Shared memory is allocated with TEE_IOC_SHM_ALLOC which returns an * identifier representing the shared memory object. A memref can reference @@ -411,4 +421,23 @@ struct tee_ioctl_shm_register_data { * munmap(): unmaps previously shared memory */ =20 +/** + * struct tee_ioctl_invoke_func_arg - Invokes an object in a Trusted Appli= cation. + * @object: [in] Object id + * @op: [in] Object operation, specific to the object + * @ret: [out] return value + * @num_params [in] number of parameters following this struct + */ +struct tee_ioctl_object_invoke_arg { + __u64 object; + __u32 op; + __u32 ret; + __u32 num_params; + /* num_params tells the actual number of element in params */ + struct tee_ioctl_param params[]; +}; + +#define TEE_IOC_OBJECT_INVOKE _IOR(TEE_IOC_MAGIC, TEE_IOC_BASE + 10, \ + struct tee_ioctl_buf_data) + #endif /*__TEE_H*/ --=20 2.34.1 From nobody Wed Dec 4 19:03:26 2024 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9C2D3168C3F; Tue, 3 Dec 2024 04:20:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199626; cv=none; b=PYuPTOD5ZY6JyKBmcktgth8W1egmZlp2rqOvhXmEY2/DO2PtKoUSGkT8vGlU0kerzqWBRvdhpPJTi/rHZm5QMwPc/mORpb9Mgi9AsVMCJwHpM/xLXg5zGfWuiHZRr1sfPgDs3jUkTX2/97lRN5PZRjfdzb8ID2PNtmMws459oc8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199626; c=relaxed/simple; bh=vnCvb8DTnuXUtOqPsmMgGN528lcqeEpps7PRy26Pz4A=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=cLFc/C5fFeB4VIzCZKCOAcPx/UwSnMvPi4g2RXpvvqVpk2Aq1SKPjooptT5fyUP5Tr8p2AcgHdqf2kvKqrVq0n3zBkwhc6ZLcz98REvT5TfEl5hLoCcjjbhYDIWnwX/OWJkX5htwyeins9BwWx84jdCXFS8yUjvII2326bP6lWc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=aBykya2D; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="aBykya2D" Received: from pps.filterd (m0279867.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4B2Jogfr030309; Tue, 3 Dec 2024 04:20:07 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= c3HYS7VmL64aOOJ8dIb1wHXjUaW7hAiOt5iz2Ar+nzo=; b=aBykya2DekEUT8E+ FyFfWeG76CqLq9Ssj7HaLr704BYgT9Suzw/m/0UWOUMknlFrzEwBatpAvAbhYnm+ hOZgmC09HUUMy/6izKSmIM4A6weOnyLVUpH/Q4v5yeMSsA09t/4oLkd3a84+98T4 L9WOs9wsAiBU7D/pYa6ywLdk/f2lY3GK8TumIrbutYSvOWYrYrj9hCOjvR2FXVKX ZINgEUv/T+ug7+HVI7GAj7cxyFzWihkAyfRlSYBZPUrmmsLhd2sioXneL20L5WLY HAr/eqTsSIj+8ctQVknr2AYZlxYAcU9wMGNqSBvnZAJf+HzzqSYMtEYnE9aMaKTO uepKpA== Received: from nasanppmta04.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 437rdeeycc-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 03 Dec 2024 04:20:07 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA04.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 4B34K6qK004322 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 3 Dec 2024 04:20:06 GMT Received: from hu-azarrabi-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 2 Dec 2024 20:20:05 -0800 From: Amirreza Zarrabi Date: Mon, 2 Dec 2024 20:19:20 -0800 Subject: [PATCH 04/10] firmware: qcom: scm: add support for object invocation Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-4-f502ef01e016@quicinc.com> References: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> In-Reply-To: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> To: Jens Wiklander , Sumit Garg , Bjorn Andersson , "Konrad Dybcio" , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Bartosz Golaszewski" , Srinivas Kandagatla CC: , , , , , Amirreza Zarrabi X-Mailer: b4 0.13.0 X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: O0wZ70XPl-fToVsOs_2kbIYqTls9LMmc X-Proofpoint-GUID: O0wZ70XPl-fToVsOs_2kbIYqTls9LMmc X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.60.29 definitions=2024-09-06_09,2024-09-06_01,2024-09-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 mlxscore=0 adultscore=0 malwarescore=0 mlxlogscore=999 phishscore=0 suspectscore=0 impostorscore=0 clxscore=1015 lowpriorityscore=0 bulkscore=0 priorityscore=1501 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2411120000 definitions=main-2412030034 Qualcomm TEE (QTEE) hosts Trusted Applications (TAs) and services in the secure world, accessed via objects. A QTEE client can invoke these objects to request services. Similarly, QTEE can request services from the nonsecure world using objects exported to the secure world. This introduces low-level primitives to facilitate the invocation of objects hosted in QTEE, as well as those hosted in the nonsecure world. Signed-off-by: Amirreza Zarrabi --- drivers/firmware/qcom/qcom_scm.c | 60 ++++++++++++++++++++++++++++++= ++++ drivers/firmware/qcom/qcom_scm.h | 7 ++++ include/linux/firmware/qcom/qcom_scm.h | 9 +++++ 3 files changed, 76 insertions(+) diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_= scm.c index 72bf87ddcd96..8ac570df192b 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -1862,6 +1862,66 @@ static int qcom_scm_qseecom_init(struct qcom_scm *sc= m) =20 #endif /* CONFIG_QCOM_QSEECOM */ =20 +#ifdef CONFIG_QCOMTEE + +int qcom_scm_qtee_invoke_smc(u64 inbuf, u64 inbuf_size, u64 outbuf, u64 ou= tbuf_size, + u64 *result, u64 *response_type, u64 *data) +{ + struct qcom_scm_desc desc =3D { + .svc =3D QCOM_SCM_SVC_SMCINVOKE, + .cmd =3D QCOM_SCM_SMCINVOKE_INVOKE, + .owner =3D ARM_SMCCC_OWNER_TRUSTED_OS, + .args[0] =3D inbuf, + .args[1] =3D inbuf_size, + .args[2] =3D outbuf, + .args[3] =3D outbuf_size, + .arginfo =3D QCOM_SCM_ARGS(4, QCOM_SCM_RW, QCOM_SCM_VAL, QCOM_SCM_RW, QC= OM_SCM_VAL), + }; + struct qcom_scm_res res; + int ret; + + ret =3D qcom_scm_call(__scm->dev, &desc, &res); + if (ret) + return ret; + + *response_type =3D res.result[0]; + *result =3D res.result[1]; + if (data) + *data =3D res.result[2]; + + return 0; +} +EXPORT_SYMBOL(qcom_scm_qtee_invoke_smc); + +int qcom_scm_qtee_callback_response(u64 buf, u64 buf_size, u64 *result, u6= 4 *response_type, + u64 *data) +{ + struct qcom_scm_desc desc =3D { + .svc =3D QCOM_SCM_SVC_SMCINVOKE, + .cmd =3D QCOM_SCM_SMCINVOKE_CB_RSP, + .owner =3D ARM_SMCCC_OWNER_TRUSTED_OS, + .args[0] =3D buf, + .args[1] =3D buf_size, + .arginfo =3D QCOM_SCM_ARGS(2, QCOM_SCM_RW, QCOM_SCM_VAL), + }; + struct qcom_scm_res res; + int ret; + + ret =3D qcom_scm_call(__scm->dev, &desc, &res); + if (ret) + return ret; + + *response_type =3D res.result[0]; + *result =3D res.result[1]; + if (data) + *data =3D res.result[2]; + + return 0; +} +EXPORT_SYMBOL(qcom_scm_qtee_callback_response); + +#endif /* CONFIG_QCOMTEE */ + /** * qcom_scm_is_available() - Checks if SCM is available */ diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_= scm.h index e36b2f67607f..6b6bdee39236 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -148,6 +148,13 @@ struct qcom_tzmem_pool *qcom_scm_get_tzmem_pool(void); #define QCOM_SCM_SVC_GPU 0x28 #define QCOM_SCM_SVC_GPU_INIT_REGS 0x01 =20 +/* ARM_SMCCC_OWNER_TRUSTED_OS calls */ + +#define QCOM_SCM_SVC_SMCINVOKE 0x06 +#define QCOM_SCM_SMCINVOKE_INVOKE_LEGACY 0x00 +#define QCOM_SCM_SMCINVOKE_CB_RSP 0x01 +#define QCOM_SCM_SMCINVOKE_INVOKE 0x02 + /* common error codes */ #define QCOM_SCM_V2_EBUSY -12 #define QCOM_SCM_ENOMEM -5 diff --git a/include/linux/firmware/qcom/qcom_scm.h b/include/linux/firmwar= e/qcom/qcom_scm.h index 4621aec0328c..2e2a6abf9e34 100644 --- a/include/linux/firmware/qcom/qcom_scm.h +++ b/include/linux/firmware/qcom/qcom_scm.h @@ -168,4 +168,13 @@ static inline int qcom_scm_qseecom_app_send(u32 app_id, =20 #endif /* CONFIG_QCOM_QSEECOM */ =20 +#ifdef CONFIG_QCOMTEE + +int qcom_scm_qtee_invoke_smc(u64 inbuf, u64 inbuf_size, u64 outbuf, u64 ou= tbuf_size, + u64 *result, u64 *response_type, u64 *data); +int qcom_scm_qtee_callback_response(u64 buf, u64 buf_size, u64 *result, u6= 4 *response_type, + u64 *data); + +#endif /* CONFIG_QCOMTEE */ + #endif --=20 2.34.1 From nobody Wed Dec 4 19:03:26 2024 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A7CD818FC80; Tue, 3 Dec 2024 04:20:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199631; cv=none; b=XwX5d4klxATG+c3tBwnYExLLPONYXwMTv7yG8o/S+AzFYN093qYL8xscQyYZ9VHSSdM6EXdLmhrvFKSs+FnzHv2iVeBiAK2WbW7t/039PpYfdQT/YsVSSJrv19ZI8QRj9K/zQg3HSJfZ2IX4wzMn//A85t0fHuCfXYNAMPDiBY8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199631; c=relaxed/simple; bh=8Jh/aSX0JJL0rIc/IRSdMgo0eHFG8jKGrP5w5BL14gI=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=Hw2S05AsISjBCYhnKyijnJHtXJHBl6kCEIsIswvJBCnre6nQTB7Z8k5Ez9v8Ds4/COuIS4XMJtwkif5AGsrUZRF6vMt8+rLjC4iezd8YhqfjZOXjJNZyZD9OtK2nI/N2EEvlmKW9WaU/GOPn+u7ZjeIhtzTX06JrXc7t01JhJIg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=BfO/PHFI; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="BfO/PHFI" Received: from pps.filterd (m0279864.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4B2JkpZ1020570; Tue, 3 Dec 2024 04:20:08 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= LoAge/Z1b8cRAGZHXk69+Y73mAmVDaNtF/gHLP4ydGk=; b=BfO/PHFIPYvA40xS AzzzMerxdXCmV6+i31lUI5j5VaJKvjPQRIWMeUrYvAob60VWzD4Ou+Vdv/i7ZCNc Gl8oMCISVD4yAsEIL9vO7TbsLqXW+vCkTEeagGo6X9pGiYbsg4W+Sl1iNaHfB73W NiVVP1pfYvRR1Ebv4tkF3f2yc7LG1daWKRh4TtP2DLE4qc/ZX0hdB468U6V8EPms lz5CDXdiujpsLJgBwKoXXM3NO7f7boja5MMZhp+RUlcXG4Q2oLMy/HH2oyMLrraK DT5c9pOVrcbwyZ6XjNKCsSz214Tuz0ND2VTDn0d8FUYl3V1Ur2+ZoJYu9ZLJB6eP oVhM/A== Received: from nasanppmta01.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 437uvjxmq6-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 03 Dec 2024 04:20:07 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA01.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 4B34K67b031210 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 3 Dec 2024 04:20:07 GMT Received: from hu-azarrabi-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 2 Dec 2024 20:20:06 -0800 From: Amirreza Zarrabi Date: Mon, 2 Dec 2024 20:19:21 -0800 Subject: [PATCH 05/10] qcomtee: implement object invoke support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-5-f502ef01e016@quicinc.com> References: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> In-Reply-To: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> To: Jens Wiklander , Sumit Garg , Bjorn Andersson , "Konrad Dybcio" , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Bartosz Golaszewski" , Srinivas Kandagatla CC: , , , , , Amirreza Zarrabi X-Mailer: b4 0.13.0 X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-GUID: h7Zz9DlfSQnRnHUTrqwNvazoXYwXy5hv X-Proofpoint-ORIG-GUID: h7Zz9DlfSQnRnHUTrqwNvazoXYwXy5hv X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.60.29 definitions=2024-09-06_09,2024-09-06_01,2024-09-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 mlxscore=0 malwarescore=0 suspectscore=0 mlxlogscore=999 impostorscore=0 adultscore=0 phishscore=0 bulkscore=0 priorityscore=1501 clxscore=1011 spamscore=0 lowpriorityscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2411120000 definitions=main-2412030034 Introduce qcom_tee_object, which represents an object in both QTEE and the kernel. QTEE clients can invoke an instance of qcom_tee_object to access QTEE services. If this invocation produces a new object in QTEE, an instance of qcom_tee_object will be returned. Similarly, QTEE can request services from the kernel by issuing a callback request, which invokes an instance of qcom_tee_object in the kernel. Any subsystem that exposes a service to QTEE should allocate and initialize an instance of qcom_tee_object with a dispatcher callback that is called when the object is invoked. Signed-off-by: Amirreza Zarrabi --- drivers/tee/Kconfig | 1 + drivers/tee/Makefile | 1 + drivers/tee/qcomtee/Kconfig | 10 + drivers/tee/qcomtee/Makefile | 6 + drivers/tee/qcomtee/async.c | 153 ++++++ drivers/tee/qcomtee/core.c | 928 +++++++++++++++++++++++++++++= ++++ drivers/tee/qcomtee/qcom_scm.c | 36 ++ drivers/tee/qcomtee/qcomtee_msg.h | 217 ++++++++ drivers/tee/qcomtee/qcomtee_private.h | 47 ++ drivers/tee/qcomtee/release.c | 66 +++ include/linux/firmware/qcom/qcom_tee.h | 284 ++++++++++ 11 files changed, 1749 insertions(+) diff --git a/drivers/tee/Kconfig b/drivers/tee/Kconfig index 61b507c18780..3a995d7f0d74 100644 --- a/drivers/tee/Kconfig +++ b/drivers/tee/Kconfig @@ -16,5 +16,6 @@ if TEE source "drivers/tee/optee/Kconfig" source "drivers/tee/amdtee/Kconfig" source "drivers/tee/tstee/Kconfig" +source "drivers/tee/qcomtee/Kconfig" =20 endif diff --git a/drivers/tee/Makefile b/drivers/tee/Makefile index 5488cba30bd2..74e987f8f7ea 100644 --- a/drivers/tee/Makefile +++ b/drivers/tee/Makefile @@ -6,3 +6,4 @@ tee-objs +=3D tee_shm_pool.o obj-$(CONFIG_OPTEE) +=3D optee/ obj-$(CONFIG_AMDTEE) +=3D amdtee/ obj-$(CONFIG_ARM_TSTEE) +=3D tstee/ +obj-$(CONFIG_QCOMTEE) +=3D qcomtee/ diff --git a/drivers/tee/qcomtee/Kconfig b/drivers/tee/qcomtee/Kconfig new file mode 100644 index 000000000000..d180a6d07d33 --- /dev/null +++ b/drivers/tee/qcomtee/Kconfig @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Qualcomm Trusted Execution Environment Configuration +config QCOMTEE + tristate "Qualcomm TEE Support" + select QCOM_SCM + help + This option enables the Qualcomm Trusted Execution Environment (QTEE) + driver. It provides an API to access services offered by QTEE and any + loaded Trusted Applications (TAs), as well as exporting kernel + services to QTEE. diff --git a/drivers/tee/qcomtee/Makefile b/drivers/tee/qcomtee/Makefile new file mode 100644 index 000000000000..7dc5e6373042 --- /dev/null +++ b/drivers/tee/qcomtee/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_QCOMTEE) +=3D qcomtee.o +qcomtee-objs +=3D async.o +qcomtee-objs +=3D core.o +qcomtee-objs +=3D qcom_scm.o +qcomtee-objs +=3D release.o diff --git a/drivers/tee/qcomtee/async.c b/drivers/tee/qcomtee/async.c new file mode 100644 index 000000000000..218ec0209722 --- /dev/null +++ b/drivers/tee/qcomtee/async.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include + +#include "qcomtee_private.h" +#include "qcomtee_msg.h" + +#define QCOM_TEE_ASYNC_VERSION_1_0 0x00010000U /* Major: 0x0001, Minor: 0x= 0000. */ +#define QCOM_TEE_ASYNC_VERSION_1_1 0x00010001U /* Major: 0x0001, Minor: 0x= 0001. */ +#define QCOM_TEE_ASYNC_VERSION_1_2 0x00010002U /* Major: 0x0001, Minor: 0x= 0002. */ +#define QCOM_TEE_ASYNC_VERSION QCOM_TEE_ASYNC_VERSION_1_2 /* Current Versi= on. */ + +#define QCOM_TEE_ASYNC_VERSION_MAJOR(n) upper_16_bits(n) +#define QCOM_TEE_ASYNC_VERSION_MINOR(n) lower_16_bits(n) + +/** + * struct qcom_tee_async_msg_hdr - Asynchronous message header format. + * @version: current async protocol version of remote endpoint + * @op: async operation + * + * @version specifies the endpoints (QTEE or driver) supported async proto= col, e.g. + * if QTEE set @version to %QCOM_TEE_ASYNC_VERSION_1_1, QTEE handles opera= tions + * supported in %QCOM_TEE_ASYNC_VERSION_1_1 or %QCOM_TEE_ASYNC_VERSION_1_0. + * @op determins the message format. + */ +struct qcom_tee_async_msg_hdr { + u32 version; + u32 op; +}; + +/** + * struct qcom_tee_async_release_msg - Release asynchronous message. + * @hdr: message header as &struct qcom_tee_async_msg_hdr + * @counts: number of objects in @object_ids + * @object_ids: array of object ids should be released + * + * Available in Major =3D 0x0001, Minor >=3D 0x0000. + */ +struct qcom_tee_async_release_msg { + struct qcom_tee_async_msg_hdr hdr; + u32 counts; + u32 object_ids[] __counted_by(counts); +}; + +/** + * qcom_tee_get_async_buffer() - Get start of the asynchronous message in = outbound buffer. + * @oic: context used for current invocation + * @async_buffer: return buffer to extract from or fill in async messages + * + * If @oic is used for direct object invocation, whole outbound buffer is = available for + * async message. If @oic is used for callback request, the tail of outbou= nd buffer (after + * the callback request message) is available for async message. + */ +static void qcom_tee_get_async_buffer(struct qcom_tee_object_invoke_ctx *o= ic, + struct qcom_tee_buffer *async_buffer) +{ + struct qcom_tee_msg_callback *msg; + unsigned int offset; + int i; + + if (!(oic->flags & QCOM_TEE_OIC_FLAG_BUSY)) { + /* The outbound buffer is empty. Using the whole buffer. */ + offset =3D 0; + } else { + msg =3D (struct qcom_tee_msg_callback *)oic->out_msg.addr; + + /* Start offset in a message for buffer arguments. */ + offset =3D qcom_tee_msg_buffer_args(struct qcom_tee_msg_callback, + qcom_tee_msg_args(msg)); + + /* Add size of IB arguments. */ + qcom_tee_msg_for_each_input_buffer(i, msg) + offset +=3D qcom_tee_msg_offset_align(msg->args[i].b.size); + + /* Add size of OB arguments. */ + qcom_tee_msg_for_each_output_buffer(i, msg) + offset +=3D qcom_tee_msg_offset_align(msg->args[i].b.size); + } + + async_buffer->addr =3D oic->out_msg.addr + offset; + async_buffer->size =3D oic->out_msg.size - offset; +} + +/** + * qcom_tee_async_release_handler() - Process QTEE async requests for rele= asing objects. + * @oic: context used for current invocation + * @msg: async message for object release + * @size: size of the async buffer available + * + * Return: Size of outbound buffer used when processing @msg. + */ +static size_t qcom_tee_async_release_handler(struct qcom_tee_object_invoke= _ctx *oic, + struct qcom_tee_async_msg_hdr *async_msg, size_t size) +{ + struct qcom_tee_async_release_msg *msg =3D (struct qcom_tee_async_release= _msg *)async_msg; + struct qcom_tee_object *object; + int i; + + for (i =3D 0; i < msg->counts; i++) { + object =3D qcom_tee_idx_erase(msg->object_ids[i]); + qcom_tee_object_put(object); + } + + return struct_size_t(struct qcom_tee_async_release_msg, object_ids, i); +} + +/** + * qcom_tee_fetch_async_reqs() - Fetch and process asynchronous messages. + * @oic: context used for current invocation + * + * It looks for handler to process the requested operations in the async m= essage. + * Currently, only support async release requests. + */ +void qcom_tee_fetch_async_reqs(struct qcom_tee_object_invoke_ctx *oic) +{ + struct qcom_tee_async_msg_hdr *async_msg; + struct qcom_tee_buffer async_buffer; + size_t consumed, used =3D 0; + + qcom_tee_get_async_buffer(oic, &async_buffer); + + while (async_buffer.size - used > sizeof(struct qcom_tee_async_msg_hdr)) { + async_msg =3D (struct qcom_tee_async_msg_hdr *)(async_buffer.addr + used= ); + + if (QCOM_TEE_ASYNC_VERSION_MAJOR(async_msg->version) !=3D + QCOM_TEE_ASYNC_VERSION_MAJOR(QCOM_TEE_ASYNC_VERSION)) + goto out; + + switch (async_msg->op) { + case QCOM_TEE_MSG_OBJECT_OP_RELEASE: + consumed =3D qcom_tee_async_release_handler(oic, async_msg, + async_buffer.size - used); + break; + default: + /* Unsupported operations. */ + goto out; + } + + /* Supported operation but unable to parse the message. */ + if (!consumed) + goto out; + + used +=3D qcom_tee_msg_offset_align(consumed); + } + + out: + /* Reset the async messages buffer so async requests do not loopback to Q= TEE. */ + memzero_explicit(async_buffer.addr, async_buffer.size); +} diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c new file mode 100644 index 000000000000..a949ef4cceee --- /dev/null +++ b/drivers/tee/qcomtee/core.c @@ -0,0 +1,928 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qcomtee_msg.h" +#include "qcomtee_private.h" + +/* Static instance of object represents QTEE root object. */ +struct qcom_tee_object qcom_tee_object_root =3D { + .name =3D "root", + .object_type =3D QCOM_TEE_OBJECT_TYPE_ROOT, + .info.qtee_id =3D QCOM_TEE_MSG_OBJECT_ROOT, +}; +EXPORT_SYMBOL_GPL(qcom_tee_object_root); + +/* Next argument of type @type after index @i. */ +int qcom_tee_next_arg_type(struct qcom_tee_arg *u, int i, enum qcom_tee_ar= g_type type) +{ + while (u[i].type !=3D QCOM_TEE_ARG_TYPE_INV && u[i].type !=3D type) + i++; + return i; +} + +/* QTEE expects IDs with QCOM_TEE_MSG_OBJECT_NS_BIT set for object of + * QCOM_TEE_OBJECT_TYPE_CB_OBJECT type. + */ +#define QCOM_TEE_OBJECT_ID_START (QCOM_TEE_MSG_OBJECT_NS_BIT + 1) +#define QCOM_TEE_OBJECT_ID_END (UINT_MAX) + +#define QCOM_TEE_OBJECT_SET(p, type, ...) __QCOM_TEE_OBJECT_SET(p, type, #= #__VA_ARGS__, 0UL) +#define __QCOM_TEE_OBJECT_SET(p, type, optr, ...) do { \ + (p)->object_type =3D (type); \ + (p)->info.qtee_id =3D (unsigned long)(optr); \ + } while (0) + +static struct qcom_tee_object *qcom_tee_object_alloc(void) +{ + struct qcom_tee_object *object; + + object =3D kzalloc(sizeof(*object), GFP_KERNEL); + if (object) { + QCOM_TEE_OBJECT_SET(object, QCOM_TEE_OBJECT_TYPE_NULL); + kref_init(&object->refcount); + } + + return object; +} + +void qcom_tee_object_free(struct qcom_tee_object *object) +{ + kfree(object->name); + kfree(object); +} + +static void qcom_tee_object_release(struct kref *refcount) +{ + struct qcom_tee_object *object; + struct module *owner; + const char *name; + + object =3D container_of(refcount, struct qcom_tee_object, refcount); + + synchronize_rcu(); + + switch (typeof_qcom_tee_object(object)) { + case QCOM_TEE_OBJECT_TYPE_TEE: + qcom_tee_release_tee_object(object); + + break; + case QCOM_TEE_OBJECT_TYPE_CB_OBJECT: + /* Copy, as after release we should not access object. */ + name =3D object->name; + owner =3D object->owner; + + if (object->ops->release) + object->ops->release(object); + + module_put(owner); + kfree_const(name); + + break; + case QCOM_TEE_OBJECT_TYPE_ROOT: + case QCOM_TEE_OBJECT_TYPE_NULL: + default: + break; + } +} + +/** + * qcom_tee_object_get() - Increase object's refcount. + * @object: object to increase the refcount + */ +int qcom_tee_object_get(struct qcom_tee_object *object) +{ + if (object !=3D NULL_QCOM_TEE_OBJECT && + object !=3D ROOT_QCOM_TEE_OBJECT) + return kref_get_unless_zero(&object->refcount); + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_tee_object_get); + +/** + * qcom_tee_object_put() - Decrease object's refcount + * @object: object to decrease the refcount + */ +void qcom_tee_object_put(struct qcom_tee_object *object) +{ + if (object !=3D NULL_QCOM_TEE_OBJECT && + object !=3D ROOT_QCOM_TEE_OBJECT) + kref_put(&object->refcount, qcom_tee_object_release); +} +EXPORT_SYMBOL_GPL(qcom_tee_object_put); + +/* ''Local Object Table''. */ +/* Object from kernel that are exported to QTEE are assigned an id and sto= red in + * xa_qcom_local_objects (kernel object table). QTEE uses this id to refer= ence the + * object using qcom_tee_local_object_get. + */ +static DEFINE_XARRAY_ALLOC(xa_qcom_local_objects); + +static int qcom_tee_idx_alloc(u32 *idx, struct qcom_tee_object *object) +{ + static u32 xa_last_id =3D QCOM_TEE_OBJECT_ID_START; + + /* Every id allocated here, has QCOM_TEE_MSG_OBJECT_NS_BIT set. */ + return xa_alloc_cyclic(&xa_qcom_local_objects, idx, object, + XA_LIMIT(QCOM_TEE_OBJECT_ID_START, QCOM_TEE_OBJECT_ID_END), + &xa_last_id, GFP_KERNEL); +} + +struct qcom_tee_object *qcom_tee_idx_erase(u32 idx) +{ + if (idx < QCOM_TEE_OBJECT_ID_START || idx > QCOM_TEE_OBJECT_ID_END) + return NULL_QCOM_TEE_OBJECT; + + return xa_erase(&xa_qcom_local_objects, idx); +} + +/** + * qcom_tee_object_id_get() - Get an id for an object to sent to QTEE. + * @object: object to get its id. + * @object_id: object id. + * + * For object hosted in REE, they are added to object table, and the idx i= n the + * object table is used as id. For object hosted in QTEE, use the QTEE id = stored in + * @object. This is called on a path to QTEE to construct a message, see + * qcom_tee_prepare_msg() and qcom_tee_update_msg(). + * + * Return: On success return 0 or <0 on failure. + */ +static int qcom_tee_object_id_get(struct qcom_tee_object *object, unsigned= int *object_id) +{ + u32 idx; + + switch (typeof_qcom_tee_object(object)) { + case QCOM_TEE_OBJECT_TYPE_CB_OBJECT: + if (qcom_tee_idx_alloc(&idx, object) < 0) + return -ENOSPC; + + *object_id =3D idx; + + break; + case QCOM_TEE_OBJECT_TYPE_ROOT: + case QCOM_TEE_OBJECT_TYPE_TEE: + *object_id =3D object->info.qtee_id; + + break; + case QCOM_TEE_OBJECT_TYPE_NULL: + *object_id =3D QCOM_TEE_MSG_OBJECT_NULL; + + break; + } + + return 0; +} + +/* Release object id assigned in qcom_tee_object_id_get. */ +static void qcom_tee_object_id_put(unsigned int object_id) +{ + qcom_tee_idx_erase(object_id); +} + +/** + * qcom_tee_local_object_get() - Get an object in REE referenced by the id. + * @object_id: object id. + * + * It is called on behalf of QTEE to obtain instance of object for an id. = It is + * called on a path from QTEE to construct an argument of &struct qcom_tee= _arg, + * see qcom_tee_update_args() and qcom_tee_prepare_args(). + * + * It increases the object's refcount on success. + * + * Return: On error returns %NULL_QCOM_TEE_OBJECT. On success, the object. + */ +static struct qcom_tee_object *qcom_tee_local_object_get(unsigned int obje= ct_id) +{ + struct qcom_tee_object *object; + + /* We trust QTEE does not mess the refcounts. + * It does not issue RELEASE request and qcom_tee_object_get(), simultane= ously. + */ + + object =3D xa_load(&xa_qcom_local_objects, object_id); + + qcom_tee_object_get(object); + + return object; +} + +/** + * __qcom_tee_object_user_init() - Initialize an object for user. + * @object: object to initialize. + * @ot: type of object as &enum qcom_tee_object_type. + * @ops: instance of callbacks. + * @fmt: name assigned to the object. + * + * Return: On success return 0 or <0 on failure. + */ +int __qcom_tee_object_user_init(struct qcom_tee_object *object, enum qcom_= tee_object_type ot, + struct qcom_tee_object_operations *ops, struct module *owner, + const char *fmt, ...) +{ + va_list ap; + int ret; + + kref_init(&object->refcount); + QCOM_TEE_OBJECT_SET(object, QCOM_TEE_OBJECT_TYPE_NULL); + + va_start(ap, fmt); + switch (ot) { + case QCOM_TEE_OBJECT_TYPE_NULL: + ret =3D 0; + + break; + case QCOM_TEE_OBJECT_TYPE_CB_OBJECT: + object->ops =3D ops; + if (!object->ops->dispatch) + return -EINVAL; + + object->owner =3D owner; + if (!try_module_get(object->owner)) + return -EINVAL; + + /* If failed, "no-name"; it is not really a reason to fail here. */ + object->name =3D kvasprintf_const(GFP_KERNEL, fmt, ap); + QCOM_TEE_OBJECT_SET(object, QCOM_TEE_OBJECT_TYPE_CB_OBJECT); + + ret =3D 0; + break; + case QCOM_TEE_OBJECT_TYPE_ROOT: + case QCOM_TEE_OBJECT_TYPE_TEE: + default: + ret =3D -EINVAL; + } + va_end(ap); + + return ret; +} +EXPORT_SYMBOL_GPL(__qcom_tee_object_user_init); + +/** + * qcom_tee_object_type() - Returns type of object represented by an objec= t id. + * @object_id: object id for the object. + * + * This is similar to typeof_qcom_tee_object() but instead of receiving ob= ject + * as argument it receives object id. It is used internally on return path + * from QTEE. + * + * Return: Returns type of object referenced by @object_id. + */ +static enum qcom_tee_object_type qcom_tee_object_type(unsigned int object_= id) +{ + if (object_id =3D=3D QCOM_TEE_MSG_OBJECT_NULL) + return QCOM_TEE_OBJECT_TYPE_NULL; + + if (object_id & QCOM_TEE_MSG_OBJECT_NS_BIT) + return QCOM_TEE_OBJECT_TYPE_CB_OBJECT; + + return QCOM_TEE_OBJECT_TYPE_TEE; +} + +/** + * qcom_tee_object_init() - Initialize an object for QTEE. + * @object: return object + * @object_id: object id received form QTEE + * + * Return: On success return 0 or <0 on failure. + */ +static int qcom_tee_object_init(struct qcom_tee_object **object, unsigned = int object_id) +{ + struct qcom_tee_object *qto; + int ret =3D 0; + + switch (qcom_tee_object_type(object_id)) { + case QCOM_TEE_OBJECT_TYPE_NULL: + *object =3D NULL_QCOM_TEE_OBJECT; + + break; + case QCOM_TEE_OBJECT_TYPE_CB_OBJECT: + qto =3D qcom_tee_local_object_get(object_id); + if (qto !=3D NULL_QCOM_TEE_OBJECT) + *object =3D qto; + else + ret =3D -EINVAL; + + break; + case QCOM_TEE_OBJECT_TYPE_TEE: + qto =3D qcom_tee_object_alloc(); + if (qto) { + /* If failed, "no-name"; it is not really a reason to fail here. */ + qto->name =3D kasprintf(GFP_KERNEL, "qcom_tee-%u", object_id); + QCOM_TEE_OBJECT_SET(qto, QCOM_TEE_OBJECT_TYPE_TEE, object_id); + + *object =3D qto; + } else { + ret =3D -ENOMEM; + } + + break; + default: + + break; + } + + if (ret) + *object =3D NULL_QCOM_TEE_OBJECT; + + return ret; +} + +/* Marshaling API. */ +/* qcom_tee_prepare_msg - Prepares inbound buffer for sending to QTEE + * qcom_tee_update_args - Parses QTEE response in inbound buffer + * qcom_tee_prepare_args - Parses QTEE request from outbound buffer + * qcom_tee_update_msg - Updates outbound buffer with response for QTEE = request + */ + +static int qcom_tee_prepare_msg(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u) +{ + struct qcom_tee_msg_object_invoke *msg; + unsigned int object_id; + int ib, ob, io, oo; + size_t off; + int i; + + /* Use input message buffer in 'oic'. */ + msg =3D (struct qcom_tee_msg_object_invoke *)oic->in_msg.addr; + + /* Start offset in a message for buffer arguments. */ + off =3D qcom_tee_msg_buffer_args(struct qcom_tee_msg_object_invoke, qcom_= tee_args_len(u)); + + /* Get id of object being invoked. */ + if (qcom_tee_object_id_get(object, &object_id)) + return -ENOSPC; + + ib =3D 0; + qcom_tee_arg_for_each_input_buffer(i, u) { + void *ptr; + + /* qcom_tee_msg_buffers_alloc() already checked overflow in message! */ + msg->args[ib].b.offset =3D off; + msg->args[ib].b.size =3D u[i].b.size; + + ptr =3D qcom_tee_msg_offset_to_ptr(msg, off); + if (!(u[i].flags & QCOM_TEE_ARG_FLAGS_UADDR)) + memcpy(ptr, u[i].b.addr, u[i].b.size); + else if (copy_from_user(ptr, u[i].b.uaddr, u[i].b.size)) + return -EINVAL; + + off +=3D qcom_tee_msg_offset_align(u[i].b.size); + ib++; + } + + ob =3D ib; + qcom_tee_arg_for_each_output_buffer(i, u) { + /* qcom_tee_msg_buffers_alloc() already checked overflow in message! */ + msg->args[ob].b.offset =3D off; + msg->args[ob].b.size =3D u[i].b.size; + + off +=3D qcom_tee_msg_offset_align(u[i].b.size); + ob++; + } + + io =3D ob; + qcom_tee_arg_for_each_input_object(i, u) { + if (qcom_tee_object_id_get(u[i].o, &msg->args[io].o)) { + /* Unable to qcom_tee_object_id_get; put whatever we got. */ + qcom_tee_object_id_put(object_id); + for (--io; io >=3D ob; io--) + qcom_tee_object_id_put(msg->args[io].o); + + return -ENOSPC; + } + + io++; + } + + oo =3D io; + qcom_tee_arg_for_each_output_object(i, u) + oo++; + + /* Set object, operation, and argument counts. */ + qcom_tee_msg_init(msg, object_id, op, ib, ob, io, oo); + + return 0; +} + +static int qcom_tee_update_args(struct qcom_tee_arg *u, struct qcom_tee_ob= ject_invoke_ctx *oic) +{ + struct qcom_tee_msg_object_invoke *msg; + int ib, ob, io, oo; + int i, ret =3D 0; + + /* Use input message buffer in 'oic'. */ + msg =3D (struct qcom_tee_msg_object_invoke *)oic->in_msg.addr; + + ib =3D 0; + qcom_tee_arg_for_each_input_buffer(i, u) + ib++; + + ob =3D ib; + qcom_tee_arg_for_each_output_buffer(i, u) { + void *ptr =3D qcom_tee_msg_offset_to_ptr(msg, msg->args[ob].b.offset); + + if (!(u[i].flags & QCOM_TEE_ARG_FLAGS_UADDR)) { + memcpy(u[i].b.addr, ptr, msg->args[ob].b.size); + } else if (copy_to_user(u[i].b.uaddr, ptr, msg->args[ob].b.size)) { + /* On ERROR, continue to process arguments to get to output object. */ + ret =3D -EINVAL; + } + + u[i].b.size =3D msg->args[ob].b.size; + ob++; + } + + io =3D ob; + qcom_tee_arg_for_each_input_object(i, u) + io++; + + oo =3D io; + qcom_tee_arg_for_each_output_object(i, u) { + int err; + + /* On ERROR, continue to process arguments so that we can issue the RELE= ASE. */ + err =3D qcom_tee_object_init(&u[i].o, msg->args[oo].o); + if (err) + ret =3D err; + + oo++; + } + + return ret; +} + +static int qcom_tee_prepare_args(struct qcom_tee_object_invoke_ctx *oic) +{ + int i, ret =3D 0; + + /* Use output message buffer in 'oic'. */ + struct qcom_tee_msg_callback *msg =3D (struct qcom_tee_msg_callback *)oic= ->out_msg.addr; + + qcom_tee_msg_for_each_input_buffer(i, msg) { + oic->u[i].b.addr =3D qcom_tee_msg_offset_to_ptr(msg, msg->args[i].b.offs= et); + oic->u[i].b.size =3D msg->args[i].b.size; + oic->u[i].type =3D QCOM_TEE_ARG_TYPE_IB; + } + + qcom_tee_msg_for_each_output_buffer(i, msg) { + oic->u[i].b.addr =3D qcom_tee_msg_offset_to_ptr(msg, msg->args[i].b.offs= et); + oic->u[i].b.size =3D msg->args[i].b.size; + oic->u[i].type =3D QCOM_TEE_ARG_TYPE_OB; + } + + qcom_tee_msg_for_each_input_object(i, msg) { + int err; + + /* On ERROR, continue to process arguments so that we can issue the RELE= ASE. */ + err =3D qcom_tee_object_init(&oic->u[i].o, msg->args[i].o); + if (err) + ret =3D err; + + oic->u[i].type =3D QCOM_TEE_ARG_TYPE_IO; + } + + qcom_tee_msg_for_each_output_object(i, msg) + oic->u[i].type =3D QCOM_TEE_ARG_TYPE_OO; + + /* End of Arguments. */ + oic->u[i].type =3D QCOM_TEE_ARG_TYPE_INV; + + return ret; +} + +static int qcom_tee_update_msg(struct qcom_tee_object_invoke_ctx *oic) +{ + int ib, ob, io, oo; + int i; + + /* Use output message buffer in 'oic'. */ + struct qcom_tee_msg_callback *msg =3D (struct qcom_tee_msg_callback *)oic= ->out_msg.addr; + + ib =3D 0; + qcom_tee_arg_for_each_input_buffer(i, oic->u) + ib++; + + ob =3D ib; + qcom_tee_arg_for_each_output_buffer(i, oic->u) { + /* Only reduce size; never increase it. */ + if (msg->args[ob].b.size < oic->u[i].b.size) + return -EINVAL; + + msg->args[ob].b.size =3D oic->u[i].b.size; + ob++; + } + + io =3D ob; + qcom_tee_arg_for_each_input_object(i, oic->u) + io++; + + oo =3D io; + qcom_tee_arg_for_each_output_object(i, oic->u) { + if (qcom_tee_object_id_get(oic->u[i].o, &msg->args[oo].o)) { + /* Unable to qcom_tee_object_id_get; put whatever we got. */ + for (--oo; oo >=3D io; --oo) + qcom_tee_object_id_put(msg->args[oo].o); + + return -ENOSPC; + } + + oo++; + } + + return 0; +} + +/** + * define MAX_BUFFER_SIZE - Maximum size of inbound and outbound buffers. + * + * QTEE transport does not impose any restriction on these buffers. Howeve= r, if size of + * buffers are larger then %MAX_BUFFER_SIZE, user should probably use some= other + * form of shared memory with QTEE. + */ +#define MAX_BUFFER_SIZE SZ_8K + +/* Pool to allocate inbound and outbound buffers. */ +static struct qcom_tzmem_pool *tzmem_msg_pool; + +static int qcom_tee_msg_buffers_alloc(struct qcom_tee_object_invoke_ctx *o= ic, + struct qcom_tee_arg *u) +{ + size_t size; + int i; + + /* Start offset in a message for buffer arguments. */ + size =3D qcom_tee_msg_buffer_args(struct qcom_tee_msg_object_invoke, qcom= _tee_args_len(u)); + if (size > MAX_BUFFER_SIZE) + return -EINVAL; + + /* Add size of IB arguments. */ + qcom_tee_arg_for_each_input_buffer(i, u) { + size =3D size_add(size, qcom_tee_msg_offset_align(u[i].b.size)); + if (size > MAX_BUFFER_SIZE) + return -EINVAL; + } + + /* Add size of OB arguments. */ + qcom_tee_arg_for_each_output_buffer(i, u) { + size =3D size_add(size, qcom_tee_msg_offset_align(u[i].b.size)); + if (size > MAX_BUFFER_SIZE) + return -EINVAL; + } + + /* QTEE requires inbound buffer size to be page aligned. */ + size =3D PAGE_ALIGN(size); + + /* Do allocations. */ + oic->in_msg.size =3D size; + oic->in_msg.addr =3D qcom_tzmem_alloc(tzmem_msg_pool, size, GFP_KERNEL); + if (!oic->in_msg.addr) + return -EINVAL; + + oic->out_msg.size =3D MAX_BUFFER_SIZE; + oic->out_msg.addr =3D qcom_tzmem_alloc(tzmem_msg_pool, MAX_BUFFER_SIZE, G= FP_KERNEL); + if (!oic->out_msg.addr) { + qcom_tzmem_free(oic->in_msg.addr); + + return -EINVAL; + } + + oic->in_msg_paddr =3D qcom_tzmem_to_phys(oic->in_msg.addr); + oic->out_msg_paddr =3D qcom_tzmem_to_phys(oic->out_msg.addr); + + /* QTEE assume unused buffers are zeroed; Do it now! */ + memzero_explicit(oic->in_msg.addr, oic->in_msg.size); + memzero_explicit(oic->out_msg.addr, oic->out_msg.size); + + return 0; +} + +static void qcom_tee_msg_buffers_free(struct qcom_tee_object_invoke_ctx *o= ic) +{ + qcom_tzmem_free(oic->in_msg.addr); + qcom_tzmem_free(oic->out_msg.addr); +} + +static int qcom_tee_msg_buffers_init(void) +{ + struct qcom_tzmem_pool_config config =3D { + .policy =3D QCOM_TZMEM_POLICY_ON_DEMAND, + /* 4M seems enough, it is used for QTEE meg header and qcom_tee_msg_arg = array. */ + .max_size =3D SZ_4M + }; + + tzmem_msg_pool =3D qcom_tzmem_pool_new(&config); + if (IS_ERR(tzmem_msg_pool)) + return PTR_ERR(tzmem_msg_pool); + + return 0; +} + +static void qcom_tee_msg_buffers_destroy(void) +{ + qcom_tzmem_pool_free(tzmem_msg_pool); +} + +/* Invoke a REE object. */ +static void qcom_tee_object_invoke(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_msg_callback *msg) +{ + int i, errno; + u32 op; + + /* Get object being invoked. */ + unsigned int object_id =3D msg->cxt; + struct qcom_tee_object *object; + + /* QTEE can not invoke NULL object or objects it hosts. */ + if (qcom_tee_object_type(object_id) =3D=3D QCOM_TEE_OBJECT_TYPE_NULL || + qcom_tee_object_type(object_id) =3D=3D QCOM_TEE_OBJECT_TYPE_TEE) { + errno =3D -EINVAL; + goto out; + } + + object =3D qcom_tee_local_object_get(object_id); + if (object =3D=3D NULL_QCOM_TEE_OBJECT) { + errno =3D -EINVAL; + goto out; + } + + oic->object =3D object; + + /* Filter bits used by transport. */ + op =3D msg->op & QCOM_TEE_MSG_OBJECT_OP_MASK; + + switch (op) { + case QCOM_TEE_MSG_OBJECT_OP_RELEASE: + qcom_tee_object_id_put(object_id); + qcom_tee_object_put(object); + errno =3D 0; + + break; + case QCOM_TEE_MSG_OBJECT_OP_RETAIN: + qcom_tee_object_get(object); + errno =3D 0; + + break; + default: + errno =3D qcom_tee_prepare_args(oic); + if (errno) { + /* Unable to parse the message. Release any object arrived as input. */ + qcom_tee_arg_for_each_input_buffer(i, oic->u) + qcom_tee_object_put(oic->u[i].o); + + break; + } + + errno =3D object->ops->dispatch(oic, object, op, oic->u); + if (!errno) { + /* On SUCCESS, notify object at appropriate time. */ + oic->flags |=3D QCOM_TEE_OIC_FLAG_NOTIFY; + } + } + +out: + + oic->errno =3D errno; +} + +/** + * __qcom_tee_object_do_invoke() - Submit an invocation for an object. + * @oic: context to use for current invocation. + * @object: object being invoked. + * @op: requested operation on object. + * @u: array of argument for the current invocation. + * @result: result returned from QTEE. + * + * The caller is responsible to keep track of the refcount for each object, + * including @object. On return, the caller loses the ownership of all inp= ut + * object of type %QCOM_TEE_OBJECT_TYPE_CB_OBJECT. + * + * Return: On success return 0. On error returns -EINVAL and -ENOSPC if un= able to initiate + * the invocation, -EAGAIN if invocation failed and user may retry the inv= ocation. + * Otherwise, -ENODEV on fatal failure. + */ +int __qcom_tee_object_do_invoke(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u, + int *result) +{ + struct qcom_tee_msg_callback *cb_msg; + u64 response_type; + int i, ret, errno; + + ret =3D qcom_tee_msg_buffers_alloc(oic, u); + if (ret) + return ret; + + ret =3D qcom_tee_prepare_msg(oic, object, op, u); + if (ret) + goto out; + + cb_msg =3D (struct qcom_tee_msg_callback *)oic->out_msg.addr; + + while (1) { + if (oic->flags & QCOM_TEE_OIC_FLAG_BUSY) { + errno =3D oic->errno; + /* Update output buffer only if result is SUCCESS. */ + if (!errno) + errno =3D qcom_tee_update_msg(oic); + + qcom_tee_msg_translate_err(cb_msg, errno); + } + + /* Invoke remote object. */ + ret =3D qcom_tee_object_invoke_ctx_invoke(oic, result, &response_type); + + if (oic->flags & QCOM_TEE_OIC_FLAG_BUSY) { + struct qcom_tee_object *qto =3D oic->object; + + if (qto) { + if (oic->flags & QCOM_TEE_OIC_FLAG_NOTIFY) { + if (qto->ops->notify) + qto->ops->notify(oic, qto, errno || ret); + } + + /* Matching get is in qcom_tee_object_invoke. */ + qcom_tee_object_put(qto); + } + + oic->object =3D NULL_QCOM_TEE_OBJECT; + oic->flags &=3D ~(QCOM_TEE_OIC_FLAG_BUSY | QCOM_TEE_OIC_FLAG_NOTIFY); + } + + if (ret) { + if (!(oic->flags & QCOM_TEE_OIC_FLAG_SHARED)) { + /* Release QCOM_TEE_OBJECT_TYPE_CB_OBJECT input objects. */ + qcom_tee_arg_for_each_input_object(i, u) + if (typeof_qcom_tee_object(u[i].o) =3D=3D + QCOM_TEE_OBJECT_TYPE_CB_OBJECT) + qcom_tee_object_put(u[i].o); + + ret =3D -EAGAIN; + } else { + /* On error, there is no clean way to exit. */ + /* For some reason we can not communicate with QTEE, so we can not + * notify QTEE about the failure and do further cleanup. + */ + ret =3D -ENODEV; + } + + goto out; + + } else { + /* QTEE obtained the ownership of QCOM_TEE_OBJECT_TYPE_CB_OBJECT + * input objects in 'u'. On further failure, QTEE is responsible + * to release them. + */ + oic->flags |=3D QCOM_TEE_OIC_FLAG_SHARED; + } + + /* Is it a callback request? */ +#define QCOM_TEE_RESULT_INBOUND_REQ_NEEDED 3 + if (response_type !=3D QCOM_TEE_RESULT_INBOUND_REQ_NEEDED) { + if (!*result) { + ret =3D qcom_tee_update_args(u, oic); + if (ret) { + qcom_tee_arg_for_each_output_object(i, u) + qcom_tee_object_put(u[i].o); + + ret =3D -EAGAIN; + } + } + + break; + + } else { + oic->flags |=3D QCOM_TEE_OIC_FLAG_BUSY; + /* Before dispatching the request, handle any pending async requests. */ + qcom_tee_fetch_async_reqs(oic); + qcom_tee_object_invoke(oic, cb_msg); + } + } + + qcom_tee_fetch_async_reqs(oic); + +out: + qcom_tee_msg_buffers_free(oic); + + return ret; +} + +int qcom_tee_object_do_invoke(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u, + int *result) +{ + /* User can not set bits used by transport. */ + if (op & ~QCOM_TEE_MSG_OBJECT_OP_MASK) + return -EINVAL; + + /* User can only invoke QTEE hosted objects. */ + if (typeof_qcom_tee_object(object) !=3D QCOM_TEE_OBJECT_TYPE_TEE && + typeof_qcom_tee_object(object) !=3D QCOM_TEE_OBJECT_TYPE_ROOT) + return -EINVAL; + + /* User can not issue reserved operations to QTEE. */ + if (op =3D=3D QCOM_TEE_MSG_OBJECT_OP_RELEASE || op =3D=3D QCOM_TEE_MSG_OB= JECT_OP_RETAIN) + return -EINVAL; + + return __qcom_tee_object_do_invoke(oic, object, op, u, result); +} +EXPORT_SYMBOL_GPL(qcom_tee_object_do_invoke); + +/* Dump object table. */ +static ssize_t qcom_tee_object_table_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct qcom_tee_object *object; + unsigned long idx; + size_t len =3D 0; + + xa_for_each_start(&xa_qcom_local_objects, idx, object, QCOM_TEE_OBJECT_ID= _START) { + len +=3D sysfs_emit_at(buf, len, "%4lx %4d %s\n", idx, + kref_read(&object->refcount), + qcom_tee_object_name(object)); + } + + return len; +} + +static struct kobj_attribute object_table =3D __ATTR_RO(qcom_tee_object_ta= ble); +static struct kobj_attribute release =3D __ATTR_RO(qcom_tee_release_wq); +static struct attribute *attrs[] =3D { + &object_table.attr, + &release.attr, + NULL +}; + +static struct attribute_group attr_group =3D { + .attrs =3D attrs, +}; + +static struct kobject *qcom_tee_object_invoke_kobj; +static int __init qcom_tee_object_invoke_init(void) +{ + int ret; + + ret =3D qcom_tee_release_init(); + if (ret) + return ret; + + ret =3D qcom_tee_msg_buffers_init(); + if (ret) + goto err_release_destroy; + + /* Create '/sys/firmware/qcom_tee'. */ + qcom_tee_object_invoke_kobj =3D kobject_create_and_add("qcom_tee", firmwa= re_kobj); + if (!qcom_tee_object_invoke_kobj) { + ret =3D -ENOMEM; + + goto err_msg_buffers_destroy; + } + + /* Create 'qcom_tee_object_table' and 'qcom_tee_release_wq'. */ + ret =3D sysfs_create_group(qcom_tee_object_invoke_kobj, &attr_group); + if (ret) + goto err_kobject_put; + + return 0; + +err_kobject_put: + /* Remove '/sys/firmware/qcom_tee'. */ + kobject_put(qcom_tee_object_invoke_kobj); +err_msg_buffers_destroy: + qcom_tee_msg_buffers_destroy(); +err_release_destroy: + qcom_tee_release_destroy(); + + return ret; +} +module_init(qcom_tee_object_invoke_init); + +static void __exit qcom_tee_object_invoke_deinit(void) +{ + /* Wait for RELEASE operations for QTEE objects. */ + qcom_tee_release_destroy(); + qcom_tee_msg_buffers_destroy(); + sysfs_remove_group(qcom_tee_object_invoke_kobj, &attr_group); + kobject_put(qcom_tee_object_invoke_kobj); +} +module_exit(qcom_tee_object_invoke_deinit); + +MODULE_AUTHOR("Qualcomm"); +MODULE_DESCRIPTION("QTEE driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/tee/qcomtee/qcom_scm.c b/drivers/tee/qcomtee/qcom_scm.c new file mode 100644 index 000000000000..230faf249095 --- /dev/null +++ b/drivers/tee/qcomtee/qcom_scm.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include + +#include "qcomtee_private.h" + +int qcom_tee_object_invoke_ctx_invoke(struct qcom_tee_object_invoke_ctx *o= ic, + int *result, u64 *response_type) +{ + int ret; + u64 res; + + if (!(oic->flags & QCOM_TEE_OIC_FLAG_BUSY)) { + /* Direct QTEE object invocation. */ + ret =3D qcom_scm_qtee_invoke_smc(oic->in_msg_paddr, + oic->in_msg.size, + oic->out_msg_paddr, + oic->out_msg.size, + &res, response_type, NULL); + } else { + /* Submit callback response. */ + ret =3D qcom_scm_qtee_callback_response(oic->out_msg_paddr, + oic->out_msg.size, + &res, response_type, NULL); + } + + if (ret) + pr_err("QTEE returned with %d.\n", ret); + else + *result =3D (int)res; + + return ret; +} diff --git a/drivers/tee/qcomtee/qcomtee_msg.h b/drivers/tee/qcomtee/qcomte= e_msg.h new file mode 100644 index 000000000000..7c968834ec9d --- /dev/null +++ b/drivers/tee/qcomtee/qcomtee_msg.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef QCOMTEE_MSG_H +#define QCOMTEE_MSG_H + +#include + +/** + * DOC: ''Qualcomm TEE'' (QTEE) Transport Message + * + * There are two buffers shared with QTEE, inbound and outbound buffers. + * The inbound buffer is used for direct object invocation and the outboun= d buffer is + * used to make a request from QTEE to kernel, i.e. callback request. + * + * The unused tail of the outbound buffer is also used for sending and rec= eiving + * asynchronous messages. An asynchronous message is independent from the = current + * object invocation (i.e. contents of the inbound buffer) or callback req= uest + * (i.e. the head of the outbound buffer), see qcom_tee_get_async_buffer()= . It is + * used by endpoints (QTEE or kernel) as an optimization to reduce number = of context + * switches between secure and non-secure world. + * + * For instance, QTEE never sends an explicit callback request to release = an object in + * kernel. Instead, it sends asynchronous release messages in outbound buf= fer when QTEE + * returns from previous direct object invocation, or append asynchronous = release + * messages after the current callback request. + * + * QTEE supports two types of arguments in a message: buffer and object ar= guments. + * Depending on the direction of data flow, they could be input buffer (IO= ) to QTEE, + * output buffer (OB) from QTEE, input object (IO) to QTEE, or output obje= ct (OO) from + * QTEE. Object arguments hold object ids. Buffer arguments hold (offset, = size) pairs + * into the inbound or outbound buffers. + * + * QTEE holds an object table for objects, it hosts and exposes to kernel.= An object id + * is an index to the object table in QTEE. + * + * For direct object invocation message format in inbound buffer see + * &struct qcom_tee_msg_object_invoke. For callback request message format= in outbound + * buffer see &struct qcom_tee_msg_callback. For the message format for as= ynchronous message + * in outbound buffer see &struct qcom_tee_async_msg_hdr. + */ + +/** + * define QCOM_TEE_MSG_OBJECT_NS_BIT - Non-secure bit + * + * Object id is a globally unique 32-bit number. Ids referencing objects i= n kernel should + * have %QCOM_TEE_MSG_OBJECT_NS_BIT set. + */ +#define QCOM_TEE_MSG_OBJECT_NS_BIT BIT(31) + +/* Static object ids recognized by QTEE. */ +#define QCOM_TEE_MSG_OBJECT_NULL (0U) +#define QCOM_TEE_MSG_OBJECT_ROOT (1U) + +/* Definitions from QTEE as part of the transport protocol. */ + +/* qcom_tee_msg_arg is argument as recognized by QTEE. */ +union qcom_tee_msg_arg { + struct { + u32 offset; + u32 size; + } b; + u32 o; +}; + +/* BI and BO payloads in a QTEE messages should be at 64-bit boundaries. */ +#define qcom_tee_msg_offset_align(o) ALIGN((o), sizeof(u64)) + +/* Operation for objects is 32-bit. Transport uses upper 16-bits internall= y. */ +#define QCOM_TEE_MSG_OBJECT_OP_MASK 0x0000FFFFU + +/* Reserved Operation IDs sent to QTEE: */ +/* QCOM_TEE_MSG_OBJECT_OP_RELEASE - Reduces the refcount and releases the = object. + * QCOM_TEE_MSG_OBJECT_OP_RETAIN - Increases the refcount. + * + * These operation id are valid for all objects. They are not available ou= tside of this + * driver. Developers should use qcom_tee_object_get() and qcom_tee_object= _put(), to + * achieve the same. + */ + +#define QCOM_TEE_MSG_OBJECT_OP_RELEASE (QCOM_TEE_MSG_OBJECT_OP_MASK - 0) +#define QCOM_TEE_MSG_OBJECT_OP_RETAIN (QCOM_TEE_MSG_OBJECT_OP_MASK - 1) + +/** + * struct qcom_tee_msg_object_invoke - Direct object invocation message + * @ctx: object id hosted in QTEE + * @op: operation for the object + * @counts: number of different type of arguments in @args + * @args: array of arguments + * + * @counts consists of 4 * 4-bits felids. Bits 0 - 3, is number of input b= uffers, + * bits 4 - 7, is number of output buffers, bits 8 - 11, is number of inpu= t objects, + * and bits 12 - 15, is number of output objects. Remaining bits should be= zero. + * + * Maximum number of arguments of each type is defined by %QCOM_TEE_ARGS_P= ER_TYPE. + */ +struct qcom_tee_msg_object_invoke { + u32 cxt; + u32 op; + u32 counts; + union qcom_tee_msg_arg args[]; +}; + +/** + * struct qcom_tee_msg_callback - Callback request message + * @result: result of operation @op on object referenced by @cxt + * @cxt: object id hosted in kernel + * @op: operation for the object + * @counts: number of different type of arguments in @args + * @args: array of arguments + * + * For details of @counts, see &qcom_tee_msg_object_invoke.counts. + */ +struct qcom_tee_msg_callback { + u32 result; + u32 cxt; + u32 op; + u32 counts; + union qcom_tee_msg_arg args[]; +}; + +/* Offset in the message for the beginning of buffer argument's contents. = */ +#define qcom_tee_msg_buffer_args(t, n) \ + qcom_tee_msg_offset_align(struct_size_t(t, args, n)) +/* Pointer to the beginning of a buffer argument's content at an offset in= a message. */ +#define qcom_tee_msg_offset_to_ptr(m, off) ((void *)&((char *)(m))[(off)]) + +/* Some helpers to manage msg.counts. */ + +#define QCOM_TEE_MSG_NUM_IB(x) ((x) & 0xfU) +#define QCOM_TEE_MSG_NUM_OB(x) (((x) >> 4) & 0xfU) +#define QCOM_TEE_MSG_NUM_IO(x) (((x) >> 8) & 0xfU) +#define QCOM_TEE_MSG_NUM_OO(x) (((x) >> 12) & 0xfU) + +#define QCOM_TEE_MSG_IDX_IB(x) (0U) +#define QCOM_TEE_MSG_IDX_OB(x) (QCOM_TEE_MSG_IDX_IB(x) + QCOM_TEE_MSG_NUM_= IB(x)) +#define QCOM_TEE_MSG_IDX_IO(x) (QCOM_TEE_MSG_IDX_OB(x) + QCOM_TEE_MSG_NUM_= OB(x)) +#define QCOM_TEE_MSG_IDX_OO(x) (QCOM_TEE_MSG_IDX_IO(x) + QCOM_TEE_MSG_NUM_= IO(x)) + +#define qcom_tee_msg_for_each(i, c, type) \ + for (i =3D QCOM_TEE_MSG_IDX_##type(c); \ + i < (QCOM_TEE_MSG_IDX_##type(c) + QCOM_TEE_MSG_NUM_##type(c)); \ + i++) + +#define qcom_tee_msg_for_each_input_buffer(i, m) qcom_tee_msg_for_each(i,= (m)->counts, IB) +#define qcom_tee_msg_for_each_output_buffer(i, m) qcom_tee_msg_for_each(i,= (m)->counts, OB) +#define qcom_tee_msg_for_each_input_object(i, m) qcom_tee_msg_for_each(i,= (m)->counts, IO) +#define qcom_tee_msg_for_each_output_object(i, m) qcom_tee_msg_for_each(i,= (m)->counts, OO) + +/* Sum of arguments in a message. */ +#define qcom_tee_msg_args(m) (QCOM_TEE_MSG_IDX_OO((m)->counts) + QCOM_TEE_= MSG_NUM_OO((m)->counts)) + +static inline void qcom_tee_msg_init(struct qcom_tee_msg_object_invoke *ms= g, u32 cxt, u32 op, + int in_buffer, int out_buffer, int in_object, int out_object) +{ + msg->counts |=3D (in_buffer & 0xfU); + msg->counts |=3D ((out_buffer - in_buffer) & 0xfU) << 4; + msg->counts |=3D ((in_object - out_buffer) & 0xfU) << 8; + msg->counts |=3D ((out_object - in_object) & 0xfU) << 12; + msg->cxt =3D cxt; + msg->op =3D op; +} + +/* Generic error codes. */ +#define QCOM_TEE_MSG_OK 0 /* non-specific success code. */ +#define QCOM_TEE_MSG_ERROR 1 /* non-specific error. */ +#define QCOM_TEE_MSG_ERROR_INVALID 2 /* unsupported/unrecognized request. = */ +#define QCOM_TEE_MSG_ERROR_SIZE_IN 3 /* supplied buffer/string too large. = */ +#define QCOM_TEE_MSG_ERROR_SIZE_OUT 4 /* supplied output buffer too small.= */ +#define QCOM_TEE_MSG_ERROR_USERBASE 10 /* start of user-defined error rang= e. */ + +/* Transport layer error codes. */ +#define QCOM_TEE_MSG_ERROR_DEFUNCT -90 /* object no longer exists. */ +#define QCOM_TEE_MSG_ERROR_ABORT -91 /* calling thread must exit. */ +#define QCOM_TEE_MSG_ERROR_BADOBJ -92 /* invalid object context. */ +#define QCOM_TEE_MSG_ERROR_NOSLOTS -93 /* caller's object table full. */ +#define QCOM_TEE_MSG_ERROR_MAXARGS -94 /* too many args. */ +#define QCOM_TEE_MSG_ERROR_MAXDATA -95 /* buffers too large. */ +#define QCOM_TEE_MSG_ERROR_UNAVAIL -96 /* the request could not be process= ed. */ +#define QCOM_TEE_MSG_ERROR_KMEM -97 /* kernel out of memory. */ +#define QCOM_TEE_MSG_ERROR_REMOTE -98 /* local method sent to remote objec= t. */ +#define QCOM_TEE_MSG_ERROR_BUSY -99 /* Object is busy. */ +#define QCOM_TEE_MSG_ERROR_TIMEOUT -103 /* Call Back Object invocation tim= ed out. */ + +static inline void qcom_tee_msg_translate_err(struct qcom_tee_msg_callback= *cb_msg, int err) +{ + if (!err) { + cb_msg->result =3D QCOM_TEE_MSG_OK; + } else if (err < 0) { + /* If err < 0, then it is a transport error. */ + switch (err) { + case -ENOMEM: + cb_msg->result =3D QCOM_TEE_MSG_ERROR_KMEM; + break; + case -ENODEV: + cb_msg->result =3D QCOM_TEE_MSG_ERROR_DEFUNCT; + break; + case -ENOSPC: + case -EBUSY: + cb_msg->result =3D QCOM_TEE_MSG_ERROR_BUSY; + break; + case -EBADF: + case -EINVAL: + cb_msg->result =3D QCOM_TEE_MSG_ERROR_UNAVAIL; + break; + default: + cb_msg->result =3D QCOM_TEE_MSG_ERROR; + } + } else { + /* If err > 0, then it is user defined error, pass it as is. */ + cb_msg->result =3D err; + } +} + +#endif /* QCOMTEE_MSG_H */ diff --git a/drivers/tee/qcomtee/qcomtee_private.h b/drivers/tee/qcomtee/qc= omtee_private.h new file mode 100644 index 000000000000..e3e4ef51c0b2 --- /dev/null +++ b/drivers/tee/qcomtee/qcomtee_private.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef QCOM_TEE_PRIVATE_H +#define QCOM_TEE_PRIVATE_H + +#include +#include +#include + +struct qcom_tee_object *qcom_tee_idx_erase(u32 idx); +void qcom_tee_object_free(struct qcom_tee_object *object); + +/* Process async messages form QTEE. */ +void qcom_tee_fetch_async_reqs(struct qcom_tee_object_invoke_ctx *oic); + +int qcom_tee_release_init(void); +void qcom_tee_release_destroy(void); +void qcom_tee_release_tee_object(struct qcom_tee_object *object); +ssize_t qcom_tee_release_wq_show(struct kobject *kobj, struct kobj_attribu= te *attr, char *buf); + +/* SCM call. */ +int qcom_tee_object_invoke_ctx_invoke(struct qcom_tee_object_invoke_ctx *o= ic, + int *result, u64 *response_type); + +/** + * __qcom_tee_object_do_invoke() - Submit an invocation for an object. + * @oic: context to use for current invocation. + * @object: object being invoked. + * @op: requested operation on object. + * @u: array of argument for the current invocation. + * @result: result returned from QTEE. + * + * Same as qcom_tee_object_do_invoke() without @object and @op is 32-bit, + * upper 16-bits are for internal use. + * + * Return: On success return 0. On error returns -EINVAL and -ENOSPC if un= able to initiate + * the invocation, -EAGAIN if invocation failed and user can retry the inv= ocation. + * Otherwise, -ENODEV on fatal failure. + */ +int __qcom_tee_object_do_invoke(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u, + int *result); + +#endif /* QCOM_TEE_PRIVATE_H */ diff --git a/drivers/tee/qcomtee/release.c b/drivers/tee/qcomtee/release.c new file mode 100644 index 000000000000..f2e048418e23 --- /dev/null +++ b/drivers/tee/qcomtee/release.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "qcomtee_private.h" +#include "qcomtee_msg.h" + +static struct workqueue_struct *qcom_tee_release_wq; + +/* Number of all release requests pending for processing. */ +static atomic_t qcom_tee_pending_releases =3D ATOMIC_INIT(0); + +/* qcom_tee_object_do_release makes direct object invocation to release an= object. */ +static void qcom_tee_destroy_user_object(struct work_struct *work) +{ + static struct qcom_tee_object_invoke_ctx oic; + static struct qcom_tee_arg args[1] =3D { 0 }; + struct qcom_tee_object *object; + int ret, result; + + object =3D container_of(work, struct qcom_tee_object, work); + + ret =3D __qcom_tee_object_do_invoke(&oic, object, QCOM_TEE_MSG_OBJECT_OP_= RELEASE, args, + &result); + + /* Is it safe to retry the release? */ + if (ret =3D=3D -EAGAIN) { + queue_work(qcom_tee_release_wq, &object->work); + } else { + if (ret || result) + pr_err("%s: %s release failed, ret =3D %d (%x).\n", + __func__, qcom_tee_object_name(object), ret, result); + + atomic_dec(&qcom_tee_pending_releases); + qcom_tee_object_free(object); + } +} + +/* qcom_tee_release_tee_object puts object in release work queue. */ +void qcom_tee_release_tee_object(struct qcom_tee_object *object) +{ + INIT_WORK(&object->work, qcom_tee_destroy_user_object); + atomic_inc(&qcom_tee_pending_releases); + queue_work(qcom_tee_release_wq, &object->work); +} + +ssize_t qcom_tee_release_wq_show(struct kobject *kobj, struct kobj_attribu= te *attr, char *buf) +{ + return sysfs_emit(buf, "%d\n", atomic_read(&qcom_tee_pending_releases)); +} + +int qcom_tee_release_init(void) +{ + qcom_tee_release_wq =3D alloc_ordered_workqueue("qcom_tee_release_wq", 0); + if (!qcom_tee_release_wq) + return -ENOMEM; + + return 0; +} + +void qcom_tee_release_destroy(void) +{ + /* It drains the wq. */ + destroy_workqueue(qcom_tee_release_wq); +} diff --git a/include/linux/firmware/qcom/qcom_tee.h b/include/linux/firmwar= e/qcom/qcom_tee.h new file mode 100644 index 000000000000..90e5e10a0e62 --- /dev/null +++ b/include/linux/firmware/qcom/qcom_tee.h @@ -0,0 +1,284 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __QCOM_TEE_H +#define __QCOM_TEE_H + +#include +#include +#include + +struct qcom_tee_object; + +/** + * DOC: Overview + * + * qcom_tee_object provides object ref-counting, id allocation for objects= hosted in + * REE, and necessary message marshaling for Qualcomm TEE (QTEE). + * + * To invoke an object in QTEE, user calls qcom_tee_object_do_invoke() whi= le passing + * an instance of &struct qcom_tee_object and the requested operation + ar= guments. + * + * After the boot, QTEE provides a static object %ROOT_QCOM_TEE_OBJECT (ty= pe of + * %QCOM_TEE_OBJECT_TYPE_ROOT). The root object is invoked to pass user's = credentials and + * obtain other instances of &struct qcom_tee_object (type of %QCOM_TEE_OB= JECT_TYPE_TEE) + * that represents services and TAs in QTEE, see &enum qcom_tee_object_typ= e. + * + * The object received from QTEE are refcounted. So the owner of these obj= ects can + * issue qcom_tee_object_get(), to increase the refcount, and pass objects= to other + * clients, or issue qcom_tee_object_put() to decrease the refcount, and r= eleasing + * the resources in QTEE. + * + * REE can host services accessible to QTEE. A driver should embed an inst= ance of + * &struct qcom_tee_object in the struct it wants to export to QTEE (it is= called + * callback object). It issues qcom_tee_object_user_init() to set the disp= atch() + * operation for the callback object and set its type to %QCOM_TEE_OBJECT_= TYPE_CB_OBJECT. + * + * core.c holds an object table for callback objects. An object id is assi= gned + * to each callback object which is an index to the object table. QTEE use= s these ids + * to reference or invoke callback objects. + * + * If QTEE invoke a callback object in REE, the dispatch() operation is ca= lled in the + * context of thread that called qcom_tee_object_do_invoke(), originally. + */ + +/** + * enum qcom_tee_object_typ - Object types. + * @QCOM_TEE_OBJECT_TYPE_TEE: object hosted on QTEE. + * @QCOM_TEE_OBJECT_TYPE_CB_OBJECT: object hosted on REE. + * @QCOM_TEE_OBJECT_TYPE_ROOT: 'primordial' object. + * @QCOM_TEE_OBJECT_TYPE_NULL: NULL object. + * + * Primordial object is used for bootstrapping the IPC connection between = a REE + * and QTEE. It is invoked by REE when it wants to get a 'client env'. + */ +enum qcom_tee_object_type { + QCOM_TEE_OBJECT_TYPE_TEE, + QCOM_TEE_OBJECT_TYPE_CB_OBJECT, + QCOM_TEE_OBJECT_TYPE_ROOT, + QCOM_TEE_OBJECT_TYPE_NULL, +}; + +/** + * enum qcom_tee_arg_type - Type of QTEE argument. + * @QCOM_TEE_ARG_TYPE_INV: invalid type. + * @QCOM_TEE_ARG_TYPE_IB: input buffer (IO). + * @QCOM_TEE_ARG_TYPE_OO: output object (OO). + * @QCOM_TEE_ARG_TYPE_OB: output buffer (OB). + * @QCOM_TEE_ARG_TYPE_IO: input object (IO). + * + * Use invalid type to specify end of argument array. + */ +enum qcom_tee_arg_type { + QCOM_TEE_ARG_TYPE_INV =3D 0, + QCOM_TEE_ARG_TYPE_OB, + QCOM_TEE_ARG_TYPE_OO, + QCOM_TEE_ARG_TYPE_IB, + QCOM_TEE_ARG_TYPE_IO, + QCOM_TEE_ARG_TYPE_NR, +}; + +/** + * define QCOM_TEE_ARGS_PER_TYPE - Maximum arguments of specific type. + * + * QTEE transport protocol limits maximum number of argument of specific t= ype + * (i.e. IB, OB, IO, and OO). + */ +#define QCOM_TEE_ARGS_PER_TYPE 16 + +/* Maximum arguments that can fit in a QTEE message, ignoring the type. */ +#define QCOM_TEE_ARGS_MAX (QCOM_TEE_ARGS_PER_TYPE * (QCOM_TEE_ARG_TYPE_NR = - 1)) + +struct qcom_tee_buffer { + union { + void *addr; + void __user *uaddr; + }; + size_t size; +}; + +/** + * struct qcom_tee_arg - Argument for QTEE object invocation. + * @type: type of argument as &enum qcom_tee_arg_type. + * @flags: extra flags. + * @b: address and size if type of argument is buffer. + * @o: object instance if type of argument is object. + * + * &qcom_tee_arg.flags only accept %QCOM_TEE_ARG_FLAGS_UADDR for now which= states + * that &qcom_tee_arg.b contains userspace address in uaddr. + */ +struct qcom_tee_arg { + enum qcom_tee_arg_type type; +/* 'b.uaddr' holds a __user address. */ +#define QCOM_TEE_ARG_FLAGS_UADDR 1 + unsigned int flags; + union { + struct qcom_tee_buffer b; + struct qcom_tee_object *o; + }; +}; + +static inline int qcom_tee_args_len(struct qcom_tee_arg *args) +{ + int i =3D 0; + + while (args[i].type !=3D QCOM_TEE_ARG_TYPE_INV) + i++; + return i; +} + +#define QCOM_TEE_OIC_FLAG_BUSY BIT(1) /* Context is busy (callback is in = progress). */ +#define QCOM_TEE_OIC_FLAG_NOTIFY BIT(2) /* Context needs to notify the cur= rent object. */ +#define QCOM_TEE_OIC_FLAG_SHARED BIT(3) /* Context has shared state with Q= TEE. */ + +struct qcom_tee_object_invoke_ctx { + unsigned long flags; + int errno; + + /* Current object invoked in this callback context. */ + struct qcom_tee_object *object; + + /* Arguments passed to dispatch callback (+1 for ending QCOM_TEE_ARG_TYPE= _INV). */ + struct qcom_tee_arg u[QCOM_TEE_ARGS_MAX + 1]; + + /* Inbound and Outbound buffers shared with QTEE. */ + struct qcom_tee_buffer in_msg; /* Inbound Buffer. */ + phys_addr_t in_msg_paddr; /* Physical address of inbound buffer. */ + struct qcom_tee_buffer out_msg; /* Outbound Buffer. */ + phys_addr_t out_msg_paddr; /* Physical address of outbound buffer. */ + + /* Extra data attached to this context. */ + void *data; +}; + +/** + * qcom_tee_object_do_invoke() - Submit an invocation for an object. + * @oic: context to use for current invocation. + * @object: object being invoked. + * @op: requested operation on object. + * @u: array of argument for the current invocation. + * @result: result returned from QTEE. + * + * The caller is responsible to keep track of the refcount for each object, + * including @object. On return, the caller loses the ownership of all inp= ut object of + * type %QCOM_TEE_OBJECT_TYPE_CB_OBJECT. + * + * @object can be of %QCOM_TEE_OBJECT_TYPE_ROOT or %QCOM_TEE_OBJECT_TYPE_T= EE types. + * + * Return: On success return 0. On error returns -EINVAL and -ENOSPC if un= able to initiate + * the invocation, -EAGAIN if invocation failed and user may retry the inv= ocation. + * Otherwise, -ENODEV on fatal failure. + */ +int qcom_tee_object_do_invoke(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u, + int *result); + +/** + * struct qcom_tee_object_operations - Callback object operations. + * @release: release object if QTEE is not using it. + * @dispatch: dispatch the operation requested by QTEE. + * @notify: report status of any pending response submitted by @dispatch. + * + * Transport may fail (e.g. object table is full) even after @dispatch suc= cessfully submitted + * the response. @notify is called to do the necessary cleanup. + */ +struct qcom_tee_object_operations { + void (*release)(struct qcom_tee_object *object); + int (*dispatch)(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *args); + void (*notify)(struct qcom_tee_object_invoke_ctx *oic, + struct qcom_tee_object *object, int err); +}; + +/** + * struct qcom_tee_object - QTEE or REE object. + * @name: object name. + * @refcount: reference counter. + * @object_type: object type as &enum qcom_tee_object_type. + * @info: extra information for object. + * @owner: owning module/driver. + * @ops: callback operations for object of type %QCOM_TEE_OBJECT_TYPE_CB_O= BJECT. + * @work: work for async operation on object. + * + * @work is currently only used for release object of %QCOM_TEE_OBJECT_TYP= E_TEE type. + */ +struct qcom_tee_object { + const char *name; + struct kref refcount; + + enum qcom_tee_object_type object_type; + union object_info { + /* QTEE object id if object_type is %QCOM_TEE_OBJECT_TYPE_TEE. */ + unsigned long qtee_id; + } info; + + struct module *owner; + struct qcom_tee_object_operations *ops; + struct work_struct work; +}; + +/* Static instances of qcom_tee_object objects. */ +#define NULL_QCOM_TEE_OBJECT ((struct qcom_tee_object *)(0)) +extern struct qcom_tee_object qcom_tee_object_root; +#define ROOT_QCOM_TEE_OBJECT (&qcom_tee_object_root) + +static inline enum qcom_tee_object_type typeof_qcom_tee_object(struct qcom= _tee_object *object) +{ + if (object =3D=3D NULL_QCOM_TEE_OBJECT) + return QCOM_TEE_OBJECT_TYPE_NULL; + return object->object_type; +} + +static inline const char *qcom_tee_object_name(struct qcom_tee_object *obj= ect) +{ + if (object =3D=3D NULL_QCOM_TEE_OBJECT) + return "null"; + + if (!object->name) + return "no-name"; + return object->name; +} + +/** + * __qcom_tee_object_user_init() - Initialize an object for user. + * @object: object to initialize. + * @object_type: type of object as &enum qcom_tee_object_type. + * @ops: instance of callbacks. + * @owner: owning module/driver. + * @fmt: name assigned to the object. + * + * Return: On success return 0 or <0 on failure. + */ +int __qcom_tee_object_user_init(struct qcom_tee_object *object, enum qcom_= tee_object_type ot, + struct qcom_tee_object_operations *ops, struct module *owner, + const char *fmt, ...); +#define qcom_tee_object_user_init(obj, ot, ops, fmt, ...) \ + __qcom_tee_object_user_init((obj), (ot), (ops), THIS_MODULE, (fmt), __VA_= ARGS__) + +/* Object release is RCU protected. */ +int qcom_tee_object_get(struct qcom_tee_object *object); +void qcom_tee_object_put(struct qcom_tee_object *object); + +#define qcom_tee_arg_for_each(i, args) \ + for (i =3D 0; args[i].type !=3D QCOM_TEE_ARG_TYPE_INV; i++) + +/* Next argument of type @type after index @i. */ +int qcom_tee_next_arg_type(struct qcom_tee_arg *u, int i, enum qcom_tee_ar= g_type type); + +/* Iterate over argument of given type. */ +#define qcom_tee_arg_for_each_type(i, args, at) \ + for (i =3D 0, i =3D qcom_tee_next_arg_type(args, i, at); \ + args[i].type !=3D QCOM_TEE_ARG_TYPE_INV; \ + i++, i =3D qcom_tee_next_arg_type(args, i, at)) + +#define qcom_tee_arg_for_each_input_buffer(i, args) \ + qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_IB) +#define qcom_tee_arg_for_each_output_buffer(i, args) \ + qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_OB) +#define qcom_tee_arg_for_each_input_object(i, args) \ + qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_IO) +#define qcom_tee_arg_for_each_output_object(i, args) \ + qcom_tee_arg_for_each_type(i, args, QCOM_TEE_ARG_TYPE_OO) + +#endif /* __QCOM_TEE_H */ --=20 2.34.1 From nobody Wed Dec 4 19:03:26 2024 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9C1F315FD01; Tue, 3 Dec 2024 04:20:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199626; cv=none; b=cs0y/i5zxvuIQ9eG0NhVqFlC7i9jsTjaMVxQQPqybZjjAj+m2gfe8VbYKoRcOhKM2FVUa0Ah/6/Djtd/VRSGmGgA+5mJvhH1YD6msolkDbzklPXsiPG6QVDKkj1xtu5MP1t+M6+d0Lvv2Gg9D7d9xlCFylQVH6Pn15BigzCem1s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199626; c=relaxed/simple; bh=4Ip/eYMJTKWmEjDinKD9UL0HIK6h/JR0O4OY7QZiKM0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=E2ofD8P9nuJav769KrWBhUZ5ZnJsdSaHqkJPtYiQ8HodlnooU3FPMiLENIyb4Rt2DlLFiaOvy04a+LJRq2cAyeoPoDBwC8wydmBH7zOq43jbqmzkBzgRTdWapDC5IM2rtOipZzkmrVmnk0eKl4DR2FCWoC8ZmCu9JAMMSvGxU0s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=mX9ReGUt; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="mX9ReGUt" Received: from pps.filterd (m0279863.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4B2KfxWX011526; Tue, 3 Dec 2024 04:20:08 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= 4pW94t0mQwR/YAdL0hlJgtjMU3iWcKHn55GbIYlcqUM=; b=mX9ReGUt+pO77iN5 ouFA0Sdwe1RgQoC2CtHk8hoHo9v7rdB7Aa1x0KG2OZxJ8iknn/2Y8W3vnzQu6ROp mD5ZjS/eBWiNzlR37WKRbOHnowwAfdYorHNJ3vvt0NxCXQhcrUAiALFe0FSFaDNA Zs0s6RHtgQIwKVECB3/jZsTQVTXojn79b9af9bqYnnwUZdf24D6b9H3xqZQt54KW kXITdZL2sAgRJpZy3f0B8Vn4dgp07wyTFLQALyFJLjkBw7Nt5XTc9K6ZZ2vRI926 Q2tBDLfNwErcULQqMkeRvmIaJdbth41n7TFl0slBNqtWNCRZxAoTa42iwrpk2xGQ lwiTKw== Received: from nasanppmta05.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4393mpbqre-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 03 Dec 2024 04:20:07 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA05.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 4B34K7vE024250 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 3 Dec 2024 04:20:07 GMT Received: from hu-azarrabi-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 2 Dec 2024 20:20:06 -0800 From: Amirreza Zarrabi Date: Mon, 2 Dec 2024 20:19:22 -0800 Subject: [PATCH 06/10] qcomtee: add primordial object Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-6-f502ef01e016@quicinc.com> References: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> In-Reply-To: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> To: Jens Wiklander , Sumit Garg , Bjorn Andersson , "Konrad Dybcio" , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Bartosz Golaszewski" , Srinivas Kandagatla CC: , , , , , Amirreza Zarrabi X-Mailer: b4 0.13.0 X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: wQdIXnWhvwQwx4MFC-ExCuKmECqWa2Vg X-Proofpoint-GUID: wQdIXnWhvwQwx4MFC-ExCuKmECqWa2Vg X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.60.29 definitions=2024-09-06_09,2024-09-06_01,2024-09-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 phishscore=0 mlxlogscore=999 impostorscore=0 mlxscore=0 malwarescore=0 suspectscore=0 lowpriorityscore=0 adultscore=0 spamscore=0 priorityscore=1501 clxscore=1011 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2411120000 definitions=main-2412030034 After booting, the kernel provides a static object known as the primordial object. This object is utilized by QTEE for native kernel services such as yield or privileged operations. Signed-off-by: Amirreza Zarrabi --- drivers/tee/qcomtee/Makefile | 1 + drivers/tee/qcomtee/core.c | 12 +++++-- drivers/tee/qcomtee/primordial_obj.c | 63 +++++++++++++++++++++++++++++++= ++++ drivers/tee/qcomtee/qcomtee_private.h | 5 +++ 4 files changed, 78 insertions(+), 3 deletions(-) diff --git a/drivers/tee/qcomtee/Makefile b/drivers/tee/qcomtee/Makefile index 7dc5e6373042..108bc7fdabcb 100644 --- a/drivers/tee/qcomtee/Makefile +++ b/drivers/tee/qcomtee/Makefile @@ -2,5 +2,6 @@ obj-$(CONFIG_QCOMTEE) +=3D qcomtee.o qcomtee-objs +=3D async.o qcomtee-objs +=3D core.o +qcomtee-objs +=3D primordial_obj.o qcomtee-objs +=3D qcom_scm.o qcomtee-objs +=3D release.o diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c index a949ef4cceee..79f1181cf676 100644 --- a/drivers/tee/qcomtee/core.c +++ b/drivers/tee/qcomtee/core.c @@ -34,9 +34,11 @@ int qcom_tee_next_arg_type(struct qcom_tee_arg *u, int i= , enum qcom_tee_arg_type } =20 /* QTEE expects IDs with QCOM_TEE_MSG_OBJECT_NS_BIT set for object of - * QCOM_TEE_OBJECT_TYPE_CB_OBJECT type. + * QCOM_TEE_OBJECT_TYPE_CB_OBJECT type. The first ID with QCOM_TEE_MSG_OBJ= ECT_NS_BIT set is + * reserved for primordial object. */ -#define QCOM_TEE_OBJECT_ID_START (QCOM_TEE_MSG_OBJECT_NS_BIT + 1) +#define QCOM_TEE_OBJECT_PRIMORDIAL (QCOM_TEE_MSG_OBJECT_NS_BIT) +#define QCOM_TEE_OBJECT_ID_START (QCOM_TEE_OBJECT_PRIMORDIAL + 1) #define QCOM_TEE_OBJECT_ID_END (UINT_MAX) =20 #define QCOM_TEE_OBJECT_SET(p, type, ...) __QCOM_TEE_OBJECT_SET(p, type, #= #__VA_ARGS__, 0UL) @@ -118,7 +120,8 @@ EXPORT_SYMBOL_GPL(qcom_tee_object_get); */ void qcom_tee_object_put(struct qcom_tee_object *object) { - if (object !=3D NULL_QCOM_TEE_OBJECT && + if (object !=3D &qcom_tee_primordial_object && + object !=3D NULL_QCOM_TEE_OBJECT && object !=3D ROOT_QCOM_TEE_OBJECT) kref_put(&object->refcount, qcom_tee_object_release); } @@ -209,6 +212,9 @@ static struct qcom_tee_object *qcom_tee_local_object_ge= t(unsigned int object_id) { struct qcom_tee_object *object; =20 + if (object_id =3D=3D QCOM_TEE_OBJECT_PRIMORDIAL) + return &qcom_tee_primordial_object; + /* We trust QTEE does not mess the refcounts. * It does not issue RELEASE request and qcom_tee_object_get(), simultane= ously. */ diff --git a/drivers/tee/qcomtee/primordial_obj.c b/drivers/tee/qcomtee/pri= mordial_obj.c new file mode 100644 index 000000000000..9065074b02e6 --- /dev/null +++ b/drivers/tee/qcomtee/primordial_obj.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "qcomtee_private.h" + +/** + * DOC: Primordial Object + * + * After the boot, REE provides a static object of type %QCOM_TEE_OBJECT_T= YPE_CB_OBJECT + * called primordial object. This object is used for native REE services o= r privileged operations. + * + * We support + * - %QCOM_TEE_OBJECT_OP_YIELD to yield by the thread running in QTEE. + * - %QCOM_TEE_OBJECT_OP_SLEEP to wait for period of time. + */ + +#define QCOM_TEE_OBJECT_OP_YIELD 1 +#define QCOM_TEE_OBJECT_OP_SLEEP 2 + +static int qcom_tee_primordial_object_dispatch(struct qcom_tee_object_invo= ke_ctx *oic, + struct qcom_tee_object *primordial_object_unused, + u32 op, struct qcom_tee_arg *args) +{ + int err =3D 0; + + switch (op) { + case QCOM_TEE_OBJECT_OP_YIELD: + cond_resched(); + /* No output object. */ + oic->data =3D NULL; + break; + case QCOM_TEE_OBJECT_OP_SLEEP: + /* Check message format matched QCOM_TEE_OBJECT_OP_SLEEP op. */ + if (qcom_tee_args_len(args) !=3D 1 || /* Expect 1 argument. */ + args[0].type !=3D QCOM_TEE_ARG_TYPE_IB || /* Time to sleep in ms. */ + args[0].b.size < sizeof(u32)) /* Buffer should hold a u32. */ + return -EINVAL; + + msleep(*(u32 *)(args[0].b.addr)); + /* No output object. */ + oic->data =3D NULL; + break; + default: + err =3D -EINVAL; + } + + return err; +} + +static struct qcom_tee_object_operations qcom_tee_primordial_object_ops = =3D { + .dispatch =3D qcom_tee_primordial_object_dispatch, +}; + +struct qcom_tee_object qcom_tee_primordial_object =3D { + .name =3D "primordial", + .object_type =3D QCOM_TEE_OBJECT_TYPE_CB_OBJECT, + .ops =3D &qcom_tee_primordial_object_ops +}; diff --git a/drivers/tee/qcomtee/qcomtee_private.h b/drivers/tee/qcomtee/qc= omtee_private.h index e3e4ef51c0b2..c718cd2d8463 100644 --- a/drivers/tee/qcomtee/qcomtee_private.h +++ b/drivers/tee/qcomtee/qcomtee_private.h @@ -44,4 +44,9 @@ int __qcom_tee_object_do_invoke(struct qcom_tee_object_in= voke_ctx *oic, struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u, int *result); =20 +/* OBJECTS: */ + +/* (1) Primordial Object. */ +extern struct qcom_tee_object qcom_tee_primordial_object; + #endif /* QCOM_TEE_PRIVATE_H */ --=20 2.34.1 From nobody Wed Dec 4 19:03:26 2024 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C0427A32; Tue, 3 Dec 2024 04:20:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199615; cv=none; b=i2sU56XfXDa1YHyjmW+5qO05oHoZLWFwkRqpnZU9Z2L6H8mJFGiW56becCV/gx1G7H2lgMzPiHhrdboTmXXVWKIsrXkzEev65Ollh6J2i0t3j5eogHVCqMs7DgGu+PKlkj+RRjDb1B3i+4zk9X1fH9Sp2qrsieQsC2bgNGaELmw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199615; c=relaxed/simple; bh=1k74yh5rMWor9nCD5PeVEFmS/qSH1qXubMdHAr4kg4g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=fFdhrdqsOHXxbCwq8iLXxbGWRfdBz1gyiyOUi9KizhWRQ1WfBNOvGTOG1eNOL61KthBeQzELnfPWoISXIZE14LRb9ZrRhowDxycOVBxSMD3p8uPXI+K7fdRMmCc5em90FzDZ+BJO8pj+mvCHR0nQy5kJtvkIM0Q0xuKIkzi/QjI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=pfE3mqjV; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="pfE3mqjV" Received: from pps.filterd (m0279870.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4B2K0u0D028930; Tue, 3 Dec 2024 04:20:08 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= owDSHhy8Sr3+CM51CcotyNAWcQJ8/9cPHNsDP/LdFTQ=; b=pfE3mqjVKdhkq0AE bsm9/pd4+k8Q3P6Li3/cqBioGbaoiP0MSaRT+Is4WchaIRKVIgQF5acA7bgFnLBV 4tV7xqPgNyShxoD8/K/3+JScB5jg0bLsjkVFiOv5S94pc+WD36IwS+Y3EzYceNLf 0ZWXhiolPSOH5nAffUWaHBspvKhNxcdaO8jp7YVVmvOWNuFLFVyiAHm80EHvmRdu MvwOomyTqNNMgnyBLtSSAaJShvE9UPBCm4L3L8/iqd73jJA/4ofF5DlLXQO6TxDB kaASJ+jGEst4MAsD0+w2QeOn0OlrAFL2tZZpOKOtGT2rF6I04o0rgZt2iwXtEZQ0 ct7T4Q== Received: from nasanppmta03.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 437tstesja-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 03 Dec 2024 04:20:08 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA03.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 4B34K70c029965 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 3 Dec 2024 04:20:07 GMT Received: from hu-azarrabi-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 2 Dec 2024 20:20:06 -0800 From: Amirreza Zarrabi Date: Mon, 2 Dec 2024 20:19:23 -0800 Subject: [PATCH 07/10] dt-bindings: arm: qcomtee: add QTEE driver devicetree binding for TEE subsystem Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-7-f502ef01e016@quicinc.com> References: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> In-Reply-To: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> To: Jens Wiklander , Sumit Garg , Bjorn Andersson , "Konrad Dybcio" , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Bartosz Golaszewski" , Srinivas Kandagatla CC: , , , , , Amirreza Zarrabi X-Mailer: b4 0.13.0 X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: 8lovn5mFuGbrdZsva5UXpvSHwQRofjvf X-Proofpoint-GUID: 8lovn5mFuGbrdZsva5UXpvSHwQRofjvf X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.60.29 definitions=2024-09-06_09,2024-09-06_01,2024-09-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 lowpriorityscore=0 malwarescore=0 spamscore=0 impostorscore=0 mlxlogscore=999 mlxscore=0 priorityscore=1501 suspectscore=0 phishscore=0 adultscore=0 clxscore=1015 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2411120000 definitions=main-2412030033 Introduce qcom,tee compatible string. Signed-off-by: Amirreza Zarrabi --- .../devicetree/bindings/arm/firmware/qcom,tee.yaml | 34 ++++++++++++++++++= ++++ 1 file changed, 34 insertions(+) diff --git a/Documentation/devicetree/bindings/arm/firmware/qcom,tee.yaml b= /Documentation/devicetree/bindings/arm/firmware/qcom,tee.yaml new file mode 100644 index 000000000000..43b7e8ac944e --- /dev/null +++ b/Documentation/devicetree/bindings/arm/firmware/qcom,tee.yaml @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/arm/firmware/qcom,tee.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm TEE + +maintainers: + - Amirreza Zarrabi + +description: | + QTEE is a piece of software provide a Trusted Execution Environment usin= g ARM + TrustZone for Qualcomm SoC. + +properties: + $nodename: + const: qcom_tee + + compatible: + const: qcom,tee + +required: + - compatible + +additionalProperties: false + +examples: + - | + firmware { + qcom_tee { + compatible =3D "qcom,tee"; + }; + }; --=20 2.34.1 From nobody Wed Dec 4 19:03:26 2024 Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1427217C21B; Tue, 3 Dec 2024 04:20:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.180.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199620; cv=none; b=mipeOvFmvON8GlSnJYNH7Ync2DJLWEa1msp/Z+iclZerDTwYYix1QpmImPXoZqRgX36u0RXLshbUn2BNLHu5Fghj5dQaH0+cTcQTkQ6En/Nh11eDN4HUymbHHXbZ6tiHi+VbOTLrKrQ3DxCg4etBPN5FMsgTokvr5h3TZAvBW5s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199620; c=relaxed/simple; bh=4czpIWIbbW+HF40KlLhyA7y8AN6snPxTO9DRnj4n2aY=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=HYYail6QiA3bBhvS42REZSIURU04L3ipj8t0u3vYJ2ZqXC461y416z9kf/prwz4YrsrqVvsj2YbnWL10zMdDBj29bbu4eYtn/s5UWgT3Rhjn2sdI1KKuebw0veXUOx3aZSdlnpbsugcfS62y6xZpgiZRPx3tHF1GvRqlfk2dRec= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=Q04QIT5U; arc=none smtp.client-ip=205.220.180.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="Q04QIT5U" Received: from pps.filterd (m0279869.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4B2I7OrZ025063; Tue, 3 Dec 2024 04:20:09 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= pqXDsRvA2nwfupXbMIvTWXc6tkFglTK6wh3qKIXtKWk=; b=Q04QIT5UJgPgXJd/ HopuvJXj2xw/1giAeq/XpVMptTrCX6O7rSZgVd9oYN4Di20NCmuxlJuZZmjAMBLb NHTVG4PeDPQ3O+IZUTwIDy26Vk8jVI7Pcjfgqg1Z4JBi7OayZesYovCQX7+ufohq kTSRRrtaxJI7ZkviapjCdjrgU5ucGw2X3tug02yJB4nqvg5ESwcjvKghCVMKEA0F td42zRl0l/7uuQfn7LI6TD9lLLr52bGhgpt7YwXwczuPMbCQD8yxbumvkP8zVFGF kWRsE9NhJlYqBX+yXoafx2K4JJpD2may3fDrEbDhS/C8XOSjY7VtBMJeJCYlyVCn 3RxAKw== Received: from nasanppmta05.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 437sq66suc-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 03 Dec 2024 04:20:08 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA05.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 4B34K7CR024265 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 3 Dec 2024 04:20:07 GMT Received: from hu-azarrabi-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 2 Dec 2024 20:20:07 -0800 From: Amirreza Zarrabi Date: Mon, 2 Dec 2024 20:19:24 -0800 Subject: [PATCH 08/10] tee: add Qualcomm TEE driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-8-f502ef01e016@quicinc.com> References: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> In-Reply-To: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> To: Jens Wiklander , Sumit Garg , Bjorn Andersson , "Konrad Dybcio" , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Bartosz Golaszewski" , Srinivas Kandagatla CC: , , , , , Amirreza Zarrabi X-Mailer: b4 0.13.0 X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-GUID: XMqJi43RdkOybvAWw-pvpwyB17D833e9 X-Proofpoint-ORIG-GUID: XMqJi43RdkOybvAWw-pvpwyB17D833e9 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.60.29 definitions=2024-09-06_09,2024-09-06_01,2024-09-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 malwarescore=0 bulkscore=0 priorityscore=1501 mlxscore=0 clxscore=1015 lowpriorityscore=0 suspectscore=0 adultscore=0 spamscore=0 impostorscore=0 mlxlogscore=999 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2411120000 definitions=main-2412030034 Introduce basic support for invoking objects hosted in QTEE and userspace through the TEE subsystem. Signed-off-by: Amirreza Zarrabi --- drivers/tee/qcomtee/Makefile | 2 + drivers/tee/qcomtee/call.c | 707 ++++++++++++++++++++++++++++++= ++++ drivers/tee/qcomtee/core.c | 8 + drivers/tee/qcomtee/qcomtee_private.h | 166 ++++++++ drivers/tee/qcomtee/user_obj.c | 625 ++++++++++++++++++++++++++++++ include/uapi/linux/tee.h | 1 + 6 files changed, 1509 insertions(+) diff --git a/drivers/tee/qcomtee/Makefile b/drivers/tee/qcomtee/Makefile index 108bc7fdabcb..6bf91481fde3 100644 --- a/drivers/tee/qcomtee/Makefile +++ b/drivers/tee/qcomtee/Makefile @@ -1,7 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_QCOMTEE) +=3D qcomtee.o qcomtee-objs +=3D async.o +qcomtee-objs +=3D call.o qcomtee-objs +=3D core.o qcomtee-objs +=3D primordial_obj.o qcomtee-objs +=3D qcom_scm.o qcomtee-objs +=3D release.o +qcomtee-objs +=3D user_obj.o diff --git a/drivers/tee/qcomtee/call.c b/drivers/tee/qcomtee/call.c new file mode 100644 index 000000000000..11bb31836808 --- /dev/null +++ b/drivers/tee/qcomtee/call.c @@ -0,0 +1,707 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#include "qcomtee_private.h" + +/** + * enum qcom_tee_object_host - Object host where it is managed. + * @QCOM_TEE_OBJECT_HOST_USER: objects in userspace. + * @QCOM_TEE_OBJECT_HOST_TEE: objects in QTEE. + * + * We refer to object hosted in userspace as 'Local Object' and objects ho= sted + * in QTEE as 'Remote Object'. + */ +enum qcom_tee_object_host { + QCOM_TEE_OBJECT_HOST_USER, /* Object that is managed in userspace. */ + QCOM_TEE_OBJECT_HOST_TEE, /* Object that is managed in QTEE. */ +}; + +/* Read object ID host information. */ +static enum qcom_tee_object_host qcom_tee_object_host(struct tee_param *pa= ram) +{ + if (param->u.objref.flags & QCOM_TEE_OBJREF_FLAG_USER) + return QCOM_TEE_OBJECT_HOST_USER; + return QCOM_TEE_OBJECT_HOST_TEE; +} + +/* qcom_tee_context_add_qtee_object() - Add a QTEE object to the context. + * @param: TEE parameter represents @object. + * @object: QTEE object. + * @ctx: context to add the object. + * + * It assumes @object is %QCOM_TEE_OBJECT_TYPE_TEE and caller has already = issued + * qcom_tee_object_get() for @object. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_context_add_qtee_object(struct tee_param *param, struct qcom_= tee_object *object, + struct qcom_tee_context *ctx) +{ + int ret; + + guard(mutex)(&ctx->lock); + ret =3D idr_alloc(&ctx->qtee_objects_idr, object, 0, 0, GFP_KERNEL); + if (ret < 0) + return ret; + + param->u.objref.id =3D ret; + /* QTEE Object: !QCOM_TEE_OBJREF_FLAG_USER. */ + param->u.objref.flags =3D 0; + + return 0; +} + +static int find_qtee_object(struct qcom_tee_object **object, unsigned long= id, + struct qcom_tee_context *ctx) +{ + int err =3D 0; + + guard(rcu)(); + /* Object release is RCU protected. */ + *object =3D idr_find(&ctx->qtee_objects_idr, id); + if (!qcom_tee_object_get(*object)) + err =3D -EINVAL; + + return err; +} + +static void del_qtee_object(unsigned long id, struct qcom_tee_context *ctx) +{ + struct qcom_tee_object *object; + + scoped_guard(mutex, &ctx->lock) + object =3D idr_remove(&ctx->qtee_objects_idr, id); + qcom_tee_object_put(object); +} + +/* Get the QTEE object added with qcom_tee_context_add_qtee_object(). */ +int qcom_tee_context_find_qtee_object(struct qcom_tee_object **object, str= uct tee_param *param, + struct qcom_tee_context *ctx) +{ + /* 'qtee_objects_idr' stores QTEE objects only. */ + if (qcom_tee_object_host(param) !=3D QCOM_TEE_OBJECT_HOST_TEE) + return -EINVAL; + return find_qtee_object(object, param->u.objref.id, ctx); +} + +/** + * qcom_tee_context_del_qtee_object() - Delete a QTEE object from the cont= ext. + * @param: TEE parameter represents @object. + * @ctx: context to delete the object. + * + * @param returned by qcom_tee_context_add_qtee_object(). + */ +void qcom_tee_context_del_qtee_object(struct tee_param *param, struct qcom= _tee_context *ctx) +{ + /* 'qtee_objects_idr' stores QTEE objects only. */ + if (qcom_tee_object_host(param) =3D=3D QCOM_TEE_OBJECT_HOST_TEE) + del_qtee_object(param->u.objref.id, ctx); +} + +/** + * qcom_tee_objref_to_arg() - Convert OBJREF parameter to QTEE argument in= a context. + * @arg: QTEE argument. + * @param: TEE parameter. + * @ctx: context in which the conversion should happen. + * + * It assumes @param is OBJREF. + * It does not set @arg.type; caller should initialize it to a correct + * &enum qcom_tee_arg_type value. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_objref_to_arg(struct qcom_tee_arg *arg, struct tee_param *par= am, + struct qcom_tee_context *ctx) +{ + struct qcom_tee_object *object; + int err; + + if (arg->type !=3D QCOM_TEE_ARG_TYPE_IO && + arg->type !=3D QCOM_TEE_ARG_TYPE_OO) + return -EINVAL; + + /* It is a NULL object?! */ + if (param->u.objref.id =3D=3D TEE_OBJREF_NULL) { + arg->o =3D NULL_QCOM_TEE_OBJECT; + + return 0; + } + + switch (qcom_tee_object_host(param)) { + case QCOM_TEE_OBJECT_HOST_USER: + err =3D qcom_tee_user_param_to_object(&object, param, ctx); + if (err) + break; + + /* Keep a copy for driver as QTEE may release it (e.g. using async msg).= */ + qcom_tee_object_get(object); + + break; + case QCOM_TEE_OBJECT_HOST_TEE: + err =3D qcom_tee_context_find_qtee_object(&object, param, ctx); + + break; + } + + arg->o =3D err ? NULL_QCOM_TEE_OBJECT : object; + + return err; +} + +/** + * qcom_tee_objref_from_arg() - Convert QTEE argument to OBJREF param in a= context. + * @param: TEE parameter. + * @arg: QTEE argument. + * @ctx: context in which the conversion should happen. + * + * It assumes @arg is of %QCOM_TEE_ARG_TYPE_IO or %QCOM_TEE_ARG_TYPE_OO. + * It does not set @param.attr; caller should initialize it to a correct O= BJREF type. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_objref_from_arg(struct tee_param *param, struct qcom_tee_arg = *arg, + struct qcom_tee_context *ctx) +{ + struct qcom_tee_object *object; + int err; + + /* param should be of OBJREF. */ + if (param->attr !=3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT && + param->attr !=3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT && + param->attr !=3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INOUT) + return -EINVAL; + + object =3D arg->o; + + switch (typeof_qcom_tee_object(object)) { + case QCOM_TEE_OBJECT_TYPE_NULL: + param->u.objref.id =3D TEE_OBJREF_NULL; + err =3D 0; + + break; + case QCOM_TEE_OBJECT_TYPE_CB_OBJECT: + err =3D qcom_tee_user_param_from_object(param, object, ctx); + + break; + case QCOM_TEE_OBJECT_TYPE_TEE: + err =3D qcom_tee_context_add_qtee_object(param, object, ctx); + + break; + case QCOM_TEE_OBJECT_TYPE_ROOT: + default: + return -EINVAL; + } + + return err; +} + +/** + * qcom_tee_params_to_args() - Convert TEE parameters to QTEE arguments in= a context. + * @u: QTEE arguments. + * @params: TEE parameters. + * @num_params: number of elements in the parameter array. + * @ctx: context in which the conversion should happen. + * + * It assumes @u has at least @num_param + 1 entries and has been initiali= zed + * with %QCOM_TEE_ARG_TYPE_INV as &struct qcom_tee_arg.type. + * + * Return: On success return 0 or <0 on failure. + */ +static int qcom_tee_params_to_args(struct qcom_tee_arg *u, + struct tee_param *params, int num_params, + struct qcom_tee_context *ctx) +{ + struct qcom_tee_object *object; + int i; + + for (i =3D 0; i < num_params; i++) { + switch (params[i].attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT: + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT: + u[i].flags =3D QCOM_TEE_ARG_FLAGS_UADDR; + u[i].b.uaddr =3D params[i].u.membuf.uaddr; + u[i].b.size =3D params[i].u.membuf.size; + + if (params[i].attr =3D=3D TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT) + u[i].type =3D QCOM_TEE_ARG_TYPE_IB; + else /* TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT */ + u[i].type =3D QCOM_TEE_ARG_TYPE_OB; + + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: + u[i].type =3D QCOM_TEE_ARG_TYPE_IO; + if (qcom_tee_objref_to_arg(&u[i], ¶ms[i], ctx)) + goto out_failed; + + break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: + u[i].type =3D QCOM_TEE_ARG_TYPE_OO; + u[i].o =3D NULL_QCOM_TEE_OBJECT; + break; + default: /* Unsupported TEE parameters. */ + goto out_failed; + } + } + + return 0; + +out_failed: + + /* On ERROR: */ + /* Put IO objects processed so far. */ + for (--i; i >=3D 0; i--) { + if (u[i].type !=3D QCOM_TEE_ARG_TYPE_IO) + continue; + + object =3D u[i].o; + qcom_tee_user_object_set_notify(object, false); + /* For callback object, we hold a reference for the driver, put it. */ + if (typeof_qcom_tee_object(object) =3D=3D QCOM_TEE_OBJECT_TYPE_CB_OBJECT) + qcom_tee_object_put(object); + /* Put QTEE copy of object. */ + qcom_tee_object_put(object); + } + + return -EINVAL; +} + +/** + * qcom_tee_params_from_args() - Convert QTEE arguments to TEE parameters = in a context. + * @params: TEE parameters. + * @u: QTEE arguments. + * @num_params: number of elements in the parameter array. + * @ctx: context in which the conversion should happen. + * + * @u should have been already initialized by qcom_tee_params_to_args(). + * This also represents end of a QTEE invocation that started with qcom_te= e_params_to_args() + * by releasing %QCOM_TEE_ARG_TYPE_IO objects. + * + * Return: On success return 0 or <0 on failure. + */ +static int qcom_tee_params_from_args(struct tee_param *params, + struct qcom_tee_arg *u, int num_params, + struct qcom_tee_context *ctx) +{ + int i, np; + + for (np =3D 0; u[np].type; np++) { + enum qcom_tee_arg_type at =3D u[np].type; + + if (at =3D=3D QCOM_TEE_ARG_TYPE_OB) { + /* TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT */ + params[np].u.value.b =3D u[np].b.size; + + } else if (at =3D=3D QCOM_TEE_ARG_TYPE_IO) { + /* IEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT */ + qcom_tee_object_put(u[np].o); + + } else if (at =3D=3D QCOM_TEE_ARG_TYPE_OO) { + /* TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT */ + if (qcom_tee_objref_from_arg(¶ms[np], &u[np], ctx)) + goto out_failed; + } + } + + return 0; + +out_failed: + + /* On ERROR: */ + /* - Release params associated to QTEE objects in this context so far. */ + for (i =3D 0; i < np; i++) { + if (params[i].attr =3D=3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) + qcom_tee_context_del_qtee_object(¶ms[i], ctx); + } + /* - Release any IO and OO objects not processed so far. */ + while (u[i].type) { + if (u[i].type =3D=3D QCOM_TEE_ARG_TYPE_OO || + u[i].type =3D=3D QCOM_TEE_ARG_TYPE_IO) + qcom_tee_object_put(u[i++].o); + } + + return -EINVAL; +} + +/* TEE Device Ops. */ + +static int qcom_tee_params_check(struct tee_param *params, int num_params) +{ + int io =3D 0, oo =3D 0, ib =3D 0, ob =3D 0; + int i; + + /* QTEE accepts 64 arguments. */ + if (num_params > QCOM_TEE_ARGS_MAX) + return -EINVAL; + + /* Supported parameter types. */ + for (i =3D 0; i < num_params; i++) { + switch (params[i].attr) { + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT: + ib++; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT: + ob++; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT: + io++; break; + case TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT: + oo++; break; + default: + return -EINVAL; + } + } + + /* QTEE accepts 16 arguments of each supported types. */ + if (io > QCOM_TEE_ARGS_PER_TYPE || oo > QCOM_TEE_ARGS_PER_TYPE || + ib > QCOM_TEE_ARGS_PER_TYPE || ob > QCOM_TEE_ARGS_PER_TYPE) + return -EINVAL; + + return 0; +} + +/* Check if user issued a permitted operation on ROOT_QCOM_TEE_OBJECT from= userspace. */ +static int qcom_tee_root_object_check(u32 op, struct tee_param *params, in= t num_params) +{ + /* Some privileged operations recognized by QTEE. */ + if (op =3D=3D 4 || op =3D=3D 8 || op =3D=3D 9) + return -EINVAL; + + /* OP 5 is to register with QTEE by passing credential object as input OB= JREF. */ + /* TEE_OBJREF_NULL as credential object represents a privileged client fo= r QTEE, + * only kernel can pass TEE_OBJREF_NULL. + */ + if (op =3D=3D 5) { + if (num_params !=3D 2) + return -EINVAL; + + if (params[0].attr =3D=3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT && + params[1].attr =3D=3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) { + if (params[0].u.objref.id =3D=3D TEE_OBJREF_NULL) + return -EINVAL; + + } else if (params[0].attr =3D=3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT= && + params[1].attr =3D=3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT) { + if (params[1].u.objref.id =3D=3D TEE_OBJREF_NULL) + return -EINVAL; + } + } + + return 0; +} + +/** + * qcom_tee_object_invoke() - Invoke a QTEE object. + * @tee_ctx: TEE context. + * @arg: ioctl arguments. + * @params: parameters for the object. + * + * Return: On success return 0 or <0 on failure. + */ +static int qcom_tee_object_invoke(struct tee_context *tee_ctx, + struct tee_ioctl_object_invoke_arg *arg, + struct tee_param *params) +{ + struct qcom_tee_object_invoke_ctx *oic __free(kfree) =3D NULL; + struct qcom_tee_context *ctx =3D tee_ctx->data; + struct qcom_tee_arg *u __free(kfree) =3D NULL; + struct qcom_tee_object *object; + int i, ret, result; + + if (qcom_tee_params_check(params, arg->num_params)) + return -EINVAL; + + /* Handle OBJREF reserved operations. */ + if (arg->op =3D=3D QCOM_TEE_OBJREF_OP_RELEASE) { + del_qtee_object(arg->object, ctx); + + return 0; + } + + /* Unsupported reserved operation. */ + if (arg->op > QCOM_TEE_OBJREF_OP_MIN) + return -EINVAL; + + oic =3D kzalloc(sizeof(*oic), GFP_KERNEL); + if (!oic) + return -ENOMEM; + + /* +1 for ending QCOM_TEE_ARG_TYPE_INV. */ + u =3D kzalloc(sizeof(*u) * (arg->num_params + 1), GFP_KERNEL); + if (!u) + return -ENOMEM; + + if (arg->object =3D=3D TEE_OBJREF_NULL && + !qcom_tee_root_object_check(arg->op, params, arg->num_params)) { + /* Use ROOT if NULL is invoked. */ + object =3D ROOT_QCOM_TEE_OBJECT; + } else { + /* Get object being invoked. */ + ret =3D find_qtee_object(&object, arg->object, ctx); + if (ret) + return ret; + } + + ret =3D qcom_tee_params_to_args(u, params, arg->num_params, ctx); + if (ret) + goto out; + + ret =3D qcom_tee_object_do_invoke(oic, object, arg->op, u, &result); + if (!ret) { + if (!result) { + /* Only parse QTEE response on SUCCESS. */ + ret =3D qcom_tee_params_from_args(params, u, arg->num_params, ctx); + } else { + /* Put driver's IO objects copy; get in qcom_tee_params_to_args(). */ + qcom_tee_arg_for_each_input_object(i, u) + qcom_tee_object_put(u[i++].o); + } + } else if (ret !=3D -EAGAIN && ret !=3D -ENODEV) { + /* Unable to initiate a QTEE invocation; cleanup qcom_tee_params_to_args= (). */ + qcom_tee_arg_for_each_input_object(i, u) { + qcom_tee_user_object_set_notify(u[i].o, false); + if (typeof_qcom_tee_object(u[i].o) =3D=3D QCOM_TEE_OBJECT_TYPE_CB_OBJEC= T) + qcom_tee_object_put(u[i].o); + qcom_tee_object_put(u[i].o); + } + } + + arg->ret =3D result; +out: + qcom_tee_object_put(object); + + return ret; +} + +/** + * qcom_tee_supp_recv() - Pick a request for the supplicant. + * @tee_ctx: TEE context. + * @op: requested operation on object. + * @num_params: number of elements in the parameter array, updated with nu= mber used. + * @params: parameters for @op. + * + * The first parameter is a %TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT meta pa= rameter. + * On input, it provides a user buffer. This buffer is used for parameters= of type + * %TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT in qcom_tee_cb_params_from_args= (). + * On output, object id and request id are stored in the meta parameter. + * + * Return: On success return 0 or <0 on failure. + */ +static int qcom_tee_supp_recv(struct tee_context *tee_ctx, u32 *op, u32 *n= um_params, + struct tee_param *params) +{ + struct qcom_tee_user_object_request_data data; + struct qcom_tee_context *ctx =3D tee_ctx->data; + void __user *uaddr; + size_t ubuf_size; + int i, ret; + + if (!*num_params) + return -EINVAL; + + /* We expect the first parameter to be an INOUT + meta parameter. */ + if (params->attr !=3D (TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INOUT | TEE_IOCTL_= PARAM_ATTR_META)) + return -EINVAL; + + /* Others parameters are none. */ + for (i =3D 1; i < *num_params; i++) + if (params[i].attr) + return -EINVAL; + + if (IS_ALIGNED(params->u.value.a, 8)) + return -EINVAL; + + /* User buffer and size from meta parameter. */ + uaddr =3D u64_to_user_ptr(params->u.value.a); + ubuf_size =3D params->u.value.b; + /* Process TEE parameters. +/-1 to ignore meta parameter. */ + ret =3D qcom_tee_user_object_pop(ctx, params + 1, *num_params - 1, uaddr,= ubuf_size, &data); + if (ret) + return ret; + + params->u.value.a =3D data.object_id; + params->u.value.b =3D data.id; + params->u.value.c =3D 0; + *op =3D data.op; + *num_params =3D data.np + 1; + + return 0; +} + +/** + * qcom_tee_supp_send() - Pick a request for the supplicant. + * @tee_ctx: TEE context. + * @ret: return value of the request. + * @num_params: number of elements in the parameter array. + * @params: returned parameters. + * + * The first parameter is a %TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT meta pa= rameter. + * It specifies the request id this response is belong to. + * + * Return: On success return 0 or <0 on failure. + */ +static int qcom_tee_supp_send(struct tee_context *tee_ctx, u32 errno, u32 = num_params, + struct tee_param *params) +{ + struct qcom_tee_context *ctx =3D tee_ctx->data; + int id; + + if (!num_params) + return -EINVAL; + + /* We expect the first parameter to be an INPUT + meta parameter. */ + if (params->attr !=3D (TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT | TEE_IOCTL_= PARAM_ATTR_META)) + return -EINVAL; + + /* Get the req_id of response. */ + id =3D params->u.value.a; + + /* Process TEE parameters. +/-1 to ignore meta parameter. */ + return qcom_tee_user_object_submit(ctx, params + 1, num_params - 1, id, e= rrno); +} + +static int qcom_tee_open(struct tee_context *tee_context) +{ + struct qcom_tee_context *ctx __free(kfree) =3D NULL; + int err; + + ctx =3D kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + err =3D init_srcu_struct(&ctx->req_srcu); + if (err) + return err; + + ctx->tee_context =3D tee_context; + idr_init(&ctx->qtee_objects_idr); + idr_init(&ctx->reqs_idr); + mutex_init(&ctx->lock); + init_completion(&ctx->req_c); + kref_init(&ctx->ref_cnt); + + tee_context->data =3D no_free_ptr(ctx); + + return 0; +} + +static void qcom_tee_release(struct tee_context *tee_context) +{ + struct qcom_tee_context *ctx =3D tee_context->data; + struct qcom_tee_object *object; + int id; + + /* Process QUEUED or PROCESSING requests. */ + qcom_tee_requests_destroy(ctx); + + /* Release QTEE objects. */ + idr_for_each_entry(&ctx->qtee_objects_idr, object, id) + qcom_tee_object_put(object); + + /* Put the context; wait for all user objects to go away. */ + kref_put(&ctx->ref_cnt, __qcom_tee_context_destroy); +} + +void __qcom_tee_context_destroy(struct kref *ref_cnt) +{ + struct qcom_tee_context *ctx =3D container_of(ref_cnt, struct qcom_tee_co= ntext, ref_cnt); + + idr_destroy(&ctx->qtee_objects_idr); + idr_destroy(&ctx->reqs_idr); + cleanup_srcu_struct(&ctx->req_srcu); + kfree(ctx); +} + +static void qcom_tee_get_version(struct tee_device *teedev, struct tee_ioc= tl_version_data *vers) +{ + struct tee_ioctl_version_data v =3D { + .impl_id =3D TEE_IMPL_ID_QTEE, + .gen_caps =3D TEE_GEN_CAP_OBJREF, + }; + + *vers =3D v; +} + +static const struct tee_driver_ops qcom_tee_ops =3D { + .get_version =3D qcom_tee_get_version, + .open =3D qcom_tee_open, + .release =3D qcom_tee_release, + .object_invoke_func =3D qcom_tee_object_invoke, + .supp_recv =3D qcom_tee_supp_recv, + .supp_send =3D qcom_tee_supp_send, +}; + +static const struct tee_desc qcom_tee_desc =3D { + .name =3D "qcom_tee", + .ops =3D &qcom_tee_ops, + .owner =3D THIS_MODULE, +}; + +static int qcom_tee_probe(struct platform_device *pdev) +{ + struct tee_device *teedev; + int err; + + if (!qcom_scm_is_available()) + return -EPROBE_DEFER; + + teedev =3D tee_device_alloc(&qcom_tee_desc, NULL, NULL, NULL); + if (IS_ERR(teedev)) + return PTR_ERR(teedev); + + err =3D tee_device_register(teedev); + if (err) + goto err_unreg_teedev; + + platform_set_drvdata(pdev, teedev); + return 0; + +err_unreg_teedev: + tee_device_unregister(teedev); + + return err; +} + +static void qcom_tee_remove(struct platform_device *pdev) +{ + struct tee_device *teedev =3D platform_get_drvdata(pdev); + + /* Keep a copy, tee_device_unregister() sets it to NULL. */ + struct tee_shm_pool *pool =3D teedev->pool; + + /* Wait for users to go away. */ + tee_device_unregister(teedev); + tee_shm_pool_free(pool); +} + +static const struct of_device_id qcom_tee_dt_match[] =3D { + { .compatible =3D "qcom,tee" }, + {}, +}; +MODULE_DEVICE_TABLE(of, qcom_tee_dt_match); + +static struct platform_driver qcom_tee_platform_driver =3D { + .probe =3D qcom_tee_probe, + .remove =3D qcom_tee_remove, + .driver =3D { + .name =3D "qcom_tee", + .of_match_table =3D qcom_tee_dt_match, + }, +}; + +int qcom_tee_driver_register(void) +{ + return platform_driver_register(&qcom_tee_platform_driver); +} + +void qcom_tee_driver_unregister(void) +{ + platform_driver_unregister(&qcom_tee_platform_driver); +} diff --git a/drivers/tee/qcomtee/core.c b/drivers/tee/qcomtee/core.c index 79f1181cf676..545857e117db 100644 --- a/drivers/tee/qcomtee/core.c +++ b/drivers/tee/qcomtee/core.c @@ -904,8 +904,14 @@ static int __init qcom_tee_object_invoke_init(void) if (ret) goto err_kobject_put; =20 + ret =3D qcom_tee_driver_register(); + if (ret) + goto err_remove_group; + return 0; =20 +err_remove_group: + sysfs_remove_group(qcom_tee_object_invoke_kobj, &attr_group); err_kobject_put: /* Remove '/sys/firmware/qcom_tee'. */ kobject_put(qcom_tee_object_invoke_kobj); @@ -920,6 +926,8 @@ module_init(qcom_tee_object_invoke_init); =20 static void __exit qcom_tee_object_invoke_deinit(void) { + qcom_tee_driver_unregister(); + /* Wait for RELEASE operations for QTEE objects. */ qcom_tee_release_destroy(); qcom_tee_msg_buffers_destroy(); diff --git a/drivers/tee/qcomtee/qcomtee_private.h b/drivers/tee/qcomtee/qc= omtee_private.h index c718cd2d8463..15f358260ed7 100644 --- a/drivers/tee/qcomtee/qcomtee_private.h +++ b/drivers/tee/qcomtee/qcomtee_private.h @@ -10,6 +10,14 @@ #include #include =20 +/* Flags relating to object reference. */ +#define QCOM_TEE_OBJREF_FLAG_USER 1 + +/* Reserved OBJREF operations. */ +/* These operations are not sent to QTEE and handled in driver. */ +#define QCOM_TEE_OBJREF_OP_MIN USHRT_MAX +#define QCOM_TEE_OBJREF_OP_RELEASE (QCOM_TEE_OBJREF_OP_MIN + 1) + struct qcom_tee_object *qcom_tee_idx_erase(u32 idx); void qcom_tee_object_free(struct qcom_tee_object *object); =20 @@ -44,9 +52,167 @@ int __qcom_tee_object_do_invoke(struct qcom_tee_object_= invoke_ctx *oic, struct qcom_tee_object *object, u32 op, struct qcom_tee_arg *u, int *result); =20 +/** + * struct qcom_tee_context - Clients or supplicants context. + * @tee_context: TEE context. + * @qtee_objects_idr: QTEE objects in this context. + * @reqs_idr: Requests currently being processed. + * @lock: mutex for @reqs_idr and @qtee_objects_idr. + * @req_srcu: srcu for exclusive access to requests. + * @req_c: completion used when supplicant is waiting for requests. + * @released: state of this context. + * @ref_cnt: ref count. + */ +struct qcom_tee_context { + struct tee_context *tee_context; + + struct idr qtee_objects_idr; + struct idr reqs_idr; + /* Synchronize access to @reqs_idr, @qtee_objects_idr and updating reques= ts state. */ + struct mutex lock; + struct srcu_struct req_srcu; + struct completion req_c; + + int released; + + struct kref ref_cnt; +}; + +void __qcom_tee_context_destroy(struct kref *ref_cnt); + +/* qcom_tee_context_add_qtee_object() - Add a QTEE object to the context. + * @param: TEE parameter represents @object. + * @object: QTEE object. + * @ctx: context to add the object. + * + * It assumes @object is %QCOM_TEE_OBJECT_TYPE_TEE and caller has already = issued + * qcom_tee_object_get() for @object. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_context_add_qtee_object(struct tee_param *param, struct qcom_= tee_object *object, + struct qcom_tee_context *ctx); + +/* Get the QTEE object added with qcom_tee_context_add_qtee_object(). */ +int qcom_tee_context_find_qtee_object(struct qcom_tee_object **object, str= uct tee_param *param, + struct qcom_tee_context *ctx); + +/** + * qcom_tee_context_del_qtee_object() - Delete a QTEE object from the cont= ext. + * @param: TEE parameter represents @object. + * @ctx: context to delete the object. + * + * @param returned by qcom_tee_context_add_qtee_object(). + */ +void qcom_tee_context_del_qtee_object(struct tee_param *param, struct qcom= _tee_context *ctx); + +/** + * qcom_tee_objref_to_arg() - Convert OBJREF parameter to QTEE argument in= a context. + * @arg: QTEE argument. + * @param: TEE parameter. + * @ctx: context in which the conversion should happen. + * + * It assumes @param is OBJREF. + * It does not set @arg.type; caller should initialize it to a correct + * &enum qcom_tee_arg_type value. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_objref_to_arg(struct qcom_tee_arg *arg, struct tee_param *par= am, + struct qcom_tee_context *ctx); + +/** + * qcom_tee_objref_from_arg() - Convert QTEE argument to OBJREF param in a= context. + * @param: TEE parameter. + * @arg: QTEE argument. + * @ctx: context in which the conversion should happen. + * + * It assumes @arg is of %QCOM_TEE_ARG_TYPE_IO or %QCOM_TEE_ARG_TYPE_OO. + * It does not set @param.attr; caller should initialize it to a correct O= BJREF type. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_objref_from_arg(struct tee_param *param, struct qcom_tee_arg = *arg, + struct qcom_tee_context *ctx); + +int qcom_tee_driver_register(void); +void qcom_tee_driver_unregister(void); + /* OBJECTS: */ =20 /* (1) Primordial Object. */ extern struct qcom_tee_object qcom_tee_primordial_object; =20 +/* (2) User Object API. */ + +/* Is it a user object? */ +int is_qcom_tee_user_object(struct qcom_tee_object *object); + +/* Set user object's 'notify on release' flag. */ +void qcom_tee_user_object_set_notify(struct qcom_tee_object *object, bool = notify); + +/** + * qcom_tee_user_param_to_object() - Convert OBJREF parameter to &struct q= com_tee_object. + * @object: object returned. + * @param: TEE parameter. + * @ctx: context in which the conversion should happen. + * + * @param is OBJREF with %TEE_IOCTL_OBJREF_USER flags. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_user_param_to_object(struct qcom_tee_object **object, struct = tee_param *param, + struct qcom_tee_context *ctx); + +/* Reverse what qcom_tee_user_param_to_object() does. */ +int qcom_tee_user_param_from_object(struct tee_param *param, struct qcom_t= ee_object *object, + struct qcom_tee_context *ctx); + +struct qcom_tee_user_object_request_data { + int id; /* Id assigned to the request. */ + u64 object_id; /* Object id being invoked by QTEE. */ + u32 op; /* Requested operation on object. */ + int np; /* Number of parameters in the request.*/ +}; + +/** + * qcom_tee_user_object_pop() - Pop a request for a user object. + * @ctx: context to look for user object. + * @params: parameters for @op. + * @num_params: number of elements in the parameter array. + * @uaddr: user buffer for output MEMBUF parameters. + * @size: size of user buffer @uaddr. + * @data: information for the pop request. + * + * @params is filled along with @data for the picked request. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_user_object_pop(struct qcom_tee_context *ctx, + struct tee_param *params, int num_params, + void __user *uaddr, size_t size, + struct qcom_tee_user_object_request_data *data); + +/** + * qcom_tee_user_object_submit() - Submit a response for a user object. + * @ctx: context to look for user object. + * @params: returned parameters. + * @num_params: number of elements in the parameter array. + * @id: request id for the response. + * @errno: result of user object invocation. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_user_object_submit(struct qcom_tee_context *ctx, + struct tee_param *params, int num_params, int id, int errno); + +/** + * qcom_tee_requests_destroy() - Destroy requests in a context. + * @ctx: context for which to destroy requests. + * + * After calling qcom_tee_requests_destroy(), @ctx can not be reused. + * It should be called on @ctx cleanup path. + */ +void qcom_tee_requests_destroy(struct qcom_tee_context *ctx); + #endif /* QCOM_TEE_PRIVATE_H */ diff --git a/drivers/tee/qcomtee/user_obj.c b/drivers/tee/qcomtee/user_obj.c new file mode 100644 index 000000000000..4c671a3ae0de --- /dev/null +++ b/drivers/tee/qcomtee/user_obj.c @@ -0,0 +1,625 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "qcomtee_private.h" + +/** + * DOC: User Objects aka Supplicants + * + * Any userspace process with access to the TEE device file can behave as = a supplicant + * by creating a user object. Any TEE parameter of type OBJREF with %QCOM_= TEE_OBJREF_FLAG_USER + * flag set is considered as user object. + * + * A supplicant uses qcom_tee_user_object_pick() (i.e. TEE_IOC_SUPPL_RECV)= to receive a + * QTEE user object request and qcom_tee_user_object_submit() (i.e. TEE_IO= C_SUPPL_SEND) + * to submit a response. QTEE expects to receive the response, including O= B and OO in + * specific order in the message; parameters submitted with qcom_tee_user_= object_submit() + * should maintain this order. + */ + +/** + * struct qcom_tee_user_object - User object. + * @object: &struct qcom_tee_object representing this user object. + * @ctx: context for which user object is defined. + * @object_id: object ID in @ctx. + * @nor: notify userspace if object is released. + * + * Any object managed in userspace is represented with this struct. + * If @nor is set, on release a notification message is send back to the u= serspace. + */ +struct qcom_tee_user_object { + struct qcom_tee_object object; + struct qcom_tee_context *ctx; + u64 object_id; + + bool nor; +}; + +#define to_qcom_tee_user_object(o) container_of((o), struct qcom_tee_user_= object, object) + +static struct qcom_tee_object_operations qcom_tee_user_object_ops; + +/* Is it a user object? */ +int is_qcom_tee_user_object(struct qcom_tee_object *object) +{ + return object !=3D NULL_QCOM_TEE_OBJECT && + typeof_qcom_tee_object(object) =3D=3D QCOM_TEE_OBJECT_TYPE_CB_OBJECT && + object->ops =3D=3D &qcom_tee_user_object_ops; +} + +/* Set user object's 'notify on release' flag. */ +void qcom_tee_user_object_set_notify(struct qcom_tee_object *object, bool = notify) +{ + if (is_qcom_tee_user_object(object)) + WRITE_ONCE(to_qcom_tee_user_object(object)->nor, notify); +} + +/** + * enum qcom_tee_req_state - Current state of request. + * @QCOM_TEE_REQ_QUEUED: Request is waiting for supplicant. + * @QCOM_TEE_REQ_PROCESSING: Request has been picked by the supplicant. + * @QCOM_TEE_REQ_PROCESSED: Response has been submitted for the request. + */ +enum qcom_tee_req_state { + QCOM_TEE_REQ_QUEUED =3D 1, + QCOM_TEE_REQ_PROCESSING, + QCOM_TEE_REQ_PROCESSED, +}; + +/* User requests sent to supplicants. */ +struct qcom_tee_user_req { + enum qcom_tee_req_state state; + + int req_id; /* Request ID. */ + u64 object_id; /* User object ID. */ + u32 op; /* Operation to perform on object. */ + struct qcom_tee_arg *args; /* QTEE arguments for this operation. */ + int errno; /* Result of operation. */ + + struct completion c; /* Completion for whoever wait for results. */ +}; + +/* Static placeholder for a request in PROCESSING state in qcom_tee_contex= t.reqs_idr. + * If the thread initiated the QTEE call using qcom_tee_object_invoke() di= es, and supplicant + * is processing the request, we replace the entry in qcom_tee_context.req= s_idr with + * __empty_ureq. So (1) the req_id remains busy and not reused, and (2) su= pplicant fails to + * submit response and does the necessary rollback. + */ +static struct qcom_tee_user_req __empty_ureq =3D { .state =3D QCOM_TEE_REQ= _PROCESSING }; + +/* Enqueue a user request for a context. */ +static int qcom_tee_request_enqueue(struct qcom_tee_user_req *ureq, struct= qcom_tee_context *ctx) +{ + int ret; + + guard(mutex)(&ctx->lock); + /* Supplicant is dying. */ + if (ctx->released) + return -ENODEV; + + ret =3D idr_alloc(&ctx->reqs_idr, ureq, 0, 0, GFP_KERNEL); + if (ret < 0) + return ret; + + ureq->req_id =3D ret; + ureq->state =3D QCOM_TEE_REQ_QUEUED; + + return 0; +} + +/** + * qcom_tee_requests_destroy() - Destroy requests in a context. + * @ctx: context for which to destroy requests. + */ +void qcom_tee_requests_destroy(struct qcom_tee_context *ctx) +{ + struct qcom_tee_user_req *ureq; + int id; + + guard(mutex)(&ctx->lock); + + /* So qcom_tee_request_enqueue() refuses new requests. */ + ctx->released =3D 1; + idr_for_each_entry(&ctx->reqs_idr, ureq, id) { + if (ureq =3D=3D &__empty_ureq) + continue; + /* ureq in QUEUED or PROCESSING state, terminate them. */ + if (ureq->op =3D=3D QCOM_TEE_OBJREF_OP_RELEASE) { + kfree(ureq); + } else { + ureq->state =3D QCOM_TEE_REQ_PROCESSED; + ureq->errno =3D -ENODEV; + complete(&ureq->c); + } + } +} + +/** + * qcom_tee_supp_pop_entry() - Pop the next request in a context. + * @ctx: context from which to pop a request. + * @ubuf_size: size of available buffer for MEMBUF parameters. + * @num_params: number of entries for TEE parameter array. + * + * It does not remove the request from &qcom_tee_context.reqs_idr. + * It checks if @num_params is large enough to fit the next request argume= nts. + * It checks if @ubuf_size is large enough to fit IB buffer arguments from= QTEE. + * It updates request state to %QCOM_TEE_REQ_PROCESSING state. + * + * Return: On success return a request or NULL and ERR_PTR on failure. + */ +static struct qcom_tee_user_req *qcom_tee_supp_pop_entry(struct qcom_tee_c= ontext *ctx, + size_t ubuf_size, int num_params) +{ + struct qcom_tee_user_req *ureq; + struct qcom_tee_arg *u; + int i, id; + + guard(mutex)(&ctx->lock); + + /* Find the a QUEUED request. */ + idr_for_each_entry(&ctx->reqs_idr, ureq, id) + if (ureq->state =3D=3D QCOM_TEE_REQ_QUEUED) + break; + + if (!ureq) + return NULL; + + u =3D ureq->args; + /* (1) Is there enough TEE parameters? */ + if (num_params < qcom_tee_args_len(u)) + return ERR_PTR(-EINVAL); + + /* (2) Is there enough space to pass input buffers? */ + qcom_tee_arg_for_each_input_buffer(i, u) { + ubuf_size =3D size_sub(ubuf_size, u[i].b.size); + if (ubuf_size =3D=3D SIZE_MAX) + return ERR_PTR(-EINVAL); + + ubuf_size =3D round_down(ubuf_size, 8); + } + + /* Ready to process request 'QUEUED -> PROCESSING'. */ + ureq->state =3D QCOM_TEE_REQ_PROCESSING; + + return ureq; +} + +/* User object dispatcher. */ +static int qcom_tee_user_object_dispatch(struct qcom_tee_object_invoke_ctx= *oic, + struct qcom_tee_object *object, u32 op, + struct qcom_tee_arg *args) +{ + struct qcom_tee_user_object *uo =3D to_qcom_tee_user_object(object); + struct qcom_tee_user_req *ureq __free(kfree); + struct qcom_tee_context *ctx =3D uo->ctx; + int errno; + + ureq =3D kzalloc(sizeof(*ureq), GFP_KERNEL); + if (!ureq) + return -ENOMEM; + + init_completion(&ureq->c); + ureq->object_id =3D uo->object_id; + ureq->op =3D op; + ureq->args =3D args; + + /* Queue the request. */ + if (qcom_tee_request_enqueue(ureq, ctx)) + return -ENODEV; + + /* Wakeup supplicant to process it. */ + complete(&ctx->req_c); + + /* Wait for supplicant to process the request. */ + /* Supplicant is expected to process request in a timely manner. We wait = as KILLABLE, + * in case supplicant and invoke thread both running from a same user pro= cess, otherwise + * the process stuck on fatal signal. + */ + if (!wait_for_completion_state(&ureq->c, TASK_KILLABLE | TASK_FREEZABLE))= { + errno =3D ureq->errno; + /* On SUCCESS, end_cb_notify frees the request. */ + if (!errno) + oic->data =3D no_free_ptr(ureq); + } else { + enum qcom_tee_req_state prev_state; + + errno =3D -ENODEV; + + scoped_guard(mutex, &ctx->lock) { + prev_state =3D ureq->state; + /* Replace ureq with '__empty_ureq' to keep req_id reserved. */ + if (prev_state =3D=3D QCOM_TEE_REQ_PROCESSING) + idr_replace(&ctx->reqs_idr, &__empty_ureq, ureq->req_id); + /* Remove ureq as supplicant has never seen this request. */ + else if (prev_state =3D=3D QCOM_TEE_REQ_QUEUED) + idr_remove(&ctx->reqs_idr, ureq->req_id); + } + + /* Wait for exclusive access to ureq. */ + synchronize_srcu(&ctx->req_srcu); + + /* Supplicant did some work for us, we should not discard it. */ + if (prev_state =3D=3D QCOM_TEE_REQ_PROCESSED) { + errno =3D ureq->errno; + /* On SUCCESS, end_cb_notify frees the request. */ + if (!errno) + oic->data =3D no_free_ptr(ureq); + } + } + + return errno; +} + +/* Called after submitting the callback response. */ +static void qcom_tee_user_object_end_cb_notify(struct qcom_tee_object_invo= ke_ctx *oic, + struct qcom_tee_object *unused_object, int err) +{ + struct qcom_tee_user_req *ureq =3D oic->data; + struct qcom_tee_arg *u =3D ureq->args; + struct qcom_tee_object *object; + int i; + + qcom_tee_arg_for_each_output_object(i, u) { + object =3D u[i].o; + /* If err, drop QTEE copy otherwise just drop driver's copy. */ + if (err && (typeof_qcom_tee_object(object) =3D=3D QCOM_TEE_OBJECT_TYPE_C= B_OBJECT)) + qcom_tee_object_put(object); + qcom_tee_object_put(object); + } + + kfree(ureq); +} + +static void qcom_tee_user_object_release(struct qcom_tee_object *object) +{ + struct qcom_tee_user_object *uo =3D to_qcom_tee_user_object(object); + struct qcom_tee_context *ctx =3D uo->ctx; + struct qcom_tee_user_req *ureq; + + static struct qcom_tee_arg args[] =3D { { .type =3D QCOM_TEE_ARG_TYPE_INV= } }; + + if (READ_ONCE(uo->nor)) { + ureq =3D kzalloc(sizeof(*ureq), GFP_KERNEL); + if (ureq) { + ureq->object_id =3D uo->object_id; + ureq->op =3D QCOM_TEE_OBJREF_OP_RELEASE; + ureq->args =3D args; + + /* Queue the RELEASE request and wake supplicant to process it. */ + qcom_tee_request_enqueue(ureq, ctx); + + complete(&ctx->req_c); + } else { + pr_err("failed to notify user object (%s) release.\n", + qcom_tee_object_name(object)); + } + } + + /* Matching get is in qcom_tee_user_param_to_object(). */ + kref_put(&ctx->ref_cnt, __qcom_tee_context_destroy); + kfree(uo); +} + +static struct qcom_tee_object_operations qcom_tee_user_object_ops =3D { + .release =3D qcom_tee_user_object_release, + .notify =3D qcom_tee_user_object_end_cb_notify, + .dispatch =3D qcom_tee_user_object_dispatch, +}; + +/** + * qcom_tee_user_param_to_object() - Convert OBJREF parameter to &struct q= com_tee_object. + * @object: object returned. + * @param: TEE parameter. + * @ctx: context in which the conversion should happen. + * + * @param is OBJREF with %QCOM_TEE_OBJREF_FLAG_USER flags. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_user_param_to_object(struct qcom_tee_object **object, struct = tee_param *param, + struct qcom_tee_context *ctx) +{ + struct qcom_tee_user_object *user_object __free(kfree) =3D NULL; + struct qcom_tee_object *uo; + int err; + + user_object =3D kzalloc(sizeof(*user_object), GFP_KERNEL); + if (!user_object) + return -ENOMEM; + + user_object->ctx =3D ctx; + user_object->object_id =3D param->u.objref.id; + /* By default, always notify userspace on release. */ + user_object->nor =3D true; + + err =3D qcom_tee_object_user_init(&user_object->object, QCOM_TEE_OBJECT_T= YPE_CB_OBJECT, + &qcom_tee_user_object_ops, "uo-%lu", param->u.objref.id); + if (err) + return err; + + uo =3D &no_free_ptr(user_object)->object; + /* Keep context alive as user object is alive. */ + kref_get(&ctx->ref_cnt); + + *object =3D uo; + + return 0; +} + +/* Reverse what qcom_tee_user_param_to_object() does. */ +int qcom_tee_user_param_from_object(struct tee_param *param, struct qcom_t= ee_object *object, + struct qcom_tee_context *ctx) +{ + struct qcom_tee_user_object *uo; + + if (!is_qcom_tee_user_object(object)) + return -EINVAL; + + uo =3D to_qcom_tee_user_object(object); + /* Sure if the object is in a same context as caller? */ + if (uo->ctx !=3D ctx) + return -EINVAL; + + param->u.objref.id =3D uo->object_id; + param->u.objref.flags =3D QCOM_TEE_OBJREF_FLAG_USER; + + /* User objects are valid in the context of userspace; drop the driver co= py. */ + qcom_tee_object_put(object); + + return 0; +} + +/** + * qcom_tee_cb_params_from_args() - Convert QTEE arguments to TEE paramete= rs in a context. + * @params: TEE parameters. + * @u: QTEE arguments. + * @num_params: number of elements in the parameter array. + * @ubuf_addr: user buffer for argument of type %QCOM_TEE_ARG_TYPE_IB. + * @ubuf_size: size of user buffer. + * @ctx: context in which the conversion should happen. + * + * It expects @params to have enough entries for @u. Entries in @params ar= e of + * %TEE_IOCTL_PARAM_ATTR_TYPE_NONE. On failure, it puts IO objects. + * + * Return: On success return number of input parameters processed or <0 on= failure. + */ +static int qcom_tee_cb_params_from_args(struct tee_param *params, + struct qcom_tee_arg *u, int num_params, + void __user *ubuf_addr, size_t ubuf_size, + struct qcom_tee_context *ctx) +{ + int i, np =3D 0; + + qcom_tee_arg_for_each(i, u) { + enum qcom_tee_arg_type at =3D u[i].type; + + if (at =3D=3D QCOM_TEE_ARG_TYPE_IB) { + params[np].attr =3D TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_INPUT; + + /* Underflow already checked in qcom_tee_supp_pop_entry(). */ + ubuf_size =3D round_down(ubuf_size - u[i].b.size, 8); + params[np].u.membuf.uaddr =3D (void * __user)(ubuf_addr + ubuf_size); + params[np].u.membuf.size =3D u[i].b.size; + if (copy_to_user(params[np].u.membuf.uaddr, u[i].b.addr, u[i].b.size)) + goto out_failed; + + np++; + } else if (at =3D=3D QCOM_TEE_ARG_TYPE_IO) { + params[np].attr =3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT; + if (qcom_tee_objref_from_arg(¶ms[np], &u[i], ctx)) + goto out_failed; + + np++; + } + } + + return np; + +out_failed: + + /* On ERROR: */ + /* - Release params associated to QTEE objects in this context so far. */ + for (; np >=3D 0; np--) { + if (params[np].attr =3D=3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_INPUT) + qcom_tee_context_del_qtee_object(¶ms[np], ctx); + } + /* - Release any IO objects not processed so far. */ + while (u[i].type) { + if (u[i].type =3D=3D QCOM_TEE_ARG_TYPE_IO) + qcom_tee_object_put(u[i++].o); + } + + return -EINVAL; +} + +/** + * qcom_tee_cb_params_to_args() - Convert TEE parameters to QTEE arguments= in a context. + * @u: QTEE arguments. + * @params: TEE parameters. + * @num_params: number of elements in the parameter array. + * @ctx: context in which the conversion should happen. + * + * Return: On success return 0 or <0 on failure. + */ +static int qcom_tee_cb_params_to_args(struct qcom_tee_arg *u, + struct tee_param *params, int num_params, + struct qcom_tee_context *ctx) +{ + struct qcom_tee_object *object; + int i, np =3D 0; + + qcom_tee_arg_for_each(i, u) { + enum qcom_tee_arg_type at =3D u[i].type; + + if (at =3D=3D QCOM_TEE_ARG_TYPE_OB) { + if (params[np].attr !=3D TEE_IOCTL_PARAM_ATTR_TYPE_MEMBUF_OUTPUT || + params[np].u.membuf.size > u[i].b.size) + goto out_failed; + + if (copy_from_user(u[i].b.addr, params[np].u.membuf.uaddr, u[i].b.size)) + goto out_failed; + + u[i].b.size =3D params[np].u.membuf.size; + + np++; + } else if (at =3D=3D QCOM_TEE_ARG_TYPE_OO) { + if (params[np].attr =3D=3D TEE_IOCTL_PARAM_ATTR_TYPE_OBJREF_OUTPUT) { + if (qcom_tee_objref_to_arg(&u[i], ¶ms[np], ctx)) + goto out_failed; + } else { + goto out_failed; + } + + np++; + } + } + + return 0; + +out_failed: + + /* On ERROR: */ + /* Put OO objects processed so far. */ + for (--i; i >=3D 0; i--) { + if (u[i].type !=3D QCOM_TEE_ARG_TYPE_OO) + continue; + + object =3D u[i].o; + qcom_tee_user_object_set_notify(object, false); + /* For callback object, we hold a reference for the driver, put it. */ + if (typeof_qcom_tee_object(object) =3D=3D QCOM_TEE_OBJECT_TYPE_CB_OBJECT) + qcom_tee_object_put(object); + /* Put QTEE copy of object. */ + qcom_tee_object_put(object); + } + + return -EINVAL; +} + +/** + * qcom_tee_user_object_pop() - Pop a request for a user object. + * @ctx: context to look for user object. + * @params: parameters for @op. + * @num_params: number of elements in the parameter array. + * @uaddr: user buffer for output MEMBUF parameters. + * @size: size of user buffer @uaddr. + * @data: information for the pop request. + * + * @params is filled along with @data for the picked request. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_user_object_pop(struct qcom_tee_context *ctx, + struct tee_param *params, int num_params, + void __user *uaddr, size_t size, + struct qcom_tee_user_object_request_data *data) +{ + struct qcom_tee_user_req *ureq; + struct qcom_tee_arg *req_args; + u64 req_object_id; + u32 req_op; + int req_id; + int ret; + + while (1) { + scoped_guard(srcu, &ctx->req_srcu) { + /* Pop a request 'QUEUED -> PROCESSING'. */ + ureq =3D qcom_tee_supp_pop_entry(ctx, size, num_params); + if (!ureq) + goto wait_for_request; + + /* On failure, issue with params, e.g. not enough space in user buffer.= */ + if (IS_ERR(ureq)) + return PTR_ERR(ureq); + + /* ''Prepare user request:'' */ + req_id =3D ureq->req_id; + req_object_id =3D ureq->object_id; + req_op =3D ureq->op; + req_args =3D ureq->args; + ret =3D qcom_tee_cb_params_from_args(params, req_args, + num_params, uaddr, + size, ctx); + } + + if (ret >=3D 0) + break; + + /* On failure, issue with req_args, e.g. invalid object. */ + scoped_guard(mutex, &ctx->lock) { + /* If (!=3D __empty_req) then 'PROCESSING -> PROCESSED'. */ + if (idr_remove(&ctx->reqs_idr, req_id) =3D=3D ureq) { + ureq->state =3D QCOM_TEE_REQ_PROCESSED; + ureq->errno =3D ret; + /* Send error to QTEE. */ + complete(&ureq->c); + } + } + + /* Try next request. */ + continue; + +wait_for_request: + /* Wait for a new QUEUED request. */ + if (wait_for_completion_interruptible(&ctx->req_c)) + return -ERESTARTSYS; + } + + /* It is a RELEASE message; no one is waiting for result. */ + if (req_op =3D=3D QCOM_TEE_OBJREF_OP_RELEASE) { + scoped_guard(mutex, &ctx->lock) + idr_remove(&ctx->reqs_idr, req_id); + kfree(ureq); + } + + /* ''Pick a pending request:'' */ + data->id =3D req_id; + data->object_id =3D req_object_id; + data->op =3D req_op; + data->np =3D ret; + + return 0; +} + +/** + * qcom_tee_user_object_submit() - Submit a response for a user object. + * @ctx: context to look for user object. + * @params: returned parameters. + * @num_params: number of elements in the parameter array. + * @id: request id for the response. + * @errno: result of user object invocation. + * + * Return: On success return 0 or <0 on failure. + */ +int qcom_tee_user_object_submit(struct qcom_tee_context *ctx, + struct tee_param *params, int num_params, int id, int errno) +{ + struct qcom_tee_user_req *ureq; + + scoped_guard(srcu, &ctx->req_srcu) { + scoped_guard(mutex, &ctx->lock) { + ureq =3D idr_remove(&ctx->reqs_idr, id); + /* Is id invalid, or no one is waiting on response. */ + if (ureq =3D=3D &__empty_ureq || !ureq) + return -ENODEV; + + ureq->state =3D QCOM_TEE_REQ_PROCESSED; + } + + ureq->errno =3D errno; + /* Process params only on SUCCESS. */ + if (!errno) + ureq->errno =3D qcom_tee_cb_params_to_args(ureq->args, params, + num_params, ctx); + + errno =3D (!errno && ureq->errno) ? ureq->errno : 0; + /* Send result to QTEE. */ + complete(&ureq->c); + } + + return errno; +} diff --git a/include/uapi/linux/tee.h b/include/uapi/linux/tee.h index 5d33a8009efb..00e599d1582d 100644 --- a/include/uapi/linux/tee.h +++ b/include/uapi/linux/tee.h @@ -59,6 +59,7 @@ #define TEE_IMPL_ID_OPTEE 1 #define TEE_IMPL_ID_AMDTEE 2 #define TEE_IMPL_ID_TSTEE 3 +#define TEE_IMPL_ID_QTEE 4 =20 /* * OP-TEE specific capabilities --=20 2.34.1 From nobody Wed Dec 4 19:03:26 2024 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9C247166F25; Tue, 3 Dec 2024 04:20:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199626; cv=none; b=fkq5iB974wZa8CetvPGpYlvvxU/ImPUw347T3qVd/we2EwU7D//wfz7k6Pmu3a0BUoGtUz+LC2qIGkLBb5o44kt7Mkz6/2DJtys8SoFTV2yb8NXpsCXUpDR8I3Smi8I42ORIq0GO29jEdzzDob/1HEu5Wdu8oKTTa7QjN+bPnWg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199626; c=relaxed/simple; bh=DfsTpQhuDVPTpBwxnbE96woq0PpXEf79V1MdViROPLM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=JZkzBC+XRF4xMXi7M7cHOg5ITBoe+sxalrRbc8W26wYBOnFplqv32prHRiATzmp5M0hOxW87WK35RDB0zGBJCQDMnQ/hxtl8W25r9ZbplhHXLpB+/vUK0TKcy4R/P48otNWFHqZVKw824SOPqCJSK2NJMDqZmFv7RFVLM3LQ7cU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=YVW+/Xvp; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="YVW+/Xvp" Received: from pps.filterd (m0279865.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4B2Jn2e0003080; Tue, 3 Dec 2024 04:20:08 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= O4bWYQjlUTtsncfGn51zMsyUq6OeHDQHavuJcZ+eD3A=; b=YVW+/XvpNRduslLv 5rbM+jGmoIJyU8shvcToFXbyWaGRnOQV4bcPiZpiG3rGun+RcEdorbWGMwDMMbvD KhiXSSFySEunczvA7Ln7BgOUwe8+GDVkckfZ5lYHwAbNadCoYpyxGCfreYK10+sr DjQZGsWL/kRbhyHdEdySYi9Z1SsBa23rKP0ESYN9AxPjF/ojDPDmpqxrZusJTeeK G4mNx/Qmkw/mODYOhrohEf/VectW2aQuSY6yDbRm7rbyp4uoG+lRAa1jis4FUhNC +aw40cHStsAtkvNFb7934fBLEJXOQlwDANozHJpVlJI9umWTYVY1JiRhSkGb/kTP dEDXng== Received: from nasanppmta05.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 437snqxv5e-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 03 Dec 2024 04:20:08 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA05.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 4B34K8wn024268 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 3 Dec 2024 04:20:08 GMT Received: from hu-azarrabi-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 2 Dec 2024 20:20:07 -0800 From: Amirreza Zarrabi Date: Mon, 2 Dec 2024 20:19:25 -0800 Subject: [PATCH 09/10] arm64: dts: qcom: sm8650: add support for QTEE Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-9-f502ef01e016@quicinc.com> References: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> In-Reply-To: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> To: Jens Wiklander , Sumit Garg , Bjorn Andersson , "Konrad Dybcio" , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Bartosz Golaszewski" , Srinivas Kandagatla CC: , , , , , Amirreza Zarrabi X-Mailer: b4 0.13.0 X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: _8JIZmuVEHs8ZWhLHMzQa9YHubpc-4US X-Proofpoint-GUID: _8JIZmuVEHs8ZWhLHMzQa9YHubpc-4US X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.60.29 definitions=2024-09-06_09,2024-09-06_01,2024-09-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 bulkscore=0 phishscore=0 adultscore=0 impostorscore=0 malwarescore=0 lowpriorityscore=0 suspectscore=0 priorityscore=1501 mlxscore=0 mlxlogscore=923 clxscore=1015 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2411120000 definitions=main-2412030034 Add qcom_tee node. Signed-off-by: Amirreza Zarrabi --- arch/arm64/boot/dts/qcom/sm8650.dtsi | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qco= m/sm8650.dtsi index 25e47505adcb..41505b032e3a 100644 --- a/arch/arm64/boot/dts/qcom/sm8650.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi @@ -372,6 +372,10 @@ scm: scm { interconnects =3D <&aggre2_noc MASTER_CRYPTO QCOM_ICC_TAG_ALWAYS &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>; }; + + qcom_tee { + compatible =3D "qcom,tee"; + }; }; =20 clk_virt: interconnect-0 { --=20 2.34.1 From nobody Wed Dec 4 19:03:26 2024 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6E301186616; Tue, 3 Dec 2024 04:20:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199627; cv=none; b=UBCQR0j1Q7atybXBwJF2Nw0rWqS1GfC5dhn+T/TaUYaZSpLoszjlPZc7TqcmzRqsnx6SJ/9xYV19i9DoCgCVMNGpftKSPWztFuY+zNxtLTq4EcVWJnSTV5+b4FKLpBa0dV1SfGotXekTCWdCeu0EGtspRfda/nsjvXwBiMK1YUo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733199627; c=relaxed/simple; bh=6ooM/9AY9xoHRBcOk7ycakkouxWVLDV98VBpDCj+F2k=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=tIoqcBR8KOa2tozQNopX4Ld2JVYlDTwVvo/XI00KPpV2kwRv9JTgG8HM6gHw9I4bxYmZKqdtpbsK5dbTxltEIjKs8pa/dy+Sinkhgnezx3q4ePv7aYKwhRgBSpGNQYT/TtlbP+30OhNEm9q9sYHexDOpxivhBuhAgNvhPy/VFRY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com; spf=pass smtp.mailfrom=quicinc.com; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b=kK2GnW7t; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=quicinc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=quicinc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=quicinc.com header.i=@quicinc.com header.b="kK2GnW7t" Received: from pps.filterd (m0279867.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 4B2JjaPl009592; Tue, 3 Dec 2024 04:20:09 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= cc:content-transfer-encoding:content-type:date:from:in-reply-to :message-id:mime-version:references:subject:to; s=qcppdkim1; bh= nJ8oUk0DjJmQHwnQTgTBlinyuDwrdPECWWpX6XK+l/Q=; b=kK2GnW7t/OUg+a79 iQtwfyd1lHO7fGjal+vJ+xGK13rMRVgy3MhP+dqFgwAsEBNkW7v4w4d3EwhrQNyw Njd6j3/0UjFij7sA6jy6k28//criU/LZxvDy+ru/lUjZVgOQN36gLxRH3DHJnkc9 p4yfRFsUA5nnZqZd4fzjY1ebeO+Wt6caEKnTQZslIC3F9cUQBhPeIfejeyNydOO1 B1yH0mCBhtqbwoOdZbYgDC91/Fhxf1qSTpcGMyPkhZ0nXy+1RPfi/EgOsffDC7Xf mhSQ6coRPE8BvE6d0Di/uOR2BoIM0kCHf/J5YeG7fBaTgpUUptV/zGkkUyiXe0zy B295tg== Received: from nasanppmta05.qualcomm.com (i-global254.qualcomm.com [199.106.103.254]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 437rdeeycg-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 03 Dec 2024 04:20:09 +0000 (GMT) Received: from nasanex01b.na.qualcomm.com (nasanex01b.na.qualcomm.com [10.46.141.250]) by NASANPPMTA05.qualcomm.com (8.18.1.2/8.18.1.2) with ESMTPS id 4B34K8xw024278 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Tue, 3 Dec 2024 04:20:08 GMT Received: from hu-azarrabi-lv.qualcomm.com (10.49.16.6) by nasanex01b.na.qualcomm.com (10.46.141.250) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.9; Mon, 2 Dec 2024 20:20:07 -0800 From: Amirreza Zarrabi Date: Mon, 2 Dec 2024 20:19:26 -0800 Subject: [PATCH 10/10] Documentation: tee: Add Qualcomm TEE driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-ID: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-10-f502ef01e016@quicinc.com> References: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> In-Reply-To: <20241202-qcom-tee-using-tee-ss-without-mem-obj-v1-0-f502ef01e016@quicinc.com> To: Jens Wiklander , Sumit Garg , Bjorn Andersson , "Konrad Dybcio" , Rob Herring , "Krzysztof Kozlowski" , Conor Dooley , "Bartosz Golaszewski" , Srinivas Kandagatla CC: , , , , , Amirreza Zarrabi X-Mailer: b4 0.13.0 X-ClientProxiedBy: nalasex01c.na.qualcomm.com (10.47.97.35) To nasanex01b.na.qualcomm.com (10.46.141.250) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-ORIG-GUID: iM7jLcEG1YVF7CY-ICZHAjpc1FI2Vv0o X-Proofpoint-GUID: iM7jLcEG1YVF7CY-ICZHAjpc1FI2Vv0o X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1039,Hydra:6.0.680,FMLib:17.12.60.29 definitions=2024-09-06_09,2024-09-06_01,2024-09-02_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 mlxscore=0 adultscore=0 malwarescore=0 mlxlogscore=999 phishscore=0 suspectscore=0 impostorscore=0 clxscore=1011 lowpriorityscore=0 bulkscore=0 priorityscore=1501 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2411120000 definitions=main-2412030034 Add documentation for the Qualcomm TEE driver. Signed-off-by: Amirreza Zarrabi --- Documentation/tee/index.rst | 1 + Documentation/tee/qtee.rst | 143 ++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 144 insertions(+) diff --git a/Documentation/tee/index.rst b/Documentation/tee/index.rst index 4be6e69d7837..62afb7ee9b52 100644 --- a/Documentation/tee/index.rst +++ b/Documentation/tee/index.rst @@ -11,6 +11,7 @@ TEE Subsystem op-tee amd-tee ts-tee + qtee =20 .. only:: subproject and html =20 diff --git a/Documentation/tee/qtee.rst b/Documentation/tee/qtee.rst new file mode 100644 index 000000000000..3185af2c2b2b --- /dev/null +++ b/Documentation/tee/qtee.rst @@ -0,0 +1,143 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D +QTEE (Qualcomm Trusted Execution Environment) +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +The QTEE driver handle communication with Qualcomm TEE [1]. + +Lowest level of communication with QTEE builds on ARM SMC Calling Conventi= on +(SMCCC) [2], which is the foundation for QTEE's Secure Channel Manager (SC= M) [3] +used internally by the driver [4]. + +In a QTEE-based system, services are represented as objects with series of +operations that can be called to produce results, including other objects.= When +an object is hosted within QTEE, executing its operations is referred to a= s direct +invocation. QTEE can invoke objects hosted in the kernel or userspace usin= g a +method known as callback requests. + +The SCM provides two functions for object invocation and callback request: + +- QCOM_SCM_SMCINVOKE_INVOKE for direct invocation. It can return either a = result or a callback request. +- QCOM_SCM_SMCINVOKE_CB_RSP submits a response for a previous callback req= uest. + +The QTEE Transport Message [5] is stacked on top of SCM driver functions. + +A message consists of two buffers shared with QTEE, inbound and outbound b= uffers. +The inbound buffer is used for direct invocation and the outbound buffer i= s used +to make callback request. This picture shows the contents of a QTEE transp= ort message:: + + +---------------------+ + | v + +-----------------+-------+-------+------+--------------------------+ + | qcom_tee_msg_ |object | buffer | | + | object_invoke | id | offset, size | | = (inbound buffer) + +-----------------+-------+--------------+--------------------------+ + <---- header -----><---- arguments ------><- in/out buffer payload -> + + +-----------+ + | v + +-----------------+-------+-------+------+----------------------+ + | qcom_tee_msg_ |object | buffer | | + | callback | id | offset, size | | (out= bound buffer) + +-----------------+-------+--------------+----------------------+ + +Each buffer is started with a header and array of arguments. + +QTEE Transport Message supports four types of arguments: + +- Input object is an object parameter to the current invocation or callbac= k request +- Output object is an object parameter from the current invocation or call= back request +- Input buffer is (offset, size) pair to the inbound or outbound region to= store parameter to the current invocation or callback request +- Output buffer is (offset, size) pair to the inbound or outbound region t= o store parameter from the current invocation or callback request + +The QTEE driver offers the qcom_tee_object, which represents an object wit= hin both +QTEE and the kernel. To access any service in QTEE, client needs to invoke= an +instance of this object. Any structure intended to represent a service for= export +to QTEE should include an instance of qcom_tee_object:: + + struct driver_service { + struct qcom_tee_object object; + ... + }; + + #define to_driver_service_object(o) container_of((o), struct driver_servi= ce, object) + + static int driver_service_dispatch(struct qcom_tee_object *object, u32 op, + struct qcom_tee_arg *args) + { + struct driver_service *so =3D to_driver_service_object(object); + + switch(op) { + case OBJECT_OP1: + ... + break; + default: + return -EINVAL; + } + } + + static void driver_service_object_release(struct si_object *object) + { + struct driver_service *so =3D to_driver_service_object(object); + kfree(so); + } + + struct si_object_operations driver_service_ops =3D { + .release =3D driver_service_object_release; + .dispatch =3D driver_service_dispatch; + }; + + void service_init(void) + { + struct driver_service *so =3D kzalloc(sizeof(*so), GFP_KERNEL); + + /* Initialize so->object as a callback object. */ + qcom_tee_object_user_init(&so->object, QCOM_TEE_OBJECT_TYPE_CB_OBJECT, + &driver_service_ops, "driver_service_object"); + + /* Invoke a QTEE object and pass/register 'so->object' with QTEE. */ + ... + } + module_init(service_init); + +The QTEE driver utilizes qcom_tee_object to encapsulate userspace objects.= When a +callback request is made, it translates into calling the dispatch callback= . For +userspace objects, this is converted into requests accessible to callback = servers +and available through generic TEE API IOCTLs. + +Picture of the relationship between the different components in the QTEE +architecture:: + + User space Kernel Secure world + ~~~~~~~~~~ ~~~~~~ ~~~~~~~~~~~~ + +--------+ +----------+ +-------------= -+ + | Client | |callback | | Trusted = | + +--------+ |server | | Application = | + /\ +----------+ +-------------= -+ + || +----------+ /\ /\ + || |callback | || || + || |server | || \/ + || +----------+ || +-------------= -+ + \/ /\ || | TEE Internal= | + +-------+ || || | API = | + | TEE | || || +--------+--------+ +-------------= -+ + | Client| || || | TEE | QTEE | | QTEE = | + | API | \/ \/ | subsys | driver | | Trusted OS = | + +-------+----------------+----+-------+----+-------------+-------------= -+ + | Generic TEE API | | QTEE MSG = | + | IOCTL (TEE_IOC_*) | | SMCCC (QCOM_SCM_SMCINVOKE_*) = | + +-----------------------------+ +--------------------------------= -+ + +References +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +[1] https://docs.qualcomm.com/bundle/publicresource/topics/80-70015-11/qua= lcomm-trusted-execution-environment.html + +[2] http://infocenter.arm.com/help/topic/com.arm.doc.den0028a/index.html + +[3] drivers/firmware/qcom/qcom_scm.c + +[4] /local/mnt/workspace/Linux/drivers/tee/qcomtee/qcom_scm.c + +[5] drivers/tee/qcomtee/qcomtee_msg.h \ No newline at end of file --=20 2.34.1