From nobody Mon Sep 28 21:03:43 2026 Received: from SA9PR02CU001.outbound.protection.outlook.com (mail-southcentralusazon11013022.outbound.protection.outlook.com [40.93.196.22]) (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 77B8541F367; Mon, 17 Aug 2026 12:59:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.93.196.22 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971553; cv=fail; b=pD77G8WUXx63nmbN61pJ+pXAYI7UbaHreGTP7Trs/8uosnRR7BsGxOlghhVUuqkvqewL/pl7ZpuYHad56r68e6DDJk1N52xqw+7jDLKsqxMx45cowZQwFYVBuZc8ZqhsL00Giqfbpg4UTaOFqVOGCEsPA1ivqpZBrZIsRGpI08Q= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971553; c=relaxed/simple; bh=LxR1j0jHaBsV3C38E8URJ/TSikb5TSeio3+8Z6R7CGU=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=aDYGhKR3qOjNn6gWRUaIoyDIQJuzmgL5ThUXqPOz7iTwGLpfmYDxKtJu1Gz2ynnIXszkfrVMyD7EbsN0JCMkZnLkFZbPsHEwQb6ysrOsA72Cm00qAe0xUUh1qpX8kBbi42EdX/VOwmVciWmUn/JYPUSSLForvtWBHxpZpnlZtJM= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=Se9FV6SE; arc=fail smtp.client-ip=40.93.196.22 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="Se9FV6SE" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=wYMJ1JsHAmcYbeK6EjFi58s0DN2AzO3sxDtCnzgQXHscW3bxc8qjkMJ5qjBnGXF4B7ExJeJ7SbBgm0L9ER/4+bZWO98YJns1v3Rd5ryy7eOLnD7+ylTUfnyWU8zOTAWCX2lSTdz3ND/AqeHaYr8HBRFiqgJzMxFILPs0Sy/xKm23FAvTTphoOb8yufRHywdrc3KeEv/siPZOqW5kZ/5p7Jj2HkNqGEFgBO6eyQsM/YJ8EnaI82oD6XgeLscJQvKEHnGudm86KVo/JugnjWMM/l1xRTx41su8VDdSXpgUfE0LjxFI+Ja43yXRgYh2zl5alvzJoc17J/bfgLe7poesMw== 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=jOmkd5T88Bq0wU++qSGlZULAYsPUNErSajC+nEysSng=; b=Rjz+akV4WJ8blMOALa1jRhwAaaY7ubo2i/tWJGKatkpSMtcXvJ3Y9nJdjBpP+SvGEBun6jSOffOV4rHloMaqhj+h2JnC7rxcqHO5HDo3o36aR4jHa8MkbONVqcew4//53KfR27p0bqd9z/7w6XE7NAU46mDHKtfp/YHFOvKT/Iu4mM0f/ZLoE8d5RquIur847sIeIA57AIqMEToaxBLYNSEZtmATrwAElhRZ7x3/50JeZkA4sEmvf3f+twM4f3+h8zLWe4eiRvofyl6/cENz+KivmnBgCOpIfBvbqWTXomNb4zo9Cy6/1PQDRUdPWcsdigv6IyQY2el1ILkrqEkxNA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=jOmkd5T88Bq0wU++qSGlZULAYsPUNErSajC+nEysSng=; b=Se9FV6SEJZNymnJdnYBSeVPGUkaiRIW10WqX/rSqRuIJD1B7IXQLsyXrv0NDEGM54N2phrpP60mKSnyB7Oyq99UShRbV4x0xjsRf7d5AthKTtcU4ApNorIPVaHpJ9CqHSCDMgv2ZHXhYSEn83ZiKR1C3VX37iYJS+G++zZmzSkQS5h041tZFCy4n85VB7DC+LjcpiRT3Lv/o6CkWZnCeqhYRCL0N6mPm6/JTZ41MdGcrlQe6CNiI/xhleqaWvvGZJE28bY9p2wMdZd9dsJGhSFjVh7uO1uN/vQTCXkjZO73VquBU9INytFpfFAc5K5ecQTmKd5lXEW9flnv+qUTPbA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) by PH7PR12MB6833.namprd12.prod.outlook.com (2603:10b6:510:1af::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.315.13; Mon, 17 Aug 2026 12:59:07 +0000 Received: from BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0]) by BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0%4]) with mapi id 15.21.0315.016; Mon, 17 Aug 2026 12:59:07 +0000 From: Eliot Courtney Date: Mon, 17 Aug 2026 21:56:36 +0900 Subject: [PATCH 1/6] rust: alloc: add Vec::push_init Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260817-b4-nvkv-v1-1-b84db5e84b67@nvidia.com> References: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> In-Reply-To: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> To: Danilo Krummrich , Lorenzo Stoakes , Vlastimil Babka , "Liam R. Howlett" , Uladzislau Rezki , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , David Airlie , Simona Vetter Cc: John Hubbard , Alistair Popple , Timur Tabi , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, Eliot Courtney X-Mailer: b4 0.15.2 X-ClientProxiedBy: TYCP286CA0260.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:455::18) To BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) 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: BL0PR12MB2353:EE_|PH7PR12MB6833:EE_ X-MS-Office365-Filtering-Correlation-Id: 25a49a6b-0959-4ef7-f4a0-08defc5f52d8 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|376014|23010399003|1800799024|366016|7416014|10070799003|18002099003|921020|22082099003|56012099006|10067099003|11063799006|6133799003; X-Microsoft-Antispam-Message-Info: 7gKFtynk8U+69avo0EWn4bufUSTqiMLRK18W9mRRz9ZRQg3bHHSSChkMimmvdw+souPQzBryLw2vCIt3akcB+JMpzzrvyqfugNnqJlg8bVALA+Gr3cHJmB0oCBsG+42J+tFAfPbae4jJh3b6Ep+KZXXU4P0RFnDbebaPZC8QjfLEwr+XjjlJiCorrNBhg4bSPsScaT8naBT69k+ECnKZxaXRY92rpwykgsHKK25rvKIX+e/m/XroNn0IMdNQf/MCEf3WQE2h0NdveAK+29l8clCYvHfIrTrj/DVYVz73qPdWEz51butT8YEuZVBnqlRt8RbR7x7Uzt6raGrInRSgDyxT6xwkp+teWAKcJ+qwWAqgHU5nwvY2s3FN9QVCK+upgipQVFplOnVmKRZ+1WUaYWFhywQjXTHawV3h18hJWBYNXIK3LE6jToKwIvmRQr49F5FjjKxy0BCeJGWoRnFRz1roe4YxvDIRBl5HAzffRmXqB/okCEZdKAqHXJixv9PFnYQ9P9Gfwb5HlBBK0At9nfd5l9GBuejIC4mNkKsMLS87nGoLKYwjtBPFtZc6CmgFW2DwM53utpXj9W3hGzRYhwcZXWSFPq1C+05uurgPAOLucC6MOzsxyWciDIxJJWOPSJAGTBS/JBK3h7xQ0HsniQ1cor5zKeyTYtbFALRK52mYv8xyxak30Rlc7YAvJfqfqnFipP6zINdotp/fPbjuCg== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BL0PR12MB2353.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(376014)(23010399003)(1800799024)(366016)(7416014)(10070799003)(18002099003)(921020)(22082099003)(56012099006)(10067099003)(11063799006)(6133799003);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?NzFrdmsxWitjTDBEaGRYWGVYaFl5U1JiYTlQUGpDL2lmOEp5MTJLNGpLbnB3?= =?utf-8?B?ekw5aGxyRjJMaGRVNjNKcDFUNmIvakRUK083dU1Oa0tFVjdCUFJoc1ZDWmJO?= =?utf-8?B?MmxsMWtCTlZWYjFxSmE1UnpiRFRlU3g0S1JhaDlQRVQxanJ0V0k4eTNCVy9P?= =?utf-8?B?VHdJcVVqV0ZHb2FmZW5paFg5d3hhOWVNMmpTaUYvN0IvQ3RZREMvb2xROEtn?= =?utf-8?B?UzJOaHBadkZPVmRlU09CSmR0WGE5OXdVZ0s5cXVSdTRIQkdwK1N4V3NRTzRD?= =?utf-8?B?Um9NTnA0VzkxVFdyYkk2NG9rUFBBdW5Mc3RNZUovYisrdkl0b1paYWYwVFdt?= =?utf-8?B?OFBvSWF0MjR0NWNIU05xcTlTTDRraUd5OUpPcHV5YWlNOEIwTnpTcHZqbjJC?= =?utf-8?B?UUIzWUQ4eDNRc01ocW5JWDI1WmRHYUJzY1FkaHNKQ1hqc0pJQzJmTERmS0pu?= =?utf-8?B?MFNFZTRHcGNySmlpa1dmYTFUZE5IZzU3VEg5b0dvZHR6cENEc2RnbzNDN2ls?= =?utf-8?B?MEpGSGJ5YXhySXBoUXpSNnlGcnd4bE5jenQwcW5Ydk5udFd0Nk5FN1RlWFRF?= =?utf-8?B?SXA4akx3MFdHSHBwL0o4UlFqZHErcDZ6RjlpUHUrWjExd1RJa2RJMUJHMzZW?= =?utf-8?B?S09GcHNoS2I1ZWF5TDk1QjArKzV0anlXRnhQUGtHM0VmZGljQ3JuK1JaMldG?= =?utf-8?B?K0NUMnlOVFdOQnRuR1g3VzhWWXNVNVFtWlN3cmFJaXNaeW5ycFh0Q1I4UFZz?= =?utf-8?B?U1g4VHB5bktDeEFsdG91ejEwcHRrdk54NWhSTlo1NG1oZTB3TksweTBuMFo2?= =?utf-8?B?UjNESVJabHVCT3hVTDY1WWN1a0tMUzg3RFJ1eWg3NnNGUXIxT1ZSWUNXNGFW?= =?utf-8?B?NjE1NXpza25QRTVnR0NqK0tnK2xOdHU5eDM0Z2U3K2ErWXFIU2lEdlZTMk03?= =?utf-8?B?YkFXN1ZKSmV3SExHN1hyQW15bUg3MGR3UU1YRGVrT2FqdWN6RDBNSEs4d0Z2?= =?utf-8?B?YWhWQWgrRTFPMHgrZVhiM29vUFBiazE2SCtuc29nT3hHWS9XamZuRDZpZ254?= =?utf-8?B?SWptb2RHMUlkSnNjRzRKcm9NQmRYSlpJUGhNNE5SdmkyRVREVWs2bjJJNnRM?= =?utf-8?B?aERNbE9wS2wxaWh6eC9VQ3Z0K1ZjMkJtbjJTaE1iZCtFNTY3Nmw5cUJVQzFH?= =?utf-8?B?VTg1RDYvNzgyeExTSk0rZ0lmQWlIbUZ0SExHcWZUanFaTVBVS21JRGxaT2Za?= =?utf-8?B?cmlyRE9HSzlONDMrak8zb0NTNnJxMmEya1ZlV25pdDZJaWttOFJ2UXpZRm5C?= =?utf-8?B?dFIyanFDR001WXRmclBXUE1JSlFCRXpaVkkyN0tHMmtyOFlRamlwYmRJT2lN?= =?utf-8?B?UWlTQURvVWRHV1lBekNLaFlMMVhJZWJEbmtIM2FNdG5nM2lzaTEzcC82c0lS?= =?utf-8?B?cU1pOUcwRTk2bTNQdE1sNFB1czkzdUt6SFN6SjlBT2pVWEt4dzd2bXZhbG5j?= =?utf-8?B?TWhtNG1HenF6b2lCRVJPOVg1OWVRblJaS2Z4eGdwWHdZRWdHT0dDSFMrRHRz?= =?utf-8?B?MytoNzQyaXFNUmc4WTFNOW5TMHZXdlR4aUlPMFBsRm54TFNva2Y1d0J5UVo5?= =?utf-8?B?QmE4VTJJWHNVNUFuNU1oT0NVN0xwVi8rU2daRllzcTJKM2RtcXdIV255eHN6?= =?utf-8?B?YXpxbCtQYm1hSTF6K0krOVovbjRjOHN2MVlwd1h4YkhnMldKYmFna0MwUXdI?= =?utf-8?B?U0RzZElxR1gvWjVzV1JEL1AxTG83OXNIbnQ2QXZDRWlDd2I5VEVrU28zQm9h?= =?utf-8?B?OHFyUTlyWDUxeVN4VXZvS1VJbnh0b1F5WDVDa1FNYnFrOFNrWVFyL2RBbHk4?= =?utf-8?B?Rm9GcGNzR2NLZDFkRGo5SjFOK1RhVkQ0WlBvbUh3aHRkSDZtN1M5dTJ3L1J4?= =?utf-8?B?bWtPNnljNTZsWkJ2bG4zTDJUTjMzSnFLd0tnMituS0JQMlJCMGJHOE95Vm8z?= =?utf-8?B?azdxYXRHcUEzNzg5RkFTdExnYXQ4Zi9XeFI5MTdoQ29RaXYyZ3kyZzhBSTZq?= =?utf-8?B?L2krbkordjQ5RFZ4aVdPc2psMXJzemV3dm51cndQYkVkcGhiQTAzQVhCMnl5?= =?utf-8?B?OGhxSXp0OTRLUHVpcUZGZlp1TU1hak9VWXFGVDF5NENucTNMb2RNaFREekE5?= =?utf-8?B?bnZIR1NHOFJJa25kdmNFalY1bEJXT0ZmQThZMDQ4NlEyOFVKYTBpcFFZUGN3?= =?utf-8?B?RlkycjEwVXFZdlk4alErc2NPVm8rdWVlYUswZFNtdzVPYjduRUJueGRZMGI4?= =?utf-8?B?VzlNUUc1bjJiZkI0ZzZ1T1VhQjVaZVJSd0pTL21JbTlEdXNyUEtaU1lEYThJ?= =?utf-8?Q?CnfOf4iB1gv3uJmgTly9v5Lyy1j9XCVonXDvUo96E3nTt?= X-MS-Exchange-AntiSpam-MessageData-1: ZnrnIZShce3oOQ== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 25a49a6b-0959-4ef7-f4a0-08defc5f52d8 X-MS-Exchange-CrossTenant-AuthSource: BL0PR12MB2353.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Aug 2026 12:59:07.7847 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: MveHWVIytEiLPjQZUedxBORZQ+O5RfygC1vVln8GfB5QNOLildDtaJqcE6cN7d7ceiGXG7C4dfJaKpwUsxe3og== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH7PR12MB6833 Add `Vec::push_init` which initializes a new element in place. We can't modify the existing `Vec::push` signature to take an `impl Init` without changing its Error type. Signed-off-by: Eliot Courtney --- rust/kernel/alloc/kvec.rs | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs index c7546b9da4fa..9f6f25d7e218 100644 --- a/rust/kernel/alloc/kvec.rs +++ b/rust/kernel/alloc/kvec.rs @@ -52,7 +52,10 @@ }, // }; =20 -use pin_init::Zeroable; +use pin_init::{ + Init, + Zeroable, // +}; =20 mod errors; pub use self::errors::{InsertError, PushError, RemoveError}; @@ -359,6 +362,43 @@ pub fn push(&mut self, v: T, flags: Flags) -> Result<(= ), AllocError> { Ok(()) } =20 + /// Appends an element to the back of the [`Vec`] instance by initiali= zing it in place. + /// + /// # Examples + /// + /// ``` + /// struct Element { + /// buf: KVec, + /// } + /// + /// impl Element { + /// fn new() -> impl Init { + /// try_init!(Element { + /// buf: KVec::with_capacity(16, GFP_KERNEL)?, + /// }? Error) + /// } + /// } + /// + /// let mut v: KVec =3D KVec::new(); + /// v.push_init(Element::new(), GFP_KERNEL)?; + /// assert!(v[0].buf.is_empty()); + /// # Ok::<(), Error>(()) + /// ``` + pub fn push_init(&mut self, init: impl Init, flags: Flags) ->= Result<(), E> + where + E: From, + { + self.reserve(1, flags)?; + // SAFETY: The call to `reserve` was successful, so there is at le= ast one spare slot; the + // pointer therefore refers to allocated, aligned memory valid for= a write of one `T`. + unsafe { init.__init(self.spare_capacity_mut().as_mut_ptr().cast::= ())? }; + // SAFETY: The call to `__init` returned `Ok`, so the first spare = slot now holds an + // initialized `T`. The new length does not exceed the capacity be= cause `reserve` ensured + // the capacity is greater than the length by at least one. + unsafe { self.inc_len(1) }; + Ok(()) + } + /// Appends an element to the back of the [`Vec`] instance without rea= llocating. /// /// Fails if the vector does not have capacity for the new element. --=20 2.55.0 From nobody Mon Sep 28 21:03:43 2026 Received: from CH1PR05CU001.outbound.protection.outlook.com (mail-northcentralusazon11010043.outbound.protection.outlook.com [52.101.193.43]) (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 C074441F367; Mon, 17 Aug 2026 12:59:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.193.43 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971561; cv=fail; b=qkJbvNlFilZOV++7mX+SqDOzCvTPAd/0Syt3H4riwwtVZjpvHrG2e1MN6+4UYoQNN2Fh9ylJqxdpWd836tmmNYat5n0WOLAxVUrzazYEXmTDYZ25xQ2cw9QivNRlS/Y+Eh/uuc5TZ19U/wPfVcno5Tl0KAzR8RXw4IohtjhTdlU= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971561; c=relaxed/simple; bh=6j/yS4/cOw3BhtWZ/dROyoUbyyrjwBBtB/N4awyIkMc=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=U4i+KKIgFvLqsc17ksKsCgcxzMfxKlQfVK3ensNyumc+vlNGIVP/cfH+JAiz5dRVl/gE/WdqsRko83kWv2bbI/gnJcafQnmc+H7W0lRR+gl8CZ5LRwk06q9zcK6r9nGiAUKX36/zXnrF3cR3R6Y1jbzk/LmHhKKdUWOLqzdsmdA= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=DUefOhoQ; arc=fail smtp.client-ip=52.101.193.43 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="DUefOhoQ" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=f1/E+CoVVV+4vHavB7kSNjiX+VJqIzqamQrifRb1t4DhijAJ15VIX8MqpCXhDlobfl1bN+jW+Vy4IX+TnUgUYFYKwwkvLCZj6n5PN8MRNCpQ0QIOzshuqORme2r22wQQ7W+KIvRIp7rihpiq+KhidAmmdXX7c+eaddW8tpAkYoaJ2gNj+kObaKS7CpJSa256+BiNIm0AwwSaX/B5JIEHQ+zxszveZe8OIYlmWj9+yGen3/03UkNDOZgAy+RgH3WkAcesYW6J/erODULdhaCu9uDwf7MA0wMTX0VBorukFNCCDYhWjg5o/bOQOn1rsZcc2h5CvJfhvfaavKk93dw48w== 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=8dmgzgs//qoGkCsXTx8uYv+k2DmRFJqaBIKrbMYo4mg=; b=H71Xr+J00VeGzDydaNMQX2cgIlozgd3TQECHbqvf5j5ln5GJtHKNYj4PLa42FdhSamZ78RVAkT5t79y9itqHm8ZLGQLZ9tjSgxCcizwycWF1/JO7g8RITJYlgv5DPChO2Ro9sA9kxg+2ksQITrc9xpHnQKvrazXC1JJJ0w84bs8FLpY86t3MEKX0GbiK0xUKQH+ycOHSrJJg1VlMxmBkh2NNm5u3gtidyEO06pHWl+NlEgwUYyDqLwnKjhseHg8b+HjxycaPJBXMqJwrINL3J7aa0Ox+a1ICWMO8ZYFL9gHfLrDhCldTNhBp1xelp18+RGc96D6Vma1DIMrLMDbA9A== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=8dmgzgs//qoGkCsXTx8uYv+k2DmRFJqaBIKrbMYo4mg=; b=DUefOhoQXGBXNynTAqAfqxOG+bJDg4qaXZRdx4uAy4KkvxZIKv7FoZeIK4MTjbGZBitNMKwI+5tjbKaADj0eP2JO6QqNSLZ4nCoVWLf86wiFM0b5sHdVXV8IcU8T1P93D4JNwPVNcu6edF8P413b8b0rGIT04TzcfKyQmq3ivBEb8W9gpZOz4b9HPET8Y2TSrqkHrM9Os/rjxb6c3bpMdcGAKt/5EQ2ijQ//AS3GkowsVGrDP9apBUsqT5HG5Sq/r3K/Y/8A4eqr9QA0ng6IF85qEzT2erb17xMbVeteCsXCl1I8NaBwm+4y4zDSBXlvNQ0yuNLyPgEPbFiL3i9z0w== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) by PH7PR12MB6833.namprd12.prod.outlook.com (2603:10b6:510:1af::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.315.13; Mon, 17 Aug 2026 12:59:11 +0000 Received: from BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0]) by BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0%4]) with mapi id 15.21.0315.016; Mon, 17 Aug 2026 12:59:11 +0000 From: Eliot Courtney Date: Mon, 17 Aug 2026 21:56:37 +0900 Subject: [PATCH 2/6] gpu: nova-core: add NVKV encoder Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260817-b4-nvkv-v1-2-b84db5e84b67@nvidia.com> References: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> In-Reply-To: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> To: Danilo Krummrich , Lorenzo Stoakes , Vlastimil Babka , "Liam R. Howlett" , Uladzislau Rezki , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , David Airlie , Simona Vetter Cc: John Hubbard , Alistair Popple , Timur Tabi , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, Eliot Courtney X-Mailer: b4 0.15.2 X-ClientProxiedBy: TYCP286CA0045.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:29d::19) To BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) 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: BL0PR12MB2353:EE_|PH7PR12MB6833:EE_ X-MS-Office365-Filtering-Correlation-Id: ed692529-0cc3-4d4e-3a6e-08defc5f554e X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|376014|23010399003|1800799024|366016|7416014|10070799003|18002099003|921020|22082099003|56012099006|10067099003|11063799006|6133799003|3023799007; X-Microsoft-Antispam-Message-Info: r07FUjZd5t3GLUIz/XBMif4NS04FA56L64nf9ML1+7Ef5auDm9RKY4rXO2XM9jrlyfLyWPHWyjhXHgz1YwpRUbR0uL77CKppntwbJYRs+/MIDOO4xxu43Fs9lKU9jbBSHZo7sIJEr3d4pTrYXCMeIaAKuRktCw71rZbtJQxCcNddWsDJMeC4wOt+0MfUQzi8MQsw+N/NqghhP4MPPCgcBnKzv/koXdfnfDTrpXrgReNg2sFyU4i3V5lK42HFZwte1q/M+JaX6TDjrVFmick8xmIhldESdyCY3OCm+Bf/RmCmDsY04Ha8SthUbZ4kzDHIHx/G6Schx0tG+qFqb5/X/TBu/UV57pArHplMO38evzNZQzoYhBY7rLtww+z3yKd2ETYk/67yq5Q6hGvdWWsAP07YgLlkW9acG1ZhicJnJ66CsQDkm9BlL/zrtc992uJS9jl3C/k22OetYonBfAHn8iHHbkDT+CNntKnQ5sLKjM9OGx022+Z93kJ2CE+hd3Dj9QsNfemZTXYYnQq+0a1ogqQxoMybzAEQodipN/c55tWO0CkCYwOdacOFqkV3fLX/JmC32hh9nOA8KXjNOy3hiUdITPap/4Je25ErJb6KVYU8fB3uao1d4btm5dp815CaZYAfLvNj8Qd95ssoLc8RHWkHh8M4SkOMXoyKRgdgKJ0I2/a1g2by876beuSswDORW/OjZ/JbvR2ECwRk/8YTcw== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BL0PR12MB2353.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(376014)(23010399003)(1800799024)(366016)(7416014)(10070799003)(18002099003)(921020)(22082099003)(56012099006)(10067099003)(11063799006)(6133799003)(3023799007);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?UHhhTStpeFpseUZNcTkyYml2QUp3cnZ2NHlaeGJrNEZmMGRXc1lueFhvOUpP?= =?utf-8?B?V0FpRVNoYlVDV1VaKzFOMUtlRm1iMkFXaVdCR1JtNlJtVDhYNHRVL0c5MDNv?= =?utf-8?B?OEViZzgyQk81Y0JLQy9HMDZkZ1F5eW82cUxLdXRRQU5xenJtdWdTV0FwMHpj?= =?utf-8?B?a1JkamszS3FZcG41VHR1WFUycDl0R2U1QXNJaXBrR0lsZ2RCZW15eEhSdHh5?= =?utf-8?B?cTR0cDJ5aUFzR3Y5a3RLQm0yR0R5TlZBRmNvQW5NUkVPeXNQUWx3K0Nkb2xu?= =?utf-8?B?b1d5WEpyemEvaTJHNEp0Vmw1RlNHRit0S1kyRzRVMHdmaXN5empaNXJNUXBB?= =?utf-8?B?blU3WDRoYmRWSzkvb1o3U2pkKzZ6cDlGV2VySW5mdHVmZlV2N3Mza0JWeGsx?= =?utf-8?B?d0dIczdwNnd5OEZtYUhPUTZLSDVKeERhb3dqRTVkK2lRZ0c3VXFScmRxNlpr?= =?utf-8?B?WGFvS2V4R2dSUk94MEgyVXd6Mi90TE5PUUR5Z3hqWVViS2pvMW8zbHNxV0tu?= =?utf-8?B?eVBSaC9qMzdVYlFSTlp2enNHSHVmUzhwaUpEQXFvUWN6U3A3RXV6L0ZRbmt4?= =?utf-8?B?YWl6QWhyTVdzdkhLNkkxQkl0QXBlSkNWcHhRaEpXVldBY1FYUkR4bXdkQ2tw?= =?utf-8?B?bG5xYXVtZnhjSExqVHlvN0hJSWZIbGxRL25kTmVSSWpTaDE4a3RBdEkwLzZi?= =?utf-8?B?T2pwamg0ZFIva3BBdUZLa0ZqMDZseVRoaGEwSk8rS2o3cjJRQ05jaFVQZXhV?= =?utf-8?B?NWhuN0JaK2ZEcUs3UUQvQzlWWFpuaFQ3amQ1K1plWmxtaytTT1p6b1JVdFhs?= =?utf-8?B?M1VPNXdlNS9BbE5IZ2tIYXJlbThydkNFeE1TYkY0NE1lR2JHN0lzazVEa29P?= =?utf-8?B?YUZyQ210ZXgxL01hMGYzRmFjL3NSNEhtbERGaEFCdVpRaU9Fc3lwUmFHdC9i?= =?utf-8?B?VllucVlhYVdEUmNEV0xXeXVmcXJ4OWpSQ2xET0RlVjBNOUcxZzBFOHR5emJJ?= =?utf-8?B?VTlFK2MwYytMNGZoT1k4cFplYXY5QXpmL3l4dG1ZcjlPZVFRUkgybjJtRjhK?= =?utf-8?B?K1BWak1tOG1WQy9jVURrUFBWZFF1aDh6ZytJU2RJOU5IMFQzNGZqbEF3clBK?= =?utf-8?B?M1lCM0NQVDRDVE1UOHJwMmdsZTc2TW92c1NvT2xmL0hiZ0FudDVqK3dUWklr?= =?utf-8?B?d3RNVXRab3AyaURFVDNINjdqbG5icjBJUWxUalgxZndnQ2NVaWM0WTh3Wkw1?= =?utf-8?B?Yk1qNEtLdThZQVBnWERiclF2TVZ6a095aGtzTkJHaGprRTlTREtMb0lXNmMr?= =?utf-8?B?QWxaVXZzR243NlNsOWhTZXVqalo3SmFmejlHaFovVUNWczBRMUpUMCt0cy9J?= =?utf-8?B?eEV4Z05CS0NmbzJ6d1Jyd2Z5ejA5N0hQTHFyOGNCUGhlUlJBNFpoRkFxbHVz?= =?utf-8?B?U2tGVUZwRUxHbXFjTXY2VWVkV1lqcFpMa3E1ZGRMeTFqYllwZDYzNWsvRXhm?= =?utf-8?B?eHV4SVF0ZUNwclYvYUhjdzRhbDczVkF2YWhHb3FtUjhINEZOdG5CMi9QRk5s?= =?utf-8?B?VnBRby9lRmdhRUZTKzNGdHZYTFlVVmVSUW1HTTJsZGhmUmI5Q0RXdTJCemVx?= =?utf-8?B?NXZicnNTRUpVazUzdlVKaE1OSjJpWjhYZ0cxZVpKaHQ1bUc1WHV2ZVo4ZEN5?= =?utf-8?B?SC9naXR4L25WTllONGRPQjhFeHJRRTJkVzh4SlN2bGt2MDZnSzhSN2FKTnRv?= =?utf-8?B?ckxPYTAyRzRaWjUvZEszd3ZTTG5aNTRjSy9Qd05pVVFrSXN4VWxoSWNlNVRK?= =?utf-8?B?aG1aSk5uQjdYNlloWFlRT3JVUURmTWxxdVlYbXFuSkRaL2lxWTZicnp0bHp2?= =?utf-8?B?RXZDdUZMNnpuSlhwd0NMSVBmNFZrc2ZFYndwa2RUQytkLytVVUlRNHV5N1Zx?= =?utf-8?B?aE4zMVJNcStWNHQ3Q0EvSjZXNTR2WS9tdXRyZlo2RDVvQjhsYkwxVTczOVdX?= =?utf-8?B?ZStkS3c5Nm1xb1BibmM4TjNzR2srTTlUUFFMQ3NBTTc4dDBvSVdkMkluS1d0?= =?utf-8?B?L281bllrR2I5ZVNlREtxNlRpVEEwdXhLNnJtYzNlaFVTdElIOWt2cXlHMlBL?= =?utf-8?B?U2hUWVhVYXM2S2dxaHFvVlE1YmN2bGUzdSthSGlWOGdTVms1OVNPSkk2aS9s?= =?utf-8?B?ZnZFUWhyVTRhY1JoKzBuMWN0ZnBHUzlXaytzWk1mZERRYVJ1TGZEM2Vkd0I1?= =?utf-8?B?cDlnb242YmorZ1h2bklqdVVaRkYzRWhiTG9NczhPQm9uTU16QmRJUWRPbDdk?= =?utf-8?B?TlVVUHhONnVaK3pHQ1BBT3FJYmRsS3BEU3RJM1NBbHI1SUhGbU40TmMvRXls?= =?utf-8?Q?9UmErxzx5gKhJxZD8GUvu8VdWH8cyJmmBKRxXGaniyoMG?= X-MS-Exchange-AntiSpam-MessageData-1: GZ/4WeCelu5YXA== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: ed692529-0cc3-4d4e-3a6e-08defc5f554e X-MS-Exchange-CrossTenant-AuthSource: BL0PR12MB2353.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Aug 2026 12:59:11.7480 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: MCSiRropxhEVA2/9sEqVc/kkfFzVaL5gVobRgtlAaKhGuokZw/mAMxojyjZLllNMlCUskFUfVawf5k22Wvb2lg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH7PR12MB6833 Add an encoder for NVKV, which is the wire format for GMCAPI. The encoded stream is a sequence of 64-bit values. The first 64-bit value encodes an op word which describes the function of the next N values. Essentially, the format encodes a sequence of calls to some function f(key, index, value), where value is a [u8], u32, u64, [u32], or a [u64]. The key is a u16 and the index is a 12 bit integer. The interpretation of these function calls is per GMCAPI. Add tests for the wire encoding for each primitive. Signed-off-by: Eliot Courtney --- drivers/gpu/nova-core/gsp.rs | 1 + drivers/gpu/nova-core/gsp/nvkv.rs | 79 ++++++++++ drivers/gpu/nova-core/gsp/nvkv/encode.rs | 245 +++++++++++++++++++++++++++= ++++ 3 files changed, 325 insertions(+) diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs index 13f361406a6c..84dfe07ae6ba 100644 --- a/drivers/gpu/nova-core/gsp.rs +++ b/drivers/gpu/nova-core/gsp.rs @@ -24,6 +24,7 @@ pub(crate) mod cmdq; pub(crate) mod commands; mod fw; +mod nvkv; mod regs; mod sequencer; =20 diff --git a/drivers/gpu/nova-core/gsp/nvkv.rs b/drivers/gpu/nova-core/gsp/= nvkv.rs new file mode 100644 index 000000000000..b908f66e760d --- /dev/null +++ b/drivers/gpu/nova-core/gsp/nvkv.rs @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIA= TES. All rights reserved. + +//! Codec for NVKV, the binary key-value format of GMCAPI. +//! +//! Essentially, the format encodes a sequence of calls to some function f= (key, index, value), +//! where value is a [u8], u32, u64, [u32], or a [u64]. The key is a u16 a= nd the index is a 12 bit +//! integer. The interpretation of these function calls is per GMCAPI. Gen= erally speaking, the +//! function calls will map to some struct - for example, f(GPU_NAME_STRIN= G_KEY, 0, b"some gpu") +//! naturally maps to storing a &str with the GPU name. + +#![expect(unused_imports)] + +use kernel::{ + bitfield, + num::Bounded, + prelude::*, // +}; + +mod encode; +pub(crate) use encode::*; + +/// The identifier of an NVKV key. +pub(crate) type KeyId =3D u16; + +/// The index of an NVKV value. +pub(crate) type Index =3D Bounded; + +bitfield! { + /// The op word that starts each NVKV operation. + struct Op(u64) { + 15:0 key; + 27:16 index =3D> Index; + 31:28 opcode ?=3D> Opcode; + 63:32 value; + } +} + +/// Describes the format of the following NVKV operation. +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(u8)] +enum Opcode { + /// A 32-bit value in the op word. + Imm32 =3D 0, + /// 32-bit values for consecutive keys, starting at the op word's key. + Seq32 =3D 1, + /// 64-bit values for consecutive keys, starting at the op word's key. + Seq64 =3D 2, + /// An array of bytes. + Array8 =3D 3, + /// An array of 32-bit elements. + Array32 =3D 4, + /// An array of 64-bit elements. + Array64 =3D 5, +} + +// TODO[FPRI]: This is a temporary solution to be replaced with the corres= ponding derive macros once +// they land. +impl TryFrom> for Opcode { + type Error =3D Error; + + fn try_from(value: Bounded) -> Result { + match value.get() { + 0 =3D> Ok(Self::Imm32), + 1 =3D> Ok(Self::Seq32), + 2 =3D> Ok(Self::Seq64), + 3 =3D> Ok(Self::Array8), + 4 =3D> Ok(Self::Array32), + 5 =3D> Ok(Self::Array64), + _ =3D> Err(EINVAL), + } + } +} + +impl From for Bounded { + fn from(value: Opcode) -> Self { + Bounded::from_expr(value as u64) + } +} diff --git a/drivers/gpu/nova-core/gsp/nvkv/encode.rs b/drivers/gpu/nova-co= re/gsp/nvkv/encode.rs new file mode 100644 index 000000000000..6da81a371651 --- /dev/null +++ b/drivers/gpu/nova-core/gsp/nvkv/encode.rs @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIA= TES. All rights reserved. + +#![cfg_attr(not(CONFIG_KUNIT), expect(dead_code))] + +use kernel::prelude::*; + +use super::{ + Index, + KeyId, + Op, + Opcode, // +}; + +/// An encoder for an NVKV stream. +pub(crate) struct Encoder { + backing: KVVec, +} + +impl Encoder { + /// Creates an empty encoder. + pub(crate) fn new() -> Self { + Self { + backing: KVVec::new(), + } + } + + /// Appends `bytes` to the stream, padded to a multiple of 8 bytes. + fn push_bytes_with_padding(&mut self, bytes: &[u8]) -> Result { + let num_entries =3D bytes.len().div_ceil(size_of::()); + self.backing.reserve(num_entries, GFP_KERNEL)?; + + let spare =3D self.backing.spare_capacity_mut(); + let dst =3D spare.as_mut_ptr().cast::(); + + // SAFETY: At least `bytes.len()` bytes of space are guaranteed si= nce `num_entries` + // worth of space was just reserved. + unsafe { core::ptr::copy_nonoverlapping(bytes.as_ptr(), dst, bytes= .len()) }; + + let padding =3D num_entries * size_of::() - bytes.len(); + if padding > 0 { + // SAFETY: At least `num_entries * size_of::()` bytes of = space are guaranteed. + unsafe { core::ptr::write_bytes(dst.add(bytes.len()), 0, paddi= ng) }; + } + + // SAFETY: These bytes were just initialized and every bit pattern= is valid for `u64`. + unsafe { self.backing.inc_len(num_entries) }; + + Ok(()) + } + + /// Returns the encoded data. + #[must_use =3D "encoded data must be consumed"] + pub(crate) fn finish(self) -> KVVec { + self.backing + } + + #[inline(always)] + fn encode_op(&mut self, op: Op) -> Result { + self.backing.push(op.into_raw(), GFP_KERNEL)?; + Ok(()) + } + + /// Encodes a 32-bit value as an IMM32 pair, with the value in the op = word. + #[inline(always)] + pub(crate) fn encode_u32(&mut self, key: KeyId, index: Index, value: u= 32) -> Result { + // TODO: Consider automatically merging sequential keys. + self.encode_op( + Op::zeroed() + .with_key(key) + .with_index(index) + .with_opcode(Opcode::Imm32) + .with_value(value), + )?; + Ok(()) + } + + /// Encodes a 64-bit value as a single-element SEQ64 pair. + #[inline(always)] + pub(crate) fn encode_u64(&mut self, key: KeyId, index: Index, value: u= 64) -> Result { + // TODO: Consider automatically merging sequential keys. + const KEY_COUNT: u32 =3D 1; + self.backing.reserve(2, GFP_KERNEL)?; + self.encode_op( + Op::zeroed() + .with_key(key) + .with_index(index) + .with_opcode(Opcode::Seq64) + .with_value(KEY_COUNT), + )?; + self.backing.push_within_capacity(value)?; + Ok(()) + } + + /// Encodes a byte array as an ARRAY8 pair, zero-padded to a multiple = of 8 bytes. + #[inline(always)] + pub(crate) fn encode_array8(&mut self, key: KeyId, index: Index, array= : &[u8]) -> Result { + let value_count =3D u32::try_from(array.len()).map_err(|_| EMSGSIZ= E)?; + let num_entries =3D array.len().div_ceil(size_of::()); + self.backing.reserve(num_entries + 1, GFP_KERNEL)?; + self.encode_op( + Op::zeroed() + .with_key(key) + .with_index(index) + .with_opcode(Opcode::Array8) + .with_value(value_count), + )?; + self.push_bytes_with_padding(array.as_bytes())?; + Ok(()) + } + + /// Encodes a 32-bit array as an ARRAY32 pair, zero-padded to a multip= le of 8 bytes. + #[inline(always)] + pub(crate) fn encode_array32(&mut self, key: KeyId, index: Index, arra= y: &[u32]) -> Result { + let value_count =3D u32::try_from(array.len()).map_err(|_| EMSGSIZ= E)?; + let num_entries =3D array.len().div_ceil(2); + self.backing.reserve(num_entries + 1, GFP_KERNEL)?; + self.encode_op( + Op::zeroed() + .with_key(key) + .with_index(index) + .with_opcode(Opcode::Array32) + .with_value(value_count), + )?; + self.push_bytes_with_padding(array.as_bytes())?; + Ok(()) + } + + /// Encodes a 64-bit array as an ARRAY64 pair. + #[inline(always)] + pub(crate) fn encode_array64(&mut self, key: KeyId, index: Index, arra= y: &[u64]) -> Result { + let value_count =3D u32::try_from(array.len()).map_err(|_| EMSGSIZ= E)?; + self.backing.reserve(array.len() + 1, GFP_KERNEL)?; + self.encode_op( + Op::zeroed() + .with_key(key) + .with_index(index) + .with_opcode(Opcode::Array64) + .with_value(value_count), + )?; + self.push_bytes_with_padding(array.as_bytes())?; + Ok(()) + } +} + +#[kunit_tests(nova_core_nvkv_encode)] +mod tests { + use super::*; + + // Tests that each kind of value is encoded to NVKV wire format proper= ly. + #[test] + fn encode_all_value_kinds() -> Result { + // All keys, indexes, and values are distinct but arbitrary values= to make it easier for the + // test to catch bugs in the encoded output. + const U32_KEY: KeyId =3D 0x1001; + const U64_KEY: KeyId =3D 0x1002; + const ARRAY8_KEY: KeyId =3D 0x1003; + const ARRAY32_KEY: KeyId =3D 0x1004; + const ARRAY64_KEY: KeyId =3D 0x1005; + + const U32_VALUE: u32 =3D 0x1111_2222; + const U64_VALUE: u64 =3D 0x3333_4444_5555_6666; + const ARRAY8_VALUE: &[u8] =3D &[0xaa, 0xbb, 0xcc]; + const ARRAY32_VALUE: &[u32] =3D &[0xbbbb_cccc, 0xdddd_eeee]; + const ARRAY64_VALUE: &[u64] =3D &[0x0123_4567_89ab_cdef, 0xfedc_ba= 98_7654_3210]; + + let mut encoder =3D Encoder::new(); + encoder.encode_u32(U32_KEY, Index::new::<0>(), U32_VALUE)?; + encoder.encode_u64(U64_KEY, Index::new::<1>(), U64_VALUE)?; + encoder.encode_array8(ARRAY8_KEY, Index::new::<2>(), ARRAY8_VALUE)= ?; + encoder.encode_array32(ARRAY32_KEY, Index::new::<3>(), ARRAY32_VAL= UE)?; + encoder.encode_array64(ARRAY64_KEY, Index::new::<4>(), ARRAY64_VAL= UE)?; + + let encoded =3D encoder.finish(); + assert_eq!(encoded.len(), 10); + + // IMM32 has its value in the op word. + assert_eq!( + encoded[0], + Op::zeroed() + .with_key(U32_KEY) + .with_index(Index::new::<0>()) + .with_opcode(Opcode::Imm32) + .with_value(U32_VALUE) + .into_raw() + ); + + // The SEQ64 op word followed by the value. + assert_eq!( + encoded[1], + Op::zeroed() + .with_key(U64_KEY) + .with_index(Index::new::<1>()) + .with_opcode(Opcode::Seq64) + .with_value(1u32) + .into_raw() + ); + assert_eq!(encoded[2], U64_VALUE); + + // The ARRAY8 op word has the byte count. The bytes follow, padded= out to a whole word. + assert_eq!( + encoded[3], + Op::zeroed() + .with_key(ARRAY8_KEY) + .with_index(Index::new::<2>()) + .with_opcode(Opcode::Array8) + .with_value(3u32) + .into_raw() + ); + assert_eq!( + encoded[4], + u64::from_le_bytes([0xaa, 0xbb, 0xcc, 0, 0, 0, 0, 0]) + ); + + // The ARRAY32 op word has the element count. The two elements fol= low in little endian. + assert_eq!( + encoded[5], + Op::zeroed() + .with_key(ARRAY32_KEY) + .with_index(Index::new::<3>()) + .with_opcode(Opcode::Array32) + .with_value(2u32) + .into_raw() + ); + assert_eq!( + encoded[6], + u64::from(ARRAY32_VALUE[1]) << 32 | u64::from(ARRAY32_VALUE[0]) + ); + + // The ARRAY64 op word has the element count with the two elements= after. + assert_eq!( + encoded[7], + Op::zeroed() + .with_key(ARRAY64_KEY) + .with_index(Index::new::<4>()) + .with_opcode(Opcode::Array64) + .with_value(2u32) + .into_raw() + ); + assert_eq!(encoded[8], ARRAY64_VALUE[0]); + assert_eq!(encoded[9], ARRAY64_VALUE[1]); + + Ok(()) + } +} --=20 2.55.0 From nobody Mon Sep 28 21:03:43 2026 Received: from BN1PR04CU002.outbound.protection.outlook.com (mail-eastus2azon11010063.outbound.protection.outlook.com [52.101.56.63]) (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 323C541F5C5; Mon, 17 Aug 2026 12:59:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.56.63 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971566; cv=fail; b=bIJEth8nC/h9zCtqOo4+p3z6AVJngZvflFmKa7/nkrEklCvE0AlLYUVs8Yg2cIpwNpo++MPrizToP0oCXfacqJ1dGMS3nFoH+QSVoAp8qHVnoInlfw94gDXRfq5B+E3wFYsId6wekInhi9RkDsldbrB8DfZSYoT9gOjhLGPgNcI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971566; c=relaxed/simple; bh=wiIrYLCqwy96rWdHdmde+OQtRRainemxMqFIEVX2TtY=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=Lz61R/12yWBoFnfb289a/sVlDUyzgXSXkiQjhMAEdLVIsZw2967XNEBE9Yf4ssrZq83GnGXXnwYgC22eB/6vc0eKZ0UjRWLTGatK5F95VaQhpSECSxzR9N2nEuay8qcnL5/YMrP0GbWZ07kHdsHDl1PmA67s92DS/1xR78MhhRY= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=p2PvxBcW; arc=fail smtp.client-ip=52.101.56.63 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="p2PvxBcW" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=c8GgprwrPme29+qJ5ksE0qiIsm29JeaG3SuDdJFrcrqNQMQJ1w/LqbMM9GZQ+g9CNfS4TpzOefgrivyIVH8pLSvA40MOjVs55r6GzcJSiLI9VMe1hMfpCDm89xVYA5YizFK875J6zp+udgNFkJJokos/zrRvNr/nC/1+m0KK2Ic2N+uTa42wD5wcRxigR5bDM3Z00Z7PzB/J5GoIiSq/viLwdJ1wezbTVJg3mxkn30C9LzF9ciz6aBjOnScrzTO64aBWZdf1SUq2aofhxH8zRgnHvsBL0m7nmNu92/IHwmBoVJB4dhKBYd9QL+Bg/SLw8sggUKJvfPbipCjTVoYiwg== 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=QWuzfJc2Ddb58WWGGah14l7Au5HXeV0jV9L2DSjSv4c=; b=wTjUZvCCjKypZK504Sb5HQdA1RzcPZYWo5RHTI/AFSlg3uIk9zsrTrHISQ6ZcxNhGjliVhZ8h1sWJdzWSAFegfZzfox35/7K5QAy2YkxmuDinS6Yc71u3QS+rXb8buOGjGEIhov+fBU+doW6Mn/GrkySl5HsxUxaU+aXqbkTl7Z3TaohJvn2gmQHY640zruszzJf7kNryZTSyulTAUdsckK+i1agfhceW4UIcrt3vtv2NbnfLpPiuqv5maLpmqPC9RvmjqU6APaW0cXAt9xooo/Q5DpRts+pQnvFEMs5Ei+7UKPmAJkG0YbZeK+Ei146/1mcpYHe8MhZYJDF/CtCtQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=QWuzfJc2Ddb58WWGGah14l7Au5HXeV0jV9L2DSjSv4c=; b=p2PvxBcW/H4C8YQjU79fzNN8oQwvFWwvi2/s4aCfpP7JbiQk7mCSZQLiP97hb0CIrKm+YX5H0Xo4UN9dS0sAb97JjvJdw1fcydk+jtsezW9bwzWqJ9flUaio458GrvzwHueGKMXS+1/47eqmeeHH/APRZUxsVd9eov2i+en4DuacROb5mftf/OcWDB1NbVhNCQ5dAr6Y6G67IB411ZKGZyNchVbrGU6MKOu0Mt65Kl1ybAjxRxHcvCtTrKB/3FaY8j2ohhfMBxcIXbVF4+lXmGa+cwvGsx9X/cEVwdOCgudsnl9IoE/306qQih79WZ/+lslujRQ6TpUBkX8vu1ct9A== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) by PH7PR12MB6833.namprd12.prod.outlook.com (2603:10b6:510:1af::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.315.13; Mon, 17 Aug 2026 12:59:16 +0000 Received: from BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0]) by BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0%4]) with mapi id 15.21.0315.016; Mon, 17 Aug 2026 12:59:16 +0000 From: Eliot Courtney Date: Mon, 17 Aug 2026 21:56:38 +0900 Subject: [PATCH 3/6] gpu: nova-core: add NVKV decoder Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260817-b4-nvkv-v1-3-b84db5e84b67@nvidia.com> References: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> In-Reply-To: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> To: Danilo Krummrich , Lorenzo Stoakes , Vlastimil Babka , "Liam R. Howlett" , Uladzislau Rezki , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , David Airlie , Simona Vetter Cc: John Hubbard , Alistair Popple , Timur Tabi , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, Eliot Courtney X-Mailer: b4 0.15.2 X-ClientProxiedBy: TY6P286CA0020.JPNP286.PROD.OUTLOOK.COM (2603:1096:405:3b9::11) To BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) 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: BL0PR12MB2353:EE_|PH7PR12MB6833:EE_ X-MS-Office365-Filtering-Correlation-Id: 33b9aaa7-c581-45dd-4941-08defc5f57d6 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|376014|23010399003|1800799024|366016|7416014|10070799003|18002099003|921020|22082099003|56012099006|10067099003|5023799004|11063799006|6133799003|3023799007; X-Microsoft-Antispam-Message-Info: 31vS6h8Zyv14+nBvzWcGwsgauE3JHbBa7uaJrX1EDeeeXhpruyG5ERxietMjgA4o1ZeE8WQi9aPM/U3tzXCIBO1q/2fSGQxpzU/K6YOAIhKZ43TbC99rAmj8rVW38XxtTXfJ6K5Y0lkfyKdyrQAJqSZVUbkog6oSVxtK6DYc4YcygbLli7XWsuAfvDPce6QMhJFNmEpUYgqrnWd055XdW1+uXMkoimOkGKjszjrTNERf5mya9drQQXHaw615CPIB5E7sZJ43CipWXKGCc+sAEEEMu2lfH6PH95Bdqr7IaPzHyZSRhBADFckaB9tV0B54JJJsMLFF4uQFXmx2q0KDC5gUlKBbNsBIRHEZ7U2qkTHMbv35cjjCehvxB1tlcRKcg9ZuzWDBvfO5R+DYVi3t1X4JuRu2mF7qSPBeIv9Ho419GUyx2W293dauYfTjzRxCygOOGHIZJrpZ529SUt6pgPN6PKI37PzaTdwXvUuVqJVE62iUnvLrcfbMJkBQjfy+/lJ5R/PhjcsufCZ2jHO3MSPsHXE3ZoygpIj1/2YJ6C09emUJEhwtUjJ0JB9xeMVjgjNz9Wr03rxhKyf/Pz8jEwUlvTntBw805RdvrEirmd8ZtQpc/AdRiON3tm5UGDFk5XCh5cecStabEzeTqICz2Swsvc6jb6ssTIBUFBxh8qwCTB9gdVTLsnn0qFyPPtzLdsoArtiyQl2RKoPVRCtx8A== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BL0PR12MB2353.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(376014)(23010399003)(1800799024)(366016)(7416014)(10070799003)(18002099003)(921020)(22082099003)(56012099006)(10067099003)(5023799004)(11063799006)(6133799003)(3023799007);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?M09scGNMNEtFenA0OXRCZkRxRVRCanJlbWY1NUNaTjZhY0gyMXp0Mm94VWo1?= =?utf-8?B?TmRuaXFjRDlneHNaTVE4eGpJSS9iVzFCK3dydXF1dXdjVjFOUjFpZ1JCV1R4?= =?utf-8?B?S01EZE1WU2ZaL0xQdzFCYVB1R1NHd3hqQjd5T2xXWldFQ2JIcmpmelJYNnhj?= =?utf-8?B?TFB3eVlzLzhraEtQNHpTeU5PVk5VS3J5WTJmYVJUVU5XdmZ6eHJMYkhkWnlB?= =?utf-8?B?eWJQU0pOYXAvRkxOYjNTeG1IQmZSdDIra3JGSjY2bG0rMmVpbDlQeGo3VlZ6?= =?utf-8?B?c21SSExiZnlabUpaVTRodkpMMFp5RUFieDNOZGd4dVNMd0xnejVYbStQbEd4?= =?utf-8?B?YWN2OXJUUlU2N00wc2E0OURKSlhEaFkvMCtKN3QxT0V3SCsxSHdHdUo0SzlY?= =?utf-8?B?OVBuQ3NoeVNyL254bnlRSFFyVG5naWllQUVaWVNFaHpMWFphUHI5aUdZMHI0?= =?utf-8?B?eWNCdHUxQXY0N2NSa0J5d2ZEcEdneXJFS0U1VGVHRVRrMmRLVndQZ3pCVmlB?= =?utf-8?B?ZkJNSnExcTlWZk5uUmhvS1Q0UzhaMEtNOTNYT3Y2SkhVRVgwMUZ3ZXRPMkMv?= =?utf-8?B?TjBwamUvTHMwYjVpalVNWjFzekpFcWRyUldwTmx1T2wrTDdKQ3BYSzV5eXNW?= =?utf-8?B?S0VSUDB5SFJpNHN2N2dkSFNHKzI3RUZuUEMzQ1cyREdzWGQ0VkZHZG41S3BF?= =?utf-8?B?anVLMDBVRDV3MWdOT01EbHZCTlBFTXhobWNoazFFb005VE1TZ1llRkJyQy9J?= =?utf-8?B?YnBPeGJjNUx6UlJNdG1YMFZncW1HeGNvVUdmNWZ0enFoNkNMNGxEUGl0NUhq?= =?utf-8?B?ZFNidkU4aElFalNtckFhdUJGQ084aGg2MEIwaTBRM3UwaTArcFB2Q2Y0WW8w?= =?utf-8?B?ZHFpNDFIajk2TDd4NXNoLzUxWExtY0VtTjlrZUZMU1YvVld1aFZERTBMMzJn?= =?utf-8?B?UnVKaWFpb1MyaURqNWZoZEVKYkRMNHRxclZkdHUrWVFJZmNuSVFkQmVnamlS?= =?utf-8?B?YWhvWkxGY25XSG5KbGljcEIvZDVCZmc3bjVCcUxwdHZWdEpLN0xLZmhhQnM3?= =?utf-8?B?N0YwRm1WSVhRTmZLVjBBKy9CZVU4RWw0OFRyS3BFWTkwWTRMNnlkZFUrbXh6?= =?utf-8?B?UEc0S3lJdmlPZGx1K0YyS0VuY21DazRjcVJtZkovY3d3N1ZWdFFQMno3Y0lm?= =?utf-8?B?UEdKREFZcTM0Uk1UYk0rMU04c0MyVmo3TlExdzdMZXArV201SVA0ejZzZ3Qz?= =?utf-8?B?emxrZERKNXlMeEQxT1F5NWVjZFF1YWR2MkxOWm5SaFZ3cklhNkVmM3haRUc5?= =?utf-8?B?OFdsMTRWRmNZTWpzbXpXRC9yMUNqL2hHMklZemZIK3g2S2xsVHhHRkQyTFlB?= =?utf-8?B?SHc5d1pVQ2pTdVZZbmc2MXdvNVhOMzZ5emx1QzlGcndjVUhNRUp6eC9MaHhs?= =?utf-8?B?eUVOVW9iT05ndlBjaUJwQnVQekJhdm5LQWtDMUY0VmcwQ3h2L1E1UUpPeTZl?= =?utf-8?B?TDF3Wk1wdTR6bXNBeXRRb3ByZGNjOEpxTmZTdDhCNUZkckVSc01nUXhLRDVS?= =?utf-8?B?NFM5Z0xlWGErcXhuL0hyQ3RtOC9yd3R6ZlhRUzN0Q1RLTFhvWThjbHRza0d4?= =?utf-8?B?ZUxZb1VFUmRLaUw3TXNGYnRFZDlpSVd2UHZXc3o0bTE3aTVHdHk4dFpiMmdh?= =?utf-8?B?L2s2WHlvZmlZQytuRVE0V2tXdGk0ZmdrdnFZVkptREVhak5RZ0dvY29EaVZR?= =?utf-8?B?UlNxenlJSGJDTTN0b2RrRWJGNXV6dTVhMDhhSWhEMlA1Vks1TXJ3Wm9hOFZ0?= =?utf-8?B?dlF6RzJocFdqYTNyaXNRVTgwK3ovQ2ZZc2dtMnVyMjRMNS82L05mY3NYT1Jm?= =?utf-8?B?akxMZGROeEZESzB3MkZndWVqTTJOOFVuOGVUQTh3RHBQZTBSQms0cU04U2Fi?= =?utf-8?B?TTFVa0NYdHRWcGhRamZnVVlsV3FHbENlV1R6WGIxU0R1Ymp6eFlaZERtNGR2?= =?utf-8?B?eGhyVElxT0VremJrOEU2NE44K0xOVnJ1Mmg1eVNJajRBUVVaWWlibGp1R1ZZ?= =?utf-8?B?MkkzOGNjNmZYUUorZU40RFUyK1prMmY0eFVIZkxXOHlKVi9qN1V4OHlsVWxT?= =?utf-8?B?MFFoOXFPK0ZyTFpqYUdFUGRzSStHUkJFZG9XUXZ1S2VaQWlxQzZxMUlmRXY1?= =?utf-8?B?QmFLSzlreHY3am9IaWswUU16VFB1TldNK0RrVWx6MjNRbmF0UVFmZ0lGeHZJ?= =?utf-8?B?SENwRkdRRGhKSlB0dmI5QjNZU2FtWVI2eTlsT1F6NVppNW0wWG9lM1NsbXZU?= =?utf-8?B?R0p0ZVZqV29oMStzaFd4UXJUU2JOWlpDMUpMVTNHK1U2ajArY1V2TEtyU3VL?= =?utf-8?Q?V1u+93thxCxnoBkuOl/eYNwdaI1G70iltX//Bjw1SSRiw?= X-MS-Exchange-AntiSpam-MessageData-1: uOWSXwBMIsO0vQ== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 33b9aaa7-c581-45dd-4941-08defc5f57d6 X-MS-Exchange-CrossTenant-AuthSource: BL0PR12MB2353.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Aug 2026 12:59:15.9973 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: h0+rwfZUpOW1bVDaBAx8+7ISJKhdMUa3ZJk9n/qaWtOWNnSIaMOqlwn4Sucp9pvGiyVEOS1H8t7ruMT4D6azMw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH7PR12MB6833 Add a decoder for NVKV. This is for receiving messages from GSP for GMCAPI calls. The NVKV format essentially encodes a sequence of function calls f(key, index, value). This decoder reads an encoded stream and invokes a type implementing the new `Schema` visitor trait. The `Schema` trait can either consume the value or not, which is useful for composing Schemas. If a (key, index, value) is not consumed, error out depending on `UnknownKeyPolicy`. Whether ignoring unknown keys is ok or not is per each GMCAPI call. Add kunit tests for the decoder. Signed-off-by: Eliot Courtney --- drivers/gpu/nova-core/gsp/nvkv.rs | 3 + drivers/gpu/nova-core/gsp/nvkv/decode.rs | 258 +++++++++++++++++++++++++++= ++++ 2 files changed, 261 insertions(+) diff --git a/drivers/gpu/nova-core/gsp/nvkv.rs b/drivers/gpu/nova-core/gsp/= nvkv.rs index b908f66e760d..64d8d0118452 100644 --- a/drivers/gpu/nova-core/gsp/nvkv.rs +++ b/drivers/gpu/nova-core/gsp/nvkv.rs @@ -20,6 +20,9 @@ mod encode; pub(crate) use encode::*; =20 +mod decode; +pub(crate) use decode::*; + /// The identifier of an NVKV key. pub(crate) type KeyId =3D u16; =20 diff --git a/drivers/gpu/nova-core/gsp/nvkv/decode.rs b/drivers/gpu/nova-co= re/gsp/nvkv/decode.rs new file mode 100644 index 000000000000..ee8b6ab5a3a4 --- /dev/null +++ b/drivers/gpu/nova-core/gsp/nvkv/decode.rs @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIA= TES. All rights reserved. + +#![cfg_attr(not(CONFIG_KUNIT), expect(dead_code))] + +use kernel::prelude::*; + +use crate::gsp::nvkv::{ + Index, + KeyId, + Op, + Opcode, // +}; +use crate::num; + +/// A decoded NVKV value. +#[derive(Copy, Clone)] +pub(crate) enum DecoderValue<'a> { + Scalar32(u32), + Scalar64(u64), + Array8(&'a [u8]), + Array32(&'a [u32]), + Array64(&'a [u64]), +} + +/// Implements `TryFrom` from the given `DecoderValue` variant to the give= n type. +/// +/// `TryFrom` is used by the `Schema` implementations in this file to conv= ert from the +/// `DecoderValue`s into the types to store. Provide the implementations f= or basic types here. +macro_rules! impl_try_from_decoder_value { + ($ty:ty, $variant:ident) =3D> { + impl<'a> TryFrom> for $ty { + type Error =3D Error; + + fn try_from(value: DecoderValue<'a>) -> Result { + if let DecoderValue::$variant(v) =3D value { + Ok(v) + } else { + Err(EINVAL) + } + } + } + }; +} + +impl_try_from_decoder_value!(u32, Scalar32); +impl_try_from_decoder_value!(u64, Scalar64); +impl_try_from_decoder_value!(&'a [u8], Array8); +impl_try_from_decoder_value!(&'a [u32], Array32); +impl_try_from_decoder_value!(&'a [u64], Array64); + +/// A visitor that consumes decoded NVKV and produces a `Target`. +pub(crate) trait Schema { + type Target; + + /// Visits one decoded pair. Returns `Ok(true)` if the schema consumed= it. + fn visit<'a>(&mut self, key: KeyId, index: Index, value: DecoderValue<= 'a>) -> Result; + + /// Returns an initializer that makes the decoded `Target`. + fn finish(self) -> impl Init; +} + +/// A read position in an NVKV stream. +struct Cursor<'a> { + data: &'a [u64], +} + +impl<'a> Cursor<'a> { + fn new(data: &'a [u64]) -> Self { + Self { data } + } + + fn is_empty(&self) -> bool { + self.data.is_empty() + } + + fn take_u64(&mut self) -> Result { + // PANIC: `take_u64s(1)` returns exactly one element on success. + Ok(self.take_u64s(1)?[0]) + } + + fn take_u8s(&mut self, count: usize) -> Result<&[u8]> { + let values =3D self.take_u64s(count.div_ceil(8))?; + values.as_bytes().get(..count).ok_or(EINVAL) + } + + fn take_u32s(&mut self, count: usize) -> Result<&[u32]> { + let values =3D self.take_u64s(count.div_ceil(2))?; + // SAFETY: `values` is 8 byte aligned and only 4 byte alignment is= required. All bit + // patterns are valid for `u32`. + Ok(unsafe { core::slice::from_raw_parts(values.as_ptr().cast::(), count) }) + } + + fn take_u64s(&mut self, count: usize) -> Result<&[u64]> { + let (prefix, suffix) =3D self.data.split_at_checked(count).ok_or(E= INVAL)?; + self.data =3D suffix; + Ok(prefix) + } +} + +/// A decoder for an NVKV stream. +pub(crate) struct Decoder<'a> { + data: &'a [u64], + policy: UnknownKeyPolicy, +} + +impl<'a> Decoder<'a> { + /// Creates a decoder for `data` that handles unknown keys per `policy= `. + pub(crate) fn new(data: &'a [u64], policy: UnknownKeyPolicy) -> Self { + Self { data, policy } + } + + fn visit( + &self, + schema: &mut S, + key: KeyId, + index: Index, + value: DecoderValue<'_>, + ) -> Result { + let consumed =3D schema.visit(key, index, value)?; + if !consumed && self.policy =3D=3D UnknownKeyPolicy::Error { + Err(EINVAL) + } else { + Ok(()) + } + } + + fn seq_key(base: KeyId, offset: usize) -> Result { + base.checked_add(KeyId::try_from(offset)?).ok_or(EINVAL) + } + + /// Decodes every pair into `schema` and returns the result of [`Schem= a::finish`]. + pub(crate) fn decode(&self, mut schema: S) -> Result> { + let mut cursor =3D Cursor::new(self.data); + while !cursor.is_empty() { + let op: Op =3D cursor.take_u64()?.into(); + + let key =3D op.key().into(); + let index =3D op.index(); + let op_value: u32 =3D op.value().into(); + match op.opcode()? { + Opcode::Imm32 =3D> { + self.visit(&mut schema, key, index, DecoderValue::Scal= ar32(op_value))?; + } + Opcode::Seq32 =3D> { + let values =3D cursor.take_u32s(num::u32_as_usize(op_v= alue))?; + for (i, &value) in values.iter().enumerate() { + let key =3D Self::seq_key(key, i)?; + self.visit(&mut schema, key, index, DecoderValue::= Scalar32(value))?; + } + } + Opcode::Seq64 =3D> { + let values =3D cursor.take_u64s(num::u32_as_usize(op_v= alue))?; + for (i, &value) in values.iter().enumerate() { + let key =3D Self::seq_key(key, i)?; + self.visit(&mut schema, key, index, DecoderValue::= Scalar64(value))?; + } + } + Opcode::Array8 =3D> { + let value =3D cursor.take_u8s(num::u32_as_usize(op_val= ue))?; + self.visit(&mut schema, key, index, DecoderValue::Arra= y8(value))?; + } + Opcode::Array32 =3D> { + let value =3D cursor.take_u32s(num::u32_as_usize(op_va= lue))?; + self.visit(&mut schema, key, index, DecoderValue::Arra= y32(value))?; + } + Opcode::Array64 =3D> { + let value =3D cursor.take_u64s(num::u32_as_usize(op_va= lue))?; + self.visit(&mut schema, key, index, DecoderValue::Arra= y64(value))?; + } + }; + } + Ok(schema.finish()) + } +} + +/// This is defined per call. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum UnknownKeyPolicy { + Ignore, + Error, +} + +#[kunit_tests(nova_core_nvkv_decode)] +mod tests { + use super::*; + + use crate::gsp::nvkv::Encoder; + + // Tests that basic decoding into a manually implemented `Schema` work= s correctly. + #[test] + fn decode_raw_schema() -> Result { + // Decodes an IMM32 pair and a SEQ64 pair (the encoder emits a u64= as a single-element + // SEQ64) with a hand written `Schema`. Keys and value constants c= hosen to distinguish e.g. + // saving the wrong value to the wrong location. + const SCALAR32_KEY: KeyId =3D 0x1001; + const SCALAR64_KEY: KeyId =3D 0x1002; + const UNKNOWN_KEY: KeyId =3D 0x2001; + + const SCALAR32_VALUE: u32 =3D 0x1111_2222; + const SCALAR64_VALUE: u64 =3D 0x3333_4444_5555_6666; + + // The output type of the hand written Schema. In this case, we ca= n have it also implement + // `Schema` on itself rather than having a separate carrier type, = since the `Schema` + // implementation is completely stateless. + #[derive(Default)] + struct RawSchema { + scalar32: u32, + scalar64: u64, + } + + impl Schema for RawSchema { + type Target =3D Self; + + fn visit(&mut self, key: KeyId, index: Index, value: DecoderVa= lue<'_>) -> Result { + if index !=3D Index::new::<0>() { + return Err(EINVAL); + } + match key { + SCALAR32_KEY =3D> self.scalar32 =3D value.try_into()?, + SCALAR64_KEY =3D> self.scalar64 =3D value.try_into()?, + _ =3D> return Ok(false), + } + Ok(true) + } + + fn finish(self) -> impl Init { + Ok(self) + } + } + + let mut encoder =3D Encoder::new(); + encoder.encode_u32(SCALAR32_KEY, Index::new::<0>(), SCALAR32_VALUE= )?; + encoder.encode_u64(SCALAR64_KEY, Index::new::<0>(), SCALAR64_VALUE= )?; + let serialized =3D encoder.finish(); + + let decoder =3D Decoder::new(&serialized, UnknownKeyPolicy::Error); + let decoded =3D KBox::try_init(decoder.decode(RawSchema::default()= )?, GFP_KERNEL)?; + + assert_eq!(decoded.scalar32, SCALAR32_VALUE); + assert_eq!(decoded.scalar64, SCALAR64_VALUE); + + // An unknown key should fail with under `UnknownKeyPolicy::Error`= and be skipped under + // `UnknownKeyPolicy::Ignore`. + let mut encoder =3D Encoder::new(); + encoder.encode_u32(UNKNOWN_KEY, Index::new::<0>(), 1)?; + + let serialized =3D encoder.finish(); + let decoder =3D Decoder::new(&serialized, UnknownKeyPolicy::Error); + assert!(decoder.decode(RawSchema::default()).is_err()); + + let decoder =3D Decoder::new(&serialized, UnknownKeyPolicy::Ignore= ); + let decoded =3D KBox::try_init(decoder.decode(RawSchema::default()= )?, GFP_KERNEL)?; + assert_eq!(decoded.scalar32, 0); + + Ok(()) + } +} --=20 2.55.0 From nobody Mon Sep 28 21:03:43 2026 Received: from BN1PR04CU002.outbound.protection.outlook.com (mail-eastus2azon11010063.outbound.protection.outlook.com [52.101.56.63]) (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 3C47641F34C; Mon, 17 Aug 2026 12:59:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.56.63 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971568; cv=fail; b=gEn7PYD2drBM4FnByg8rQ/s+7fNOo/sl9HBV6U/HmWZwAc4n20EcY+1tTS+VrlWhH9y8W0L3GioY4+kyBq3YK/8SQHPeu3z5rsPq+iaEKyvPqCl6KdX+3zc05D+COIUdBYXPO8BUjRKvrvaaPdrN5Pg29tZxrlHmy2Jl60vSP94= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971568; c=relaxed/simple; bh=kILpmdve0m7Ji6aGKS1VWG8YBbOg/++ePHpMq/mZa4k=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=MPm+Nmqw2H+CilSWvNcRCPZTjPOpiZi1EjkfKU9nZZf127rIUM5EOpWww2y6Ojn/kYZabZM1JzlnYCZQFrvF4ghKAB/cXsDEOqgBE138YtKBGHmckvaVOtc4lf8mwCLgtbefqD9BWAHUg6yEhIGjJWLdCR+UFIe/vHLHv8HU+Ik= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=tvCq4SvI; arc=fail smtp.client-ip=52.101.56.63 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="tvCq4SvI" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=UtBPs0Gjx2BAk+U7Z2mnq8lOb9i8AxjvuXQWDjiC91g9fGEArWO00an/U5oQ3xyLdUHj1iTefgi7bNdOrMC8OV84pc9Q6M6z0kzyv6ErQmgGkuAue8mG3oHOvA8+WicHi//ieE2UFhW1uRm3A+H5VnGO5nklSdtofztBA+OcnCsuErTKocie0o5rwE7zy+EOPmt3mKv3D7BJpM7c3Xupb68DnpTBmzJH+UZLzQp8lL5zNhxqXB1fktDmvoGvrAjjB5eE+sed81k+t2wOBuGiRsCQbn3UTb/699YJNS+JcbNR7nKQrQWA0ut1CqRi0YOUDyRdq+GS3ghvaRvgg3tDew== 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=GLSAaXzPw0WqoBFDH5K+TgNKYlW/Z0crlO8b4Fmtkc4=; b=pzj5EteqeXmgWWFBKBZktMMqyxrDiZ9fm3F8s3auZ4xOITDXCUZqhb1GAp6NxSBfDk/95pIl/mCZqQkW0o0iFeuZ56jgrzArJ0P18A5HEH8xf1KTv8sbHiV1cDd/pYUGPQZLXH6An7+yQmJWF6uuW5wmc1ypDH7V2UEnTS+5es3J9mHZ7AP/A9gmhRjSwxUm3Y7pZDrP8QGIbiH3KA6dG/0yZTlrUjzCxXW2Qcr55yv+o7W+Pwh5L6eSxN7PyGKVYC4EGQCB0KVB85sqY2P3YctL8ZuaGAZ9ydat/elAglP6OKEYExzsxJ1jAtyz0BBnYbh5hkG+RBPlAtw787bxtA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=GLSAaXzPw0WqoBFDH5K+TgNKYlW/Z0crlO8b4Fmtkc4=; b=tvCq4SvIOuAZrp7tGaGJDkjGQdXzypH2RY+n/HvlIapUUXvZLYEO5LmIaVTzalOzbaTGE324USmjWiEjvuJi+Fp0Xq8kGrnGBNLiA+mzrzf0sy2w+/v37IgHXUeEl+DX9gOPSQoNBKOt1IzW80Z+niZjtlIfJbEFplEcHUwivb/rmTOBQhFQcpAvgwalyfoQRbo9Z/vnRSEQ1gMK5KyFNYY9PAW0J90GZnAstcI97PA9SVRJ87F+vc4LQ9V5yKlVVVTimao5aVcW9+AMd9cao7a9gfNMm12IXBOLIfKcjah87WokoYoUAANKmmd/Lj7f4gKBm90EoqptF+8h6ASjyA== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) by PH7PR12MB6833.namprd12.prod.outlook.com (2603:10b6:510:1af::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.315.13; Mon, 17 Aug 2026 12:59:19 +0000 Received: from BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0]) by BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0%4]) with mapi id 15.21.0315.016; Mon, 17 Aug 2026 12:59:19 +0000 From: Eliot Courtney Date: Mon, 17 Aug 2026 21:56:39 +0900 Subject: [PATCH 4/6] gpu: nova-core: add NVKV typed encoding Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260817-b4-nvkv-v1-4-b84db5e84b67@nvidia.com> References: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> In-Reply-To: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> To: Danilo Krummrich , Lorenzo Stoakes , Vlastimil Babka , "Liam R. Howlett" , Uladzislau Rezki , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , David Airlie , Simona Vetter Cc: John Hubbard , Alistair Popple , Timur Tabi , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, Eliot Courtney X-Mailer: b4 0.15.2 X-ClientProxiedBy: TY4P301CA0044.JPNP301.PROD.OUTLOOK.COM (2603:1096:405:36b::8) To BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) 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: BL0PR12MB2353:EE_|PH7PR12MB6833:EE_ X-MS-Office365-Filtering-Correlation-Id: 10d1bc4c-9560-4a98-3701-08defc5f5a17 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|376014|23010399003|1800799024|366016|7416014|10070799003|18002099003|921020|22082099003|56012099006|10067099003|11063799006|3023799007; X-Microsoft-Antispam-Message-Info: w0IuVFXRpEYQL/wDxHpECtFiQxdKGpX4udB5fnTSJH3qx9nsA/0+iPMwKHDH8jkwM+rIb4qdkSJx2crg6c5pgFKq8av7eDBjbQ8FvfBEh5egfUetV8FeF23IRuLQCBZErbVrE5PQNosf4dbsjQEqW1NFVfeVmfhu6t1Vl8nvdBMdw7/HwquDJfBBOcg2rsS+OxxXM4vECtHY+x4MUDyPffP0MKwj+Yu46JeDjpRbj/V/hvBONLjsQpQkx/tCR2B5B9RVbOvbay4OCGBS+E5C336jPdOzfRYl7OzkLZQr5CLc2FgXxoLPegXwbQCm8Adsax+4vCrcgiKP2Cen/rgV7NB1lCHD6qW3b7uVJbTww+fFbrrU/rXQ5XDjZV5kF+yk4ilv68KXDtU4/5PtPdf4FcCaqL2zXIi7hu9br+Tg0F2mmk4DE9jYlzAxoIAv56jQW0zNeZRTgIygovRmkLJlYHZf03c4lABNMciId8BFcvQWNaNrcAZqGDoAUuc41Ld2LYiNzsVQeCJo643TkfEcgELYth0S6nzo8ad3hP3YkfF3wT2KmaTu/G24MM3E9D+eeabEyvoDoLS+L2tfZohs1b7m/E8QDg1z8wVZBaUbXCplCQgFfJ1ZC7GZI00+qKYfM1H75m2L/j3EVi6FPX/ad7SpVk98qZ21xf6+AKTu9TMo3pDHdGvoUVA0GMULycIQZnbB2Bb9uOVDm16iV3DC5w== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BL0PR12MB2353.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(376014)(23010399003)(1800799024)(366016)(7416014)(10070799003)(18002099003)(921020)(22082099003)(56012099006)(10067099003)(11063799006)(3023799007);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?VnNneXU5UnF5RGs3VE9xVkNleDg2USthcWYwM1AyMkFuZ1pnQksvS09FMEpF?= =?utf-8?B?QzVCSTQyOFYxck5TLzFITHZ0Sk9wOWNUY0dOZElIQm5VcXFvUW9JN2NCUTh4?= =?utf-8?B?ZHpqZC9DYXU0bnZtVXlHL3JJTGw4TnRQVUNlMWtCRVpNM01rd2RmQWU5SWYx?= =?utf-8?B?aHVwVlJJZ2M1ZzFEQlE0cm5EVVE2UERnZEEvVFpqb29UNVduR3c0SGpQVkZQ?= =?utf-8?B?V281ZG9KMUNQNS9ocGc1R29LZGtudVBiMU5hNnBPb3M1andQY252SEpCZkVm?= =?utf-8?B?bHRTZ2cvY1piYkkrKzFXME1kT2FNVlpVZUt0a3NjZGhmWUVQWnpqYkJMQ01r?= =?utf-8?B?ZUVTeExzSytXMml6eTVENmNoUlhLWjJFRmFKL0JXZmM2YW1XUHdNZzlRbVJZ?= =?utf-8?B?a3JlYlRLYzU1M0RwNGc3dG51VmFoVHBraWppL2hDYlVLRmZjR3FLYnRwWEdz?= =?utf-8?B?cU84WmhRaW1qK243bVRHYTUvNTE1ZXdGWG84Tm5HZDZ6VUY3dlpGMk5aTk9K?= =?utf-8?B?R2cvU3VqcVVuYXJLUjFVU2ZUM1RNWkpZc0t1OFZRM05rQ3dndjJ4dTJRTmNa?= =?utf-8?B?ZXlIOHNzeThwWTVRK1dDc2tKbEEyeWt2MWFlakRmbmNuZjlFWnU5TEpGRFBC?= =?utf-8?B?TVBQR3JZd25ROHh2VXBiUkxCeUcyWlZuT1poaEZnMkwzazNlMlo3Q0ZXeDBE?= =?utf-8?B?Zis3bEpaNi9LeFRqb0hJNlR1WnphR2g2YlBYTVkxVEIvN3A2M2NaNW0yMzZL?= =?utf-8?B?Q3NMR3l0eUJ1RnBJMGtNbGMyeEFYd1VtZjIwbnREOXBnMFU3czg4OVJqVXJl?= =?utf-8?B?MGx1WUxxQzNSS1R2U1NKNmFOTlY2ZnZ1aUhUdjZrWEtnaDVIaGtPeHB0N1pu?= =?utf-8?B?N2VHVXZONEJIS0lZcVNTTmVacVE3V2MwcjZUemhudFdUajVCREZDV2d4VUdU?= =?utf-8?B?Q2xzeHFBRS90Vk1VY1pDREt6R2tVMWdpcUpUakhkWXNqV0ZiaWk2WDJSRzBs?= =?utf-8?B?NS9oRUFpODdWSTU3VkVxRmRzakk1Qzd6QytONkRBWjMyNWJ4VTl1WnltOW5s?= =?utf-8?B?YWxXc2QyS2JIRnAzbmVPakVPZ3Z4MEcrbllsN095REU5M3JCNkVxQ3ZBamhh?= =?utf-8?B?Y0I0ZEg2YmdLbGtMa21Za3VwRUl3RDg3dnZsYi8zU0w1MFI5SXovVnBvS0dY?= =?utf-8?B?cXJTMkQ0bVRpc01heTVMSW1UOStUNGo4MzhOWWs5d0QrcENDVzYzR3JFZnRu?= =?utf-8?B?RFZIZFFUVCt2dXU0M2w2Z1FOUkZoTWd6eUhhZ1RjTGdtVEpaVkVZeEx0SGtq?= =?utf-8?B?YVRCV2k2UjBUUHgwbWp1cDJocUVkQnlrbzBWU0w5L0J4SThEcjJmL1FaTkNa?= =?utf-8?B?YXRldU5kTGU3bUNUQWp2SjRCVlRyQllwdFhQcGEvajMxelVDbUFZZDh6WXNV?= =?utf-8?B?WnhRRTVQNzZ5WU91bjFGc3lmY056ZHN1aG5iQ25lYUtkM2VjVnpBYXpCaWJO?= =?utf-8?B?VUJUckxBNkZHZis5TFhEbThkZE1BN3VHelkzdElZTXZOaUFmQnRUeHN2UnJS?= =?utf-8?B?MHVhMjVuVzdUZmczTFFKOWVmR0k4YnNlS0hKaWNGdm12YWNNUmt6UjFmMmlz?= =?utf-8?B?YWthUDNtZXV6VmhjWjVKeVY1dmQwUHh0VTErRFlBOWltVWt2S2dkNDNrWERI?= =?utf-8?B?M0xpOStkK2xXMjhFZCtmeDA5MkJEOUZPOFpFa0g3OHNzdk1ZaFBkWU5LaTB6?= =?utf-8?B?S0xGc1lkMEpNRyszZ21uTUtWSUZqUmtOM2t6aFBMSHVPRG93STB5a1hZVC9h?= =?utf-8?B?bkpuVFpnb3hOL05NQTlMK3ErWXNvWGlSaU9Wcmo4bCs2TXVmUUhEa003QWNH?= =?utf-8?B?Sk9qakFMY2NmNFFBS0FNOGhmdWRvVWp4SmI5WGI1T0FkMmFsRTVKeUEvSzUy?= =?utf-8?B?Z0NaendvdE1vMHl1WkE3SXhHdlV5djc5M2NKcFJWQ0M5bWtqMnlkSVBuV3Vl?= =?utf-8?B?cmdLaXZuOG5wa3RrTkhZQlZONlRRUkY5VGZBa2hQS3ZQNzczcEFXckl6aGZH?= =?utf-8?B?MEhSbHgwYlUwUXFYNnRaNW1hR3ZtN0R2MUI0UHNtOU9oOFpXcWlDNDNJcUxP?= =?utf-8?B?cXJSQ2JCdXB4YjI4cWE4UW9Yb1JDRW1aUldLYUx2L04zQUc3NVg2bjMyOWFa?= =?utf-8?B?eHYzRERwRkc4cWFoRTAyRlVLSHBEbVZWTmlOd0cxTjl5SmRkdUN0Wmd3L3ZC?= =?utf-8?B?RVhpU3NLMDVkNzdSY0k4dlpOTzQrOHlCZkhaTytvN2ZjK2JwSENHMXZvZ3BJ?= =?utf-8?B?MlRBVnBzUzRQVWdOMWlWbEh3QVRFMUVXVUVrUHpySXU5WlFGWStGN3JRU3NB?= =?utf-8?Q?t2jcl0F6BQPe1HHSbcXlFxDha4FD68qCYr6iELMs9Oa5p?= X-MS-Exchange-AntiSpam-MessageData-1: LpCOnZbTgSc9Eg== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 10d1bc4c-9560-4a98-3701-08defc5f5a17 X-MS-Exchange-CrossTenant-AuthSource: BL0PR12MB2353.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Aug 2026 12:59:19.8271 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: yJPrWYpNRdMvdxDs5Eek0f4Z5LdipRTeaJl/O7pW+cDxd8r+8ms4wjP85NzQoV5gGuucTiYX1LYMqXLJbrpQmQ== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH7PR12MB6833 For struct-like GMCAPI messages encoding field by field manually is noisy. Add some type machinery and a macro to automate encoding of struct-like messages. The `Encodeable` trait can be implemented by any type to say that it can be encoded into an NVKV `Encoder`. Add a simple `nvkv_encode!` macro that works on structs and encodes each field in order. Provide some base types, such as `Key` which statically associates a NVKV key with some value, to avoid having to make a lot of newtypes and implement `Encodeable` on them. Signed-off-by: Eliot Courtney --- drivers/gpu/nova-core/gsp/nvkv.rs | 49 +++++++++ drivers/gpu/nova-core/gsp/nvkv/encode.rs | 178 +++++++++++++++++++++++++++= ++++ 2 files changed, 227 insertions(+) diff --git a/drivers/gpu/nova-core/gsp/nvkv.rs b/drivers/gpu/nova-core/gsp/= nvkv.rs index 64d8d0118452..bf6500d54b21 100644 --- a/drivers/gpu/nova-core/gsp/nvkv.rs +++ b/drivers/gpu/nova-core/gsp/nvkv.rs @@ -10,6 +10,13 @@ //! naturally maps to storing a &str with the GPU name. =20 #![expect(unused_imports)] +#![cfg_attr(not(CONFIG_KUNIT), expect(unused_macros))] + +use core::marker::PhantomData; +use core::ops::{ + Deref, + DerefMut, // +}; =20 use kernel::{ bitfield, @@ -29,6 +36,48 @@ /// The index of an NVKV value. pub(crate) type Index =3D Bounded; =20 +/// A static association between an NVKV key `KEY_ID` and the storage of i= ts value. +/// +/// Use with the encoder or decoder macros `nvkv_encode!` and `nvkv_decode= !` to let them know how to +/// map the value `Key` to/from encoded data. For brevity, = `As` inserts an additional +/// conversion (`From`) to avoid having to implement [`Encodable`] for man= y types. For example, +/// enums that are easily convertible to a u32 can have `As =3D u32` and r= ely on the existing encoding +/// for u32. +#[repr(transparent)] +pub(crate) struct Key(pub(crate) T, Phan= tomData); + +impl From for Key { + fn from(value: T) -> Self { + Self(value, PhantomData) + } +} + +impl<'a, T, const KEY_ID: KeyId, As, const N: usize> From<&'a [T; N]> for = Key<&'a [T], KEY_ID, As> { + fn from(value: &'a [T; N]) -> Self { + Self(&value[..], PhantomData) + } +} + +impl Deref for Key { + type Target =3D T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Key { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Default for Key { + fn default() -> Self { + Self(T::default(), PhantomData) + } +} + bitfield! { /// The op word that starts each NVKV operation. struct Op(u64) { diff --git a/drivers/gpu/nova-core/gsp/nvkv/encode.rs b/drivers/gpu/nova-co= re/gsp/nvkv/encode.rs index 6da81a371651..31ea5788e772 100644 --- a/drivers/gpu/nova-core/gsp/nvkv/encode.rs +++ b/drivers/gpu/nova-core/gsp/nvkv/encode.rs @@ -7,11 +7,153 @@ =20 use super::{ Index, + Key, KeyId, Op, Opcode, // }; =20 +/// A type that can encode itself into an [`Encoder`]. +pub(crate) trait Encodable { + /// Encodes `self` into `encoder`. + fn encode(&self, encoder: &mut Encoder) -> Result; +} + +/// Defines a struct together with its [`Encodable`] implementation. +/// +/// The implementation encodes each field in declaration order. Each field= type must implement +/// [`Encodable`], which is done already for types like `Key`. +/// +/// # Examples +/// +/// ``` +/// nvkv_encode! { +/// struct Request { +/// id: Key, +/// name: Key<&'static [u8], 0x0002>, +/// } +/// } +/// ``` +macro_rules! nvkv_encode { + ( + $(#[$attr:meta])* + $vis:vis struct $name:ident { + $( + $(#[$field_attr:meta])* + $field_vis:vis $field:ident : $ty:ty + ),* $(,)? + } + ) =3D> { + $(#[$attr])* + $vis struct $name { + $( + $(#[$field_attr])* + $field_vis $field: $ty, + )* + } + + impl $crate::gsp::nvkv::Encodable for $name { + #[inline(always)] + fn encode(&self, encoder: &mut $crate::gsp::nvkv::Encoder) -> = ::kernel::error::Result { + $( $crate::gsp::nvkv::Encodable::encode(&self.$field, enco= der)?; )* + Ok(()) + } + } + }; +} +pub(crate) use nvkv_encode; + +/// A value with a specific index that encodes under the NVKV key `KEY_ID`. +struct IndexedKey { + index: Index, + value: T, +} + +impl IndexedKey { + /// Creates a key with the given index and value. + pub(crate) fn new(index: Index, value: T) -> Self { + Self { index, value } + } +} + +impl Encodable for IndexedKey { + #[inline(always)] + fn encode(&self, encoder: &mut Encoder) -> Result { + encoder.encode_u32(KEY_ID, self.index, self.value) + } +} + +impl Encodable for IndexedKey { + #[inline(always)] + fn encode(&self, encoder: &mut Encoder) -> Result { + encoder.encode_u64(KEY_ID, self.index, self.value) + } +} + +impl Encodable for IndexedKey<&[u8], KEY_ID> { + #[inline(always)] + fn encode(&self, encoder: &mut Encoder) -> Result { + encoder.encode_array8(KEY_ID, self.index, self.value) + } +} + +impl Encodable for IndexedKey<&[u32], KEY_ID> { + #[inline(always)] + fn encode(&self, encoder: &mut Encoder) -> Result { + encoder.encode_array32(KEY_ID, self.index, self.value) + } +} + +impl Encodable for IndexedKey<&[u64], KEY_ID> { + #[inline(always)] + fn encode(&self, encoder: &mut Encoder) -> Result { + encoder.encode_array64(KEY_ID, self.index, self.value) + } +} + +impl Encodable for IndexedKey<[u8; N]= , KEY_ID> { + #[inline(always)] + fn encode(&self, encoder: &mut Encoder) -> Result { + encoder.encode_array8(KEY_ID, self.index, &self.value) + } +} + +impl Encodable for IndexedKey<[u32; N= ], KEY_ID> { + #[inline(always)] + fn encode(&self, encoder: &mut Encoder) -> Result { + encoder.encode_array32(KEY_ID, self.index, &self.value) + } +} + +impl Encodable for IndexedKey<[u64; N= ], KEY_ID> { + #[inline(always)] + fn encode(&self, encoder: &mut Encoder) -> Result { + encoder.encode_array64(KEY_ID, self.index, &self.value) + } +} + +impl Encodable for Key +where + IndexedKey: Encodable, + As: From, + T: Copy, +{ + #[inline(always)] + fn encode(&self, encoder: &mut Encoder) -> Result { + IndexedKey::new(Index::new::<0>(), As::from(self.0)).encode(encode= r) + } +} + +impl Encodable for Option { + #[inline(always)] + fn encode(&self, encoder: &mut Encoder) -> Result { + if let Some(value) =3D self { + value.encode(encoder)?; + } + Ok(()) + } +} + /// An encoder for an NVKV stream. pub(crate) struct Encoder { backing: KVVec, @@ -242,4 +384,40 @@ fn encode_all_value_kinds() -> Result { =20 Ok(()) } + + // Tests that encoding via the `nvkv_encode!` macro works correctly. + #[test] + fn encode_typed_struct() -> Result { + const U32_KEY: KeyId =3D 0x0001; + const U64_KEY: KeyId =3D 0x0002; + const NAME_KEY: KeyId =3D 0x0003; + const FIXED_KEY: KeyId =3D 0x0004; + const OPT_KEY: KeyId =3D 0x0005; + + nvkv_encode! { + struct TypedRequest { + a: Key, + b: Key, + name: Key<&'static [u8], { NAME_KEY }>, + fixed: Key<[u8; 4], { FIXED_KEY }>, + opt: Option>, + } + } + + let request =3D TypedRequest { + a: 0x89ab_cdef.into(), + b: 0x0123_4567_89ab_cdef.into(), + name: b"name\0".into(), + fixed: [1u8, 2, 3, 4].into(), + opt: None, + }; + + let mut encoder =3D Encoder::new(); + request.encode(&mut encoder)?; + let encoded =3D encoder.finish(); + + assert_eq!(encoded.len(), 7); + + Ok(()) + } } --=20 2.55.0 From nobody Mon Sep 28 21:03:43 2026 Received: from BN1PR04CU002.outbound.protection.outlook.com (mail-eastus2azon11010063.outbound.protection.outlook.com [52.101.56.63]) (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 E7F8841F5E7; Mon, 17 Aug 2026 12:59:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.56.63 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971572; cv=fail; b=r5LIWDO9+Ln8kufhBnCifScSonWftS3cR3UnhChth1T3JU0d4vjAMLvhahFeji0WrQ6ocNX1mjAtOQf48YayKF7LXnOPiFvu4HQc6wQ+5LV60jVQHPSy0rek1OGvUO/CHjWkKpFX3VZcuOanMbmEI8OztL9RSD46VhLJ/V59N6U= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971572; c=relaxed/simple; bh=7P4wAOJnHrEoOvFZYLEnYTcAJcHCi4+BYcf20KLmK5w=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=q7hJNoRjHY+cQ47hxjkIh07K/BYxJUMRlnfGZq/zZ/EqN3L7mLq6UwOUtJea4Qg8yevy7yNKc2kKMV2Ut1CkCt2uQVzebcsqW91/epka0rqGflcar/oTOM6uasNo1LyI99avHH2CLxUm5CK9FFjg9i3l1kRqEpzBbCOVtqiK2zg= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=ks7mGRYv; arc=fail smtp.client-ip=52.101.56.63 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="ks7mGRYv" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=wCmEK4MWJH8a7JPbGq8x25DZhevpXNlynPdc206F2UGHkrnjagt7I5Gp3AP5SCUqIz3oZCVpuo9CSSZMD7NE78WMuu7v3Gm23MMy0manV45ROUxeiROwOkdCqsRiyeBi4h/HdvYCwoNVgJq/h8b9jnTt5hRy5j+aOdZsSb2CfzAWzMMte7B4F3vDDWZnV5MNAv0rlTdKqPyagKr+bKNcHpRtUC/4MMKpC4+5IbZ1OvkREN+zsZsOqE8Kqfrur6h9bgdPm5zhQDPyoLr/Mj6Qm52kXTSFNF3ZBIO8FQMdFlhsyubJD7/ZyL435fwpCBLrqR56Ui8XVK1KoGgV4bVvrQ== 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=Dzo4kPKqaJ73zkodzxKW8/KYHl95STGO1/blrVcrVrU=; b=nObFTN71vsaCrhT1iZ1Mnfx245ogn5C5d3xaYD6YTU0qUJzC1ZTp58sOqYfnRC1L9Z/6Uav+02cYYgq64Vgzw8S6Dh3CVg/NvbcbTTTwROqwAgQ9GNzaIMvFP4W13faj5b0VOaC6XJ/Nn62Z7bR3wSL1Btxi9qfGWpeiXQ6zUnKeI4l89oMFhIrHQ9+Rg0EW9G6LC8goLbMua1NyAAwQdiqs/+PvjHkA3hjfkwsSMINTqUbdjMz0taFvD89L1BAMqw5A/Bb6hK05dbMqecR1J9FDpvbh60oWYr7tEKCd45LFXIu05Uw8ptAjwRM5e9f24FHadC46dFE+FMzoUbhB4Q== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=Dzo4kPKqaJ73zkodzxKW8/KYHl95STGO1/blrVcrVrU=; b=ks7mGRYvYz9iAtC0kQKbBMg/Y1iqbUNOThQawE4sXPWtMiknF2oOxIfAScKrmsa7TBNyoHq/O+WEogD4iWNuQVcVgtfalcuvNO7heqw72IKfARy/APuEhUHNwMDSzwBKRbeQpOfHcUduh2FO0UomwCxljkdtHU0rC8hkwtnYcVFs6b+j7koEyHAD+UUrETNjkYlEjl3xvcd2UugITIteaL5kUzwwHY88UEk5YPLtLgRKAvL8BKofPcKhxi5CLc+VRCH06fKRgRBnnaFT23QHbrZZtLfd1nHVWb0zs6Ip982Q068Kqp8Mgwyx2IzoyB+FTUimxEIfY4UVIsRyNfDqIg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) by PH7PR12MB6833.namprd12.prod.outlook.com (2603:10b6:510:1af::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.315.13; Mon, 17 Aug 2026 12:59:24 +0000 Received: from BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0]) by BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0%4]) with mapi id 15.21.0315.016; Mon, 17 Aug 2026 12:59:24 +0000 From: Eliot Courtney Date: Mon, 17 Aug 2026 21:56:40 +0900 Subject: [PATCH 5/6] gpu: nova-core: add NVKV typed decoding Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260817-b4-nvkv-v1-5-b84db5e84b67@nvidia.com> References: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> In-Reply-To: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> To: Danilo Krummrich , Lorenzo Stoakes , Vlastimil Babka , "Liam R. Howlett" , Uladzislau Rezki , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , David Airlie , Simona Vetter Cc: John Hubbard , Alistair Popple , Timur Tabi , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, Eliot Courtney X-Mailer: b4 0.15.2 X-ClientProxiedBy: TYCPR01CA0168.jpnprd01.prod.outlook.com (2603:1096:400:2b2::8) To BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) 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: BL0PR12MB2353:EE_|PH7PR12MB6833:EE_ X-MS-Office365-Filtering-Correlation-Id: 20dfecff-5ab1-4a67-b550-08defc5f5cb3 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|376014|23010399003|1800799024|366016|7416014|10070799003|18002099003|921020|22082099003|56012099006|10067099003|11063799006|3023799007; X-Microsoft-Antispam-Message-Info: +p/qsUk8VfshCiLM+Vo61JjlZKZkQ2RMIr9Pc9t3pKJL7aO0FEyrbldn0Qfp8Vt/+jhompaiMgXXnR4FbgAvZ5FtF1YpbwXphVext4rUYFPQoWqYxX28nx86SerQHto0Wjr49yJfP989/lrzCkRgu1tqMWd9sAGuZpnMW9mW4LUIFP4h+UretTLT+qy5/fIaHqXkgJAb7+g6WC6n0o/NKiwkvor1KfWPI8vPRKnoiEu/nA3ZYram2nrzGrUIOJxGh/1Mh0EX1kIwqjW96QKOg4WY/EMNbAotOdpOQKhR9rZk6r0bdM1cXIQjR1f97RgbX2nV/2JlIRyUeQo2P4c6kJv1FAeahj59oiDYj0xjLLRx3agISGCOdmHAfK83ssbdFIFnq3tbCcG5ywE0dwfwev9viFZ6qV7OQziff9L2yDDlH7JToxLC9J7R8DSGInX5yL0CH9slokx541ZgvWprksr/N1T4NVPIFBgD2EEBsttjE9U3cD8Rla/hJSDApGVDmM9VidLgUmfHFsWU11ol6HTUnGCs/imNM0Uzip4hdWGfEidHaKRQ/1gjFg2RSLZ6zOBWXZCexNHbvalUTJMUbQB/qyCFpIBgFiYqDGiYnBuHJt2tRnuNwIpg81DVqEjoPcconTLcmi7KkqTUdIYiZURrPN49USzKrz2bRxNqj0WNTA4Q+yBxCPFIbvb+6RCAnsoL0WlCAnjs2TiS8f1MfQ== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BL0PR12MB2353.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(376014)(23010399003)(1800799024)(366016)(7416014)(10070799003)(18002099003)(921020)(22082099003)(56012099006)(10067099003)(11063799006)(3023799007);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?ZHAvT3V0aUkrZG9GdFN2eG5WUkZkOUFFM0xud0tjOWJlNVp1NEgxNjlnVDRW?= =?utf-8?B?ZFJabTFaM0ZxY3V0WGFkc1BuTE04ZDcrekN2dVdoMkpOcENrOGs5aTA4NU1n?= =?utf-8?B?VFJWMW8xRTdLVmdUQUUwY0phKzk0Q1pZRHFKU3FhSUhRdjQzNnZpUDFnWFFh?= =?utf-8?B?RkFyRVFqaG1zY3lVbXk4WjJlSFJEOHZyYy9wSnV6SENyT0VCaHcwUzVoWWRl?= =?utf-8?B?eFNKRE1mRUZmNFdyTEFXdTVwWmc2cTgrK2ZaK0ZERGxMRXB3aUI1Q29FbTR0?= =?utf-8?B?bFpYQjlUblU5UUZMTXJ2Q0RpM0JlT0YyUEdtWEwwVzNraG5qbmpFbEhkMHAx?= =?utf-8?B?WnAxTXZnd0dPWmNiY3BrMVE0aHBFSVljL0l6S21kdlBSSGozRXl0UVo1YkNV?= =?utf-8?B?OEZocC9WdURJS0ZXU2x4Q2pvcWs1MlhUM2d4UHBNMUZLa3NrMFQrZVRBZ2Mx?= =?utf-8?B?bVJXT3hySnVXSVlZaTdlZW41Tk1QVkpSdXNya3JyZWdscGsrUGdZQVhLVnBt?= =?utf-8?B?WlJ3RVM2bmRQZFZENEx1YVpjTzBkNDR5bitncHpZNHlRZzZUaVdTN0FIL01q?= =?utf-8?B?YWExUVlJZTZWVGkzZkhIUHRhVnl6VldXMVg0eS9MVWFQNmFmN3AwMVhSc2JC?= =?utf-8?B?NVBnems5aFBoRzFrRzVobDA5QlBXZWNjMllTZkJQU1V5WDMzaW1RckxTMmY1?= =?utf-8?B?dGNLc1BkeS95MVpzbGRSQXJQMk5kSERSYk1YKzFmVitSdUdadlM1TWhWUXlp?= =?utf-8?B?V3J0MGk5NDUrNTd0M29mNWU5S01vSlN1MlorYXRBcFBBK21MYmFqU01nbEpL?= =?utf-8?B?cUdBUVpJaDFKa3JMVEQzRENsWE5ONmp0SnNGSFpnY0lYTGwzdjhwdVNpUlZY?= =?utf-8?B?MHkvUnN5T0c2b3Y0bTY5L0c1R3pINExMNjFTalVaUWl1YkNqZ2xZbSt1cXVD?= =?utf-8?B?TnMrOStKdjFEa0lIdDNEZ1BmakNFWmpTTU1adTJNZnhlR2JWRW5OZ2EwZ1B2?= =?utf-8?B?Z2h3OFEzMC9Yc1lldExsMkpXRTZKSXpQL2ZkemdZWjB1ampiWTNUWWlJZkt3?= =?utf-8?B?MHFOTVh2ZVppcTkvcnRybzR4NEZzTHNoQlE0TXpHSkhGMTFXQWZNZUZtTllV?= =?utf-8?B?VTMzQUszVmE4eVhOSGJGaVVkOHJsZTF0NTRGWmZCS0d0R2lTYy9RZ242RWdS?= =?utf-8?B?SldTM1I5eWJPUVlTclRKcGhZZmpiZ1VFWUFiZWxyYisya1VjWXd6dkliaW8r?= =?utf-8?B?aDJxRWdzV0hMU0swSUJ4ZUhLalRSV3NLcnJ1VWRtbHo4cVdhUmV5dWJIMkpi?= =?utf-8?B?UWpxaUtya0syd1YzdjFxSlhtN0c1b1YzS0hsYUtxMkNPa1Y4OGxQWlBON3hF?= =?utf-8?B?Um56NnVUTm5waTFNUGZvN3dyemtUSDJOeG1aUXdiZ3g4dVBab3EwUUxUVnBS?= =?utf-8?B?MDVPWDFBUnNIakxocnphQklVOXdYVGFFTzV0UWx5L1lHcFJmMjlaamYwUE9j?= =?utf-8?B?UUo3dVZxUjl6RGhqSVF0b3RKa2FWTk52am5hMUxUUGJydTNNUDhySWx0ekI1?= =?utf-8?B?dU1sOXdYRS8vMmljeUc2WEJaT2picTE4MHhzVjR0TWozenpOSFgrcEdvUzAw?= =?utf-8?B?U1YyOHRzUUZSVndnTVNSQUpzSDQvbDF6b1JPc1NFQm56UFlSTC9JbE9hQktt?= =?utf-8?B?K0RvU1Y0S1dydkFIRzUyQStBRjgzL0F0SFZVUDZ5VUFTZU94NFhUaDlUWjZl?= =?utf-8?B?UmU0Tmx0MFBkMTErb1drdk5oajkvV3JXRERFWG1aS1BKWHV4dXlTR2Z0eFhJ?= =?utf-8?B?K3ZpYmh5c3VMWGx0N3RJOWpDckN5NTVia3c5aXBGeFpadVpPamRyWlZzaUwr?= =?utf-8?B?M1p4Mjk5RzNmUkdJNHN5dHBWRE15UUltb3pGT0NwQk1lYXNQZHh5QldOcXF6?= =?utf-8?B?TVdjek9Ib2RQQVI0R2s3c0dTNWRXVWtnVWVianBTeFpuQWtZU3h0VFhTVU84?= =?utf-8?B?TkhDVHhPNlZCUnRMQ3l2KzlYVHV0ZFdFVXBLZTBVUG10bVJhaHpkNElmaVFq?= =?utf-8?B?ME1lSTNzRjBBMmtTUlZyZ1orS05mcFhQTXNOck1UQ1BTaEtlV0pvcVVqYVFF?= =?utf-8?B?ZDl4dFFaM0RHMzkxVmVud2xPZTRMbm0yZ29pM1JNSUNZVHB5MnE2UmErM2t5?= =?utf-8?B?R3l5NWpiS2xtRnFIWTBiY3JFZHJ2NU54dGswQWZ0cDV6ak1YTHk4V0NOT2xi?= =?utf-8?B?NS96YzdZVHdYaVlEVHlYSGNZY2Y3SE5rSDdPVC9Xb0NyLzVjVFRqc2d1Yy93?= =?utf-8?B?a1Z6YjBZemsxVFYzbVdZcnFjZnlTUVBqNyt4Vmg0SzlGRTRxeGpHNXJsVjY0?= =?utf-8?Q?Qr/ZATv5XFD8zC7tCgMKUtGQ9fLQn085OzJ3tZHmU5MYW?= X-MS-Exchange-AntiSpam-MessageData-1: 2YgEKMDAkEb/dA== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 20dfecff-5ab1-4a67-b550-08defc5f5cb3 X-MS-Exchange-CrossTenant-AuthSource: BL0PR12MB2353.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Aug 2026 12:59:24.1474 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: imtEZldSga7aUr36CsmDTYQMKKFT/8y3iLNmD4wtOm7wqvzklgUqleoI8zuGKbiVOo8nw0lc4W2f7f1Z2Vhq8Q== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH7PR12MB6833 Similar to the typed encoding layer, add some decoding type machinery. Add a simple macro `nvkv_decode!` which implements `Schema` for a struct by composing visit calls to each member. Add some common `Schema` kinds, such as `Array` which collects an array value into a fixed maximum size array, and `Required` which fails a decode if the value is not sent. Signed-off-by: Eliot Courtney --- drivers/gpu/nova-core/gsp/nvkv.rs | 60 ++++- drivers/gpu/nova-core/gsp/nvkv/decode.rs | 393 +++++++++++++++++++++++++++= ++++ 2 files changed, 452 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/nova-core/gsp/nvkv.rs b/drivers/gpu/nova-core/gsp/= nvkv.rs index bf6500d54b21..a0068847bb80 100644 --- a/drivers/gpu/nova-core/gsp/nvkv.rs +++ b/drivers/gpu/nova-core/gsp/nvkv.rs @@ -9,7 +9,7 @@ //! function calls will map to some struct - for example, f(GPU_NAME_STRIN= G_KEY, 0, b"some gpu") //! naturally maps to storing a &str with the GPU name. =20 -#![expect(unused_imports)] +#![cfg_attr(not(CONFIG_KUNIT), expect(unused_imports))] #![cfg_attr(not(CONFIG_KUNIT), expect(unused_macros))] =20 use core::marker::PhantomData; @@ -78,6 +78,64 @@ fn default() -> Self { } } =20 +/// A fixed capacity vector that holds at most `N` elements. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Zeroable)] +pub(crate) struct ArrayVec { + data: [T; N], + len: usize, +} + +impl ArrayVec { + /// Replaces the contents with a copy of `slice`. + /// + /// Fails with `EMSGSIZE` if `slice` is longer than the capacity. + pub(crate) fn set_from_slice(&mut self, slice: &[T]) -> Result + where + T: Copy, + { + let Some(dst) =3D self.data.get_mut(..slice.len()) else { + return Err(EMSGSIZE); + }; + + dst.copy_from_slice(slice); + self.len =3D slice.len(); + + Ok(()) + } + + /// Returns the initialized elements as a slice. + #[inline] + pub(crate) fn as_slice(&self) -> &[T] { + // PANIC: `len` is bounded by `N`. + &self.data[..self.len] + } +} + +impl Default for ArrayVec { + fn default() -> Self { + Self { + data: [T::default(); N], + len: 0, + } + } +} + +impl Deref for ArrayVec { + type Target =3D [T]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.as_slice() + } +} + +/// A schema field for an array value under the NVKV key `KEY_ID`. +#[derive(Default)] +#[repr(transparent)] +pub(crate) struct Array( + pub(crate) ArrayVec, +); + bitfield! { /// The op word that starts each NVKV operation. struct Op(u64) { diff --git a/drivers/gpu/nova-core/gsp/nvkv/decode.rs b/drivers/gpu/nova-co= re/gsp/nvkv/decode.rs index ee8b6ab5a3a4..9112dcf1aaca 100644 --- a/drivers/gpu/nova-core/gsp/nvkv/decode.rs +++ b/drivers/gpu/nova-core/gsp/nvkv/decode.rs @@ -3,16 +3,311 @@ =20 #![cfg_attr(not(CONFIG_KUNIT), expect(dead_code))] =20 +use core::marker::PhantomData; + use kernel::prelude::*; =20 use crate::gsp::nvkv::{ + Array, + ArrayVec, Index, + Key, KeyId, Op, Opcode, // }; use crate::num; =20 +/// Defines a schema struct together with its [`Schema`] implementation th= at decodes into `$target`. +/// +/// Each member of the struct should implement `Schema`. For every (key, i= ndex, value) triple +/// decoded from the NVKV stream, the generated parent `Schema` implementa= tion will call each member +/// in declaration order with that triple. If a member consumes that tripl= e, it will stop there. +/// Otherwise it will keep going until all members are tried. +/// +/// The schema struct holds the state required by the schema implementatio= n to do the decode. It's +/// recommended to use one of the existing Schema kinds (`Required`, `Accu= mulated`, `Key`, `Array`, +/// `Indexed`) for each member. +/// +/// # Examples +/// +/// ``` +/// nvkv_decode! { +/// #[derive(Default)] +/// struct RequestSchema =3D> Request { +/// id: Required, +/// name: Array, +/// } +/// } +/// ``` +macro_rules! nvkv_decode { + ( + $(#[$attr:meta])* + $vis:vis struct $name:ident =3D> $target:ident { + $( + $(#[$field_attr:meta])* + $field_vis:vis $field:ident : $ty:ty + ),* $(,)? + } + ) =3D> { + $(#[$attr])* + $vis struct $name { + $( + $(#[$field_attr])* + $field_vis $field: $ty, + )* + } + + impl $crate::gsp::nvkv::Schema for $name { + type Target =3D $target; + + fn visit( + &mut self, + key: $crate::gsp::nvkv::KeyId, + index: $crate::gsp::nvkv::Index, + value: $crate::gsp::nvkv::DecoderValue<'_>, + ) -> ::kernel::error::Result { + Ok(false + $( || $crate::gsp::nvkv::Schema::visit(&mut self.$fiel= d, key, index, value)? )*) + } + + #[inline(always)] + fn finish(self) -> impl ::kernel::prelude::Init { + ::kernel::try_init!(Self::Target { + $( $field <- $crate::gsp::nvkv::Schema::finish(self.$f= ield), )* + }? ::kernel::error::Error) + } + } + }; +} +pub(crate) use nvkv_decode; + +impl TryFrom, Error =3D Error> + Default, cons= t KEY_ID: KeyId> Schema + for Key +{ + type Target =3D T; + + #[inline(always)] + fn visit<'a>(&mut self, key: KeyId, index: Index, value: DecoderValue<= 'a>) -> Result { + if key !=3D KEY_ID { + Ok(false) + } else if index !=3D Index::new::<0>() { + // Single values being set must be at index 0. + Err(EINVAL) + } else { + // Overwrite and take the latest value here. + self.0 =3D value.try_into()?; + Ok(true) + } + } + + #[inline(always)] + fn finish(self) -> impl Init { + Ok(self.0) + } +} + +impl TryFrom, Error =3D Error>, const KEY_ID: = KeyId> Schema + for Key, KEY_ID> +{ + type Target =3D Option; + + #[inline(always)] + fn visit<'a>(&mut self, key: KeyId, index: Index, value: DecoderValue<= 'a>) -> Result { + if key !=3D KEY_ID { + Ok(false) + } else if index !=3D Index::new::<0>() { + // Single values being set must be at index 0. + Err(EINVAL) + } else { + // Overwrite and take the latest value here. + self.0 =3D Some(value.try_into()?); + Ok(true) + } + } + + #[inline(always)] + fn finish(self) -> impl Init { + Ok(self.0) + } +} + +impl Schema for Ar= ray +where + for<'a> &'a [T]: TryFrom, Error =3D Error>, +{ + type Target =3D ArrayVec; + + fn visit<'a>(&mut self, key: KeyId, index: Index, value: DecoderValue<= 'a>) -> Result { + if key !=3D KEY_ID { + return Ok(false); + } + // Require to be at index 0 + if index !=3D Index::new::<0>() { + return Err(EINVAL); + } + // Reject oversized and take the latest value. + self.0.set_from_slice(value.try_into()?)?; + Ok(true) + } + + #[inline(always)] + fn finish(self) -> impl Init { + Ok(self.0) + } +} + +/// A schema field for a key that must be present. +/// +/// `finish` fails with `EINVAL` if no value arrived for the key. +#[repr(transparent)] +pub(crate) struct Required(Key, KEY_ID>); + +impl TryFrom, Error =3D Error>, const KEY_ID: = KeyId> Schema + for Required +{ + type Target =3D T; + + #[inline(always)] + fn visit<'a>(&mut self, key: KeyId, index: Index, value: DecoderValue<= 'a>) -> Result { + self.0.visit(key, index, value) + } + + #[inline(always)] + fn finish(self) -> impl Init { + (self.0).0.ok_or(EINVAL) + } +} + +impl Default for Required { + fn default() -> Self { + Self(None.into()) + } +} + +/// Expects objects specified sequentially with index starting from zero. +pub(crate) struct Accumulated { + current_index: Index, + current: S, + current_started: bool, + next: S, + accumulated: KVVec, +} + +impl Accumulated { + /// Creates an empty accumulator. + pub(crate) fn new() -> Self { + Self { + current_index: Index::new::<0>(), + current: S::default(), + current_started: false, + next: S::default(), + accumulated: KVVec::new(), + } + } + + fn into_vec(mut self) -> Result> { + if self.current_started { + let done =3D core::mem::take(&mut self.current); + self.accumulated.push_init(done.finish(), GFP_KERNEL)?; + } + Ok(self.accumulated) + } +} + +impl Schema for Accumulated { + type Target =3D KVVec; + + fn visit<'a>(&mut self, key: KeyId, index: Index, value: DecoderValue<= 'a>) -> Result { + if index !=3D self.current_index { + if !self.next.visit(key, Index::new::<0>(), value)? { + // Unrelated key to us. + return Ok(false); + } + + // Require that objects at index k have all their keys sent be= fore the k + 1 th object + // can be completed. Require that objects are sent contiguousl= y in order from index 0. + if !self.current_started || index !=3D self.current_index + 1 { + return Err(EINVAL); + } + + // The current value must be finished. Finish it and start wor= king on `next`. + let done =3D core::mem::replace(&mut self.current, core::mem::= take(&mut self.next)); + self.accumulated.push_init(done.finish(), GFP_KERNEL)?; + self.current_started =3D true; + self.current_index =3D index; + Ok(true) + } else { + let consumed =3D self.current.visit(key, Index::new::<0>(), va= lue)?; + self.current_started |=3D consumed; + Ok(consumed) + } + } + + #[inline(always)] + fn finish(self) -> impl Init { + self.into_vec() + } +} + +impl Default for Accumulated { + fn default() -> Self { + Self::new() + } +} + +/// A schema field that scatters indexed values into an array of `N` slots. +#[repr(transparent)] +pub(crate) struct Indexed([T; N], PhantomData); + +/// Copies `elems`, converted to `T`, into `slots` at `start`. +/// +/// Fails with `EINVAL` if the window does not fit in `slots`. +fn scatter_window, As: Copy>(slots: &mut [T], start: usize, el= ems: &[As]) -> Result { + let end =3D start.checked_add(elems.len()).ok_or(EINVAL)?; + // Reject indices outside of the declared array size. + let dst =3D slots.get_mut(start..end).ok_or(EINVAL)?; + for (d, &e) in dst.iter_mut().zip(elems) { + *d =3D T::from(e); + } + Ok(()) +} + +impl Schema for Indexed +where + T: From, + As: Copy + for<'a> TryFrom, Error =3D Error>, + for<'a> &'a [As]: TryFrom, Error =3D Error>, +{ + type Target =3D [T; N]; + + fn visit<'a>(&mut self, key: KeyId, index: Index, value: DecoderValue<= 'a>) -> Result { + if key !=3D KEY_ID { + return Ok(false); + } + let start =3D index.cast::().get(); + // Accept both scalar vs scattered array setting for flexibility. + match <&[As]>::try_from(value) { + Ok(elems) =3D> scatter_window(&mut self.0, start, elems)?, + Err(_) =3D> scatter_window(&mut self.0, start, &[As::try_from(= value)?])?, + } + Ok(true) + } + + #[inline(always)] + fn finish(self) -> impl Init { + Ok(self.0) + } +} + +impl Default + for Indexed +{ + fn default() -> Self { + Self([T::default(); N], PhantomData) + } +} + /// A decoded NVKV value. #[derive(Copy, Clone)] pub(crate) enum DecoderValue<'a> { @@ -255,4 +550,102 @@ fn finish(self) -> impl Init { =20 Ok(()) } + + // Tests that decoding via the `nvkv_decode!` macro works correctly. + #[test] + fn decode_typed_struct() -> Result { + const SCALAR32_KEY: KeyId =3D 0x1234; + const SCALAR64_KEY: KeyId =3D 0x1235; + const ARRAY8_KEY: KeyId =3D 0x1236; + const ARRAY32_KEY: KeyId =3D 0x1237; + const ARRAY64_KEY: KeyId =3D 0x1238; + const OPT_PRESENT_KEY: KeyId =3D 0x1239; + const OPT_ABSENT_KEY: KeyId =3D 0x123a; + const X_KEY: KeyId =3D 0x0100; + const Y_KEY: KeyId =3D 0x0101; + const SLOT_KEY: KeyId =3D 0x0200; + + const SCALAR32_VALUE: u32 =3D 0x89ab_cdef; + const SCALAR64_VALUE: u64 =3D 0x0123_4567_89ab_cdef; + const ARRAY8_VALUE: &[u8] =3D &[0x12, 0x34, 0x56]; + const ARRAY32_VALUE: &[u32] =3D &[0x0123_4567, 0x89ab_cdef]; + const ARRAY64_VALUE: &[u64] =3D &[0x0123_4567_89ab_cdef, 0xfedc_ba= 98_7654_3210]; + const OPT_PRESENT_VALUE: u32 =3D 0x55; + + nvkv_decode! { + #[derive(Default)] + struct PairSchema =3D> Pair { + x: Required, + y: Required, + } + } + + struct Pair { + x: u32, + y: u32, + } + + nvkv_decode! { + #[derive(Default)] + struct TestSchema =3D> TestDecodeable { + scalar32: Required, + scalar64: Required, + array8: Array, + array32: Array, + array64: Array, + opt_present: Key, { OPT_PRESENT_KEY }>, + opt_absent: Key, { OPT_ABSENT_KEY }>, + pairs: Accumulated, + slots: Indexed, + } + } + + struct TestDecodeable { + scalar32: u32, + scalar64: u64, + array8: ArrayVec, + array32: ArrayVec, + array64: ArrayVec, + opt_present: Option, + opt_absent: Option, + pairs: KVVec, + slots: [u32; 4], + } + + let index0 =3D Index::new::<0>(); + let index1 =3D Index::new::<1>(); + let mut encoder =3D Encoder::new(); + encoder.encode_u32(SCALAR32_KEY, index0, SCALAR32_VALUE)?; + encoder.encode_u64(SCALAR64_KEY, index0, SCALAR64_VALUE)?; + encoder.encode_array8(ARRAY8_KEY, index0, ARRAY8_VALUE)?; + encoder.encode_array32(ARRAY32_KEY, index0, ARRAY32_VALUE)?; + encoder.encode_array64(ARRAY64_KEY, index0, ARRAY64_VALUE)?; + encoder.encode_u32(OPT_PRESENT_KEY, index0, OPT_PRESENT_VALUE)?; + encoder.encode_u32(X_KEY, index0, 1)?; + encoder.encode_u32(Y_KEY, index0, 2)?; + encoder.encode_u32(SLOT_KEY, index1, 20)?; + encoder.encode_u32(X_KEY, index1, 3)?; + encoder.encode_u32(Y_KEY, index1, 4)?; + encoder.encode_u32(SLOT_KEY, index0, 10)?; + let serialized =3D encoder.finish(); + + let decoder =3D Decoder::new(&serialized, UnknownKeyPolicy::Error); + let decoded =3D KBox::try_init(decoder.decode(TestSchema::default(= ))?, GFP_KERNEL)?; + + assert_eq!(decoded.scalar32, SCALAR32_VALUE); + assert_eq!(decoded.scalar64, SCALAR64_VALUE); + assert_eq!(*decoded.array8, *ARRAY8_VALUE); + assert_eq!(*decoded.array32, *ARRAY32_VALUE); + assert_eq!(*decoded.array64, *ARRAY64_VALUE); + assert_eq!(decoded.opt_present, Some(OPT_PRESENT_VALUE)); + assert_eq!(decoded.opt_absent, None); + assert_eq!(decoded.pairs.len(), 2); + assert_eq!(decoded.pairs[0].x, 1); + assert_eq!(decoded.pairs[0].y, 2); + assert_eq!(decoded.pairs[1].x, 3); + assert_eq!(decoded.pairs[1].y, 4); + assert_eq!(decoded.slots, [10, 20, 0, 0]); + + Ok(()) + } } --=20 2.55.0 From nobody Mon Sep 28 21:03:43 2026 Received: from BN1PR04CU002.outbound.protection.outlook.com (mail-eastus2azon11010026.outbound.protection.outlook.com [52.101.56.26]) (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 C569C41F5C9; Mon, 17 Aug 2026 12:59:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.56.26 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971576; cv=fail; b=Y1uf7WEyjS0DaCztPQZ3pavJlyUfVzEsOMZhfG6Uyro0PXhOZKVp3Ed0Z0X7nsL/4IiMNXH+vwJkyNqVia8ZPLwnY17BMiJtnNCImAYiC1ji/tETbRlhtJjrK/7Dd6D9AFKXukCtkFFhvGEyDwo0ITzI/ZzNrxHbtbWFxzsu9p8= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786971576; c=relaxed/simple; bh=5JbWwajGNFFCJqsw8vemEbBRHp95r46BXmmwdhM2RvE=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=H2lRlDnYimnA29Dtzbf7/UsyENkyTCvk90TEwP7a4ralKdO0jh8MVKQJAR0S0w2M0THUWt2NCTUqE0IhUAVPgrvmZ/wZC3FE0rse6vvF0Ecp9PJm6sW5ssIYVHp65qOHWValbYNlXNz3Zweix18Oji5n/909Uu3VIeMU8jSjxLg= ARC-Authentication-Results: i=2; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com; spf=fail smtp.mailfrom=nvidia.com; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b=lJmhvBp/; arc=fail smtp.client-ip=52.101.56.26 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=nvidia.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=nvidia.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=Nvidia.com header.i=@Nvidia.com header.b="lJmhvBp/" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=UKq3SQ+or917+9cCXtTlR9DqJ4VzZDmMuVSmgEWYJUpMg5yrKw2b8FY577dRFSXFqUKQJ2b7dyuv/YZYIw+9wY8MpyY5KpKgcBu5OJambb3SFc0H0odmRsKWsZj31XhD0Nite/dEwvTWSC0aAB3HKmYRlVCVt+imweQoDsBAXOArMYhhedEAS1tG1BnhauRkXtsmH12XHyDcpDh2RSuo14GKOJpcq6V8mt+QVc/CH89IHeE4PauIPQIyqjTiQi458m4s7M47lYDvWa/Lr0NOanmiaom0dpUGoHmtstsqyXOk/A0Ybf3s45mk5tn5YVYXImy2EyLSs/mrHXxHlkQCZA== 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=K4I7cy5xmBH/a46u7T8YnfSqDhfSRPzyxl0+RYOeS80=; b=U8gmcT+uToJaXvBoJA1QtuTCgNTX8kFdkMfhsrzBTFKPKEC8AsJykjmwbqnL7GjyidI47eIL3Sjxq++o1tGBEhuRU8ubNu00zkeZIVaWI9k1ZoPGdEwFQFaF2Z+oil0oKch6tvvtZxi2X3KldHWYIutnzO1M0dF7M2nkIW0ScHLEp5osF0bpuc1CUeHBHPGZd5QPjOK+cWUgZHDGSIaWrc1LM+7/H9lJYTHxdGLTPJwCLaBtjK/SKsyCmyi7fAjulwT0Dc30VXx18CigLNd2EY2FQ4ilc7AAcR4TXJY2qfnbvrQc/d/OFiwjyuK3hm7bno2aoLvavk+U6N6Yd23k3w== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=nvidia.com; dmarc=pass action=none header.from=nvidia.com; dkim=pass header.d=nvidia.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=K4I7cy5xmBH/a46u7T8YnfSqDhfSRPzyxl0+RYOeS80=; b=lJmhvBp/pP1paNgz0I6ZjGd/aJ+XFz/AYtL+1VdeGtGDnFkrHcC57Bm27z4eOJbu6/L4t15QeoAxlX3mGj77OEyAloYSmGaltyRnrM53Ag7LwfGqj7Hn+qqnVs9Jb0rrATD5gU/utNvXj8RBuGk7VpLnW+JRr6QQ+zsO4y02F7y5I389W9NUhCGHB4OuAV5PHKkfbzt0eCkgeVd7Rtz2a7IH0f48n8a4xa/jHftZxjmwrrohSDUN4xdr5GVkgWW+XqQUTv6Q0fTmDiGM2ZSrde6EwVDNgiaW5G0CF5YblCATxTnxZG8k89XSKBM8fJm58QsxokmAk53ToCFxO5YbhQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) by PH7PR12MB6833.namprd12.prod.outlook.com (2603:10b6:510:1af::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.21.315.13; Mon, 17 Aug 2026 12:59:27 +0000 Received: from BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0]) by BL0PR12MB2353.namprd12.prod.outlook.com ([fe80::99b:dcff:8d6d:78e0%4]) with mapi id 15.21.0315.016; Mon, 17 Aug 2026 12:59:27 +0000 From: Eliot Courtney Date: Mon, 17 Aug 2026 21:56:41 +0900 Subject: [PATCH 6/6] gpu: nova-core: add NVKV GSP_INIT schemas Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260817-b4-nvkv-v1-6-b84db5e84b67@nvidia.com> References: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> In-Reply-To: <20260817-b4-nvkv-v1-0-b84db5e84b67@nvidia.com> To: Danilo Krummrich , Lorenzo Stoakes , Vlastimil Babka , "Liam R. Howlett" , Uladzislau Rezki , Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Daniel Almeida , Tamir Duberstein , Alexandre Courbot , =?utf-8?q?Onur_=C3=96zkan?= , David Airlie , Simona Vetter Cc: John Hubbard , Alistair Popple , Timur Tabi , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, Eliot Courtney X-Mailer: b4 0.15.2 X-ClientProxiedBy: TYCP286CA0004.JPNP286.PROD.OUTLOOK.COM (2603:1096:400:26c::13) To BL0PR12MB2353.namprd12.prod.outlook.com (2603:10b6:207:4c::31) 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: BL0PR12MB2353:EE_|PH7PR12MB6833:EE_ X-MS-Office365-Filtering-Correlation-Id: 7bb9a681-548c-4328-8657-08defc5f5edb X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|376014|23010399003|1800799024|366016|7416014|10070799003|18002099003|13003099007|921020|22082099003|56012099006|10067099003|11063799006|6133799003|3023799007; X-Microsoft-Antispam-Message-Info: Y3YoSu6q7uxgo/XNf/ZMUuV4YnAimDvo5C5Zehj+FX7FxSegY7983dR3PUWxvM0ps3Qp/IP4MCoYJOIqU/UCIvG+p/JhFWdH58UNXJL6kP/gN0pO+RRZA+58oF2w0O6I9/mu7EDQZcP7jFtacYSEcdCdElNnJtIgg9XpVESfFuj/qqfl3ss3waosre13FKxO+m1FVEzf5sp92BvePecCEa9kg3172vUlC8eZumlDe67OckeI3WFISS4GmI5DoUyDJc+wcoDwqM+CdcgURqPywBdJZW8wPeAjJrpLPbX+vTOQ82kctVAQMJY1F4ThSd3XJ2i0QmABWkdcSRQBSz5DtuZEzlc/NcNhiJNzRCc74Ch4/SnJmxm4r3pKuzcq9gfpff03/iogUa5TmVRy4U5fThYsXrDZnnByAEWbeF/lit/Zwm/U9EEcfzjz8ZJxMKotBI09QlVr/MOay8a9P51iuyJG+Dy6yrDYT6kQcCNyP1bZnpGN6P3aj8lW4v2vAPEm0t49TCvxpRLwzTm32n6itCedSn4wFNChcYDkYJ2226Iv2ZJZjjWGan/gIs62X6Q3vNQuSk2p/NrKvhdl8/3KN16s2Cm7q+mNLPVslkQeIvu4Dbws4vntQI5Fbq/kuOx2cnUgDZdVTuukijtirXD3vJCTMSmpr/hcOQf+g9DIb0c5hIiLIgHQmAOtr5zCKVkDllo6JQ2xtt5A+qHQ3Oqy8A== X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BL0PR12MB2353.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(376014)(23010399003)(1800799024)(366016)(7416014)(10070799003)(18002099003)(13003099007)(921020)(22082099003)(56012099006)(10067099003)(11063799006)(6133799003)(3023799007);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?Y0tMRmpUeFFUdDRqY1B1azB6Z1JJTzlPZnVpcDZENWdwTjdSMXgvTU9JZkV0?= =?utf-8?B?K2dRVUJtMEVMSktlRUtvZkVFTWc3TjZNbDJZWkFWdi9MZkdDM1E4WFZkT054?= =?utf-8?B?Z1VoYzFqRzVLa1IrVXJzclVFS1VJU21kMHQrQnU3Z1VxTWd0MFRucHZLbkdM?= =?utf-8?B?RHZHdHNhbHpYMkllK1h3US9JSFBWZThicjZYdlZiYUlzN3puWTFxMU1rb3VH?= =?utf-8?B?ODlSY0F4bU1GdnBWZW5yVDMzaTNHUnd5SFNPSkoyb001WG9QUnlqNkdWZ01H?= =?utf-8?B?TW5EeUY4S2VkVEVYS2NKNmRxQU1UZ0o5NTdrc3RqMEcyRVBZMEQ0b0JNaHBN?= =?utf-8?B?ZnpnUGNUbzByQlVrME51dzhkUnNVUWJ0TGlDY01tWDhkZXFpaThFTkM5Mk4x?= =?utf-8?B?V2t3NEZYMnlPQUhKbEhjRUVldWoxL3pQZEV2azFDTytTb2FiQitTVTlteWxl?= =?utf-8?B?dldEWFB2WDdJU2dUSHIzdGphT0ljclhUQnMwRE83VElCN0tNSTU0MVpjSnls?= =?utf-8?B?WHNJdVp2VkdjOUNYWjBYcjRjVW1OaGNCZlhoUmNtSExiYy9WRVQzN3VVOFdY?= =?utf-8?B?c1JzZjJHd2h2YktJRndERU1ETURVb2l3VmJFSTdMdzNZMDFGUlQxUDE3MEZr?= =?utf-8?B?YXBwcFhaZnZ0c0g2aHlleVdudzQ3SVZGUlFOQVVjU2JRUk9KeFl1SkZ1R2Vq?= =?utf-8?B?QU5OZTZEY0sydXkvY2pZa2V0SVZlSWJrVld1UUxMNU9wWkNKMlBYMWVNMml0?= =?utf-8?B?b3o1YmowcTBjVWU5eGNnbjljZ1hURFFzMFVOWEVOQlhoKy9QK2thd1l5RndO?= =?utf-8?B?THcwR2V2MG16RGRKa08vQXV0WkJWUjVyZkZ4QVBUZWhzZTdmekk5clhGdlNm?= =?utf-8?B?cS92d2dZUzZCRnRqQWUwRnNLUk1UQWZhbytMd3hGWlR2S0xieEdPYkhoY0pi?= =?utf-8?B?c2NRb2laUWx4d2pHYjdZTHExbmhjMmNKY3NMN1JvcytvUHlRTmRFTEkzNkhm?= =?utf-8?B?dHhBT3RMeHNVMXNFMm5nNjYzRDVlVjNpTHZ2NjRRY2tzOU9hTTFKb1p5ZWRS?= =?utf-8?B?ZVdjT1E0aG9QTWExQXJTakdTTWZtdHhvR0dLcUpqWUJ0VjJ5UjZSWEE0aHNW?= =?utf-8?B?Y29JZ3grSTZKYUF1bHdSTjliZjQ0Rms4cUNLdnZDYXhjOE1xRENmQnpoc0dT?= =?utf-8?B?QlI0b093bmtpQlJoZ0JJaFVzUDhuWHY0WDF5ckRNMUJ3Tnc3aHhQZXEzWEdy?= =?utf-8?B?NCszMGZKaWs2V0hXZHZqL3dhNkNsbUV6UmZVTVpPT2Z6K2VTNGV4bkMwNHJE?= =?utf-8?B?elpnQjZka3JVVUlsMStQL1MrUitQU09YVmlNbmFuVDhEMnVZdFZvaXdSblB3?= =?utf-8?B?RWNHdEc5OGs2Mm5qS1pIZmlwZ0gxR0JOWHJHcEhWblVnMmdjYjV1bnpwRHVI?= =?utf-8?B?ZG4xNzdYekZTTzRIdzJ2QU5IVDFOR1dUWkRONWJDZjZTN0lOc1hZRFI4dkNQ?= =?utf-8?B?SGg0Y3RDNzdvUDZmNEJMVDBYK0ZIeWRtNDZvOVV5WEt6ZTZnSzl6OFB4QVdz?= =?utf-8?B?RmJ1dDVsTE1iMHlFRjRBV2xoVzNEY0xIUy91K3J3SWIrNG5vR0grZTdpUEZr?= =?utf-8?B?Rm9kYVVNRmF5MUJYamlUMWZxenN4TElIN05iQnhJZDVXVkpadzlXZTU3V2s5?= =?utf-8?B?N3ZCR00yNlNvZityM3Z6SVRqMkE5Z21OcWc5VnUzR0h2VytSOVBKSjFZRUZq?= =?utf-8?B?VFpxNlg3dDdKZlNmRWp5TmVpOTM3eGErRktXVDdQa1ZLSUMvWTBlQVVzeGM1?= =?utf-8?B?L0Q4OEg3ODljd0dEaEVUOTJhQXlzVWdmdkVzWWx3bXg0VFZtVTZhelVHc2cy?= =?utf-8?B?aUROVUVuMWlDTmZrVENEMmdJNyt3Q1BtNWJyTHBQVDlzc21iQVBSZnQ0RXNr?= =?utf-8?B?YWZjMDdDSXE4VWhqM0xFV2xKTTh0dS85N01RbDl1MWdnOG9icHpPUVNFL1N2?= =?utf-8?B?Y1BVRFg5QTRkSkV1VkQyV01jajhTSTR2M09vM1N6ZXlBNUpFRjdHcWlvUFRk?= =?utf-8?B?eFNhWFU2M09KOWhtOHJZck5qcHovTFZFam41WGxCOHgvRWlrczNkekF3d0U1?= =?utf-8?B?emNBK3JOVHgvWjB5SkVSZ2NzTUxibUpnUlNOUHg2VTVNRTRlQWJuYkNodG0z?= =?utf-8?B?Z0RMblMweE5LamFjeXhobHNqTExlMDBJRlkzUWpoQXlMWXRpVG1SNU5WQ1pV?= =?utf-8?B?bFl1Q282UEM0Z2tjaFpkNlpYcXQrMCtRWjFSc3B0MmZBamJNMkNjQ0pLMFNN?= =?utf-8?B?c3FJVGxST2Z1YlNZL3NFZEcxUFdzcHBRWDlCSXBaSFFXZThid3UvVTUvK2Zv?= =?utf-8?Q?UqZTtmJdTXgEmIdxmCAKJdYxr8PpL6plJ5AXzzTflB0ge?= X-MS-Exchange-AntiSpam-MessageData-1: Ly2oCDxXCxn4zg== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 7bb9a681-548c-4328-8657-08defc5f5edb X-MS-Exchange-CrossTenant-AuthSource: BL0PR12MB2353.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 17 Aug 2026 12:59:27.7841 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: Yz+wJHqbjcFHCUJsJ3aYOcoirdByFMiS9riAq6vUni8wVNg6mltDA7G1kX1aaS/2Wy/uFHVol3I/i7BoRBBtsg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH7PR12MB6833 Add the first user of NVKV encode/decode which is the request and response for GSP init. For now this is exercised via unit tests. Later patches will support GMCAPI in `Cmdq` and use these messages. Signed-off-by: Eliot Courtney --- drivers/gpu/nova-core/gsp/fw/commands.rs | 349 +++++++++++++++++++++++++++= ++++ drivers/gpu/nova-core/gsp/nvkv.rs | 3 - 2 files changed, 349 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/nova-core/gsp/fw/commands.rs b/drivers/gpu/nova-co= re/gsp/fw/commands.rs index 6dc31d1bf5ae..4de44c2dc6aa 100644 --- a/drivers/gpu/nova-core/gsp/fw/commands.rs +++ b/drivers/gpu/nova-core/gsp/fw/commands.rs @@ -4,6 +4,7 @@ use core::ops::Range; =20 use kernel::{ + bitfield, device, pci, prelude::*, @@ -19,6 +20,20 @@ num::IntoSafeCast, // }; =20 +use crate::gsp::nvkv::{ + nvkv_decode, + nvkv_encode, + Accumulated, + Array, + ArrayVec, + DecoderValue, + Encodable, + Encoder, + Key, + KeyId, + Required, // +}; + use super::bindings; =20 /// Payload of the `GspSetSystemInfo` command. @@ -217,3 +232,337 @@ unsafe impl AsBytes for UnloadingGuestDriver {} // SAFETY: This struct only contains integer types for which all bit patte= rns // are valid. unsafe impl FromBytes for UnloadingGuestDriver {} + +/// The host CPU architecture. +#[derive(Clone, Copy)] +pub(crate) enum HostArch { + None =3D 0, + X86_64 =3D 1, + Ppc64le =3D 2, + Arm =3D 3, + Aarch64 =3D 4, + Riscv64 =3D 5, +} + +// TODO[FPRI]: This is a temporary solution to be replaced with the corres= ponding derive macros once +// they land. +impl TryFrom for HostArch { + type Error =3D Error; + + fn try_from(value: u32) -> Result { + match value { + 0 =3D> Ok(Self::None), + 1 =3D> Ok(Self::X86_64), + 2 =3D> Ok(Self::Ppc64le), + 3 =3D> Ok(Self::Arm), + 4 =3D> Ok(Self::Aarch64), + 5 =3D> Ok(Self::Riscv64), + _ =3D> Err(EINVAL), + } + } +} + +impl From for u32 { + fn from(value: HostArch) -> Self { + value as u32 + } +} + +nvkv_encode! { + /// A GSP registry entry. + struct RegKey { + key_name: Key<&'static [u8], { Self::REGKEY_NAME_KEY }>, + key_value: Key, + } +} + +impl RegKey { + // Define the Key IDs read/written by GSP. + const REGKEY_NAME_KEY: KeyId =3D 0x3070; + const REGKEY_VALUE_U32_KEY: KeyId =3D 0x3071; +} + +impl Encodable for KVVec { + fn encode(&self, encoder: &mut Encoder) -> Result { + for regkey in self { + regkey.encode(encoder)?; + } + Ok(()) + } +} + +nvkv_encode! { + /// SR-IOV virtual function information. + struct VfInfo { + total_vfs: Key, + first_vf_offset: Key, + flags: Key, + first_bar0_address: Key, + first_bar1_address: Key, + first_bar2_address: Key, + } +} + +impl VfInfo { + // Define the Key IDs read/written by GSP. + const VF_TOTAL_VFS_KEY: KeyId =3D 0x0080; + const VF_FIRST_VF_OFFSET_KEY: KeyId =3D 0x0081; + const VF_FLAGS_KEY: KeyId =3D 0x1003; + const VF_FIRST_BAR0_ADDRESS_KEY: KeyId =3D 0x1050; + const VF_FIRST_BAR1_ADDRESS_KEY: KeyId =3D 0x1051; + const VF_FIRST_BAR2_ADDRESS_KEY: KeyId =3D 0x1052; +} + +nvkv_encode! { + /// Payload of the `GSP_INIT` command. + // TODO: expect() doesn't work here due to Self:: reference, fixed in = 1.97.0 + // https://github.com/rust-lang/rust/pull/154377 + #[cfg_attr(not(CONFIG_KUNIT), allow(dead_code))] + struct GspInitRequest { + pci_device_id: Key, + pci_sub_device_id: Key, + pci_revision_id: Key, + pci_config_mirror_base: Key, + pci_config_mirror_size: Key, + host_arch: Key, + bus_device_func: Key, + regkeys: KVVec, + vf_info: Option, + } +} + +impl GspInitRequest { + // Define the Key IDs read/written by GSP. + const PCI_DEVICE_ID_KEY: KeyId =3D 0x0001; + const PCI_SUBDEVICE_ID_KEY: KeyId =3D 0x0002; + const PCI_REVISION_ID_KEY: KeyId =3D 0x0003; + const PCI_CONFIG_MIRROR_BASE_KEY: KeyId =3D 0x0010; + const PCI_CONFIG_MIRROR_SIZE_KEY: KeyId =3D 0x0011; + const HOST_ARCH_KEY: KeyId =3D 0x0070; + const NV_DOMAIN_BUS_DEVICE_FUNC_KEY: KeyId =3D 0x1020; +} + +// Decode: + +// Should decode with UnknownKeyPolicy::Ignore. +nvkv_decode! { + /// Schema for the `GSP_INIT` response. + // TODO: expect() doesn't work here due to Self:: reference, fixed in = 1.97.0 + // https://github.com/rust-lang/rust/pull/154377 + #[cfg_attr(not(CONFIG_KUNIT), allow(dead_code))] + #[derive(Default)] + struct GspInitResponseSchema =3D> GspInitResponse { + gpu_name: + Array, + fb_regions: Accumulated, + bar1_pde_base: Required, + vmmu_segment_size: Key, + } +} + +impl GspInitResponseSchema { + // Define the Key IDs read/written by GSP. + const GPU_NAME_STRING_KEY: KeyId =3D 0x2000; + const BAR1_PDE_BASE_KEY: KeyId =3D 0x1020; + const VMMU_SEGMENT_SIZE_KEY: KeyId =3D 0x1050; +} + +/// Payload of the `GSP_INIT` response. +struct GspInitResponse { + gpu_name: ArrayVec, + fb_regions: KVVec, + bar1_pde_base: u64, + vmmu_segment_size: u64, +} + +impl GspInitResponse { + const MAX_GPU_NAME_LEN: usize =3D 64; +} + +nvkv_decode! { + /// Schema for one FB region of the `GSP_INIT` response. + #[derive(Default)] + struct FbRegionSchema =3D> FbRegion { + base: Required, + limit: Required, + flags: Required, + tag: Required, + } +} + +impl FbRegionSchema { + // Define the Key IDs read/written by GSP. + const BASE_KEY: KeyId =3D 0x1011; + const LIMIT_KEY: KeyId =3D 0x1012; + const FLAGS_KEY: KeyId =3D 0x0012; + const TAG_KEY: KeyId =3D 0x0013; +} + +bitfield! { + /// FB region attribute flags. + struct FbRegionFlags(u32) { + 0:0 support_compressed =3D> bool; + 1:1 support_iso =3D> bool; + 2:2 protected =3D> bool; + } +} + +impl TryFrom> for FbRegionFlags { + type Error =3D Error; + + fn try_from(value: DecoderValue<'_>) -> Result { + if let DecoderValue::Scalar32(v) =3D value { + Ok(v.into()) + } else { + Err(EINVAL) + } + } +} + +/// One FB memory region. +struct FbRegion { + base: u64, + limit: u64, + flags: FbRegionFlags, + tag: u32, +} + +#[kunit_tests(nova_core_fw_commands)] +mod tests { + use crate::gsp::nvkv::{ + Decoder, + Index, + UnknownKeyPolicy, // + }; + + use super::*; + + // Tests that `GspInitRequest` encodes correctly. + #[test] + fn gsp_init_request() -> Result { + let mut encoder =3D Encoder::new(); + + let mut regkeys =3D KVVec::new(); + regkeys.push( + RegKey { + key_name: b"test_key\0".into(), + key_value: 0xdead_beef.into(), + }, + GFP_KERNEL, + )?; + + let gsp_init =3D GspInitRequest { + pci_device_id: 45.into(), + pci_sub_device_id: 67.into(), + pci_revision_id: 3.into(), + pci_config_mirror_base: 0x1234_5678.into(), + pci_config_mirror_size: 0x1000.into(), + host_arch: HostArch::Aarch64.into(), + bus_device_func: 0x0001_0203_0405_0607.into(), + regkeys, + vf_info: Some(VfInfo { + total_vfs: 8.into(), + first_vf_offset: 1.into(), + flags: 0x7.into(), + first_bar0_address: 0x1000_0000.into(), + first_bar1_address: 0x2000_0000.into(), + first_bar2_address: 0x3000_0000.into(), + }), + }; + + gsp_init.encode(&mut encoder)?; + let encoded =3D encoder.finish(); + assert_eq!(encoded.len(), 22); + + Ok(()) + } + + // Tests that FB region decoding fails when required keys are missing. + #[test] + fn decode_fb_region_missing_required_fails() -> Result { + let mut encoder =3D Encoder::new(); + encoder.encode_u64(FbRegionSchema::BASE_KEY, Index::new::<0>(), 0x= 1000_0000)?; + let data =3D encoder.finish(); + + let decoder =3D Decoder::new(&data, UnknownKeyPolicy::Ignore); + let init =3D decoder.decode(FbRegionSchema::default())?; + assert!(KBox::try_init(init, GFP_KERNEL).is_err()); + + Ok(()) + } + + // Tests that a minimal and a full `GSP_INIT` response decode correctl= y. + #[test] + fn gsp_init_response() -> Result { + let name =3D b"test name\0"; + const BAR1_PDE_BASE: u64 =3D 0xdead_0000; + const FB_REGION0_BASE: u64 =3D 0x1000_0000; + const FB_REGION0_LIMIT: u64 =3D 0x1fff_ffff; + const FB_REGION0_FLAGS: u32 =3D 0x7; + const FB_REGION0_TAG: u32 =3D 0; + const FB_REGION1_BASE: u64 =3D 0x2000_0000; + const FB_REGION1_LIMIT: u64 =3D 0x2fff_ffff; + const FB_REGION1_FLAGS: u32 =3D 0x3; + const FB_REGION1_TAG: u32 =3D 1; + const VMMU_SEGMENT_SIZE: u64 =3D 0x0200_0000; + + type Resp =3D GspInitResponseSchema; + + let index0 =3D Index::new::<0>(); + let index1 =3D Index::new::<1>(); + + // A minimal response: only the BAR1 PDE base, so the FB region li= st stays empty. + let mut encoder =3D Encoder::new(); + encoder.encode_u64(Resp::BAR1_PDE_BASE_KEY, index0, BAR1_PDE_BASE)= ?; + let data =3D encoder.finish(); + + let decoder =3D Decoder::new(&data, UnknownKeyPolicy::Ignore); + let response =3D KBox::try_init(decoder.decode(Resp::default())?, = GFP_KERNEL)?; + assert_eq!(response.bar1_pde_base, BAR1_PDE_BASE); + assert!(response.fb_regions.is_empty()); + + // A full response. + let mut encoder =3D Encoder::new(); + encoder.encode_array8(Resp::GPU_NAME_STRING_KEY, index0, name)?; + encoder.encode_u64(Resp::BAR1_PDE_BASE_KEY, index0, BAR1_PDE_BASE)= ?; + encoder.encode_u64(FbRegionSchema::BASE_KEY, index0, FB_REGION0_BA= SE)?; + encoder.encode_u64(FbRegionSchema::LIMIT_KEY, index0, FB_REGION0_L= IMIT)?; + encoder.encode_u32(FbRegionSchema::FLAGS_KEY, index0, FB_REGION0_F= LAGS)?; + encoder.encode_u32(FbRegionSchema::TAG_KEY, index0, FB_REGION0_TAG= )?; + + // Test that this unrelated key can safely interleave. + encoder.encode_u64(Resp::VMMU_SEGMENT_SIZE_KEY, index0, VMMU_SEGME= NT_SIZE)?; + + encoder.encode_u64(FbRegionSchema::BASE_KEY, index1, FB_REGION1_BA= SE)?; + encoder.encode_u64(FbRegionSchema::LIMIT_KEY, index1, FB_REGION1_L= IMIT)?; + encoder.encode_u32(FbRegionSchema::FLAGS_KEY, index1, FB_REGION1_F= LAGS)?; + encoder.encode_u32(FbRegionSchema::TAG_KEY, index1, FB_REGION1_TAG= )?; + let data =3D encoder.finish(); + + let decoder =3D Decoder::new(&data, UnknownKeyPolicy::Error); + let response =3D KBox::try_init(decoder.decode(Resp::default())?, = GFP_KERNEL)?; + + assert_eq!(&*response.gpu_name, &name[..]); + assert_eq!(response.bar1_pde_base, BAR1_PDE_BASE); + assert_eq!(response.fb_regions.len(), 2); + + let fb_region0 =3D &response.fb_regions[0]; + assert_eq!(fb_region0.base, FB_REGION0_BASE); + assert_eq!(fb_region0.limit, FB_REGION0_LIMIT); + assert_eq!(fb_region0.flags.into_raw(), FB_REGION0_FLAGS); + assert!(fb_region0.flags.support_compressed()); + assert!(fb_region0.flags.support_iso()); + assert!(fb_region0.flags.protected()); + assert_eq!(fb_region0.tag, FB_REGION0_TAG); + + let fb_region1 =3D &response.fb_regions[1]; + assert_eq!(fb_region1.base, FB_REGION1_BASE); + assert_eq!(fb_region1.limit, FB_REGION1_LIMIT); + assert_eq!(fb_region1.flags.into_raw(), FB_REGION1_FLAGS); + assert_eq!(fb_region1.tag, FB_REGION1_TAG); + + assert_eq!(response.vmmu_segment_size, VMMU_SEGMENT_SIZE); + + Ok(()) + } +} diff --git a/drivers/gpu/nova-core/gsp/nvkv.rs b/drivers/gpu/nova-core/gsp/= nvkv.rs index a0068847bb80..0afd6d5c48bd 100644 --- a/drivers/gpu/nova-core/gsp/nvkv.rs +++ b/drivers/gpu/nova-core/gsp/nvkv.rs @@ -9,9 +9,6 @@ //! function calls will map to some struct - for example, f(GPU_NAME_STRIN= G_KEY, 0, b"some gpu") //! naturally maps to storing a &str with the GPU name. =20 -#![cfg_attr(not(CONFIG_KUNIT), expect(unused_imports))] -#![cfg_attr(not(CONFIG_KUNIT), expect(unused_macros))] - use core::marker::PhantomData; use core::ops::{ Deref, --=20 2.55.0