From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from SN4PR0501CU005.outbound.protection.outlook.com (mail-southcentralusazon11011005.outbound.protection.outlook.com [40.93.194.5]) (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 0D85F4519A6 for ; Thu, 30 Jul 2026 16:18:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.93.194.5 ARC-Seal:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785428319; cv=fail; b=SN1KxozBpB6jc6t3pBsTTmLoaDnd23yYAFFldfrof+wRZX5Cid+UTlu5UqEtcYll7KwW6ewideCzw6Hh42Jj4IWeqJIHeEsKMa7Hk7L5h2OksDRhwvn/enFSq1SM3RNIySmWSoBsommJLEAz/SJocEjhdNaKgW6q7daI/4BFPlg= ARC-Message-Signature:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785428319; c=relaxed/simple; bh=MalduBwZC0T8AgQ6UwzZRg5i2T0vG5DcPco2B4pz6dM=; h=Message-ID:Date:Cc:Subject:From:To:References:In-Reply-To: Content-Type:MIME-Version; b=Q3fzvKQS7YLt5wgSrfRPlEAEEI/ujrlFNb1j+Wcjmb1cY8Gi0SAXPrinOxUeP6le41bYgeLiGK7HiwnoA7Z68N4Cr3rsPBYfkFWzJ0pfj7okcP+YqtyPLsEKpTvv8JML1DgOhdCSxAtztRhSBhTXHZ3fSjxbwK7ztawBwPClE8s= ARC-Authentication-Results:i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=altera.com; spf=pass smtp.mailfrom=altera.com; dkim=pass (2048-bit key) header.d=altera.com header.i=@altera.com header.b=YijOF8kh; arc=fail smtp.client-ip=40.93.194.5 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=altera.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=altera.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=altera.com header.i=@altera.com header.b="YijOF8kh" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=cmGC3vxD/qvVCRhILveBKHqbYSv6EcYNQVY3ES8xFdJhQAVGA9o0EJSvMG+luyQ2yDOX2hFH/LXv5//yjQbHVAJWAvRGLN7+ncUtQzHcK3ylYCWsZEbbXT8hHuoDGER1KJ6RkV4fpyjv7KmjXsRxIHrY6hk0RQVcrWE8iMiAemKOK+8P06uNyQE129U8zo4K2lrtvIfXM5UuoSibnckPqXxAbwUJywBPInc6cafz177NO6/7cYHzibNEjnh58NMCsScN3JZY4lTjtVXvCLHX9bTZfu78TL+A/E/PDVQdoA/Bwa8NRwE+R7CYn9wYNwyF3+5D/RjdMZbI9CPjbAiCiw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=Ten6FN56zjlu1NjmREbmhABdJ4WLf169o4oijNF3C0Y=; b=W9h0xDx/BYEbXUosVAjLQsYSdAhQGj1FLDVaBxOVKTdSCQSq2Wha6groxLeEYCI1WQhXxAjUyBN1RnucymYEfENt7WbeYIFNgV/bcXyGE8hy+3qzgyCh673OUelX+qEifaHwsrg7I43p3jXbfGc7lvtL9eVOYimqQ/1aoPymGJxxVpjQII13e8NrYN9XzED2C1rQLqkRZo08qtvkxkQH62duwPHQlsgle796y0RSWhTec6LMYRv77FWydYWjAd4hIOTij7CxVzw6E/7qiDFkt7mpK5LonIG4RQxpU/yW//ypAUBFtikgcItyTsjeR5FdAv7RL2VEIaYH0eYUTeEGpQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=altera.com; dmarc=pass action=none header.from=altera.com; dkim=pass header.d=altera.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=altera.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Ten6FN56zjlu1NjmREbmhABdJ4WLf169o4oijNF3C0Y=; b=YijOF8kh/pqtgV1Q/4y+XIrh1kCeHF5G/jfMB1NpstAdHD0s+g4G6BWk3NOub31F2n4BDsfng+vlJbsBKcdhSIChZZRhb6N4tnPukDIC5eVCgIIz7VocJI2X5UIoyk6ztUumtTZ6HDHaCPnj3HQNfIUpI1mB+QMaxmx0qxqjIus+ttaauuIQLXvutAkD1XcKpsU2TnaNIQalJMQi7URRCPgdFrwCuFqisBqlm3DUU+A/0DVkjOypoAhI5UXnZXNw3Ote5BMU0e0lDbokwoV+pLpqAr6bXyeaztA+AzmEgnsgKc4N2BbcpvvoHRKfpSP3cwFzk1WQWMTYi6cW01IbKQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=altera.com; Received: from PH0PR03MB6560.namprd03.prod.outlook.com (2603:10b6:510:b2::12) by PH7PR03MB7389.namprd03.prod.outlook.com (2603:10b6:510:2ee::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.270.15; Thu, 30 Jul 2026 16:18:29 +0000 Received: from PH0PR03MB6560.namprd03.prod.outlook.com ([fe80::5364:bd7:85a:f07b]) by PH0PR03MB6560.namprd03.prod.outlook.com ([fe80::5364:bd7:85a:f07b%5]) with mapi id 15.21.0270.012; Thu, 30 Jul 2026 16:18:29 +0000 Message-ID: Date: Fri, 31 Jul 2026 00:18:20 +0800 User-Agent: Mozilla Thunderbird Cc: muhammad.nazim.amirul.nazle.asmade@altera.com, tze.yee.ng@altera.com, chee.nouk.phoon@altera.com, genevieve.chan@altera.com, adrian.ho.yin.ng@altera.com Subject: Re: [PATCH v2 2/2] firmware: socfpga-fcs: add Altera SoCFPGA FCS driver with SDOS From: Hang Suan Wang To: Greg Kroah-Hartman , Dinh Nguyen , linux-kernel@vger.kernel.org, "Michael S . Tsirkin" , Huacai Chen , Florian Fainelli , Chen-Yu Tsai References: <900031bb3347ba6dbb1556a8878e1d12e85050ab.1783966717.git.hang.suan.wang@altera.com> Content-Language: en-US In-Reply-To: <900031bb3347ba6dbb1556a8878e1d12e85050ab.1783966717.git.hang.suan.wang@altera.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-ClientProxiedBy: JH0PR01CA0142.apcprd01.prod.exchangelabs.com (2603:1096:990:5b::20) To PH0PR03MB6560.namprd03.prod.outlook.com (2603:10b6:510:b2::12) Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PH0PR03MB6560:EE_|PH7PR03MB7389:EE_ X-MS-Office365-Filtering-Correlation-Id: cd2096c2-8a68-4b25-f68d-08deee5630bd X-MS-Exchange-AtpMessageProperties: SA X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|23010399003|1800799024|376014|56012099006|10067099003|55112099003|18002099003|22082099003|6133799003|11063799006|5023799004|4143699003|3023799007; X-Microsoft-Antispam-Message-Info: cTNToAeXk9pahVdlVk2WP0U1NFOShxLQoCyZsn2gwO2SDvLNB9KUXOhVx9aGpuzNM5XPgUXLkT43bYep4D8xbhb7ZDezRIQ5MUrQ9DpLbJvgKGXyzd9VisbTFgDrUV0hdnAzqxNDYRvahPj9MySEm4xXf7D9Sa0fH+B8ID5mg3ijF5N4eu4VbktPHbqCKJppiCI/5sMGHjlOeD7mXs+lRayq87MxJjgiyuNGGqWzv3ekKpSkd2O6gcxEKNdCoQouD+ODOan8Oj0mme/QoOKKewk+4FJQ0z5XYfGNXpoHrdV4cuxpsUGSfRcrBhbSYYe0YSufaS+XDcVhyK/JAQAs2yonFM4zWoaQDaVzEo6mB/l5pg7zEjmEuHmO588G8lFrhYwueV8sPA0v6YmfiP/s96ob7BSiVbh9l8YttQTLV8tTRcYafJJLaPl8w6qlUntwpX59xCyPx3Mvpt3UIr8ImnvmzheCOOoc/3K/jjwaW78l1RZnpvYugnnX+lSF5WNqjT2Wp/57VbeMgSc0SRaaa6nnl99s0HeFRhFPT6lfXm0NUrgpCXa6FmX8DS89Qp9Fyh4R1UmI+v7wBby1Pie3tExU1pYBeedGtdpkE3uaW7M= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PH0PR03MB6560.namprd03.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(23010399003)(1800799024)(376014)(56012099006)(10067099003)(55112099003)(18002099003)(22082099003)(6133799003)(11063799006)(5023799004)(4143699003)(3023799007);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?a2ZZYnNRN1FIN2ZFdU5xRmdBaE5rNjI2NUdYUGN5ZXVEMkkzVVpOdUxsUGcy?= =?utf-8?B?VXY1U1FwZTJ6VlBqdHFjaXN0dUVBS0xZd1laSlFjcFRCbGh1aUdaWkI2Mktq?= =?utf-8?B?Z0haV29vZE9jN2ZKVzZwVGxZZkM4WkdOWmVWR3g0RnZXRGljczl5TTNIOUVl?= =?utf-8?B?Y2NWdUJQY0ZLeHBDVmxGZEZQRC9YVVluZzkyMmRlYjVFQ0hKanoyd1RzS1U2?= =?utf-8?B?VEsrQ3VSYzAxcUlpTnRVMEM3UFROVHJSVUcwY3RvRzFqUDJtMnNvK3FPaGJ1?= =?utf-8?B?SWNSeUNPR0xCbGRINTZ2S0oyZkJhYktYTDBxTExiTm9Nc203NEpJYjB6VVVn?= =?utf-8?B?RlpaYjFVMGNPdFJVL0pvWGVValVTOHVzejVlUkpSeWw5c3B1dWxqTHR2Ymc1?= =?utf-8?B?SUVDa1VMMHRlekUxVDREQjduSzNMZXdOTS9xVWd1WWxUZXNxQzdwZDZVZTRi?= =?utf-8?B?UUVOTG8yTHRleFE1bU9DSmpZdU5TRTAvOGhWTWVYZGVRU1djQkZlNlZMQ3gv?= =?utf-8?B?QzZTQmVrcStVdERwcEc5c2xqZVRvZU91bVhyUlNaNDRYTmFvYVBjbUFSSGdC?= =?utf-8?B?NVE1c0ozeEFaVXJBSUtLMUcrU0ZhN1diVlRqeWo2endobVV4SVpjRG9pNGhD?= =?utf-8?B?cE93UXFVSWZEOUc4cDlMcGdwRVR3M2xhTlZWbFlHZFhRZ0F0U2dnRjduUEdE?= =?utf-8?B?RTE1RG45RVZ3RmorZG9OS0Z0VlFWZ21meERibkpDLzE1Z2tmZHlUZkh5T2dF?= =?utf-8?B?T0VqSmU4bjNwZGdNV2lESDZIL2NpMS9VTm1BVzZWUWpkcWRGb05JM3dsUFFZ?= =?utf-8?B?dzM0Smpwb2hCSXVIYmF6elNzTE5XamVGYWVKTFJKRUk0QnBHUUJDVWo1MnJD?= =?utf-8?B?YXRTd054R2tYTFNvbGFsMzNvYXFSQ2ZJOHJhQ2FQeHBkQ0xVL3lwZzJIRzhU?= =?utf-8?B?MTVDeDM0ekFlYmx2aDJvT3o3NC9XOUFDcTF6MFYwb3YwUlp3R2cxMU1YcG1a?= =?utf-8?B?RXlPdUU4YXMyNVpKdEFwL2d2OTBQV2VzS2YySjNCd2RWZjFvVkhiYkU2QmFu?= =?utf-8?B?WHJlaExUcHJMZldCSXYvWHVwbU1sY3FMZkN6TVBYSWhGN0tkTUF0WE1ZQjZE?= =?utf-8?B?QVNyYU9vSUJBZ2o2NlkwcVZBSFp0eWViOU94U1pnY01LdkdFbGpnNFY4S2tG?= =?utf-8?B?aURhVHFrbnlyZmdjWkpyT1V0VEt5RXFuK0ZVbEZqZEVnVVBPcE96S2ExeXoy?= =?utf-8?B?TTVxVDlXOGNwZksySE1PckUwZVBUV1VIVUZLa2xzY2ZWNkhFOEpoRHRxN25p?= =?utf-8?B?QXBLd3JLSHJ0K2Njb2NtOFR3akR0ZUhrdDFlY1JUalhIQmhaQXpRYWVKTDNV?= =?utf-8?B?UzVwV1l5cFFranU5eFZWb3NwcFVuMHpLYVRlM1R1UDZrc1hyblJPdVp0UVlk?= =?utf-8?B?dUxXNnlzdnZKZTd1aFhkcHNKcUN1NVM4cDMrU0VUTVlwelVrRm1FWTYyQ0ly?= =?utf-8?B?ZFVmY211THQ2YjBEV2tmY0lTV3dVTVhlTTFSbXpQVjh0d3p4S2tiQ1dteWxa?= =?utf-8?B?TExNZ1hrZ2VMT0Vmc2RXT1Zrd05CRVRsK3UrZ3F2eWtMY2RCT2t4Vnh0cXlM?= =?utf-8?B?NXNLSzlCQ2Q2Wkc2eHd0YkNSelRvTDBlNS9kaitxUDBmTkhMczE5S0U0Wlps?= =?utf-8?B?Mk1xOElhbEFxSTVacE9rcEltRlIzTU4vWVpmVEVwRUUwZ1VGWkI4OGZYM3ht?= =?utf-8?B?VFhhTktrU2JnWk9qUThBNGFhd05GSVAwNnA4YWtNV29HRnZ2bW5Kckd2Wm9o?= =?utf-8?B?bHBYZTFBeFh6RUFXK1JneXVSNVVFNGl3MmhhcG5McXZnVVFCYW1wdmlMVytP?= =?utf-8?B?WDdLQ0owNHRvRXVJT3d0VEJKOUZiMVFnYWt6UHZERThwZ0g1VCtUdUx5cUxP?= =?utf-8?B?VVR3TzV6cXQ3MU9QZTVwTE9uSVJTOElrQzRIVTFKb3FwSFd2bEpodkxZRHpU?= =?utf-8?B?VEEwVkhOVkJlN2ZSclZ4L3lZajJrYXNtMjdXUk1NcW5FTHFQZlM4bno0TXRJ?= =?utf-8?B?b2VBV245bFczT3FGcGxZaEhIY09WTmd0ZVp6REVrNS9tNTVhWHB6eENGTXQ2?= =?utf-8?B?QVY5M0JxUjZVekhqOWdaREJyWm1LWG4rajU1cmUzSEZnOGRNazBtZTBmdFlI?= =?utf-8?B?VzFmT2V3cTdhNkk5Qy9ncTUxWS85QVRjdjl1U0hCSDI2R0tDZlJYZmM4ZDds?= =?utf-8?B?bHE4cmhFOTFQaHowR1BERGZhSGNyQ3hwaFhFd05UejdGeC9SZXBNbHBFUUR5?= =?utf-8?B?aUw3Yi84SDVHTktrOS96dCtIVEhMYnY5Q0JrcUxqTWtDVkhvaDg2OVVVcFRN?= =?utf-8?Q?a2kzLJ1KlypQFGGE=3D?= X-OriginatorOrg: altera.com X-MS-Exchange-CrossTenant-Network-Message-Id: cd2096c2-8a68-4b25-f68d-08deee5630bd X-MS-Exchange-CrossTenant-AuthSource: PH0PR03MB6560.namprd03.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Jul 2026 16:18:29.2414 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: fbd72e03-d4a5-4110-adce-614d51f2077a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: Ny7jNI4YCCOJp3uBxJf/Ed/Xxbjp3e3MtoYU6SywciUhRyhBbTrR5op5qJuzaVOm6xRYzk/iRwKHBvGJgWrym7bggcIdqG+XS/NfIYN8A/o= X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH7PR03MB7389 Response based on Sashiko review: https://sashiko.dev/#/patchset/cover.1783966717.git.hang.suan.wang%40altera.com On 14/7/2026 2:35 am, hang.suan.wang@altera.com wrote: > From: Hang Suan Wang > > Add the Altera SoCFPGA Crypto Service (FCS) driver, which exposes the > Secure Data Object Service (SDOS) encrypt/decrypt operation and its crypto > session lifecycle to non-secure host software. > > The SDOS is the FCS feature that protects data at rest: the SDM encrypts > and decrypts using a key derived from a device-unique SDOS root key plus an > SDM-generated IV, so the host never handles raw key material or IVs. It > only submits plaintext it already owns and receives authenticated > ciphertext objects managed by the SDM. A primary use case is black key > provisioning, where operational keys are installed without ever appearing > in cleartext. > > The driver is a standalone module and describes no hardware of its own. > It binds by name to the "stratix10-fcs" platform device that the > stratix10-svc driver registers in code, so no device-tree node is needed, > and detects the SoC by matching the service-layer compatible. It exposes > the following sysfs attributes. The SDOS requests are issued to the SDM > through the stratix10-svc asynchronous SIP SMC. Source and destination > buffers are taken from the service-layer memory pool so the SDM can reach > them via physical or SMMU-remapped addresses. > > For encryption the SDM returns a structured object (metadata, IV, HMAC, > ciphertext); for decryption it validates the HMAC, recovers the parameters > from the object header, and enforces a 64-bit owner ID so that only the > creator of an object can decrypt it. > > Signed-off-by: Hang Suan Wang > Reviewed-by: Dinh Nguyen > --- > MAINTAINERS | 8 + > drivers/firmware/Kconfig | 16 + > drivers/firmware/Makefile | 2 + > drivers/firmware/socfpga-fcs-core.c | 640 +++++++++++++++++++++ > drivers/firmware/socfpga-fcs.c | 250 ++++++++ > include/linux/firmware/intel/socfpga-fcs.h | 133 +++++ > 6 files changed, 1049 insertions(+) > create mode 100644 drivers/firmware/socfpga-fcs-core.c > create mode 100644 drivers/firmware/socfpga-fcs.c > create mode 100644 include/linux/firmware/intel/socfpga-fcs.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 15011f5752a9..72b12b32f8fe 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -946,6 +946,14 @@ ALPS PS/2 TOUCHPAD DRIVER > R: Pali Rohár > F: drivers/input/mouse/alps.* > > +ALTERA FCS DRIVER > +M: Hang Suan Wang > +M: Genevieve Chan > +L: linux-arm-kernel@lists.infradead.org > +S: Maintained > +F: drivers/firmware/socfpga-fcs* > +F: include/linux/firmware/intel/socfpga-fcs* > + > ALTERA MAILBOX DRIVER > M: Tien Sung Ang > S: Maintained > diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig > index 12dc70254842..9a70def6932a 100644 > --- a/drivers/firmware/Kconfig > +++ b/drivers/firmware/Kconfig > @@ -172,6 +172,22 @@ config INTEL_STRATIX10_RSU > > Say Y here if you want Intel RSU support. > > +config ALTERA_SOCFPGA_FCS > + tristate "Altera SoCFPGA Crypto Service (FCS) configuration" > + depends on INTEL_STRATIX10_SERVICE > + default n > + help > + Altera SoCFPGA Crypto Service (FCS) driver exposes interfaces access > + through the Intel Service Layer to user space via sysfs device > + attribute nodes. It exposes the crypto and key-management services > + of the Secure Device Manager (SDM) to the host software stack and > + requests are forwarded to Arm Trusted Firmware. The SDM then > + executes or authorizes them using device-rooted security resources. > + Protected key material remains within the secure firmware boundary > + and is not directly exposed to non-secure host software. > + > + Say Y here if you want Altera SoCFPGA FCS support. > + > config MTK_ADSP_IPC > tristate "MTK ADSP IPC Protocol driver" > depends on MTK_ADSP_MBOX > diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile > index 4ddec2820c96..e9f52f0e5f7a 100644 > --- a/drivers/firmware/Makefile > +++ b/drivers/firmware/Makefile > @@ -10,6 +10,8 @@ obj-$(CONFIG_EDD) += edd.o > obj-$(CONFIG_DMIID) += dmi-id.o > obj-$(CONFIG_INTEL_STRATIX10_SERVICE) += stratix10-svc.o > obj-$(CONFIG_INTEL_STRATIX10_RSU) += stratix10-rsu.o > +obj-$(CONFIG_ALTERA_SOCFPGA_FCS) += altera-fcs.o > +altera-fcs-y := socfpga-fcs.o socfpga-fcs-core.o > obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o > obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o > obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o > diff --git a/drivers/firmware/socfpga-fcs-core.c b/drivers/firmware/socfpga-fcs-core.c > new file mode 100644 > index 000000000000..598a4d2e3c5b > --- /dev/null > +++ b/drivers/firmware/socfpga-fcs-core.c > @@ -0,0 +1,640 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2026 Altera Corporation > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define OWNER_ID_OFFSET 12 > +#define OWNER_ID_SIZE 8 > + > +#define SDOS_DECRYPTION_REPROVISION_KEY_WARN 0x102 > +#define SDOS_DECRYPTION_NOT_LATEST_KEY_WARN 0x103 > + > +#define MSG_RETRY 3 > +#define RETRY_SLEEP_MS 1 > + > +static struct socfpga_fcs_priv *priv; > + > +/** > + * fcs_atf_version_callback() - service-layer callback for the ATF version query > + * @client: pointer to the stratix10-svc client > + * @data: pointer to the service-layer callback data > + * > + * Store the returned Arm Trusted Firmware version (or mailbox error) in @priv > + * and signal completion to the waiting caller. > + */ > +static void fcs_atf_version_callback(struct stratix10_svc_client *client, > + struct stratix10_svc_cb_data *data) > +{ > + struct socfpga_fcs_priv *p = client->priv; > + > + p->status = data->status; > + if (data->status == BIT(SVC_STATUS_OK)) { > + p->status = 0; > + p->atf_version[0] = *((unsigned int *)data->kaddr1); > + p->atf_version[1] = *((unsigned int *)data->kaddr2); > + p->atf_version[2] = *((unsigned int *)data->kaddr3); > + } else if (data->status == BIT(SVC_STATUS_ERROR)) { > + p->status = *((unsigned int *)data->kaddr1); > + dev_err(client->dev, "mbox_error=0x%x\n", p->status); > + } > + > + complete(&p->completion); > +} > + > +/** > + * fcs_async_callback() - completion callback for an async service request > + * @ptr: pointer to the completion to signal > + */ > +static void fcs_async_callback(void *ptr) > +{ > + if (ptr) > + complete(ptr); > +} > + > +/** > + * fcs_svc_send_request() - build and send an FCS command to the service layer > + * @command: FCS command code to dispatch > + * @timeout: time to wait for completion, in jiffies > + * > + * Build the service-layer message for @command and send it through the > + * stratix10-svc service driver, using the synchronous path for the ATF version > + * query and the asynchronous mailbox path (with retries) for the remaining > + * commands. > + * > + * Return: 0 on success, negative errno on failure. > + */ > +static int fcs_svc_send_request(enum fcs_command_code command, > + unsigned long timeout) > +{ > + struct fcs_cmd_context *k_ctx = &priv->k_ctx; > + struct stratix10_svc_cb_data data; > + void *handle = NULL; > + int status, index; > + int ret = 0; > + struct stratix10_svc_client_msg *msg = kzalloc(sizeof(*msg), GFP_KERNEL); > + > + priv->status = 0; > + priv->resp = 0; > + > + switch (command) { > + case FCS_DEV_CRYPTO_OPEN_SESSION: > + pr_debug("Sending command: COMMAND_FCS_CRYPTO_OPEN_SESSION\n"); > + msg->command = COMMAND_FCS_CRYPTO_OPEN_SESSION; > + break; > + > + case FCS_DEV_CRYPTO_CLOSE_SESSION: > + pr_debug("Sending command: COMMAND_FCS_CRYPTO_CLOSE_SESSION with session_id: 0x%x\n", > + priv->session_id); > + msg->arg[0] = priv->session_id; > + msg->command = COMMAND_FCS_CRYPTO_CLOSE_SESSION; > + break; > + > + case FCS_DEV_ATF_VERSION: > + pr_debug("Sending command: COMMAND_SMC_ATF_BUILD_VER\n"); > + msg->command = COMMAND_SMC_ATF_BUILD_VER; > + break; > + > + case FCS_DEV_SDOS_DATA_EXT: > + pr_debug("Sending command: COMMAND_FCS_SDOS_DATA_EXT with session_id: 0x%x, context_id: 0x%x, op_mode: 0x%x, own: 0x%llx\n", > + priv->session_id, k_ctx->sdos.context_id, > + k_ctx->sdos.op_mode, k_ctx->sdos.own); > + msg->arg[0] = priv->session_id; > + msg->arg[1] = k_ctx->sdos.context_id; > + msg->arg[2] = k_ctx->sdos.op_mode; > + msg->arg[3] = k_ctx->sdos.own; > + msg->payload = k_ctx->sdos.src; > + msg->payload_length = k_ctx->sdos.src_size; > + msg->payload_output = k_ctx->sdos.dst; > + msg->payload_length_output = *k_ctx->sdos.dst_size; > + msg->command = COMMAND_FCS_SDOS_DATA_EXT; > + break; > + > + default: > + pr_err("Unknown command: 0x%x\n", command); > + ret = -EINVAL; > + break; > + } > + > + if (ret) { > + kfree(msg); > + return ret; > + } > + > + if (command == FCS_DEV_ATF_VERSION) { > + reinit_completion(&priv->completion); > + > + /* > + * receive_cb is a persistent field on the shared client and > + * is only consumed by the synchronous stratix10_svc_send() > + * path. Set it immediately before the send and clear it right > + * after so it is non-NULL only for the duration of this > + * transaction. This keeps the callback correct per command and > + * prevents a future synchronous caller from silently > + * inheriting a stale fcs_atf_version_callback. > + */ > + priv->client.receive_cb = fcs_atf_version_callback; > + > + ret = stratix10_svc_send(priv->chan, msg); > + if (ret) { > + pr_err("failed to send message to service channel\n"); > + goto fun_ret; > + } > + > + if (!wait_for_completion_timeout(&priv->completion, > + msecs_to_jiffies(timeout))) { > + pr_err("svc timeout to get completed status\n"); > + ret = -ETIMEDOUT; > + } > +fun_ret: > + priv->client.receive_cb = NULL; > + kfree(msg); > + return ret; > + } > Could this cause a null pointer dereference if the request times out? > If wait_for_completion_timeout() expires, receive_cb is set to NULL and the > message is freed, but the service layer's background thread continues > processing the command. When the firmware eventually responds, the thread > might attempt to unconditionally call the cleared receive_cb function. when timeout happen, and priv->client.receive_cb set to NULL might create uncertainty. will fix this by set NULL when sending this message fail. will fix in v3. > + > + /* > + * Use the device-lifetime priv->completion as the async callback arg > + * rather than an on-stack completion: on a timeout/abort this function > + * returns while the svc layer still holds a pointer to it in the > + * transaction handle, so a stack object would be freed under it. FCS > + * serializes commands under priv->lock (one in-flight), so reusing > + * priv->completion here is safe. > + */ > + reinit_completion(&priv->completion); > + > + for (index = 0; index < MSG_RETRY; index++) { > + status = stratix10_svc_async_send(priv->chan, msg, &handle, > + fcs_async_callback, > + &priv->completion); > + if (status == 0) > + break; > + msleep(RETRY_SLEEP_MS); > + } > + > + if (!handle || status != 0) { > + pr_err("Failed to send async message\n"); > + kfree(msg); > + return -ETIMEDOUT; > + } > + > + ret = wait_for_completion_io_timeout(&priv->completion, > + msecs_to_jiffies(timeout)); > + if (ret > 0) > + pr_debug("Received async interrupt\n"); > + else > + pr_err("timeout occurred while waiting for async message\n"); > + > Does the service layer actually signal this completion asynchronously? > Looking at stratix10_svc_async_send(), it assigns the callback but the service > layer doesn't seem to have a background mechanism to invoke it upon > completion. It appears every asynchronous crypto request might needlessly > block for the full 2-second timeout before successfully falling back to > stratix10_svc_async_poll() below. agree, this wait for timeout might be an extra blind wait for 2s. will replace with a proper poll loop instead of always wait 2s. will fix in v3. > + ret = stratix10_svc_async_poll(priv->chan, handle, &data); > + > + if (ret == -EAGAIN) { > + /* > + * SDM still owns this transaction (STATUS_BUSY). Skip > + * stratix10_svc_async_done() so its transaction_id is not > + * recycled while in flight (which would alias responses); the > + * handle/id are reclaimed at teardown and the caller can retry. > + * msg is left unfreed to avoid a dangling handle->msg (small, > + * bounded leak). Safe only while completions are poll-delivered. > + */ > + pr_err("SDM transaction is busy, aborting\n"); > + return -EINPROGRESS; > + } > + > Does returning early here without cleaning up the transaction handle lead to > a use-after-free during module teardown? > By intentionally skipping stratix10_svc_async_done(), the transaction handle > remains linked in the service layer's actrl->trx_list hash table. Later, when > the driver is unbound, fcs_deinit() calls stratix10_svc_remove_async_client() > which frees the achan structure. If the stratix10-svc layer is unloaded, its > cleanup routine could iterate over the stale handle and dereference the > freed handler->achan pointer. will fix in v3 where when error happen, will to to the state of asyn done and unhashes the handler. > + if (ret) { > + pr_err("Failed to poll async message\n"); > + goto out; > + } > + > + priv->status = data.status; > + > + if (data.kaddr1) > + priv->resp = *((u32 *)data.kaddr1); > + else > + priv->resp = 0; > + > +out: > + stratix10_svc_async_done(priv->chan, handle); > + kfree(msg); > + > + return ret; > +} > + > +/** > + * fcs_session_open() - open an FCS crypto service session > + * @k_ctx: pointer to the kernel-side FCS command context > + * > + * Request a new session from the SDM, generate the session UUID and copy it, > + * together with the mailbox status, back to user space. > + * > + * Return: 0 on success, negative errno on failure. > + */ > +int fcs_session_open(struct fcs_cmd_context *const k_ctx) > +{ > + int ret = 0; > + > + ret = fcs_svc_send_request(FCS_DEV_CRYPTO_OPEN_SESSION, > + SVC_FCS_REQUEST_TIMEOUT_MS); > + if (ret) { > + pr_err("Failed to send the cmd=%d,ret=%d\n", > + FCS_DEV_CRYPTO_OPEN_SESSION, ret); > + return ret; > + } > + > + if (priv->status) { > + ret = -EIO; > + pr_err("Mailbox error, Failed to open session ret: %d\n", ret); > + goto copy_mbox_status; > + } > + > + uuid_gen(&priv->uuid_id); > + > + memcpy(&priv->session_id, &priv->resp, sizeof(priv->session_id)); > + > Can a user space program exhaust firmware sessions by repeatedly writing to > this interface? > It looks like priv->session_id is overwritten without first checking if a > session is already open or requesting the SDM to close it. Repeated calls to > fcs_session_open() might leave orphaned sessions in the firmware, eventually > leading to a denial of service for cryptographic operations. The SDM only allows a single crypto-service session at a time and rejects a new session id while another one is open. Therefore no orphaned sessios cannot accumulate. But there's no harm to guard on driver side, will add in v3. > + ret = copy_to_user(k_ctx->open_session.suuid, &priv->uuid_id, > + sizeof(uuid_t)) ? -EFAULT : 0; > + if (ret) { > + pr_err("Failed to copy session ID to user suuid addr: %p ret: %d\n", > + k_ctx->open_session.suuid, ret); > + } > + > +copy_mbox_status: > + if (copy_to_user(k_ctx->error_code_addr, &priv->status, > + sizeof(priv->status))) { > + pr_err("Failed to copy mail box status code to user\n"); > + /* surface the copy failure only if nothing failed earlier */ > + if (!ret) > + ret = -EFAULT; > + } > + > + return ret; > +} > + > +/** > + * fcs_session_close() - close an FCS crypto service session > + * @k_ctx: pointer to the kernel-side FCS command context > + * > + * Validate the caller-supplied session UUID, ask the SDM to close the session > + * and copy the mailbox status back to user space. > + * > + * Return: 0 on success, negative errno on failure. > + */ > +int fcs_session_close(struct fcs_cmd_context *const k_ctx) > +{ > + int ret = 0; > + struct fcs_cmd_context ctx; > + > + memcpy(&ctx, k_ctx, sizeof(struct fcs_cmd_context)); > + > + if (!uuid_equal(&priv->uuid_id, &ctx.close_session.suuid)) { > + ret = -EINVAL; > + pr_err("Session UUID Mismatch ret: %d\n", ret); > + return ret; > + } > + > + ret = fcs_svc_send_request(FCS_DEV_CRYPTO_CLOSE_SESSION, > + SVC_FCS_REQUEST_TIMEOUT_MS); > + if (ret) { > + pr_err("Failed to send the cmd=%d,ret=%d\n", > + FCS_DEV_CRYPTO_CLOSE_SESSION, ret); > + return ret; > + } > + > + memset(&priv->uuid_id, 0, sizeof(uuid_t)); > + priv->session_id = 0; > + if (priv->status) { > + ret = -EIO; > + pr_err("Mailbox error, Failed to close session ret: %d\n", ret); > + } > + > + if (copy_to_user(ctx.error_code_addr, &priv->status, > + sizeof(priv->status))) { > + pr_err("Failed to copy mail box status code to user\n"); > + /* surface the copy failure only if nothing failed earlier */ > + if (!ret) > + ret = -EFAULT; > + } > + > + return ret; > +} > + > +/** > + * fcs_get_atf_version() - return the cached Arm Trusted Firmware version > + * @version: array of three u32 entries to receive the major, minor and patch > + * version numbers > + */ > +void fcs_get_atf_version(u32 *version) > +{ > + memcpy(version, priv->atf_version, sizeof(priv->atf_version)); > +} > + > +/** > + * fcs_sdos_crypt() - perform an SDOS encrypt or decrypt operation > + * @k_ctx: pointer to the kernel-side FCS command context > + * > + * Allocate service-layer source and destination buffers, copy the input from > + * user space, drive the SDOS data command and copy the result and length back > + * to user space. The operation direction is selected by @k_ctx->sdos.op_mode. > + * > + * Return: 0 on success, negative errno on failure. > + */ > +int fcs_sdos_crypt(struct fcs_cmd_context *const k_ctx) > +{ > + void *s_buf = NULL, *d_buf = NULL; > + struct fcs_cmd_context ctx; > + u32 output_size; > + u32 dst_cap; > + u64 owner_id; > + int ret = 0; > + > + memcpy(&ctx, k_ctx, sizeof(struct fcs_cmd_context)); > + > + /* Authorize the caller against the open session before doing any work */ > + if (!uuid_equal(&priv->uuid_id, &ctx.sdos.suuid)) { > + pr_err("Session UUID mismatch\n"); > + return -EINVAL; > + } > + > + if (!ctx.sdos.dst || !ctx.sdos.dst_size) > + return -EINVAL; > + > + /* Caller-provided output buffer capacity (in/out parameter) */ > + if (copy_from_user(&dst_cap, ctx.sdos.dst_size, sizeof(dst_cap))) > + return -EFAULT; > + > + if (ctx.sdos.op_mode) { > + output_size = SDOS_ENCRYPTED_MAX_SZ; > + /* encrypt: input is header + plaintext */ > + if (ctx.sdos.src_size < SDOS_DECRYPTED_MIN_SZ || > + ctx.sdos.src_size > SDOS_DECRYPTED_MAX_SZ) { > + pr_err("Invalid SDOS src_size %u\n", ctx.sdos.src_size); > + return -EINVAL; > + } > + } else { > + output_size = SDOS_DECRYPTED_MAX_SZ; > + /* decrypt: input is header + plaintext + HMAC */ > + if (ctx.sdos.src_size < SDOS_ENCRYPTED_MIN_SZ || > + ctx.sdos.src_size > SDOS_ENCRYPTED_MAX_SZ) { > + pr_err("Invalid SDOS src_size %u\n", ctx.sdos.src_size); > + return -EINVAL; > + } > + } > + > + s_buf = stratix10_svc_allocate_memory(priv->chan, ctx.sdos.src_size); > + if (IS_ERR(s_buf)) { > + ret = -ENOMEM; > + pr_err("Failed to allocate memory for SDOS input data kernel buffer ret: %d\n", > + ret); > + return ret; > + } > + > + /* > + * The remaining k_ctx writes intentionally target priv->k_ctx (k_ctx > + * points at it): fcs_svc_send_request() reads the outgoing request from > + * priv->k_ctx, so the kernel buffers and params are staged there rather > + * than in the local ctx snapshot. dst_size must point at device-lifetime > + * storage (priv->sdos_output_size), never a caller stack variable, so > + * the staged pointer cannot dangle after this function returns. > + */ > + priv->sdos_output_size = output_size; > + k_ctx->sdos.dst_size = &priv->sdos_output_size; > + > + d_buf = stratix10_svc_allocate_memory(priv->chan, output_size); > + if (IS_ERR(d_buf)) { > + ret = -ENOMEM; > + pr_err("Failed to allocate memory for SDOS output kernel buffer ret: %d\n", ret); > + goto free_sbuf; > + } > + > + /* Copy the user space input data to the input data kernel buffer */ > + ret = copy_from_user(s_buf, ctx.sdos.src, > + ctx.sdos.src_size) ? -EFAULT : 0; > + if (ret) { > + pr_err("Failed to copy SDOS data from user to kernel buffer ret: %d\n", ret); > + goto free_dbuf; > + } > + > + /* Get Owner ID from buf */ > + memcpy(&owner_id, (u8 *)s_buf + OWNER_ID_OFFSET, OWNER_ID_SIZE); > + k_ctx->sdos.own = owner_id; > Will this properly handle owner_id endianness on big-endian hosts? > Using memcpy() to copy 8 bytes from the raw data stream directly into a u64 > will result in host-dependent byte ordering. If the parsed integer is later > sent to the SDM via a register, a big-endian system will reverse the bytes > compared to a little-endian system. agree, the memcpy() into a u64 interprets the 8 header bytes in host byte order, so the value placed in the SMC register (msg->arg[3]) would differ on a big-endian host. The owner_id is stored little-endian in the SDOS header, so I've switched to an explicit, alignment-safe accessor using get_unaligned_le64 in v3. > + k_ctx->sdos.src = s_buf; > + k_ctx->sdos.dst = d_buf; > + > + ret = fcs_svc_send_request(FCS_DEV_SDOS_DATA_EXT, > + SVC_FCS_REQUEST_TIMEOUT_MS); > + > Is a memory barrier required here before notifying the hardware? > The memory for s_buf is allocated from the service layer's genpool which is > mapped with MEMREMAP_WC (Write-Combined). Since the SMC instruction on ARM64 > acts as a context synchronization event but does not flush store buffers, the > CPU might still be holding the plaintext writes when the SDM begins its DMA, > potentially resulting in data corruption. Agreed that a barrier is needed. However, the affected memory is allocated and mapped MEMREMAP_WC by the stratix10-svc service layer, and the SMC is issued there via invoke_fn() — this path is shared by all svc clients (RSU, FPGA mgr, etc.), and the missing barrier pre-dates this series. adding the barrier before invoke_fn() in stratix10-svc.c (with an appropriate Fixes: tag) so it's reviewed on its own and can be backported independently. This FCS series doesn't introduce the issue. > + if (ret == -EINPROGRESS) { > + /* > + * SDM still owns this transaction and may still DMA into > + * d_buf. Do NOT free s_buf/d_buf or copy results: returning > + * them to the gen_pool would let the delayed firmware write > + * corrupt reallocated memory. Leak them along with the > + * abandoned transaction (bounded, exceptional stuck-SDM path). > + */ > + return -ETIMEDOUT; > + } > + > + if (ret) { > + pr_err("Failed to send the cmd=%d,ret=%d\n", FCS_DEV_SDOS_DATA_EXT, ret); > + goto free_dbuf; > + } > + if (priv->status && > + priv->status != SDOS_DECRYPTION_REPROVISION_KEY_WARN && > + priv->status != SDOS_DECRYPTION_NOT_LATEST_KEY_WARN) { > + pr_err("Failed to perform SDOS operation ret: %d Mailbox Status = %d\n", > + ret, priv->status); > + goto copy_mbox_status; > + } > + > + /* > + * priv->resp is reported by firmware; never trust it to read back > + * more than the kernel output buffer (d_buf) actually holds, > + * otherwise the copy below would leak adjacent kernel memory. > + */ > + if (priv->resp > output_size) { > + pr_err("SDOS output %u exceeds kernel buffer %u\n", > + priv->resp, output_size); > + ret = -EIO; > + goto copy_mbox_status; > + } > + > + /* Do not write past the caller-provided output buffer */ > + if (priv->resp > dst_cap) { > + pr_err("SDOS output %u exceeds caller buffer %u\n", > + priv->resp, dst_cap); > + ret = -EMSGSIZE; > + goto copy_mbox_status; > + } > + > + /* Copy the encrypted/decrypted output from kernel space to user space */ > + ret = copy_to_user(ctx.sdos.dst, d_buf, priv->resp) ? -EFAULT : 0; > + if (ret) { > + pr_err("Failed to copy encrypted output to user ret: %d\n", ret); > + goto copy_mbox_status; > + } > + > + /* Copy the encrypted output length from kernel space to user space */ > + ret = copy_to_user(ctx.sdos.dst_size, &priv->resp, > + sizeof(priv->resp)) ? -EFAULT : 0; > + if (ret) > + pr_err("Failed to copy encrypted output length to user ret: %d\n", ret); > + > +copy_mbox_status: > + if (copy_to_user(ctx.error_code_addr, &priv->status, > + sizeof(priv->status))) { > + pr_err("Failed to copy mailbox status code to user\n"); > + /* surface the copy failure only if nothing failed earlier */ > + if (!ret) > + ret = -EFAULT; > + } > +free_dbuf: > + stratix10_svc_free_memory(priv->chan, d_buf); > +free_sbuf: > + stratix10_svc_free_memory(priv->chan, s_buf); > + > + return ret; > +} > + > +/** > + * fcs_acquire_cmd_ctx() - take the FCS lock and return the command context > + * > + * Serialises access to the shared command context across concurrent callers. > + * The caller must release it with fcs_release_cmd_ctx(). > + * > + * Return: pointer to the locked FCS command context. > + */ > +struct fcs_cmd_context *fcs_acquire_cmd_ctx(void) > +{ > + if (!priv) > + return NULL; > + > + mutex_lock(&priv->lock); > + return &priv->k_ctx; > +} > + > +/** > + * fcs_release_cmd_ctx() - release the FCS command context lock > + * @k_ctx: pointer to the FCS command context previously acquired > + */ > +void fcs_release_cmd_ctx(struct fcs_cmd_context *const k_ctx) > +{ > + mutex_unlock(&priv->lock); > +} > + > +/** > + * fcs_read_version_from_atf() - query the Arm Trusted Firmware build version > + * > + * Send the ATF version command to the SDM and cache the result in @priv. > + * > + * Return: 0 on success, negative errno on failure. > + */ > +static int fcs_read_version_from_atf(void) > +{ > + int ret = 0; > + > + ret = fcs_svc_send_request(FCS_DEV_ATF_VERSION, > + SVC_FCS_REQUEST_TIMEOUT_MS); > + if (ret) { > + pr_err("Failed to send the cmd=%d,ret=%d\n", FCS_DEV_ATF_VERSION, ret); > + return ret; > + } > + > + if (priv->status) { > + ret = -EIO; > + pr_err("Mailbox error, Failed to read ATF version ret: %d\n", ret); > + } > + > + stratix10_svc_done(priv->chan); > + > + return ret; > +} > + > +/** > + * fcs_init() - allocate and initialise the FCS private state > + * @dev: pointer to fcs device > + * > + * Allocate @priv, request the service channel, register the async client, > + * and read the ATF version. > + * > + * Return: 0 on success, -EPROBE_DEFER or negative errno on failure. > + */ > +int fcs_init(struct device *dev) > +{ > + int ret; > + > + if (priv) > + return -EBUSY; /* singleton: one FCS instance only */ > + > + priv = devm_kzalloc(dev, sizeof(struct socfpga_fcs_priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + mutex_init(&priv->lock); > + > + priv->dev = dev; > + priv->client.dev = dev; > + priv->client.receive_cb = NULL; > + priv->client.priv = priv; > + > + priv->chan = stratix10_svc_request_channel_byname(&priv->client, > + SVC_CLIENT_FCS); > + if (IS_ERR(priv->chan)) { > + pr_err("couldn't get service channel %s\n", SVC_CLIENT_FCS); > + return -EPROBE_DEFER; > + } > + > Does returning an error here leave a dangling pointer in the global priv? > Since priv is allocated using devm_kzalloc(), the driver core will free the > memory upon a probe failure like -EPROBE_DEFER. However, the global priv > variable isn't set back to NULL. On a subsequent probe attempt, the initial > check (if (priv)) will see a non-NULL dangling pointer and return -EBUSY, > permanently preventing the driver from initializing. okay, make sense. Will reset the global priv to NULL if devm_kzalloc failed. will fix in v3 > + ret = stratix10_svc_add_async_client(priv->chan, true); > + if (ret) { > + pr_err("Failed to add async client\n"); > + goto free_chan; > + } > + > + init_completion(&priv->completion); > + > + fcs_read_version_from_atf(); > + > + return 0; > + > +free_chan: > + stratix10_svc_free_channel(priv->chan); > + > + return ret; > +} > + > +/** > + * fcs_deinit() - tear down the FCS private state > + * > + * Close any open session, remove the async client, free the service channel > + * and clear @priv. > + */ > +void fcs_deinit(void) > +{ > + if (priv && priv->session_id) { > + int ret = fcs_svc_send_request(FCS_DEV_CRYPTO_CLOSE_SESSION, > + SVC_FCS_REQUEST_TIMEOUT_MS); > + if (ret) > + pr_err("Failed to close FCS service session,ret=%d\n", ret); > + } > + > + if (priv) { > + stratix10_svc_remove_async_client(priv->chan); > + stratix10_svc_free_channel(priv->chan); > + } > + > + priv = NULL; > +} > + > +/** > + * fcs_cleanup() - release the FCS service channel and clear the state > + */ > +void fcs_cleanup(void) > +{ > + if (priv) > + stratix10_svc_free_channel(priv->chan); > + > + priv = NULL; > +} > diff --git a/drivers/firmware/socfpga-fcs.c b/drivers/firmware/socfpga-fcs.c > new file mode 100644 > index 000000000000..616f2735743a > --- /dev/null > +++ b/drivers/firmware/socfpga-fcs.c > @@ -0,0 +1,250 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2026, Altera Corporation > + */ > + > +#include > +#include > +#include > +#include > +#include > + > +/** > + * open_session_store() - open an FCS crypto service session > + * @dev: pointer to fcs device > + * @attr: device attribute > + * @buf: pointer to character buffer carrying the user command context > + * @buf_size: size of the buffer > + * > + * Return: @buf_size on success, negative errno on failure. > + */ > +static ssize_t open_session_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t buf_size) > +{ > + struct fcs_cmd_context *const u_ctx = *(struct fcs_cmd_context **)buf; > Is it safe to blindly cast and dereference the sysfs string buffer as a pointer? > The buf parameter provided by the sysfs store operation comes from userspace > text input. kernfs_fop_write_iter() allocates a buffer of user-provided count + 1 > bytes. If a user writes fewer than 8 bytes, this pointer dereference will read > past the allocated heap memory, triggering a KASAN out-of-bounds read. In > addition, expecting raw binary pointers over sysfs breaks compatibility on > 32-bit user space systems. > This pattern appears to be repeated in close_session_store() and sdos_store() > as well. since this is violate sysfs and the compatibility issue, will use ioctl to store the data from userspace in v3. > + struct fcs_cmd_context *k_ctx; > + int ret; > + > + k_ctx = fcs_acquire_cmd_ctx(); > + if (!k_ctx) { > + dev_err(dev, "Failed get context. Context is in use\n"); > + return -EBUSY; > + } > + > + if (copy_from_user(k_ctx, u_ctx, sizeof(*k_ctx))) { > + dev_err(dev, "Failed to copy context from user space\n"); > + ret = -EFAULT; > + goto out; > + } > + > + ret = fcs_session_open(k_ctx); > + if (ret) { > + dev_err(dev, "Failed to open session\n"); > + goto out; > + } > + > + ret = buf_size; > +out: > + fcs_release_cmd_ctx(k_ctx); > + > + return ret; > +} > + > +/** > + * close_session_store() - close an FCS crypto service session > + * @dev: pointer to fcs device > + * @attr: device attribute > + * @buf: pointer to character buffer carrying the user command context > + * @buf_size: size of the buffer > + * > + * Return: @buf_size on success, negative errno on failure. > + */ > +static ssize_t close_session_store(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t buf_size) > +{ > + struct fcs_cmd_context *const u_ctx = *(struct fcs_cmd_context **)buf; > + struct fcs_cmd_context *k_ctx; > + int ret; > + > + k_ctx = fcs_acquire_cmd_ctx(); > + if (!k_ctx) { > + dev_err(dev, "Failed get context. Context is in use\n"); > + return -EBUSY; > + } > + > + if (copy_from_user(k_ctx, u_ctx, sizeof(*k_ctx))) { > + dev_err(dev, "Failed to copy context from user space\n"); > + ret = -EFAULT; > + goto out; > + } > + > + ret = fcs_session_close(k_ctx); > + if (ret) { > + dev_err(dev, "Failed to close session\n"); > + goto out; > + } > + > + ret = buf_size; > +out: > + fcs_release_cmd_ctx(k_ctx); > + > + return ret; > +} > + > +/** > + * atf_version_show() - report the Arm Trusted Firmware build version > + * @dev: pointer to fcs device > + * @attr: device attribute > + * @buf: pointer to character buffer to receive the version string > + * > + * Return: number of bytes written to @buf. > + */ > +static ssize_t atf_version_show(struct device *dev, > + struct device_attribute *attr, char *buf) > +{ > + int version[3]; > + > + fcs_get_atf_version(version); > + return sysfs_emit(buf, "%u.%u.%u\n", version[0], version[1], version[2]); > +} > + > +/** > + * sdos_store() - perform an SDOS encrypt/decrypt operation > + * @dev: pointer to fcs device > + * @attr: device attribute > + * @buf: pointer to character buffer carrying the user command context > + * @buf_size: size of the buffer > + * > + * Return: @buf_size on success, negative errno on failure. > + */ > +static ssize_t sdos_store(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t buf_size) > +{ > + struct fcs_cmd_context *const u_ctx = *(struct fcs_cmd_context **)buf; > + struct fcs_cmd_context *k_ctx; > + int ret; > + > + k_ctx = fcs_acquire_cmd_ctx(); > + if (!k_ctx) { > + dev_err(dev, "Failed get context. Context is in use\n"); > + return -EBUSY; > + } > + > + if (copy_from_user(k_ctx, u_ctx, sizeof(*k_ctx))) { > + dev_err(dev, "Failed to copy context from user space\n"); > + ret = -EFAULT; > + goto out; > + } > + > + ret = fcs_sdos_crypt(k_ctx); > + if (ret) { > + dev_err(dev, "Failed to perform SDOS operation\n"); > + goto out; > + } > + > + ret = buf_size; > +out: > + fcs_release_cmd_ctx(k_ctx); > + > + return ret; > +} > + > +static DEVICE_ATTR_WO(open_session); > +static DEVICE_ATTR_WO(close_session); > +static DEVICE_ATTR_RO(atf_version); > +static DEVICE_ATTR_WO(sdos); > + > +static struct attribute *fcs_attrs[] = { > + &dev_attr_open_session.attr, > + &dev_attr_close_session.attr, > + &dev_attr_atf_version.attr, > + &dev_attr_sdos.attr, > + NULL > +}; > + > +static struct attribute_group fcs_group = { > + .attrs = fcs_attrs, > +}; > + > +static const struct attribute_group *fcs_groups[] = { > + &fcs_group, > + NULL, > +}; > + > +/** > + * fcs_driver_probe() - probe the FCS platform device > + * @pdev: pointer to the FCS platform device > + * > + * Initialise the FCS state. The sysfs attribute groups are published > + * automatically by the driver core via fcs_driver.driver.dev_groups. > + * > + * Return: 0 on success, negative errno on failure. > + */ > +static int fcs_driver_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + int ret; > + > + ret = fcs_init(dev); > + if (ret) { > + dev_err(dev, "Failed to initialize FCS\n"); > + return ret; > + } > + > + return 0; > +} > + > +/** > + * fcs_driver_remove() - remove the FCS platform device > + * @pdev: pointer to the FCS platform device > + * > + * Tear down the FCS state. The sysfs attribute groups are removed > + * automatically by the driver core. > + */ > +static void fcs_driver_remove(struct platform_device *pdev) > +{ > + fcs_deinit(); > +} > + > +static struct platform_driver fcs_driver = { > + .probe = fcs_driver_probe, > + .remove = fcs_driver_remove, > + .driver = { > + .name = "stratix10-fcs", > + .dev_groups = fcs_groups, > + }, > +}; > + > +/** > + * socfpga_fcs_init() - register the FCS platform driver > + * > + * Return: 0 on success, negative errno on failure. > + */ > +static int __init socfpga_fcs_init(void) > +{ > + int ret; > + > + ret = platform_driver_register(&fcs_driver); > + if (ret) > + pr_err("Failed to register platform driver: %d\n", ret); > + > + return ret; > +} > + > +/** > + * socfpga_fcs_exit() - unregister the FCS platform driver > + */ > +static void __exit socfpga_fcs_exit(void) > +{ > + platform_driver_unregister(&fcs_driver); > +} > + > +module_init(socfpga_fcs_init); > +module_exit(socfpga_fcs_exit); > + > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("Altera SoCFPGA FCS SDOS encrypt/decrypt driver"); > +MODULE_AUTHOR("Altera Corporation"); > +MODULE_ALIAS("platform:stratix10-fcs"); > diff --git a/include/linux/firmware/intel/socfpga-fcs.h b/include/linux/firmware/intel/socfpga-fcs.h > new file mode 100644 > index 000000000000..3203be4f0f90 > --- /dev/null > +++ b/include/linux/firmware/intel/socfpga-fcs.h > @@ -0,0 +1,133 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * Copyright (C) 2026 Altera Corporation > + * > + * SDOS-only subset of the SoCFPGA FCS (FPGA Crypto Service) interface, > + * shared between the driver front-end (socfpga-fcs.c) and the command > + * engine (socfpga-fcs-core.c). > + */ > +#ifndef SOCFPGA_FCS_H > +#define SOCFPGA_FCS_H > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define SDOS_HEADER_SZ 40 > +#define SDOS_HMAC_SZ 48 > +#define SDOS_PLAINDATA_MIN_SZ 32 > +#define SDOS_PLAINDATA_MAX_SZ 32672 > +#define SDOS_DECRYPTED_MIN_SZ (SDOS_PLAINDATA_MIN_SZ + SDOS_HEADER_SZ) > +#define SDOS_DECRYPTED_MAX_SZ (SDOS_PLAINDATA_MAX_SZ + SDOS_HEADER_SZ) > +#define SDOS_ENCRYPTED_MIN_SZ (SDOS_PLAINDATA_MIN_SZ + SDOS_HEADER_SZ + SDOS_HMAC_SZ) > +#define SDOS_ENCRYPTED_MAX_SZ (SDOS_PLAINDATA_MAX_SZ + SDOS_HEADER_SZ + SDOS_HMAC_SZ) > + > +#pragma pack(push, 1) > +struct fcs_cmd_context { > + /* Error status variable address */ > + int *error_code_addr; > + union { > + struct { > + uuid_t *suuid; > + unsigned int *suuid_len; > + } open_session; > + > + struct { > + uuid_t suuid; > + } close_session; > + > + struct { > + uuid_t suuid; > + u32 context_id; > + char *rng; > + u32 rng_len; > + } rng; > + > + struct { > + uuid_t suuid; > + u32 context_id; > + u32 op_mode; > + char *src; > + u32 src_size; > + char *dst; > + u32 *dst_size; > + u16 id; > + u64 own; > + int pad; > + } sdos; > + }; > +}; > + > +#pragma pack(pop) > + > +/** > + * Private driver state for the SoCFPGA FCS that holds the SDM/ATF service > + * channel, the shared command context and the lock that guards it, and the > + * latest mailbox status/response. > + */ > +struct socfpga_fcs_priv { > + /* Communication channel */ > + struct stratix10_svc_chan *chan; > + struct fcs_cmd_context k_ctx; > + struct stratix10_svc_client client; > + struct completion completion; > + /* > + * Serializes FCS command submission: guards the shared k_ctx and the > + * single in-flight mailbox transaction (completion/status/resp) so only > + * one SDM request is outstanding at a time. This is the lock taken by > + * fcs_acquire_cmd_ctx() and dropped by fcs_release_cmd_ctx(). > + */ > + struct mutex lock; > + int status; > + u32 resp; > + u32 session_id; > + uuid_t uuid_id; > + struct device *dev; > + u32 atf_version[3]; > + /* > + * Backing store for the SDOS output-buffer capacity. The outgoing > + * request stages k_ctx.sdos.dst_size to point here (device-lifetime) > + * instead of at a caller stack variable, so the pointer never dangles. > + */ > + u32 sdos_output_size; > +}; > + > +enum fcs_command_code { > + FCS_DEV_COMMAND_NONE = 0, > + FCS_DEV_CRYPTO_OPEN_SESSION, > + FCS_DEV_CRYPTO_CLOSE_SESSION, > + FCS_DEV_SDOS_DATA_EXT, > + FCS_DEV_ATF_VERSION, > +}; > + > +/* Take the FCS lock and return the shared command context. */ > +struct fcs_cmd_context *fcs_acquire_cmd_ctx(void); > + > +/* Release the FCS lock previously taken by fcs_acquire_cmd_ctx(). */ > +void fcs_release_cmd_ctx(struct fcs_cmd_context *const k_ctx); > + > +/* Allocate the FCS state and set up the service channel; read ATF version. */ > +int fcs_init(struct device *dev); > + > +/* Close any open session and release the service channel. */ > +void fcs_deinit(void); > + > +/* Release the service channel and clear the FCS state. */ > +void fcs_cleanup(void); > + > +/* Request the SDM to open a crypto service session. */ > +int fcs_session_open(struct fcs_cmd_context *const k_ctx); > + > +/* Request the SDM to close a previously opened session. */ > +int fcs_session_close(struct fcs_cmd_context *const k_ctx); > + > +/* Return the cached Arm Trusted Firmware build version. */ > +void fcs_get_atf_version(u32 *version); > + > +/* Perform an SDOS (Secure Data Object Service) encrypt/decrypt operation. */ > +int fcs_sdos_crypt(struct fcs_cmd_context *const k_ctx); > + > +#endif /* SOCFPGA_FCS_H */