From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mgamail.intel.com (mgamail.intel.com [198.175.65.19]) (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 53E322139C9; Thu, 17 Jul 2025 18:34:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=198.175.65.19 ARC-Seal:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752777262; cv=fail; b=QF7mxX0OoF3/XGdKvdh1bfaxX55b68dGLon81M0Mb3fIDce03OllD3JER+T3YZs1zCoW61VjR3fvGagmu+C6kB9hxViLR8zyxzn6++CA8hwUtuQ7UHoBuNQtjxHBDzcANWgYK7RlwOK1UbrJPZBzDZW2q/SH0ISe4jgyjPBkbsI= ARC-Message-Signature:i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1752777262; c=relaxed/simple; bh=oWv/puRk/razr/eLj8/IPv/kRsiMlPvZikayf8gMH90=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: Content-Type:MIME-Version; b=CqDulO6Per43RGXlfTm5LrRTL4r5xoDAbMM1JQP2fzRgBHbmE+X+B/Q/p8vP2vRjJEAuDGs+1ryuUthLAbu6xwEktrH+/3sYtBYQtwVQ2FKwb+9XzgRTvu7DtATSJOEDUBVSHZyyBKbtRg4oYvHyXZGgpVeX+NHyWygxggu1iRI= ARC-Authentication-Results:i=2; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=DFXWnea1; arc=fail smtp.client-ip=198.175.65.19 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="DFXWnea1" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1752777259; x=1784313259; h=from:to:cc:subject:date:message-id:in-reply-to: references:content-transfer-encoding:mime-version; bh=oWv/puRk/razr/eLj8/IPv/kRsiMlPvZikayf8gMH90=; b=DFXWnea1uBDtdeaX0kWboMNMuCmmKl/+MheQC0bzksjmR3+/jxdSoBg3 KS5XG1nDXiLm4fhRRDeWrRkwRSq7XXlown89NXp1O9w6rzVQsVKHHc88y PxyCkQ60W94l2Uy4L9Rzp2qAtITQa7mqYyqle0Sy/y6cwn4kiiPJXvQii x2LZ04lADKQbS5JcSc3oZ1YNCU870dWc0pSazPcqcUqnLW6YRVowhzxqH eyjZo61mYSCu6IdR4iu+e60rN/0nYAY5EuAxvwtQlmTH5LabaTjCLaMJ6 vLMjv68+2WLEuiMfkQbu9cSbHzMoRlavhKhfqBZPKcyhMSHRzjwojHFdT Q==; X-CSE-ConnectionGUID: 0K27JUGMR+iQoBWllU0q0g== X-CSE-MsgGUID: tnjyY6p5SSCJIjnDoLm90g== X-IronPort-AV: E=McAfee;i="6800,10657,11495"; a="54924096" X-IronPort-AV: E=Sophos;i="6.16,319,1744095600"; d="scan'208";a="54924096" Received: from orviesa006.jf.intel.com ([10.64.159.146]) by orvoesa111.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Jul 2025 11:34:16 -0700 X-CSE-ConnectionGUID: hmV9fQ1bQ8ePstLzXmjFtw== X-CSE-MsgGUID: zym7m0IuSEOPNcvoezLn6g== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.16,319,1744095600"; d="scan'208";a="157254642" Received: from orsmsx902.amr.corp.intel.com ([10.22.229.24]) by orviesa006.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 17 Jul 2025 11:34:16 -0700 Received: from ORSMSX903.amr.corp.intel.com (10.22.229.25) by ORSMSX902.amr.corp.intel.com (10.22.229.24) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.26; Thu, 17 Jul 2025 11:34:15 -0700 Received: from ORSEDG901.ED.cps.intel.com (10.7.248.11) by ORSMSX903.amr.corp.intel.com (10.22.229.25) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.26 via Frontend Transport; Thu, 17 Jul 2025 11:34:15 -0700 Received: from NAM12-DM6-obe.outbound.protection.outlook.com (40.107.243.52) by edgegateway.intel.com (134.134.137.111) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.26; Thu, 17 Jul 2025 11:34:13 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=IoaPDGl5I1HJxEbWEMYNGWE1rqObnXo83uZh0AB7gYETkSWvMmPGmTAfj5KemCzXdPuq/lEKj1qtulIuK7vbqkUkkT7ES3w8QNvEV7d8dbVoygK6sppCVfbPdx0VbBoa1FaBwcU2c7XgHP46N0FvaV9v/n8yLny4pW2RK37d7MYLgWSQxQSRqZ0g9kJ0RtNMuUxGTllpadIjVNxaq/FltVeDQv61N1ieuefhc0wc/1yIk/2dRUHOw+F7KjVW3jWFQq8zZG808FOmat7z9ehfmj3EtNl0N05fcdGe16EmKcqjx1O0D/vzBENSv9Gt0fZrHT1igSvR0zsg9KY9BEOcVw== 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=4kjSXqWNTtWqnxY47CiaHjaa57S9oudA1scXnqhSbgQ=; b=kQ/WTKC6mLUFSQIueIWZVTLSQQcSIklIO80Sgpdo3+Aqjj0MIXakQChYkcb3Ed4HbZLH2LVt9o8YLRBVuyqep7wDo4dyl0xTLvChtLH2tC3APNX0ZQMQLLeacxNgtmdnklJj3m4y4Nkhr8RIi8yjm+Js7l7bAR5Kq4ZEwi0SeO85FM9ekCrUBFNKPSmg+pQ4ITvvT59BYynIkwIM4mwvKxu85a9vwXQHPW1YIVnFza8+KdCyeILkgPVE6ZzGQyRZZO5U57AVOZsgAjXgO21tR27hDheeiFwrk7Mc2A+FP7TmnOhOXzWNAiRsjmtW+KoKAHkMUtkTIInFCPTAn58xYQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=intel.com; dmarc=pass action=none header.from=intel.com; dkim=pass header.d=intel.com; arc=none Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=intel.com; Received: from PH8PR11MB8107.namprd11.prod.outlook.com (2603:10b6:510:256::6) by SN7PR11MB7066.namprd11.prod.outlook.com (2603:10b6:806:299::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8901.35; Thu, 17 Jul 2025 18:34:07 +0000 Received: from PH8PR11MB8107.namprd11.prod.outlook.com ([fe80::6b05:74cf:a304:ecd8]) by PH8PR11MB8107.namprd11.prod.outlook.com ([fe80::6b05:74cf:a304:ecd8%6]) with mapi id 15.20.8922.028; Thu, 17 Jul 2025 18:34:06 +0000 From: Dan Williams To: , CC: , , , , Samuel Ortiz , Xu Yilun Subject: [PATCH v4 05/10] samples/devsec: Introduce a PCI device-security bus + endpoint sample Date: Thu, 17 Jul 2025 11:33:53 -0700 Message-ID: <20250717183358.1332417-6-dan.j.williams@intel.com> X-Mailer: git-send-email 2.50.1 In-Reply-To: <20250717183358.1332417-1-dan.j.williams@intel.com> References: <20250717183358.1332417-1-dan.j.williams@intel.com> Content-Transfer-Encoding: 8bit Content-Type: text/plain X-ClientProxiedBy: BYAPR21CA0030.namprd21.prod.outlook.com (2603:10b6:a03:114::40) To PH8PR11MB8107.namprd11.prod.outlook.com (2603:10b6:510:256::6) Precedence: bulk X-Mailing-List: linux-pci@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: PH8PR11MB8107:EE_|SN7PR11MB7066:EE_ X-MS-Office365-Filtering-Correlation-Id: 30aa76c6-5db6-45c9-2220-08ddc560834e X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|376014|366016|1800799024; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?45h7GHkB4vawisn8Ydi3HfuZSELrWj49A3pUInXDTpglM6Gkn2lN4yLKxuG+?= =?us-ascii?Q?tgSHyJazIOsogEJdwFhLtqEWLlTsAGustqqL9sPjyM9ZXXxeT2m+EAhbGreL?= =?us-ascii?Q?wLF29/lUaprzJcDuuPOlJLiC/grn8EN66vy4DB5mUdLr09KrfzUzz43bghJo?= =?us-ascii?Q?or7uZYoX2p+DzCSahxsGwDmJBO/UL37qhB/o2i3ytJGKa63wb8sBlXC6zlcV?= =?us-ascii?Q?HDAP84zDs9q2vloQtOQqw+l8VxNFVA8iiCoatDxPTQbbR+obzvdJwSj8C7Iy?= =?us-ascii?Q?Buur8ReveUKHz/gsdFL7PXB98ZVdHFY0mBzQU2pivVfDTe91MCluR1dns4UG?= =?us-ascii?Q?t7EGZgCU+PnACxFWkz+E4oVmOgwc8cjzQc2tM46RE/ZgQuM5eAQvK1sbOOD7?= =?us-ascii?Q?wYHsuqY3ZRbqSrg1YuX7esa5JJNKSxSCnbSw3apGMuoDACLtu1l4bP3F/FTg?= =?us-ascii?Q?gFWdTU4Z7HDdrd/UI0A/ga4lvzZDpWQuyjj7FE2gdy/EX/LdYDKnAu8o4H9g?= =?us-ascii?Q?lqBoQfjC4n6EDPB3GXIHbG6ZFeavL0kcCgg653guQTewXTMjb1uBXj0FNFlP?= =?us-ascii?Q?WTZlUsvSNOGwgdOzz3coL8rFJ5OkHiK6TXsEQgHbjN1TMAXkgBsrxxlEtbkB?= =?us-ascii?Q?nxztrnPhkC3KSgBMGHiSLbAXNjfu0zVdFgPjpKAkBLoj7+9v38Mw/749be2E?= =?us-ascii?Q?b3pPrmr8CZOB95IUI2SJR7QQ1RTgIMqrQk15EEvwzkxcmLHNAsT1x87OoBdf?= =?us-ascii?Q?gK7ZiwdLwNdWY74mUFbcubfPJIdy3oN21UwN9XK5C2RQN5P/Yql027QwTaCx?= =?us-ascii?Q?w0T60E9DLXyNkM0al5tb4dGFBjulMWqV485RjKz8RxOUIVMEUVoY35Qx315e?= =?us-ascii?Q?noBD/5fwlDMR0/SgqWIvu2Gy04CwnNA0onRfEY/yeUB/hv+3vJVj65gzc2uE?= =?us-ascii?Q?O/qlApmlfMfQBXB6eanH0nmFFJKEjFoqmssarYKvwyh6FniTtA8C3Ae2A9L3?= =?us-ascii?Q?cSSbue06+shM1skW01zyEhkP8o/hUGu98ETlrmv/c6e3XyYJbAk0EsW5gcRq?= =?us-ascii?Q?kezpxSXfrfU8szgClNZOC0LXRvOql9llwmwzYWZ0CoL+3zgsvu2y8ixSqpIL?= =?us-ascii?Q?J1w9jsg7LW6UGXCI6q8ElHVfuSJayS+LJC0CSm0tpKpwS6NLORi2lpVaUuhg?= =?us-ascii?Q?3jBXzpDvxXPqL8wrXGIliHk6KTAyPgSh3TERKCx82DthfzvANiLl4qWyiw1g?= =?us-ascii?Q?ja4qiZ6hvvSjjRQjUI0weEPY3wLuVb3ZdPjyxFoRYO0ca6ACQ97e2VeSYi+T?= =?us-ascii?Q?4JjzC7TA1mlnCfqCzpQtE4/7pFkhYvftapbQqpMMwNLFnXhkPcJqQ89YC4gV?= =?us-ascii?Q?MwkHKYZHjWvRKpJ1+zXLPu648ZpcgM0QbHcuCj0+ZJgrsLC70sA2j1qN0rZG?= =?us-ascii?Q?CPoaZWJT8Ok=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:PH8PR11MB8107.namprd11.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(376014)(366016)(1800799024);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?ayNkD0Arnd8VrUIXpqr7mm+3xvhXp0OdmF5gcYBjOkb6UUjLuBO4XSYgJMYU?= =?us-ascii?Q?N6dXBpx9v8nX1gFKEzcH6EvKwIG4HSeeXlqsqesNaKOxfsoRBwXdudkk2Zkg?= =?us-ascii?Q?s3MT6PW1LhRX+ucbIn2RgQcFp+cp/OYdYvyFUwokWVBrl70J+HXZt/8eBufB?= =?us-ascii?Q?YazNI8RNq1Lx0FQvXIhmuDmgOcS5KA2hsiof3z4N+9wM4tfoz6PQfWiRzpY7?= =?us-ascii?Q?890dgVSb+UhPlIwc3nMN16r3naNhowXNdN00weJXx/OYWevz+XSJVOTV44mC?= =?us-ascii?Q?cycQaFCA3sKblu5E4fHnI9TbSr3crwNCHnZbgy8EaGQ62Cg6zKSPQZXqUHAH?= =?us-ascii?Q?uKn0AR3DclvjN8fzt4JCXUn3aWSiDwG4h1VLzfKT83fSuw7FtdV2QQEtWgl9?= =?us-ascii?Q?ax/EYkvHoFaSVIXNLXs5rwKeaHhz/+unQK5bSuK5tPbA355Rsyu3uFv6Ug2W?= =?us-ascii?Q?CeRzcL8FfDDFmLYiSZgtejQVYN50Ww7rC8BxsiQm6Ah6achwQs5gZijX1eTT?= =?us-ascii?Q?4jk4PNDV0U+EZtXABUxdSAuFr1JFmuJUJvl0QTA76J/3oC6f8zae4wrriT30?= =?us-ascii?Q?NciyQmbzAZuQ6YAEW9c+vEyveMorsV6TKN6lBx6XUxqjBq37tzR2fLd87NO0?= =?us-ascii?Q?gHtAiKZ52AI+eSSn6ObK5P8ofKfb9pCuPNjUR/mJEUJrWEZ2FOLp5ghwHdx1?= =?us-ascii?Q?SGeO27+1RiqVsQ900qZkHa5ejEyw6CZCHtv7R4BFkTVBuagfxEYNgU8KhZ1u?= =?us-ascii?Q?NLTJqbJ2TRQLEnbwRfEwbhNCCHE31BJXE7B9ppuaMa3Q4VNzEm31lK8wGmDM?= =?us-ascii?Q?/z6UEDA5QY4vDGRJWBX/WiLL+bEdr0WvqbhN7eXldPwtmMmD3eIfN+XkG9fz?= =?us-ascii?Q?52n5GctMbXcTLSXUueyqI1sUG8xPnZPwqm1XRLXnolKpURgF9RkSF/Qa5dGN?= =?us-ascii?Q?McaaFXbYHCfKv16OowmC3Jh9Du7kt6cVD8wvqCPTi9PmO8t/4tM9v0sEe6kn?= =?us-ascii?Q?77RyCmMMpLMaIFf0Zmr8K4W3z1BG5K4Fop+2xNhvBMt23NuAKuRcVh/XWWGv?= =?us-ascii?Q?ZH8grDnssvvI7CphvFx18Nm44xBj3Wsl7g1gJDSD4mruuhdBg9uQQRCGhyRZ?= =?us-ascii?Q?k0fx+hngy68kFnPpljFzM2jhY+w1lE3rshsriEoXLFWiNvyJurKTWJ4bSlBh?= =?us-ascii?Q?kdHk6JzP9A/w02eutv0NJcikV3lQoIAE4RCXEl8YchPsFfXINDWtbLDboE0o?= =?us-ascii?Q?w6cB3OUZT7LWHhiSnyWZGJ9REGyf1zS7XEAb7bEP5ea9/GI7yAKt23HFrcwm?= =?us-ascii?Q?yFusc/ipE8T+x4HYxG8jsU/CGBexkWoEw/RPrMeHAJhuH8bbdOzFor80KHua?= =?us-ascii?Q?zQguuzuDzHgN1ulqPvzhVPE/kUPPSpxxDyExYdvcqYJUSz6SFePUxz4SjJYl?= =?us-ascii?Q?X+b+6fa8hVYVRMMT/kICVl75qVTAAKyETQK9SILNylMpDs1SZa6l1l98t28E?= =?us-ascii?Q?oqJH7Jt/kCK6NKHOg0JLH3u9Z5EGznsEuvJpMeF/ERFOA/4olS05EwZRx6Ek?= =?us-ascii?Q?axPImuLYgktTwFnS+vpT6Jff9M4gm0sIp6Hgu9tgvWryIVpOoyv4Invo1XiL?= =?us-ascii?Q?9Q=3D=3D?= X-MS-Exchange-CrossTenant-Network-Message-Id: 30aa76c6-5db6-45c9-2220-08ddc560834e X-MS-Exchange-CrossTenant-AuthSource: PH8PR11MB8107.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Jul 2025 18:34:06.9216 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 46c98d88-e344-4ed4-8496-4ed7712e255d X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: Ubfc7xZ1mFJzzVFyZ5qko0IbnlRngR8iO02ANKbV/fXmNON0nKhh9MhQEgZiJC+An5XTW5iuzVGXLqTzYUGRLTHXwl4jJWKGvU6M9P3jkZI= X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN7PR11MB7066 X-OriginatorOrg: intel.com Establish just enough emulated PCI infrastructure to register a sample TSM (platform security manager) driver and have it discover an IDE + TEE (link encryption + device-interface security protocol (TDISP)) capable device. Use the existing a CONFIG_PCI_BRIDGE_EMUL to emulate an IDE capable root port, and open code the emulation of an endpoint device via simulated configuration cycle responses. The devsec_tsm driver responds to the PCI core TSM operations as if it successfully exercised the given interface security protocol message. The devsec_bus and devsec_tsm drivers can be loaded in either order to reflect cases like SEV-TIO where the TSM is PCI-device firmware, and cases like TDX Connect where the TSM is a software agent running on the host CPU. Follow-on patches add common code for TSM managed IDE establishment. For now, just successfully complete setup and teardown of the DSM (device security manager) context as a building block for management of TDI (trusted device interface) instances. # modprobe devsec_bus devsec_bus devsec_bus: PCI host bridge to bus 10000:00 pci_bus 10000:00: root bus resource [bus 00-01] pci_bus 10000:00: root bus resource [mem 0xf000000000-0xffffffffff 64bit] pci 10000:00:00.0: [8086:7075] type 01 class 0x060400 PCIe Root Port pci 10000:00:00.0: PCI bridge to [bus 00] pci 10000:00:00.0: bridge window [io 0x0000-0x0fff] pci 10000:00:00.0: bridge window [mem 0x00000000-0x000fffff] pci 10000:00:00.0: bridge window [mem 0x00000000-0x000fffff 64bit pref] pci 10000:00:00.0: bridge configuration invalid ([bus 00-00]), reconfiguring pci 10000:01:00.0: [8086:ffff] type 00 class 0x000000 PCIe Endpoint pci 10000:01:00.0: BAR 0 [mem 0xf000000000-0xf0001fffff 64bit pref] pci_doe_abort: pci 10000:01:00.0: DOE: [100] Issuing Abort pci_doe_cache_protocols: pci 10000:01:00.0: DOE: [100] Found protocol 0 vid: 1 prot: 1 pci 10000:01:00.0: disabling ASPM on pre-1.1 PCIe device. You can enable it with 'pcie_aspm=force' pci 10000:00:00.0: PCI bridge to [bus 01] pci_bus 10000:01: busn_res: [bus 01] end is updated to 01 # modprobe devsec_tsm devsec_tsm_pci_probe: pci 10000:01:00.0: devsec: tsm enabled __pci_tsm_init: pci 10000:01:00.0: TSM: Device security capabilities detected ( ide tee ), TSM attach Cc: Bjorn Helgaas Cc: Lukas Wunner Cc: Samuel Ortiz Cc: Alexey Kardashevskiy Cc: Xu Yilun Signed-off-by: Dan Williams --- MAINTAINERS | 1 + samples/Kconfig | 16 + samples/Makefile | 1 + samples/devsec/Makefile | 10 + samples/devsec/bus.c | 708 ++++++++++++++++++++++++++++++++++++++++ samples/devsec/common.c | 26 ++ samples/devsec/devsec.h | 40 +++ samples/devsec/tsm.c | 173 ++++++++++ 8 files changed, 975 insertions(+) create mode 100644 samples/devsec/Makefile create mode 100644 samples/devsec/bus.c create mode 100644 samples/devsec/common.c create mode 100644 samples/devsec/devsec.h create mode 100644 samples/devsec/tsm.c diff --git a/MAINTAINERS b/MAINTAINERS index 8cb7ee9270d2..97494511da0c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -25251,6 +25251,7 @@ F: Documentation/driver-api/pci/tsm.rst F: drivers/pci/tsm.c F: drivers/virt/coco/guest/ F: include/linux/*tsm*.h +F: samples/devsec/ F: samples/tsm-mr/ TRUSTED SERVICES TEE DRIVER diff --git a/samples/Kconfig b/samples/Kconfig index ffef99950206..8441593fb654 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -325,6 +325,22 @@ source "samples/rust/Kconfig" source "samples/damon/Kconfig" +config SAMPLE_DEVSEC + tristate "Build a sample TEE Security Manager with an emulated PCI endpoint" + depends on PCI + depends on VIRT_DRIVERS + depends on PCI_DOMAINS_GENERIC || X86 + select PCI_BRIDGE_EMUL + select PCI_TSM + select TSM + help + Build a sample platform TEE Security Manager (TSM) driver with a + corresponding emulated PCIe topology. The resulting sample modules, + devsec_bus and devsec_tsm, exercise device-security enumeration, PCI + subsystem use ABIs, device security flows. For example, exercise IDE + (link encryption) establishment and TDISP state transitions via a + Device Security Manager (DSM). + endif # SAMPLES config HAVE_SAMPLE_FTRACE_DIRECT diff --git a/samples/Makefile b/samples/Makefile index 07641e177bd8..59b510ace9b2 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -45,3 +45,4 @@ obj-$(CONFIG_SAMPLE_DAMON_PRCL) += damon/ obj-$(CONFIG_SAMPLE_DAMON_MTIER) += damon/ obj-$(CONFIG_SAMPLE_HUNG_TASK) += hung_task/ obj-$(CONFIG_SAMPLE_TSM_MR) += tsm-mr/ +obj-y += devsec/ diff --git a/samples/devsec/Makefile b/samples/devsec/Makefile new file mode 100644 index 000000000000..c8cb5c0cceb8 --- /dev/null +++ b/samples/devsec/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SAMPLE_DEVSEC) += devsec_common.o +devsec_common-y := common.o + +obj-$(CONFIG_SAMPLE_DEVSEC) += devsec_bus.o +devsec_bus-y := bus.o + +obj-$(CONFIG_SAMPLE_DEVSEC) += devsec_tsm.o +devsec_tsm-y := tsm.o diff --git a/samples/devsec/bus.c b/samples/devsec/bus.c new file mode 100644 index 000000000000..675e185fcf79 --- /dev/null +++ b/samples/devsec/bus.c @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright(c) 2024 - 2025 Intel Corporation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../drivers/pci/pci-bridge-emul.h" +#include "devsec.h" + +#define NR_DEVSEC_BUSES 1 +#define NR_DEVSEC_ROOT_PORTS 1 +#define NR_PORT_STREAMS 1 +#define NR_ADDR_ASSOC 1 +#define NR_DEVSEC_DEVS 1 + +struct devsec { + struct pci_host_bridge hb; + struct devsec_sysdata sysdata; + struct gen_pool *iomem_pool; + struct resource resource[2]; + struct pci_bus *bus; + struct device *dev; + struct devsec_port { + union { + struct devsec_ide { + u32 cap; + u32 ctl; + struct devsec_stream { + u32 cap; + u32 ctl; + u32 status; + u32 rid1; + u32 rid2; + struct devsec_addr_assoc { + u32 assoc1; + u32 assoc2; + u32 assoc3; + } assoc[NR_ADDR_ASSOC]; + } stream[NR_PORT_STREAMS]; + } ide __packed; + char ide_regs[sizeof(struct devsec_ide)]; + }; + struct pci_bridge_emul bridge; + } *devsec_ports[NR_DEVSEC_ROOT_PORTS]; + struct devsec_dev { + struct devsec *devsec; + struct range mmio_range; + u8 __cfg[SZ_4K]; + struct devsec_dev_doe { + int cap; + u32 req[SZ_4K / sizeof(u32)]; + u32 rsp[SZ_4K / sizeof(u32)]; + int write, read, read_ttl; + } doe; + u16 ide_pos; + union { + struct devsec_ide ide __packed; + char ide_regs[sizeof(struct devsec_ide)]; + }; + } *devsec_devs[NR_DEVSEC_DEVS]; +}; + +#define devsec_base(x) ((void __force __iomem *) &(x)->__cfg[0]) + +static struct devsec *bus_to_devsec(struct pci_bus *bus) +{ + return container_of(bus->sysdata, struct devsec, sysdata); +} + +static int devsec_dev_config_read(struct devsec *devsec, struct pci_bus *bus, + unsigned int devfn, int pos, int size, + u32 *val) +{ + struct devsec_dev *devsec_dev; + struct devsec_dev_doe *doe; + void __iomem *base; + + if (PCI_FUNC(devfn) != 0 || + PCI_SLOT(devfn) >= ARRAY_SIZE(devsec->devsec_devs)) + return PCIBIOS_DEVICE_NOT_FOUND; + + devsec_dev = devsec->devsec_devs[PCI_SLOT(devfn)]; + base = devsec_base(devsec_dev); + doe = &devsec_dev->doe; + + if (pos == doe->cap + PCI_DOE_READ) { + if (doe->read_ttl > 0) { + *val = doe->rsp[doe->read]; + dev_dbg(&bus->dev, "devfn: %#x doe read[%d]\n", devfn, + doe->read); + } else { + *val = 0; + dev_dbg(&bus->dev, "devfn: %#x doe no data\n", devfn); + } + return PCIBIOS_SUCCESSFUL; + } else if (pos == doe->cap + PCI_DOE_STATUS) { + if (doe->read_ttl > 0) { + *val = PCI_DOE_STATUS_DATA_OBJECT_READY; + dev_dbg(&bus->dev, "devfn: %#x object ready\n", devfn); + } else if (doe->read_ttl < 0) { + *val = PCI_DOE_STATUS_ERROR; + dev_dbg(&bus->dev, "devfn: %#x error\n", devfn); + } else { + *val = 0; + dev_dbg(&bus->dev, "devfn: %#x idle\n", devfn); + } + return PCIBIOS_SUCCESSFUL; + } else if (pos >= devsec_dev->ide_pos && + pos < devsec_dev->ide_pos + sizeof(struct devsec_ide)) { + *val = *(u32 *) &devsec_dev->ide_regs[pos - devsec_dev->ide_pos]; + return PCIBIOS_SUCCESSFUL; + } + + switch (size) { + case 1: + *val = readb(base + pos); + break; + case 2: + *val = readw(base + pos); + break; + case 4: + *val = readl(base + pos); + break; + default: + PCI_SET_ERROR_RESPONSE(val); + return PCIBIOS_BAD_REGISTER_NUMBER; + } + return PCIBIOS_SUCCESSFUL; +} + +static int devsec_port_config_read(struct devsec *devsec, unsigned int devfn, + int pos, int size, u32 *val) +{ + struct devsec_port *devsec_port; + + if (PCI_FUNC(devfn) != 0 || + PCI_SLOT(devfn) >= ARRAY_SIZE(devsec->devsec_ports)) + return PCIBIOS_DEVICE_NOT_FOUND; + + devsec_port = devsec->devsec_ports[PCI_SLOT(devfn)]; + return pci_bridge_emul_conf_read(&devsec_port->bridge, pos, size, val); +} + +static int devsec_pci_read(struct pci_bus *bus, unsigned int devfn, int pos, + int size, u32 *val) +{ + struct devsec *devsec = bus_to_devsec(bus); + + dev_vdbg(&bus->dev, "devfn: %#x pos: %#x size: %d\n", devfn, pos, size); + + if (bus == devsec->hb.bus) + return devsec_port_config_read(devsec, devfn, pos, size, val); + else if (bus->parent == devsec->hb.bus) + return devsec_dev_config_read(devsec, bus, devfn, pos, size, + val); + + return PCIBIOS_DEVICE_NOT_FOUND; +} + +#ifndef PCI_DOE_PROTOCOL_DISCOVERY +#define PCI_DOE_PROTOCOL_DISCOVERY 0 +#define PCI_DOE_FEATURE_CMA 1 +#endif + +/* just indicate support for CMA */ +static void doe_process(struct devsec_dev_doe *doe) +{ + u8 type; + u16 vid; + + vid = FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, doe->req[0]); + type = FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, doe->req[0]); + + if (vid != PCI_VENDOR_ID_PCI_SIG) { + doe->read_ttl = -1; + return; + } + + if (type != PCI_DOE_PROTOCOL_DISCOVERY) { + doe->read_ttl = -1; + return; + } + + doe->rsp[0] = doe->req[0]; + doe->rsp[1] = FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH, 3); + doe->read_ttl = 3; + doe->rsp[2] = FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_VID, + PCI_VENDOR_ID_PCI_SIG) | + FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_PROTOCOL, + PCI_DOE_FEATURE_CMA) | + FIELD_PREP(PCI_DOE_DATA_OBJECT_DISC_RSP_3_NEXT_INDEX, 0); +} + +static int devsec_dev_config_write(struct devsec *devsec, struct pci_bus *bus, + unsigned int devfn, int pos, int size, + u32 val) +{ + struct devsec_dev *devsec_dev; + struct devsec_dev_doe *doe; + struct devsec_ide *ide; + void __iomem *base; + + dev_vdbg(&bus->dev, "devfn: %#x pos: %#x size: %d\n", devfn, pos, size); + + if (PCI_FUNC(devfn) != 0 || + PCI_SLOT(devfn) >= ARRAY_SIZE(devsec->devsec_devs)) + return PCIBIOS_DEVICE_NOT_FOUND; + + devsec_dev = devsec->devsec_devs[PCI_SLOT(devfn)]; + base = devsec_base(devsec_dev); + doe = &devsec_dev->doe; + ide = &devsec_dev->ide; + + if (pos >= PCI_BASE_ADDRESS_0 && pos <= PCI_BASE_ADDRESS_5) { + if (size != 4) + return PCIBIOS_BAD_REGISTER_NUMBER; + /* only one 64-bit mmio bar emulated for now */ + if (pos == PCI_BASE_ADDRESS_0) + val &= ~lower_32_bits(range_len(&devsec_dev->mmio_range) - 1); + else if (pos == PCI_BASE_ADDRESS_1) + val &= ~upper_32_bits(range_len(&devsec_dev->mmio_range) - 1); + else + val = 0; + } else if (pos == PCI_ROM_ADDRESS) { + val = 0; + } else if (pos == doe->cap + PCI_DOE_CTRL) { + if (val & PCI_DOE_CTRL_GO) { + dev_dbg(&bus->dev, "devfn: %#x doe go\n", devfn); + doe_process(doe); + } + if (val & PCI_DOE_CTRL_ABORT) { + dev_dbg(&bus->dev, "devfn: %#x doe abort\n", devfn); + doe->write = 0; + doe->read = 0; + doe->read_ttl = 0; + } + return PCIBIOS_SUCCESSFUL; + } else if (pos == doe->cap + PCI_DOE_WRITE) { + if (doe->write < ARRAY_SIZE(doe->req)) + doe->req[doe->write++] = val; + dev_dbg(&bus->dev, "devfn: %#x doe write[%d]\n", devfn, + doe->write - 1); + return PCIBIOS_SUCCESSFUL; + } else if (pos == doe->cap + PCI_DOE_READ) { + if (doe->read_ttl > 0) { + doe->read_ttl--; + doe->read++; + dev_dbg(&bus->dev, "devfn: %#x doe ack[%d]\n", devfn, + doe->read - 1); + } + return PCIBIOS_SUCCESSFUL; + } else if (pos >= devsec_dev->ide_pos && + pos < devsec_dev->ide_pos + sizeof(struct devsec_ide)) { + u16 ide_off = pos - devsec_dev->ide_pos; + + for (int i = 0; i < NR_PORT_STREAMS; i++) { + struct devsec_stream *stream = &ide->stream[i]; + + if (ide_off != offsetof(typeof(*ide), stream[i].ctl)) + continue; + + stream->ctl = val; + stream->status &= ~PCI_IDE_SEL_STS_STATE_MASK; + if (val & PCI_IDE_SEL_CTL_EN) + stream->status |= FIELD_PREP( + PCI_IDE_SEL_STS_STATE_MASK, + PCI_IDE_SEL_STS_STATE_SECURE); + else + stream->status |= FIELD_PREP( + PCI_IDE_SEL_STS_STATE_MASK, + PCI_IDE_SEL_STS_STATE_INSECURE); + return PCIBIOS_SUCCESSFUL; + } + } + + switch (size) { + case 1: + writeb(val, base + pos); + break; + case 2: + writew(val, base + pos); + break; + case 4: + writel(val, base + pos); + break; + default: + return PCIBIOS_BAD_REGISTER_NUMBER; + } + return PCIBIOS_SUCCESSFUL; +} + +static int devsec_port_config_write(struct devsec *devsec, struct pci_bus *bus, + unsigned int devfn, int pos, int size, + u32 val) +{ + struct devsec_port *devsec_port; + + dev_vdbg(&bus->dev, "devfn: %#x pos: %#x size: %d\n", devfn, pos, size); + + if (PCI_FUNC(devfn) != 0 || + PCI_SLOT(devfn) >= ARRAY_SIZE(devsec->devsec_ports)) + return PCIBIOS_DEVICE_NOT_FOUND; + + devsec_port = devsec->devsec_ports[PCI_SLOT(devfn)]; + return pci_bridge_emul_conf_write(&devsec_port->bridge, pos, size, val); +} + +static int devsec_pci_write(struct pci_bus *bus, unsigned int devfn, int pos, + int size, u32 val) +{ + struct devsec *devsec = bus_to_devsec(bus); + + dev_vdbg(&bus->dev, "devfn: %#x pos: %#x size: %d\n", devfn, pos, size); + + if (bus == devsec->hb.bus) + return devsec_port_config_write(devsec, bus, devfn, pos, size, + val); + else if (bus->parent == devsec->hb.bus) + return devsec_dev_config_write(devsec, bus, devfn, pos, size, + val); + return PCIBIOS_DEVICE_NOT_FOUND; +} + +static struct pci_ops devsec_ops = { + .read = devsec_pci_read, + .write = devsec_pci_write, +}; + +static void destroy_bus(void *data) +{ + struct pci_host_bridge *hb = data; + + pci_stop_root_bus(hb->bus); + pci_remove_root_bus(hb->bus); +} + +static u32 build_ext_cap_header(u32 id, u32 ver, u32 next) +{ + return FIELD_PREP(GENMASK(15, 0), id) | + FIELD_PREP(GENMASK(19, 16), ver) | + FIELD_PREP(GENMASK(31, 20), next); +} + +static void init_ide(struct devsec_ide *ide) +{ + ide->cap = PCI_IDE_CAP_SELECTIVE | PCI_IDE_CAP_IDE_KM | + PCI_IDE_CAP_TEE_LIMITED | + FIELD_PREP(PCI_IDE_CAP_SEL_NUM_MASK, NR_PORT_STREAMS - 1); + + for (int i = 0; i < NR_PORT_STREAMS; i++) + ide->stream[i].cap = + FIELD_PREP(PCI_IDE_SEL_CAP_ASSOC_NUM_MASK, NR_ADDR_ASSOC); +} + +static void init_dev_cfg(struct devsec_dev *devsec_dev) +{ + void __iomem *base = devsec_base(devsec_dev), *cap_base; + int pos, next; + + /* BAR space */ + writew(0x8086, base + PCI_VENDOR_ID); + writew(0xffff, base + PCI_DEVICE_ID); + writel(lower_32_bits(devsec_dev->mmio_range.start) | + PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, + base + PCI_BASE_ADDRESS_0); + writel(upper_32_bits(devsec_dev->mmio_range.start), + base + PCI_BASE_ADDRESS_1); + + /* Capability init */ + writeb(PCI_HEADER_TYPE_NORMAL, base + PCI_HEADER_TYPE); + writew(PCI_STATUS_CAP_LIST, base + PCI_STATUS); + pos = 0x40; + writew(pos, base + PCI_CAPABILITY_LIST); + + /* PCI-E Capability */ + cap_base = base + pos; + writeb(PCI_CAP_ID_EXP, cap_base); + writew(PCI_EXP_TYPE_ENDPOINT, cap_base + PCI_EXP_FLAGS); + writew(PCI_EXP_LNKSTA_CLS_2_5GB | PCI_EXP_LNKSTA_NLW_X1, cap_base + PCI_EXP_LNKSTA); + writel(PCI_EXP_DEVCAP_FLR | PCI_EXP_DEVCAP_TEE, cap_base + PCI_EXP_DEVCAP); + + /* DOE Extended Capability */ + pos = PCI_CFG_SPACE_SIZE; + next = pos + PCI_DOE_CAP_SIZEOF; + cap_base = base + pos; + devsec_dev->doe.cap = pos; + writel(build_ext_cap_header(PCI_EXT_CAP_ID_DOE, 2, next), cap_base); + + /* IDE Extended Capability */ + pos = next; + cap_base = base + pos; + writel(build_ext_cap_header(PCI_EXT_CAP_ID_IDE, 1, 0), cap_base); + devsec_dev->ide_pos = pos + 4; + init_ide(&devsec_dev->ide); +} + +#define MMIO_SIZE SZ_2M + +static void destroy_devsec_dev(void *data) +{ + struct devsec_dev *devsec_dev = data; + struct devsec *devsec = devsec_dev->devsec; + + gen_pool_free(devsec->iomem_pool, devsec_dev->mmio_range.start, + range_len(&devsec_dev->mmio_range)); + kfree(devsec_dev); +} + +static struct devsec_dev *devsec_dev_alloc(struct devsec *devsec) +{ + struct devsec_dev *devsec_dev __free(kfree) = + kzalloc(sizeof(*devsec_dev), GFP_KERNEL); + struct genpool_data_align data = { + .align = MMIO_SIZE, + }; + u64 phys; + + if (!devsec_dev) + return ERR_PTR(-ENOMEM); + + phys = gen_pool_alloc_algo(devsec->iomem_pool, MMIO_SIZE, + gen_pool_first_fit_align, &data); + if (!phys) + return ERR_PTR(-ENOMEM); + + *devsec_dev = (struct devsec_dev) { + .mmio_range = { + .start = phys, + .end = phys + MMIO_SIZE - 1, + }, + .devsec = devsec, + }; + init_dev_cfg(devsec_dev); + + return_ptr(devsec_dev); +} + +static int alloc_devs(struct devsec *devsec) +{ + struct device *dev = devsec->dev; + + for (int i = 0; i < ARRAY_SIZE(devsec->devsec_devs); i++) { + struct devsec_dev *devsec_dev = devsec_dev_alloc(devsec); + int rc; + + if (IS_ERR(devsec_dev)) + return PTR_ERR(devsec_dev); + rc = devm_add_action_or_reset(dev, destroy_devsec_dev, + devsec_dev); + if (rc) + return rc; + devsec->devsec_devs[i] = devsec_dev; + } + + return 0; +} + +static pci_bridge_emul_read_status_t +devsec_bridge_read_base(struct pci_bridge_emul *bridge, int pos, u32 *val) +{ + return PCI_BRIDGE_EMUL_NOT_HANDLED; +} + +static pci_bridge_emul_read_status_t +devsec_bridge_read_pcie(struct pci_bridge_emul *bridge, int pos, u32 *val) +{ + return PCI_BRIDGE_EMUL_NOT_HANDLED; +} + +static pci_bridge_emul_read_status_t +devsec_bridge_read_ext(struct pci_bridge_emul *bridge, int pos, u32 *val) +{ + struct devsec_port *devsec_port = bridge->data; + + /* only one extended capability, IDE... */ + if (pos == 0) { + *val = build_ext_cap_header(PCI_EXT_CAP_ID_IDE, 1, 0); + return PCI_BRIDGE_EMUL_HANDLED; + } + + if (pos < 4) + return PCI_BRIDGE_EMUL_NOT_HANDLED; + + pos -= 4; + if (pos < sizeof(struct devsec_ide)) { + *val = *(u32 *)(&devsec_port->ide_regs[pos]); + return PCI_BRIDGE_EMUL_HANDLED; + } + + return PCI_BRIDGE_EMUL_NOT_HANDLED; +} + +static void devsec_bridge_write_base(struct pci_bridge_emul *bridge, int pos, + u32 old, u32 new, u32 mask) +{ +} + +static void devsec_bridge_write_pcie(struct pci_bridge_emul *bridge, int pos, + u32 old, u32 new, u32 mask) +{ +} + +static void devsec_bridge_write_ext(struct pci_bridge_emul *bridge, int pos, + u32 old, u32 new, u32 mask) +{ + struct devsec_port *devsec_port = bridge->data; + + if (pos < sizeof(struct devsec_ide)) + *(u32 *)(&devsec_port->ide_regs[pos]) = new; +} + +static const struct pci_bridge_emul_ops devsec_bridge_ops = { + .read_base = devsec_bridge_read_base, + .write_base = devsec_bridge_write_base, + .read_pcie = devsec_bridge_read_pcie, + .write_pcie = devsec_bridge_write_pcie, + .read_ext = devsec_bridge_read_ext, + .write_ext = devsec_bridge_write_ext, +}; + +static int init_port(struct devsec_port *devsec_port) +{ + struct pci_bridge_emul *bridge = &devsec_port->bridge; + + *bridge = (struct pci_bridge_emul) { + .conf = { + .vendor = cpu_to_le16(0x8086), + .device = cpu_to_le16(0x7075), + .class_revision = cpu_to_le32(0x1), + .pref_mem_base = cpu_to_le16(PCI_PREF_RANGE_TYPE_64), + .pref_mem_limit = cpu_to_le16(PCI_PREF_RANGE_TYPE_64), + }, + .pcie_conf = { + .devcap = cpu_to_le16(PCI_EXP_DEVCAP_FLR), + .lnksta = cpu_to_le16(PCI_EXP_LNKSTA_CLS_2_5GB), + }, + .subsystem_vendor_id = cpu_to_le16(0x8086), + .has_pcie = true, + .data = devsec_port, + .ops = &devsec_bridge_ops, + }; + + init_ide(&devsec_port->ide); + + return pci_bridge_emul_init(bridge, 0); +} + +static void destroy_port(void *data) +{ + struct devsec_port *devsec_port = data; + + pci_bridge_emul_cleanup(&devsec_port->bridge); + kfree(devsec_port); +} + +static struct devsec_port *devsec_port_alloc(void) +{ + int rc; + + struct devsec_port *devsec_port __free(kfree) = + kzalloc(sizeof(*devsec_port), GFP_KERNEL); + + if (!devsec_port) + return ERR_PTR(-ENOMEM); + + rc = init_port(devsec_port); + if (rc) + return ERR_PTR(rc); + + return_ptr(devsec_port); +} + +static int alloc_ports(struct devsec *devsec) +{ + struct device *dev = devsec->dev; + + for (int i = 0; i < ARRAY_SIZE(devsec->devsec_ports); i++) { + struct devsec_port *devsec_port = devsec_port_alloc(); + int rc; + + if (IS_ERR(devsec_port)) + return PTR_ERR(devsec_port); + rc = devm_add_action_or_reset(dev, destroy_port, devsec_port); + if (rc) + return rc; + devsec->devsec_ports[i] = devsec_port; + } + + return 0; +} + +static int __init devsec_bus_probe(struct platform_device *pdev) +{ + int rc; + struct devsec *devsec; + u64 mmio_size = SZ_64G; + struct devsec_sysdata *sd; + struct pci_host_bridge *hb; + struct device *dev = &pdev->dev; + u64 mmio_start = iomem_resource.end + 1 - SZ_64G; + + hb = devm_pci_alloc_host_bridge( + dev, sizeof(*devsec) - sizeof(struct pci_host_bridge)); + if (!hb) + return -ENOMEM; + + devsec = container_of(hb, struct devsec, hb); + devsec->dev = dev; + devsec->iomem_pool = devm_gen_pool_create(dev, ilog2(SZ_2M), + NUMA_NO_NODE, "devsec iomem"); + if (!devsec->iomem_pool) + return -ENOMEM; + + rc = gen_pool_add(devsec->iomem_pool, mmio_start, mmio_size, + NUMA_NO_NODE); + if (rc) + return rc; + + rc = alloc_ports(devsec); + if (rc) + return rc; + + rc = alloc_devs(devsec); + if (rc) + return rc; + + devsec->resource[0] = (struct resource) { + .name = "DEVSEC BUSES", + .start = 0, + .end = NR_DEVSEC_BUSES + NR_DEVSEC_ROOT_PORTS - 1, + .flags = IORESOURCE_BUS | IORESOURCE_PCI_FIXED, + }; + pci_add_resource(&hb->windows, &devsec->resource[0]); + + devsec->resource[1] = (struct resource) { + .name = "DEVSEC MMIO", + .start = mmio_start, + .end = mmio_start + mmio_size - 1, + .flags = IORESOURCE_MEM | IORESOURCE_MEM_64, + }; + pci_add_resource(&hb->windows, &devsec->resource[1]); + + sd = &devsec->sysdata; + devsec_sysdata = sd; + hb->domain_nr = pci_bus_find_emul_domain_nr(PCI_DOMAIN_NR_NOT_SET); + if (hb->domain_nr < 0) + return hb->domain_nr; + + /* + * Note, domain_nr is set in devsec_sysdata for + * !CONFIG_PCI_DOMAINS_GENERIC platforms + */ + devsec_set_domain_nr(sd, hb->domain_nr); + + hb->dev.parent = dev; + hb->sysdata = sd; + hb->ops = &devsec_ops; + + rc = pci_scan_root_bus_bridge(hb); + if (rc) + return rc; + + return devm_add_action_or_reset(dev, destroy_bus, no_free_ptr(hb)); +} + +static struct platform_driver devsec_bus_driver = { + .driver = { + .name = "devsec_bus", + }, +}; + +static struct platform_device *devsec_bus; + +static int __init devsec_bus_init(void) +{ + struct platform_device_info devsec_bus_info = { + .name = "devsec_bus", + .id = -1, + }; + int rc; + + devsec_bus = platform_device_register_full(&devsec_bus_info); + if (IS_ERR(devsec_bus)) + return PTR_ERR(devsec_bus); + + rc = platform_driver_probe(&devsec_bus_driver, devsec_bus_probe); + if (rc) + platform_device_unregister(devsec_bus); + return 0; +} +module_init(devsec_bus_init); + +static void __exit devsec_bus_exit(void) +{ + platform_driver_unregister(&devsec_bus_driver); + platform_device_unregister(devsec_bus); +} +module_exit(devsec_bus_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Device Security Sample Infrastructure: TDISP Device Emulation"); diff --git a/samples/devsec/common.c b/samples/devsec/common.c new file mode 100644 index 000000000000..de0078e4d614 --- /dev/null +++ b/samples/devsec/common.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright(c) 2024 - 2025 Intel Corporation. All rights reserved. + +#include +#include + +/* + * devsec_bus and devsec_tsm need a common location for this data to + * avoid depending on each other. Enables load order testing + */ +struct pci_sysdata *devsec_sysdata; +EXPORT_SYMBOL_GPL(devsec_sysdata); + +static int __init common_init(void) +{ + return 0; +} +module_init(common_init); + +static void __exit common_exit(void) +{ +} +module_exit(common_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Device Security Sample Infrastructure: Shared data"); diff --git a/samples/devsec/devsec.h b/samples/devsec/devsec.h new file mode 100644 index 000000000000..ae4274c86244 --- /dev/null +++ b/samples/devsec/devsec.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +// Copyright(c) 2024 - 2025 Intel Corporation. All rights reserved. + +#ifndef __DEVSEC_H__ +#define __DEVSEC_H__ +struct devsec_sysdata { +#ifdef CONFIG_X86 + /* + * Must be first member to that x86::pci_domain_nr() can type + * pun devsec_sysdata and pci_sysdata. + */ + struct pci_sysdata sd; +#else + int domain_nr; +#endif +}; + +#ifdef CONFIG_X86 +static inline void devsec_set_domain_nr(struct devsec_sysdata *sd, + int domain_nr) +{ + sd->sd.domain = domain_nr; +} +static inline int devsec_get_domain_nr(struct devsec_sysdata *sd) +{ + return sd->sd.domain; +} +#else +static inline void devsec_set_domain_nr(struct devsec_sysdata *sd, + int domain_nr) +{ + sd->domain_nr = domain_nr; +} +static inline int devsec_get_domain_nr(struct devsec_sysdata *sd) +{ + return sd->domain_nr; +} +#endif +extern struct devsec_sysdata *devsec_sysdata; +#endif /* __DEVSEC_H__ */ diff --git a/samples/devsec/tsm.c b/samples/devsec/tsm.c new file mode 100644 index 000000000000..a4705212a7e4 --- /dev/null +++ b/samples/devsec/tsm.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright(c) 2024 - 2025 Intel Corporation. All rights reserved. */ + +#define dev_fmt(fmt) "devsec: " fmt +#include +#include +#include +#include +#include +#include "devsec.h" + +struct devsec_tsm_pf0 { + struct pci_tsm_pf0 pci; +#define NR_TSM_STREAMS 4 +}; + +struct devsec_tsm_fn { + struct pci_tsm pci; +}; + +static struct devsec_tsm_pf0 *to_devsec_tsm_pf0(struct pci_tsm *tsm) +{ + return container_of(tsm, struct devsec_tsm_pf0, pci.tsm); +} + +static struct devsec_tsm_fn *to_devsec_tsm_fn(struct pci_tsm *tsm) +{ + return container_of(tsm, struct devsec_tsm_fn, pci); +} + +static const struct pci_tsm_ops *__devsec_pci_ops; + +static struct pci_tsm *devsec_tsm_pf0_probe(struct pci_dev *pdev) +{ + int rc; + + struct devsec_tsm_pf0 *devsec_tsm __free(kfree) = + kzalloc(sizeof(*devsec_tsm), GFP_KERNEL); + if (!devsec_tsm) + return NULL; + + rc = pci_tsm_pf0_constructor(pdev, &devsec_tsm->pci, __devsec_pci_ops); + if (rc) + return NULL; + + pci_dbg(pdev, "tsm enabled\n"); + return &no_free_ptr(devsec_tsm)->pci.tsm; +} + +static struct pci_tsm *devsec_tsm_fn_probe(struct pci_dev *pdev) +{ + int rc; + + struct devsec_tsm_fn *devsec_tsm __free(kfree) = + kzalloc(sizeof(*devsec_tsm), GFP_KERNEL); + if (!devsec_tsm) + return NULL; + + rc = pci_tsm_constructor(pdev, &devsec_tsm->pci, __devsec_pci_ops); + if (rc) + return NULL; + + pci_dbg(pdev, "tsm (sub-function) enabled\n"); + return &no_free_ptr(devsec_tsm)->pci; +} + +static struct pci_tsm *devsec_tsm_pci_probe(struct pci_dev *pdev) +{ + if (pdev->sysdata != devsec_sysdata) + return NULL; + + if (is_pci_tsm_pf0(pdev)) + return devsec_tsm_pf0_probe(pdev); + return devsec_tsm_fn_probe(pdev); +} + +static void devsec_tsm_pci_remove(struct pci_tsm *tsm) +{ + struct pci_dev *pdev = tsm->pdev; + + pci_dbg(pdev, "tsm disabled\n"); + + if (is_pci_tsm_pf0(pdev)) { + struct devsec_tsm_pf0 *devsec_tsm = to_devsec_tsm_pf0(tsm); + + pci_tsm_pf0_destructor(&devsec_tsm->pci); + kfree(devsec_tsm); + } else { + struct devsec_tsm_fn *devsec_tsm = to_devsec_tsm_fn(tsm); + + kfree(devsec_tsm); + } +} + +/* + * Reference consumer for a TSM driver "connect" operation callback. The + * low-level TSM driver understands details about the platform the PCI + * core does not, like number of available streams that can be + * established per host bridge. The expected flow is: + * + * 1/ Allocate platform specific Stream resource (TSM specific) + * 2/ Allocate Stream Ids in the endpoint and Root Port (PCI TSM helper) + * 3/ Register Stream Ids for the consumed resources from the last 2 + * steps to be accountable (via sysfs) to the admin (PCI TSM helper) + * 4/ Register the Stream with the TSM core so that either PCI sysfs or + * TSM core sysfs can list the in-use resources (TSM core helper) + * 5/ Configure IDE settings in the endpoint and Root Port (PCI TSM helper) + * 6/ RPC call to TSM to perform IDE_KM and optionally enable the stream + * (TSM Specific) + * 7/ Enable the stream in the endpoint, and root port if TSM call did + * not already handle that (PCI TSM helper) + * + * The expectation is the helpers referenceed are convenience "library" + * APIs for common operations, not a "midlayer" that enforces a specific + * or use model sequencing. + */ +static int devsec_tsm_connect(struct pci_dev *pdev) +{ + return -ENXIO; +} + +static void devsec_tsm_disconnect(struct pci_dev *pdev) +{ +} + +static struct pci_tsm_ops devsec_pci_ops = { + .probe = devsec_tsm_pci_probe, + .remove = devsec_tsm_pci_remove, + .connect = devsec_tsm_connect, + .disconnect = devsec_tsm_disconnect, +}; + +static void devsec_tsm_remove(void *tsm_dev) +{ + tsm_unregister(tsm_dev); +} + +static int devsec_tsm_probe(struct faux_device *fdev) +{ + struct tsm_dev *tsm_dev; + + tsm_dev = tsm_register(&fdev->dev, NULL, &devsec_pci_ops); + if (IS_ERR(tsm_dev)) + return PTR_ERR(tsm_dev); + + return devm_add_action_or_reset(&fdev->dev, devsec_tsm_remove, + tsm_dev); +} + +static struct faux_device *devsec_tsm; + +static const struct faux_device_ops devsec_device_ops = { + .probe = devsec_tsm_probe, +}; + +static int __init devsec_tsm_init(void) +{ + __devsec_pci_ops = &devsec_pci_ops; + devsec_tsm = faux_device_create("devsec_tsm", NULL, &devsec_device_ops); + if (!devsec_tsm) + return -ENOMEM; + return 0; +} +module_init(devsec_tsm_init); + +static void __exit devsec_tsm_exit(void) +{ + faux_device_destroy(devsec_tsm); +} +module_exit(devsec_tsm_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Device Security Sample Infrastructure: Platform TSM Driver"); -- 2.50.1