From nobody Sun Feb 8 03:57:15 2026 Received: from DM5PR21CU001.outbound.protection.outlook.com (mail-centralusazon11011018.outbound.protection.outlook.com [52.101.62.18]) (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 4670436C584; Tue, 20 Jan 2026 06:23:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=52.101.62.18 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890222; cv=fail; b=SirVGAto+fZ2SXVEBHSmS9Z/PfCllpMRkdeKM0MVLd/d5ZkMIux23UdkZtSHwxzaPer3wFwUs+MFXmwCiT65aXG+lXJyYBYsqCWsozQgD3wjZdZcRyiySD8IlC5+3o8RaXqORHaF0iSVS2c1kbxs/1NHuBCZOHU+aYRe6QIsyNI= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890222; c=relaxed/simple; bh=E0gXj94bLuXQxmyi3dHgGcB7NKsXC9+8bYteKQu53qQ=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=EQvjRyx5h+JlB8WAepZ+UX19CpZJhT5gD8qb1b8MbwCCUo8Z3FKEbSSwMv0N8PWLRGzt3MU/AskS12Idy4rbQbZ8utzIovc103/Qk95ACF6B5QQWgpOdpOAJnuwZbGn0bZ6nM71YAS2gXv2Q/B8ktgOvxpcTA2XkkhBdpq5Opf4= 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=InQbJwNC; arc=fail smtp.client-ip=52.101.62.18 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="InQbJwNC" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=A3BK+d/YXgZ9KeDoJ+8GREn5ffl+JGgtP+Yf1n5eQrtHTtXacToQwP1QfHn7essx6sPH56lNh17QMrMb2dMJ0S/dJCX8ac5VVM2GxvfKgQvaeNrZYRNBp7gHUSQ1iwpX9ywJHZNDvtwY0HrUdRKPYnE/ZJx3SV7y1N5GsR6ZJ+Q10s2/dv+9VtnAlERyyNtal2R4TXLwKOakZCDTQ2BZNnFWpyV3fXZq7njvnjtyoWvAQGG8+Smw+A4U5jxQwJ2r6Vgv0XHcIdMNbaTHPTs3YsY0rES+glpDXt7mbvy1sDdBAwgQqmjZK9m3E1MV1oQp3JqAGXzI+LcBK5IKR/rY7Q== 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=0TvvMqiR6DD4lP2Vq//78/07SvOJahgCHyiE8icvYjI=; b=S0i2bTNRQzyLl5zaTNCV11GEC0pEn822lPUQvR3zPhb3tDfoLXaqFUTyE8dyn4Ol47JajoysevIYQ9jEE9LDLAMdfo2YiqBXQd1VapL87/WDy9So+CZIhlQuyM9pz+wQ2Wm3BY83249SFgkv8r8zZvUAiCUtUwK9p5UFjysk+8lgnwKpKQ6T2DAdoL8BSqAwT1aKm7uhzcmg7Ua9TA1rKq+xa4A/R/aBEXpceizInPDlLH6Md2/V/s6qLbfyfUUk99fYidaaWiEouTXUZJhQoG1l+VJrxtDOqO7CyM9rnweXy+3JcXtS2t5fMop/VQHO5ObYvx9oTAVO+qSlZcEVPQ== 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=0TvvMqiR6DD4lP2Vq//78/07SvOJahgCHyiE8icvYjI=; b=InQbJwNCexdZV8tOySHStSrXgE/7vc2b7e9OckYy+vY+IZWFFvFv8QR5n8ReFxoowNBXErRZvPfMjC5QUUvB/r5gaAydagEhhgQiJFK0GWbob2ANYzf1b56U4kz7oORgfNLwvdXuwmzX+B+Dc6EMH8l/Rg2unWZUHJq6aFnS4dgvFzHqvKycHP9Mp/63arTayy3Az4beoWv1KkjUvBbRJfVvEMR0mHiR8wWTccC1sM621W4gVfA2Mokc5WrUkZZDwKE7MZMci3FpL7OWcfhxGAnMgET3zvQJorK0LU8Nc+aGySpY50OG07ZGXCCa6m81LGgEYFZT6t7lr8vzTi7k3A== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) by PH0PR12MB5606.namprd12.prod.outlook.com (2603:10b6:510:141::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9520.12; Tue, 20 Jan 2026 06:23:36 +0000 Received: from CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989]) by CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989%6]) with mapi id 15.20.9520.011; Tue, 20 Jan 2026 06:23:36 +0000 From: Alexandre Courbot Date: Tue, 20 Jan 2026 15:17:54 +0900 Subject: [PATCH 1/6] rust: num: add `shr` and `shl` methods to `Bounded` Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260120-register-v1-1-723a1743b557@nvidia.com> References: <20260120-register-v1-0-723a1743b557@nvidia.com> In-Reply-To: <20260120-register-v1-0-723a1743b557@nvidia.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Yury Norov Cc: John Hubbard , Alistair Popple , Joel Fernandes , Timur Tabi , Edwin Peer , Eliot Courtney , Daniel Almeida , Dirk Behme , Steven Price , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Alexandre Courbot X-Mailer: b4 0.14.3 X-ClientProxiedBy: TY4PR01CA0101.jpnprd01.prod.outlook.com (2603:1096:405:378::11) To CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) 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: CH2PR12MB3990:EE_|PH0PR12MB5606:EE_ X-MS-Office365-Filtering-Correlation-Id: 974d9964-09c4-40b6-5a32-08de57ec71be X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|376014|7416014|10070799003|1800799024|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?WTRRZlJ0R20xMU5sUzZxQkw3UVZrdGpGamxBZzNKczJTR1ZBQlFqNUJrWVZK?= =?utf-8?B?cHVWSHJia3U2cVAxTUltU2NZbkRYbHdHTk02a0U2aXpkNzVPZHdzMUtrY2xS?= =?utf-8?B?ZWluT3N0RjRpQjd1Ym5PbnBUdUFQY3Foby9RV3FkbDIzNnFZam15b1Z6MUV4?= =?utf-8?B?M1I0dmRQWmhMMzI2THljS0JsZ1Q3VGVkNXhwS1JJdUZ6RFp4NlZ1aDU2V3Bq?= =?utf-8?B?Z29CRUxCaHk5YWRvTXUyQ2RiUDZSVEo3K2lQeG83Q242S1g2c01lTWdoR1BG?= =?utf-8?B?Z1liN3FFUUE3RVFCUytKMkZNbEtuVUMvbzN5d2xpREJHdmJzNlRpa0NybU9i?= =?utf-8?B?U1ZXS2oyWllWS0lxU2Zjd2VodTA5RlB2em5YNUl4cDhWTXJhQUFVUDBabU1K?= =?utf-8?B?N1BnK3h6QzFlcUFOOEtBZ3RQNXg2SEJPdHd5SHNkejJmOGZZdVdrdzlhOTlp?= =?utf-8?B?V2ZiTG9lWVo1bmpzbXZPakJzTlcyOHM1TExCR0VLTnAyRTI5eW9YWkhuVjRp?= =?utf-8?B?ekFzekVFM0g4RGJBbzlDKzkzd3lielRNUm9peUlHYW9DYW11ZWVKdEVjNlJX?= =?utf-8?B?MjJ2dWw5bkh5V1owY1JkaTFWL2VxWTloVmRLNGlLTTVRUXc2a3pSUlorRG9J?= =?utf-8?B?YVg2b0NKc3UzV0llNVladzRHc21ZdkZlVFcwcGRSNG1YMGd2VDRjSEJrLzRI?= =?utf-8?B?NHlUWHJndVd3ZXlKckN4S2lNZkZUS2UvcUJRWjhnRUJmTmxMMUxWaXJMV3Js?= =?utf-8?B?SXhSUEtJb2hxSHNWdFcrcEl6VmF2b1IvclJDWW5TODJPSGlZMmJ3cExicmM0?= =?utf-8?B?TnpTdDV2eDZyZjhDS3dXejFpK2JXUm5uK0xjditDWUIrMjhldTNIa3FwdWlx?= =?utf-8?B?QnExdUt4blI0R2ZZK2NRQ1VvU01iTER6RFlSWUhNUXJOQUY1cGZITFlZNmNE?= =?utf-8?B?c2F0aURrTnZxdkdNeTNFMnRSL2tjSmZoa2s1NDZ5R0JWUmV2UjJyZ1dPcUV1?= =?utf-8?B?a2MvUjFTcU1ZVGZyMGlacUhRc2djRVZiRGNSUDFIa1JhOXYzcHNiclFjcVFj?= =?utf-8?B?SUxFUzVhZEFuWHo1c3RtQ3ovTVZMNzdXbFpzbGJrbTROVmNyUEVObTJrbUxM?= =?utf-8?B?OG1semxmQ0pXRDlQMGdWWUViUTN1WHc1ZEZtL3gvdUVBVG10eVVOSGUvWlZB?= =?utf-8?B?OGVDemI0elcrczJOTWhJNGY3MTI2c1Q4ZkgyTXJ1QzArTllwdmc0R09wNHl1?= =?utf-8?B?UDdSUkxFR0Z6RWorYmw3MkxweVJqVmc5QmxZZCtkaVdjbHBjNTU0OTlKSW43?= =?utf-8?B?WmNoQkxyU0ttOHAzbEtSYTdvRjljeDQxU0w1Z1hCNE1qdnhLYStYbm5lemor?= =?utf-8?B?Mm5oc212T1NjWmpidFFCaXF5bzAvMmhyZEJCSDNibXNqQkVHZzRDL2EyZ3BY?= =?utf-8?B?UEZpcHFLeW1ibnhGZ1ZBaHRsRWQ4c1dFU051TzhqMUFJRlpnY0t1NUNNYTB4?= =?utf-8?B?Zjk0YThjT2xtMkFqR0hGRXZMTTV2c3hiUGk5WkJiOGNNWEgxT2ZSR0FMa1dM?= =?utf-8?B?KytKK2RQQ3pYRUJsSGhFK2RqbkJsbHA2RjlVZ3dMdFVjT1NLTkdtWlVFcEFZ?= =?utf-8?B?enRuY1RXZUdvMjZvRC9UL0VRbEZHUHJzQ25NSCtZZ2R2SlY4NHo2WEVCN3Zz?= =?utf-8?B?Q0hnM3QreHlwNkI1MkN0WXgvNlBndmgzakEwWks3amx0V2E0cHdkTDhsYlBs?= =?utf-8?B?RkRhbVhIbE95U1E0YkcydE9GT1RqNk1MKzcvQS9WTHo0WVAyMGtRemZBSFJX?= =?utf-8?B?V3ZPZVR5OG5xcHFWRnc1QUJIaWpDZ0V0RkJDN3BlSy9ORGdHYUVNM2xKV3pF?= =?utf-8?B?cHpQeWF1M3BWWnNYS0pwSHhIK3FJZFBFK1RuWkU2NGM5K0hFSkl1VnVibWkv?= =?utf-8?B?b2l5QnR6NFhoejhodGdNeWtCck5SQnNPRnlRejJLSVZqUzh4dEJsRUxyYXhy?= =?utf-8?B?Zko0MVA1VXozYzV0ZVZjU3RKUVRicHFZSy9QMitVdjR1ZHViQ1NNdFpRZTRk?= =?utf-8?B?TUR2Q0VSMkZpc2JJTUFEdDRJVnJwY0xRMkFGa0JDckF2UkI1dHNMUUVteitu?= =?utf-8?B?VFpNU1RiRmR5WWRoSVJUUGtXUFBHQkJvYjNQbVdCeUhvVGl5dENXVUlRNzJv?= =?utf-8?B?YkE9PQ==?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:CH2PR12MB3990.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(376014)(7416014)(10070799003)(1800799024)(921020);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?NTZvd1cvQ0N2UW94ZjMxeVl6WXh2eURjYXJZZnNvMjdtZ25VRTVFMG9LS0ZJ?= =?utf-8?B?TkJEbklzSC80Y3ZPWS9ySkNZRmZmcUw3NWRQQ0VHUWlxcEdNdWVPRVI5UlZ6?= =?utf-8?B?aVo5MGlDQ2pHNVF1c0M0TFJrTmNQbHFnMmNMYlBmekZvdXdaaVRpSWwvZGtz?= =?utf-8?B?UFNRL290WXZvL2tlRmRTL0JsQU1DTEZRU2RzQlRPYlhOdGNkOVFQSVFONVRS?= =?utf-8?B?Y3RuRWZ1aVdoVjJvMTQ3UXVIcFJKSmpJd1NIVkdUSkN4bUNtcmRQTjR5cXVP?= =?utf-8?B?UzI3VUUvczJYeGt5U01UY0IzTDFJQVdENnZUbGJSTHdoSUVRQm5mVUpnZEhY?= =?utf-8?B?QjA2alkvT21TS2dHRUI3MG1pZTJ1TlpzbjNncVFWUkFJd3VEU2Q1bitDZ3By?= =?utf-8?B?OWE1eDZwbVZwUHNpS0ozdFBRR3RMNlZCbXZMUlBEN0xaK3hkWk9EdmhueDNQ?= =?utf-8?B?TmdUZ2Y3aFNCemJlMUtPaTVNSCtUQWhTS1YvSWhCMHdCSjdOcVFldk80ZWM5?= =?utf-8?B?NTFCdmNleWFKRU52VFpRRTE0VTl0SEZ4ditWNS9WZDZmdXNWanlJTlJzenBo?= =?utf-8?B?VVFsN0lQeXlHN1FVTXJsampKVGtVUUMvVEJtSWhpSkdLZCswZk16OTQ4NGFH?= =?utf-8?B?R29JT3EySXA3SzVzZWNDZ2o3K1NHZXd4eE1PaG1UNHdkZmJ0RTdOQnlxcWZ5?= =?utf-8?B?RW9ldDhpeDJuNVphM3o4TzJNYjhUbkhzYkc4UGFhM2hQUFBEd1Q5bUpGVUJm?= =?utf-8?B?ODNuVDdDQUFha1pCWUIyanNSNGRiWjVnWDRFdkFHaWdKcUhreWJ3YjZTLytZ?= =?utf-8?B?RzN1UEo4bmdBb0d6ZENXM3hIbEtWWkNTbTZmSEc0N2FXb09lb0hQME80UXRw?= =?utf-8?B?bUVOZTgwMW1sZEJ6VFJ6cm1EamtxTVNnUU9GeHhYMWZJNHE1cXlwblMzUGJs?= =?utf-8?B?cTFRSW00TmxadHE1dnZkb1haWUxFWUdDajJOQ21UYWhUVElralh4VmpvR0kr?= =?utf-8?B?a1htZkRwQnFPaFFOTCttU0JueTVNbHNuRWphTFVOQm5oWGJCZW0vTW0rMDVu?= =?utf-8?B?Wk9FVEkrNGpDZ2hYdGhKOTBTcVlta1pJNWdxaEFSR2cveUVwVnQwQ1RpL2Jn?= =?utf-8?B?amZ1ck0wVnIxQUVyZUt0NUtSOUlQcEQ4NGxtOElCWjQyTENCRVRQcTgzRFh0?= =?utf-8?B?S2JrRXRZbG0rMklnN2VYVTJVUUFuVG5Va3EwWG52eSthSnFOdFpKa1RZenAx?= =?utf-8?B?blN3dkkrRTU1L3FVTzNjN1JISklBdVc3emhtZ1NhcG5jWi9qYU5lYVVlSHFP?= =?utf-8?B?NERack1icUhSeUwrTHNGZ3gwbjJyOWx3YW9YY0ZtYlIzU0dick9kNCtJc3Nu?= =?utf-8?B?RFExcUt3L1hIWXhPUmF3QnJRYitCQ09oSjExdXdZQTlVZ2s1bFFJRjdUTytz?= =?utf-8?B?S0dKdEQrRmZIbmlnc0pjcjd2KzR1cmtnM2FWNnY3WXhtLzBrTnRkVWtzeXlu?= =?utf-8?B?MXB3NW5lZU5UdGl5R2FnR25OZUxKWDV3SEVOeXZQQml1WVhVTzlOOFR0blQ1?= =?utf-8?B?R1A3UFRYQXdSSE9ZNkdkTnZHMlNFelpuRnUycGRsTFdxZ1FrOTJ0TDBUM2Yv?= =?utf-8?B?bzdPRi8rM0gxQlVYWWsrUHhVZFJkY3ZRUWhJTmFJRHVIWjZCdE01b0FoTWJw?= =?utf-8?B?ZUFuRE1Pd0dIWm8yaTN1SXRvOUhrRElON29OcTYrRnRLNnVSYVVMdVRGV2Ri?= =?utf-8?B?MnFFMk9WNmdyU2ZhNTJ4eHpSTTRlVzRaTnBFazZRTVpjNVdtdkl0Z0xUQWpr?= =?utf-8?B?NVVaci9VSzVLMTMySndzZWtzSEdDYmd0NnYyREZ5UzRhRVZhUDB4M2tESjZJ?= =?utf-8?B?dno1QUZLTldaQUIwTklHK0dscStJaGdMN2dpQkNYUGYwMTFxRHRUUDhaTkFv?= =?utf-8?B?ZWdrQS9GZXN6REtHQUI5aE84MUc5NTg3WGZqc3NlMHBzM0xPVEx4YldEZWNw?= =?utf-8?B?V0ZTNThTQytTMFgrd2N1TnRiQ2J4dngrL0UwNkF0ZTFrbks0Ry95TUVZbmVn?= =?utf-8?B?RDFxQkd6bjFjR1Y2bGd4ZDJjSGFobVo1S0NBa2Z6Zi9CRkpacDA1amFoWkZx?= =?utf-8?B?emF5YWxDWmtqQ1BpQWRsK0pjZXdnVEhHTmtoaDZvbzYrRVhTcVVzM090WTFp?= =?utf-8?B?Y1dBblJnZkhXZ1cwd3N5RmlyeHNGdTgvL2Evc3RJM2ovd0ovUHlINVJ2a2x0?= =?utf-8?B?dFFQc0hMaVNLSHdWL0F6SG8zTTlBUy9YTkVwNU9BMXM2bnZubTV2VmlUSWVR?= =?utf-8?B?NURIWU5nTlJvTWdLempNUjBJRGJ6OENLako4bTVHU2pSYTduZEVCbXU4SXp5?= =?utf-8?Q?2eW+XMlmtien8A3IRGGG5jBALrssQNzDpW8p3cVkUEK6I?= X-MS-Exchange-AntiSpam-MessageData-1: EoFNRWAD+eJeJQ== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 974d9964-09c4-40b6-5a32-08de57ec71be X-MS-Exchange-CrossTenant-AuthSource: CH2PR12MB3990.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Jan 2026 06:23:36.7538 (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: AoMvBSorPiqjhZNGCh64R2Qwt5l191YsJ8FjQJeuokgP+oWckg8y2oouNLN7e9ITPy/pE6FXcDc/ypV1ti7F2w== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR12MB5606 Shifting a `Bounded` left or right changes the number of bits required to represent the value. Add methods that perform the shift and return a `Bounded` with the appropriately adjusted bit width. These methods are particularly useful for bitfield extraction. Suggested-by: Alice Ryhl Signed-off-by: Alexandre Courbot Reviewed-by: Alice Ryhl --- rust/kernel/num/bounded.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index 5ef8361cf5d5..6e3f4a7a5262 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -475,6 +475,46 @@ pub fn cast(self) -> Bounded // `N` bits, and with the same signedness. unsafe { Bounded::__new(value) } } + + /// Right-shifts `self` by `SHIFT` and returns the result as a `Bounde= d<_, { N - SHIFT }>`. + /// + /// # Examples + /// + /// ``` + /// use kernel::num::Bounded; + /// + /// let v =3D Bounded::::new::<0xff00>(); + /// let v_shifted: Bounded:: =3D v.shr::<8, _>(); + /// + /// assert_eq!(v_shifted.get(), 0xff); + /// ``` + pub fn shr(self) -> Bounded { + const { assert!(RES =3D=3D N - SHIFT) } + + // SAFETY: we shift the value right by `SHIFT`, reducing the numbe= r of bits needed to + // represent the shifted value by as much, and just asserted that = `RES =3D=3D N - SHIFT`. + unsafe { Bounded::__new(self.0 >> SHIFT) } + } + + /// Left-shifts `self` by `SHIFT` and returns the result as a `Bounded= <_, { N + SHIFT }>`. + /// + /// # Examples + /// + /// ``` + /// use kernel::num::Bounded; + /// + /// let v =3D Bounded::::new::<0xff>(); + /// let v_shifted: Bounded:: =3D v.shl::<8, _>(); + /// + /// assert_eq!(v_shifted.get(), 0xff00); + /// ``` + pub fn shl(self) -> Bounded { + const { assert!(RES =3D=3D N + SHIFT) } + + // SAFETY: we shift the value left by `SHIFT`, augmenting the numb= er of bits needed to + // represent the shifted value by as much, and just asserted that = `RES =3D=3D N + SHIFT`. + unsafe { Bounded::__new(self.0 << SHIFT) } + } } =20 impl Deref for Bounded --=20 2.52.0 From nobody Sun Feb 8 03:57:15 2026 Received: from CY3PR05CU001.outbound.protection.outlook.com (mail-westcentralusazon11013017.outbound.protection.outlook.com [40.93.201.17]) (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 2D0F536CE0B; Tue, 20 Jan 2026 06:23:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.93.201.17 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890225; cv=fail; b=QJc6ALtlb2zX8Bc+21zmn3VTD/eMEgAVSw1XmUmKUNG1P5kWRPBgXuBXhXAWODRlI2iON/wP7UJ3XQpP53jDhKz0agi6wzZnTEGrYfCY3EBpn8RzQH3StRfj2nhXwL75eZDzF7r341tTVmDAjcDYUITyEA58cGjL0oAsMli6Rfs= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890225; c=relaxed/simple; bh=C7ez5OFeL8l3viuUvwijKSCc6c3b0GOCX7OijFYEH8I=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=lc1hdh/dJHiiZe3eHPiUBvJZv+vBHe8rY6hE/fQzBpDZn7N2ig5jzsYtC5q6pYvPad4/hue6aIdloom2GpzuxKbv3D/2d+rSlbax0bM5R59oGUd+sNgNSCDjd18FSd7nrLvZN3o6aVsWQY9M+ndxzrppHhoJWvIB9IoCJEyAY7g= 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=MVhRZixm; arc=fail smtp.client-ip=40.93.201.17 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="MVhRZixm" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=g8XfrFzw2Vv5l0qY/gXaKqkTo3UDYQQR27gtru2+aDWSat2ehx2ujzlVTbAoW32rl556kLfwo7lDFkq9t7jd/vTOGiIzrJ7M3iikG6n3QjHijx9AavvoTODDVvw8DFw/zOK7Cn4RhatzoGS8NCoC8EUCwtqrW7nq3V9BK5oCg1FFB6O2lXaZMK122ahQ7RNpT/g7QhigtYkDRyIEETiobRgDoD8PDST4CZGM6nXhE1dotyplQPKFV2H7EVFWaOBZOkse/awv/wpkivBTPrtMTQQ/SoAnIHytLHeOXCM5DuUr/iLYi6rkn8fde2L7oxDY8tdtbhQqjms0DMV3JGO04Q== 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=sjZ7D/0XfHZ2kBqQA3+9bVwri+SnPyAQcMa1pX2JGd4=; b=WigzeNelqwq5vP0O5cbtzJOUAfbwYyB7sUxtx3xuiB6LM40I5UTIw/TPiPGlT6niyv85/x+emVUOhuZucTMJmg4y3ygh01YelicCu3s4ELIqW49WutokCpP0N8cqTu9MeA9+4RwVglL/gjytgLJPTMwbkU3BO+XqTdoWLYcGM2PvnuCNrnHCqz08tEQBwvba/96Ekl1lqSk2GL+6I5pmovxzBB2On2miXoAWkjnwBOZrXoWMxZjYOTSuzWewY0XQmJZ2R0ci2lCSYoFiqUWsQvWgh6ue8LRllj2SMLejWXKQ9MGJBJmbEEnh9wkoPFfAtsMsi1ioTLPB/Oc2fOJhWw== 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=sjZ7D/0XfHZ2kBqQA3+9bVwri+SnPyAQcMa1pX2JGd4=; b=MVhRZixm6JDlSYIqc5vshyQ/RfBzYnktqUI/eNHP8hLBlAtEMh9B/h14QezAT5YJzIBM9Wgbx1aoX/D0CEBIiTOZPQMx4Dr7jdWf140mXnzaxXTqTBTdXieopa+zqYW5TroSRuAEYJ+NGY/0H2nh6TlFlmuWYaUs8f7+KZ1mjf9twMJk+kWKvqgqXGQyuMBK5ktvwPUIEINIw3ubtz1GKcFV/IVMx5jIkidor2aWxiAHciq/5yIxJVV0BM68Twtsy3T5B/hF9CK2LCc6CG2q2jwwrRmv4fNv5wsVqTufjLrBhqJxCB/3764i63GAJ1QZua/ibzF2CA7DL7jlyAM33w== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) by PH0PR12MB5606.namprd12.prod.outlook.com (2603:10b6:510:141::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9520.12; Tue, 20 Jan 2026 06:23:40 +0000 Received: from CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989]) by CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989%6]) with mapi id 15.20.9520.011; Tue, 20 Jan 2026 06:23:40 +0000 From: Alexandre Courbot Date: Tue, 20 Jan 2026 15:17:55 +0900 Subject: [PATCH 2/6] rust: num: add `as_bool` method to `Bounded<_, 1>` Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260120-register-v1-2-723a1743b557@nvidia.com> References: <20260120-register-v1-0-723a1743b557@nvidia.com> In-Reply-To: <20260120-register-v1-0-723a1743b557@nvidia.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Yury Norov Cc: John Hubbard , Alistair Popple , Joel Fernandes , Timur Tabi , Edwin Peer , Eliot Courtney , Daniel Almeida , Dirk Behme , Steven Price , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Alexandre Courbot X-Mailer: b4 0.14.3 X-ClientProxiedBy: TYCP301CA0084.JPNP301.PROD.OUTLOOK.COM (2603:1096:405:7b::8) To CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) 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: CH2PR12MB3990:EE_|PH0PR12MB5606:EE_ X-MS-Office365-Filtering-Correlation-Id: 75c950ee-9ae0-4ee3-b04f-08de57ec73f9 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|376014|7416014|10070799003|1800799024|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?WExiSUppYlVXTmo5b3hJT2hJZjZLbGdGNHlTbUNyamdqTGg4NW5tZUJoUUhs?= =?utf-8?B?NXU5ejVETWRvNzJqSFo1cFRyQ3ByKytiZGdVbi92aHI2ZVlheC9LVXdEMlZZ?= =?utf-8?B?eVNDVldldURzYUtLaXlpbklNN0kxQmYvMFBqVzF4SWlqTzN3dXRLbDJ3Z3Jk?= =?utf-8?B?bHpGdlR3UlNXYTJJQWpKVzZoQStNUGVGNUl1QVpXdHRrb2s5dG1YeHNkRXV0?= =?utf-8?B?dE14UmlueS95cUpBc3crc3kycmJKdkFvVldEOFExNzUvYWsvTWpKYlpwMnEw?= =?utf-8?B?bEFTQ2NPd0xzdnBRaTUxNm1RR0YxSlhhRUlJUkY0bzRGSFh3d25VK2dncnBv?= =?utf-8?B?S3dqYzN1dU9kOW4xWEltOE41NzRuWGVIbmo2V29jeS9CMS9XSzRqcmhHRXVP?= =?utf-8?B?YWtiVnNhYmYweFJnd2ZRSURyMEZTdkxwWnY5TFg5MUJFRlVYOGpUQlZWem9V?= =?utf-8?B?YVFlZU4yQkFJa0pCV21Td3RUYkovdlFEa1dNRDBWbllkNnhzMEtPclZuRDVy?= =?utf-8?B?ZFpkWFpweHoydjdNTFBxMUdNbUg3MTJDQUs0NS9zODB6NTlDQUt6Z3NMY3g1?= =?utf-8?B?ZFNhUEIyeWlocVpNQ1RFSDhpYW1jRkUxVUZSYWdaUjU0YUxScnp3UlQxODFE?= =?utf-8?B?NTJVbXJhM3FZeElycUs1R3hSNzRicUlrakpXRC9Na3A1alBSME9PdDdpK3J6?= =?utf-8?B?QVpkZkhwcWZUNThZM1U1MWJlN01lWmJqWDVVOEFlNldVeGpxRHdhVDQ0R2Y0?= =?utf-8?B?V3ZYeHZTQkdXM29XaGs1YVR3TFNGL0lOMWVZWjA2eUFWQ3A0U3c0dGUwVG5j?= =?utf-8?B?ZFVrdG02SzNYVVZIZzEveDdCUTF2M0FaZldkY251Rld3L2w1WThsUHh0dklQ?= =?utf-8?B?SGNFNElYbmpHNHFlRUFEaURFK2VkbGxnNk1oSHFKc1J1cU81c0FlOThnVnV0?= =?utf-8?B?NnZ4RnRoN2JaN1UzcmpBL1NyajlmbGhsYkZzY0RxdHgySEh5SnJ6RllKeTNv?= =?utf-8?B?S3E5UFRNMmpWMEpIQmkyeGRHcFlGNXBkVjAvdDNFVXJ4MWpXc0llZnZVdDZD?= =?utf-8?B?eDJZZm0wY0J4MTB0ODFOZjhSNTNCbStyTXZHdG8ybkZIMWUzbmZOVFo5TlFU?= =?utf-8?B?VEtZclJQRWFXSkFkQ1U4RnVycitXZTl3M1NUZ1owU1NWa0V1Y21VcitnYndi?= =?utf-8?B?RERtcEplZERSbWkwRFZIRHhNaDZSMUJmbWpJb201NXNrQmxoWForaU42TjRS?= =?utf-8?B?ZHVJSWdpV21Ydk9oNnBlVmx3U1pwOW9aaFRXWTFPa3pLRDNvNkdCUDI2UlFx?= =?utf-8?B?eXFsNFZTRUc3ZnRxNStCbHpFenBzRE5mR0ptSHpIcVFSTGpNa0wrdmF6N0hh?= =?utf-8?B?UDFsalBRa2F3a2ZKZ1VITXB6RG96cGM3aWdsM0M1U3kzODUxdUtPMXQ3azk0?= =?utf-8?B?cDc2b3A5Y1gzRGU3cnA0a25JQW55OURQcWxpc242Um96OENXTVZKMGp5cmRG?= =?utf-8?B?NnRLUDFmeStmNXpubGFuakFzM2M3OHFOckczM0VXMVEzZlhlVE9xaVByYUIr?= =?utf-8?B?SUp6bUpjYmdrUGd2cTVCVklseWM1eEtnNThNaXpIS1RpL1BLUWJJMUVWOUlD?= =?utf-8?B?Z0hpUWtNaW9LMWJralUvRGdLREhEZ3VxY09LWU5rQ2VDcGRRU21WNG9MNmVr?= =?utf-8?B?bk00UERWR0oraXIyRmJlaVovMFlScVptTWl5cFNWNFVVSDAvdGdXNi95MjZE?= =?utf-8?B?Zm9QYVhLd2FFTXZqbmU2Y052d1g1K3ZVMGJST1pUM2lkRUpsM0hPdE16UUVY?= =?utf-8?B?VzJvRHJCbVQ4cGpSTEltSEdpWVExRUpycUN0RzlLTnFJSnpqTDJzeTJTc2Zk?= =?utf-8?B?K2ROd3p6ZTBFN3hxa2czWHBEaWRnT1Rxbm5pbUd6dXZBQU0yUEdiK0lmK09N?= =?utf-8?B?eUJUeHRxazBWUXlENG9ZREJJREcreGUycWF2YWcybHdaWkxxRUk3TmFCZ1Mx?= =?utf-8?B?RWw1K0JUTXFpcnZTS2wrcW5idURna3o4TmZsRmlQdlo0OEczNHhORFc0cGZK?= =?utf-8?B?MjVHRU1tOGdreHA5UU5DbndSYW9POWtTblBlR1RIczNTRzYxbHpqeTJKcFdn?= =?utf-8?B?UHd0V1NheHpqRDJqRnBIZ21HT0ZjM0U0bmlJQUQzdVlVTnNCaEQzSDRjT2FJ?= =?utf-8?B?V3c9PQ==?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:CH2PR12MB3990.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(376014)(7416014)(10070799003)(1800799024)(921020);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?UGVOR2lxZjJOeGI0azR6MThLRnhnS0xIWTZXYU9iTWovVkJmemtTMjQwRVB5?= =?utf-8?B?dE0yZWE0akNFbUF5bEUybktkSEVicmtSWUpFVzQ1QTFpODZ3ZmNZa05WT1Jy?= =?utf-8?B?YXFOYmoxZzBzNVRucUVVdGlHSTkrNm10bWNMMldlZlhiaTV0aGxOTG8vOGha?= =?utf-8?B?NE9FdjRHUXRnYzVrNGg2K25HclBPaGNndTRxRnkxdXZvM2IxYzVFRHZ2SkVC?= =?utf-8?B?aGtodm5IbE81L0JZNWZtdWdBWnRDblpkVk5pWmpqY25kOWdpMmFOZG9CRzJz?= =?utf-8?B?RzFGa1Fodi9uMjNPbGY4WnF1S3d3V3ZZYmRsZ0NJa2Z0RHBMamhzTEc5M0M0?= =?utf-8?B?Y28vY3dTcnVRZzUzRk9xcWQ5b0p3dXZPOFZhOWh0c2dlYkFLajlhZDM5Z3Fs?= =?utf-8?B?RlFBWFRxM05kZ3ZXeXBCcnJWSDAxK0hqOTZNcThxeStiSEJCTXU2WXFTcmpY?= =?utf-8?B?eS95UmcwVEdXa0E1dC9ITkZ5VFB6aGR1SjZCbjdmc2JvSStmTHJCaGhvREdv?= =?utf-8?B?MmN4VGF1d0xRVUFMQUVOMEozWnRrcHZzdjErTU44aEN6TmRoTGhEWXJVR2R2?= =?utf-8?B?dzVrUUo1Y0FQYTQ1TWQ2emdtK05ObkRuN3BVc2RtSlRHOE5MUmlNbi9OWnVI?= =?utf-8?B?WXdSRWRHeDhrMEl2cG5GRzhMdndkd0N3d0NBUE1PRzMrV3lXODlBWTZzeHdV?= =?utf-8?B?WSsvRW9CUndKYURrdWlqVlAwUXd6Tzc1U3NyODB0N1JGNVIxbmF1NGZkc1ZB?= =?utf-8?B?dnNQYVE1MmV1M0piaGhTZ0tJRkFIcFdqcTVNTFN1a2hXM3pOT2svN2RzM1Vs?= =?utf-8?B?OURTdWZMZXJMUmJaekhHQVRFcm5keG1HUGt2aXFidTc2VjhqcnlQRXU4bFZy?= =?utf-8?B?NW9XdnRMQ2dxZzd5V05NZStuUlp5RXY2NFlrWGo3UXJMMm1Qd0xxd2VYa1BZ?= =?utf-8?B?LzhwMEtPQmllS0FycldHcjl4VC9DQjFnWTVKenUraUwzVm1Dckxsb283ajVq?= =?utf-8?B?dHJJUFErb1Z0MmNQZnkvR3VZSkxsVnhoSGhIZ3RVa0ZKZWdKdHB6SS8zaXM2?= =?utf-8?B?K01oZWs2VGRJMjJmL3AwRGRNZnRQekZyS1JpUWIrbklBcDVOT05OUVltVUFH?= =?utf-8?B?K2F6TFNiTSs5NTdsU2JoRFRkTGFzZTVNU3NTVzhvYWN5QWIyZWlTc2g0eWVs?= =?utf-8?B?RGdLaDVPTGx0ME5KanNJRWMyQURiK0YvSUJ4WFFuNGxzS0NhSm9KQXNsVUxV?= =?utf-8?B?UmJ6SmdicDVsNmlqU2tER3NGZEZsbTVFZlNBbUMva3dOVDNHZzZUMm83cHBP?= =?utf-8?B?TzZsakVxZUtrNHVHK2FpdXgyQW5HNGZxUlBLYVVmcnRieks3b2ZxUE12d3FX?= =?utf-8?B?eXZlOUhNVE1BcnI3Mlgzc1Y2UnQ4MU9oVHE2MlB2TXVBV21VV0RPeWc1QytL?= =?utf-8?B?bW5iWHpPWVAveVFYbEpYYkhyZzY3ZXlzVUNyTFNNUnhSdHJTVWdKdWMxY2sv?= =?utf-8?B?VWhxKy9QSnJXUTZtY3lTWUROS1d5L2crTXo5Z0tTaXNLWGM4N2d6Mm4rSE5Y?= =?utf-8?B?UnMyV0dvTHV3NTQvRTd4UDRQcTNlVVQ1ZjMvcU4rK0hQcDlQbW5OSldaQ04x?= =?utf-8?B?dkVVMHgyU25ETW5Ia1Eweng3a3Jpd0JKT0FoSVZtaGpCUkFUNjlwalhaM0E4?= =?utf-8?B?c0YyMTJINDZLMTd2UWVZUllEMEdPR3BJNmRUR2pscFhGRVZwL3BsRnpndGlx?= =?utf-8?B?UjJieTNkNnFlY3RseXp4dkxJNW5OMll0RCtLVGdtbmhiQTE2TGhZMHEzc3Vy?= =?utf-8?B?WnhRalpZY3RYQjFVbXRSR2JPbi85L0dNVGhPTVRZM25jVHlPZzZuanB2Z3NF?= =?utf-8?B?NlUwOWlwblQ4STgvOTMvZGw3UThTQy9CUUErcXZsa1JVcnJTblZDYzhWMVJO?= =?utf-8?B?VG1vdmdxdlFrdVhQa2xsUXQyVit3ZkVEb2k3M1k1UDhYNkUvbVVLU2Rva09Z?= =?utf-8?B?dndZVk92ZTgrZU5oRjhJVnpJVXpJMVVIUVVpZ0wzMzZEOWZORFA3UG9xcG9I?= =?utf-8?B?MHpuQ1QybStyK2NIcVFqNk9CMVh2UE1WYmU4TkVTdm1ZTlBibVY5UFNxSnpw?= =?utf-8?B?ZW5WTktsZXFxdlpTMUV6eUUrbzkyUU1rdEl1eHhzaDZMaW9zTXVSSDJQVktu?= =?utf-8?B?bnFXNVYyY0lacTQ1WTIzQnZTQ3hHQjdhemJOVkxPWHJPNXB5ODJYb0JqekJ3?= =?utf-8?B?VVhaYWdGVXZBTVlLRzM0Y2lqU3MwemQ2Y3FYQzA0NHFMOTNITDF6WlRoV0c2?= =?utf-8?B?L2ZZbExKd1ZjSXplVjNwa2J2UnZXZ29rVTA3WnNoYnpLRHRYUHRjSG5WVlky?= =?utf-8?Q?rCEyB6uJN77+L4Gadg7TapTAwOgc6sAE20p+Ly8ssG0+S?= X-MS-Exchange-AntiSpam-MessageData-1: Y/PQSbcyGBuC5A== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 75c950ee-9ae0-4ee3-b04f-08de57ec73f9 X-MS-Exchange-CrossTenant-AuthSource: CH2PR12MB3990.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Jan 2026 06:23:40.4211 (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: x/cxTrdTYXDmUEfU07o+UxWiG8/C2mKEBhoT3JJOsVUSgNjv3JDZH80epCR4i/TFDWAZ7/Ogu9QY6bKVfEZoIg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR12MB5606 Single-bit numbers are typically treated as booleans. There is an `Into` implementation for those, but invoking it from contexts that lack type expectations is not always convenient. Add an `as_bool` method as a simpler shortcut. Signed-off-by: Alexandre Courbot Reviewed-by: Alice Ryhl --- rust/kernel/num/bounded.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs index 6e3f4a7a5262..803ed0889ddd 100644 --- a/rust/kernel/num/bounded.rs +++ b/rust/kernel/num/bounded.rs @@ -1101,3 +1101,24 @@ fn from(value: bool) -> Self { unsafe { Self::__new(T::from(value)) } } } + +impl Bounded +where + T: Integer + Zeroable, +{ + /// Returns the value of this `Bounded` as a `bool`. + /// + /// This is a shorter way of writing `bool::from(self)`. + /// + /// # Examples + /// + /// ``` + /// use kernel::num::Bounded; + /// + /// assert_eq!(Bounded::::new::<0>().as_bool(), false); + /// assert_eq!(Bounded::::new::<1>().as_bool(), true); + /// ``` + pub fn as_bool(self) -> bool { + self.into() + } +} --=20 2.52.0 From nobody Sun Feb 8 03:57:15 2026 Received: from CY3PR05CU001.outbound.protection.outlook.com (mail-westcentralusazon11013067.outbound.protection.outlook.com [40.93.201.67]) (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 3ADF4374174; Tue, 20 Jan 2026 06:23:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.93.201.67 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890231; cv=fail; b=bj3hx7IH+v7hrBBqRnRUWXORW+Bfr3Na3IKaJBPKQgnKNu42HZS4F/rX4bAp6hte9CA5+YlDWHVOvWbDXADrHhgjxMs5B6TEzntJWpCRu/wKzGg8RyvhcPpeVOQMQMpBKjkgSA9yD0WF8LEuOoDen0TCIgzftiYmoBdGcZiZ9c8= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890231; c=relaxed/simple; bh=NtliX/eXRkWxXHu9H6XW/Y9HlwwEus42sGB3Yz8bGxo=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=ZEf0jGUDWDkqKfqAZoR+n4rY7teOv8gSgAy7+NZ3ViAW7RnvyA+/l6upvRcWdFgqctzB7JCSi/qONTJQEkSIn9ObGbLwB3Orz56yz++IVEoe75ARhHyNralUUZ1lwXGJ0WwayPsFqGY3sqF+TW8O5FDELZX3ca2prJrd61g8Xf8= 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=D94QtiB5; arc=fail smtp.client-ip=40.93.201.67 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="D94QtiB5" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=PkjiqOuRF/oSiIXykKe8YHjFqQ0SCKk03/P5Tk5Qg1K/Eq0pXbDg6ULP9g13aU1rYmugNJSJriCPlj/q2xa1076EyDatBPwZ4rnGgil0yk30422LNC/Xlgml+GEMlRyEtujOuTIeVJ7ztaAcHUgGFwBw9gK3qsI3Gta0Tvffx/gslj0zU+GR0slRQwCGIyX+Sc7Rd3J0BPJyXrT0Vi/Yhr9r6PJokdkNP/ch137DO4yk+xUn2QSEj6+PzwZFjnAg9KObiQZtuoEDtgMyT1o8OdSHg/5ISIXAk2uTO0ka+rZsTk0Q4Fvv7ay2qTavMM05NtBr6g3K4QgBeTrmv+YYFA== 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=b4DqmCmCQMVkaZ/qFPEh0HEopf9VRh8xMO7OH9uMCnk=; b=PlZ9xy8si0OVXHJ3V1q+RDdM4/Qbw3PmYNIKwGE0oZHdcXSWxxrXfVK6w92DG6H6wsEZhgpOCiXA22KXhWKfIS5nFw8IpLn5wqnP5Q6dtBZW81HxmWtto0Yi26o9Hc1ws+vQM7nZxbOXqQwoNCcbuk/JKlMZOagnq5sMHP9nggip9t9oPlGyq9CwgnrqE9GnPAFkPtNnC5ytX94xZYt/h6eRD4VWCEgY7l5ZjCPeQCzqy3fC970COww6skbNZEFoOcsJdWBPSRvx13DBNxOSQfsWSc4jT6R7p4RDfu8vr8m16WIkyga3gi2X7kouNRoTMDfWdxqLN8h9cvF2SpJT9w== 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=b4DqmCmCQMVkaZ/qFPEh0HEopf9VRh8xMO7OH9uMCnk=; b=D94QtiB5ezlvudAQbaOftqL7PZU3at2pVbbp9mgplniGhwdHadtft+vSICtIhXYaviBoa/YhmCbz5V8YAxhL52RUFo04rcrhNTyQYEnfIP8Sxr3zTcc/Mx3oDJ6DnGez7TX5aVzpsSl/usxffcWcbJj+QCGGQySmQ8yb/UFAEFKEp/Iw7oHwuZHXKYvsJiOYnac+F19JbtmxqNxtZoXZWE9HkAEMkVAGr7C69gKGuZQSCEwRUEdEaELVS6VPPrJ6j7tZPMzQ4uTmNwXeUhmUlFp3Bt+p5/8E2KsclByCELpuh8PRHy0jYc8WUsjsiUAgLxt+2gOmXiWSIc4dtWKddg== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) by PH0PR12MB5606.namprd12.prod.outlook.com (2603:10b6:510:141::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9520.12; Tue, 20 Jan 2026 06:23:44 +0000 Received: from CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989]) by CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989%6]) with mapi id 15.20.9520.011; Tue, 20 Jan 2026 06:23:44 +0000 From: Alexandre Courbot Date: Tue, 20 Jan 2026 15:17:56 +0900 Subject: [PATCH 3/6] rust: add `bitfield!` macro Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260120-register-v1-3-723a1743b557@nvidia.com> References: <20260120-register-v1-0-723a1743b557@nvidia.com> In-Reply-To: <20260120-register-v1-0-723a1743b557@nvidia.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Yury Norov Cc: John Hubbard , Alistair Popple , Joel Fernandes , Timur Tabi , Edwin Peer , Eliot Courtney , Daniel Almeida , Dirk Behme , Steven Price , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Alexandre Courbot X-Mailer: b4 0.14.3 X-ClientProxiedBy: TY4PR01CA0045.jpnprd01.prod.outlook.com (2603:1096:405:372::16) To CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) 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: CH2PR12MB3990:EE_|PH0PR12MB5606:EE_ X-MS-Office365-Filtering-Correlation-Id: de99d96b-e5d6-48ed-d69a-08de57ec761a X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|376014|7416014|10070799003|1800799024|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?bVNBUWloSWp1NjRwMW1iaFVQTkwxeXFkYThIaE03alJmeXVycWtVMlNKcFhO?= =?utf-8?B?VVZ0WUhJankrenU4S0tLMWlKbHp6UEdFc29wUzZrWEJJNCtPYWFLaUhLcVZt?= =?utf-8?B?N2J4UHhiNXFYMkJ4VVhFZnowOXJvblNpaHpXdXQ2NUhKMHk0djQrbVhGeW9i?= =?utf-8?B?UTBzTGZmQ1R2blFIczBXVG1CZTVVN2hhSFNCYkNzZXF3TExLdDZoTEZUTmxz?= =?utf-8?B?aEZha3hOODZXSVZtQUhHcmtoTXFtc01lNHlsMkh2Z0hCYlJiUkRBNk41OWZC?= =?utf-8?B?MUp5UWViME50WkllWmZ6MXh2R3VkQ0JHdkhSd3pWSW5qcGdxNldsMUl4aDdU?= =?utf-8?B?V1h6djhmU2FNU2xJT3dZb2FlYjdxOFhxYXFzVFd2TXN3emd5V2hpL1VWeFAy?= =?utf-8?B?Vzd5MnZIOUNVaHZaOFVMTmFTYjEvMDQrQU5NM0VtMEdzWHM2dE5NMXJEbE5j?= =?utf-8?B?L0lkbGRRdGJJanhkcFdDSEhaanBIc09nR0MxdEk3eGJCOUlXNnF2RE5IeUNh?= =?utf-8?B?d0lwdnJEZ0pMdXdaMFRmNGtNelFaeG8rR0FBL1duU3pHd25kTEhmdE5ZMTc3?= =?utf-8?B?dDVVZ0lOL2NEeVVCTmF1dXFJQ21RZmNSVVk1T2JyclVQSW1DdWt3Yi8yNXNI?= =?utf-8?B?ZFdPeURtQkFBeFlOSW0xaE9VMDFnaGF6S3hSdXI5a2kzVHBReEw1ck15UTlo?= =?utf-8?B?MXI2cG9ackQ3cGptSE1vY0g3YWtraXhQTFh3SWZ2V3V3Tyt1TmZwQ2dxOEZ0?= =?utf-8?B?YndMWlVIMnF6aURVL3JNb1M1Nk5DSTg5aEcvaUtIekJGNVpMeHNEKzE1QWZR?= =?utf-8?B?eHN0QmpmeklQc1BBd3JLdHBHWEcrbWVRV1ZsOWdDSXJUL0ptejBNcWY5T3dz?= =?utf-8?B?TVFkVG9RR1c3RTVXejRkWGFtQThOMWdBVnZKdUF3SHQxZGxncUVpSHlKS2R2?= =?utf-8?B?bU13T20ranJWWDZkN3dvZ1dMeWtPVmVZU1ZMRlI4MCtPcURDV3p1TytIaGM5?= =?utf-8?B?Vk1ubm5YTEJQdm5tWm93VGN5dnZuaGlabXQzZHBMekdLd3VwdjA5NWRmTFNH?= =?utf-8?B?c1lYUEN3enN6ZThCU0dPa1k1SVNxVXo0cnJiYVJBdjlwVnFGMWE5RlFiWlRN?= =?utf-8?B?cU5ONGJUcm9oRXI2dzBCcUxzK0hHKzZPdjkxZG14SCtxdnlaSStENzNxbGRL?= =?utf-8?B?MDdmb3ZCZFNxSFhXN2h5Tk1YaUQ0UDZvWHMyQ0o4UzYybUhpZWVZNzJvc0pM?= =?utf-8?B?NGtCRFJ0RlFKS25rNHZUL3JYMjA0WEFVVjgyZGRCOFBPdlBVbFlwemhVWkdv?= =?utf-8?B?NnZQSlc4NGkzeEV4SHp4UkRScmRlaVV3K2lxNzdGMkk0Y2ljUUZGZXdjbTJG?= =?utf-8?B?SDR1SFAxdmxhTFl3VlJjL2JETGR6dE02T2FZZjErblZFRWN4MHFCa25jcHE2?= =?utf-8?B?djRvUHdpbHZtd1NxNUkvcXlMTUx1TVVGNWhyVy9QMlp0SHlUWTlHQ1pyODJ6?= =?utf-8?B?d2RhSlhoazZrZG43d25PbnR2aXBDUnY4RDFueUF0UmFyMlJ4T05Kck9QRitK?= =?utf-8?B?QzEyRTBUdFpEeTdOdlhoaVNOV1dOMm5OT3ZyOWRUN2NmaXNUZ2ppc2JZZnFw?= =?utf-8?B?bmlPejF1QnF3RmJsdzZLNWorS2tlQXozSVI3eEtDMXRxb3haTS9DdHNDMFh1?= =?utf-8?B?Yy9LQ3RrNkpZdVQxVG9pbmpoQUtTVlgwYXp3Qyswa3lHbWdWaE1kU3c5cFBE?= =?utf-8?B?cEZ4cFQ4OWJpcVAxZ1pLRDhTVzE5MHM1NFpkVC9ycytWQjRMOXZuak4zTnpZ?= =?utf-8?B?VEJOR1B4dzA5U0tTVjRzUkRTNFFzclplSzNpUWs4dUNzeUREMzhLaU9qVC9a?= =?utf-8?B?RkRQUWMzSTZXaUxqMXlvZUJPOUR5d1RUeFVodmt5akZKMDdCOVBFZlVuYlUw?= =?utf-8?B?VDdIL3E0R0ptQXZhSlBUMWY3dS9UdFRIaXN1L2o0T210cXZ4aEQ2NkxBK1Bv?= =?utf-8?B?dHA1M3kyQXpPdk8rZzVRa0duSVVNSFVpazNraWlkdlZ0cG1MbUkwTnQyTE9o?= =?utf-8?B?ZkhMZk9LVUFRTkFhR3VHVmpiM0hQRUhQWW82MWQ3anV1bHA4LzBXeG5FTDhJ?= =?utf-8?B?S1FxZDBPb2wzV0hZZG1RVkFhZ1lkWE9zTFl2M1N5M25NMURlNVFLbHZZUURp?= =?utf-8?B?SFE9PQ==?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:CH2PR12MB3990.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(376014)(7416014)(10070799003)(1800799024)(921020);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?ZldwcVNIbjZSVFV1eEw2QzFKVkw3Z1JHWjFoVjN3M1NxY1JlSjE2dzk4MTVm?= =?utf-8?B?aFVBS2lUZkZoazVaSlJiV0FtTWRJZS9ISnREVFErY2NXa2tpcG1iUjFXTTAw?= =?utf-8?B?b0pLaW56V1h5WHk1MCt1M05xU1FVNnVXdTg3UFQweFRNYXc3Yk4ybXZoN2NT?= =?utf-8?B?Nmo0N2lSVk05eUtwei9jd3JIaWxDMCs5cjBaWjNWOHZSVWRUblYxT0tzU1RF?= =?utf-8?B?LzRvSUFiTWVWbXFFQy8wTXZPK2NLalAvdXJsNHY0TDJyK2J5citMSWRyWmZD?= =?utf-8?B?WmhUOHBJRWlVdCtURzVZZHNKcFN2Rk5aay8wZGhjOG8zNmhCdkFiQmVTWW5J?= =?utf-8?B?b0RVWm1ROHhqWVNKWVZuWUVBMXVFTG5iTnVjV0daaWpRUDMwc0xSQmRzRkZW?= =?utf-8?B?SWZNdVBFYmRYNmdXRXd5MU5UUDdWWkV4TEU0eS96M3dYQy9qRC9scFRLcnNk?= =?utf-8?B?RzQ0Sjg2NnF1RjlsVEFNRmI1U1VlZStUemZZaXFMYUNtMlBrblJVby9UUEth?= =?utf-8?B?SnltaTduYjNZcjJqMU9GZ1piV1JrNzhYSllvRzZ5R1N5YVQxSEdmK1ZWSkVq?= =?utf-8?B?cnVYQ3RJSmordlZTbGNpQ2FXTVB2eXIvUlpZRFVad01SQnRMUmVUSDVRMWUz?= =?utf-8?B?ZlBqdzVETUF2WGQxbWZBa0w3V0Z5cCtIOTdjUFlOL1JLMDhERnN2dmxhMU1t?= =?utf-8?B?aG1MVWExR0ZUTDYzK3Y2NXN4N0NoS2lnZktIakVONTZpRTU5NG9TQVd4VkRC?= =?utf-8?B?T1JKb2JjaDFCWkFINjJhSXRjOU1jWk5VLytBb000R0NVbHIrTlpmV3FzZ1ow?= =?utf-8?B?ZFFmSWx3SG1ESjNWTHEyK3VrajUzaGZ4c25nMjhwT1pPWXlVTTRBb1ByRkZI?= =?utf-8?B?UVBxaWNTejU1OVcveWR5aEZGWStDa2Urb2tEanVldzkxMU0rSVN3YlFxcnBh?= =?utf-8?B?OEU4QU92RGxuMy93RWV2eGI3M296NDUxNU5YSlVZNFZoOWg2SlY0NkRKV2xt?= =?utf-8?B?STk2anN0dTVtY3JndDQyZFFBcExSd3ZtYTV5L1V2V2hFY29Za2NtYkxJQkRR?= =?utf-8?B?WUNtZ3lOb29Qc25kdk1xc0tOUllJMEJ0UU1CdmlxRFdveFdsc2JWK0dGemJQ?= =?utf-8?B?TTlTQ1p6SFRZQ1pRVkFzQTdWMUFnQ0dLdHpLRVl5TndkcTV5bVpiUjRMSmI3?= =?utf-8?B?MGdMcG5yZDhjWWIrZUNvQzFsSnYrUUFzWFloSlJtdElrZDRxSGoxM05vcC9J?= =?utf-8?B?ZU42eUFpRm01TG1EZ1N0RThUY0gyL216MHBQVGl2aDh0TnZyZWk4ZFR3V0g0?= =?utf-8?B?VGQ5dUU1bXdrR2UrbWpEamhqdENWUWI2WmpFbUxDWnhyRktLbTAzaGoycDho?= =?utf-8?B?MGdaNURza3pUMUpOQ2k0R1VnWUk1T0hNQXVlcFh2MnZ1QUk5bHVwMTRmVkNL?= =?utf-8?B?RTN3QlZtcGpqU3JOQkh4NTRManViajBUYWhWQnlGZ3N5SWd2S1M2czJuUVlX?= =?utf-8?B?ZEdpM0Z0bURWNXB6empaNCtqUGQyelBuOWN4THBiQTBpeHNpRHFDNUdEWTFY?= =?utf-8?B?a0RvU05GUno5bE16clNLNU5sdTFOQ2dOd01ycHcwSFdSbXM0VUtRckkvaXY4?= =?utf-8?B?akcvREpUeE5HVlpqa000TGliRTlLdHQrTDZZeHY3dzJpV0ZVUkI2TFcySmkz?= =?utf-8?B?dFdRaFJsdGw1aGRxTTNhY0I5WEhSd2I2emVFWnZ1SnlBWXlJOFB4czYwcjhU?= =?utf-8?B?QkI1UThjeGJrRE1aNzM1WVFDb3lSZ3BIc2lvRlAvOEw1QitjSkFDT3NCSUFn?= =?utf-8?B?ZUdzUUhnckNhN3dQTzl2eXBWSXFVS0xIeXh4VkJyNVE2MWx1OTErbTJ3K09i?= =?utf-8?B?RDVHZzZva1V0cUNNdHN5VlNHdjVkTGdRNTZaUEowNGpaTllsTXF5Y3M3clpL?= =?utf-8?B?MmlKTllLdzhvSEJkTnp1RjBmUjdjMEl6REl4UkF3VTExbS9wdnZZbUFXMmNC?= =?utf-8?B?ekpPVmI2YkFSa0lEWDBucTlwaUxDaHQ2TUVIS3hSTFhod0l4enJrRXNsMjhT?= =?utf-8?B?N2F3SThoTjU3UVpEYlc1VnZHMS9KSVgyOG45QWJGck9CK05ETzAvK0lkT0da?= =?utf-8?B?ZEh1a0ZSK2FxTDRWNUdUa2dncXZlYjFWeTJ5ZS83cVp0Q3RWM0lZY011MXR1?= =?utf-8?B?YktINW5XdEhXQ0wwN3FTUHlTM3dlbk56aXJDMWNXdy9vaE5LbHI4SG9tSFdL?= =?utf-8?B?SXhYUGtuWHdmV3lEK2lmK3FmMVVMdS9HdE1DSFF1VTN2WXY1ZTUxUlZ5cWZq?= =?utf-8?B?RVNlL2J5aDF5U0o3YnIzK0pwZUZOWlBtaTliM3J5ODhlcng4NWs2S1d4aE5p?= =?utf-8?Q?w5ePKJRRMdAVm/K8xX5Q6oJ4hHpWQMtRvGljChvmjIEEc?= X-MS-Exchange-AntiSpam-MessageData-1: 2GfUc6NiTj5Pig== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: de99d96b-e5d6-48ed-d69a-08de57ec761a X-MS-Exchange-CrossTenant-AuthSource: CH2PR12MB3990.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Jan 2026 06:23:44.0432 (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: PnXMbKg3l1mjyU9dcuApRPzLaMg8jAht7q2I/gPJN6KQiCODzOGrd2yyeB5IEUYmLdrCwB8BvdPR6O0wpab+iw== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR12MB5606 Add a macro for defining bitfield structs with bounds-checked accessors. Each field is represented as a `Bounded` of the appropriate bit width, ensuring field values are never silently truncated. Fields can optionally be converted to/from custom types, either fallibly or infallibly. Signed-off-by: Alexandre Courbot --- rust/kernel/bitfield.rs | 503 ++++++++++++++++++++++++++++++++++++++++++++= ++++ rust/kernel/lib.rs | 1 + 2 files changed, 504 insertions(+) diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs new file mode 100644 index 000000000000..2926ab802227 --- /dev/null +++ b/rust/kernel/bitfield.rs @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Support for defining bitfields as Rust structures. + +/// Defines a bitfield struct with bounds-checked accessors for individual= bit ranges. +/// +/// # Example +/// +/// ```rust +/// use kernel::bitfield; +/// use kernel::num::Bounded; +/// +/// bitfield! { +/// pub struct Rgb(u16) { +/// 15:11 blue; +/// 10:5 green; +/// 4:0 red; +/// } +/// } +/// +/// // Setters can be chained. Bounded::new::() does compile-time bound= s checking. +/// let color =3D Rgb::default() +/// .set_red(Bounded::::new::<0x10>()) +/// .set_green(Bounded::::new::<0x1f>()) +/// .set_blue(Bounded::::new::<0x18>()); +/// +/// assert_eq!(color.red(), 0x10); +/// assert_eq!(color.green(), 0x1f); +/// assert_eq!(color.blue(), 0x18); +/// assert_eq!( +/// color.as_raw(), +/// (0x18 << Rgb::BLUE_SHIFT) + (0x1f << Rgb::GREEN_SHIFT) + 0x10, +/// ); +/// +/// // Convert to/from the backing storage type. +/// let raw: u16 =3D color.into(); +/// assert_eq!(Rgb::from(raw), color); +/// ``` +/// +/// # Syntax +/// +/// ```text +/// bitfield! { +/// #[attributes] +/// pub struct Name(storage_type), "Struct documentation." { +/// hi:lo field_1, "Field documentation."; +/// hi:lo field_2 =3D> ConvertedType, "Field documentation."; +/// hi:lo field_3 ?=3D> ConvertedType, "Field documentation."; +/// ... +/// } +/// } +/// ``` +/// +/// - `storage_type`: The underlying integer type (`u8`, `u16`, `u32`, `u6= 4`). +/// - `hi:lo`: Bit range (inclusive), where `hi >=3D lo`. +/// - `=3D> Type`: Optional infallible conversion (see [below](#infallible= -conversion-)). +/// - `?=3D> Type`: Optional fallible conversion (see [below](#fallible-co= nversion-)). +/// - Documentation strings and attributes are optional. +/// +/// # Generated code +/// +/// Each field is internally represented as a [`Bounded`] parameterized by= its bit width. +/// Field values can either be set/retrieved directly, or converted from/t= o another type. +/// +/// The use of [`Bounded`] for each field enforces bounds-checking (at bui= ld time or runtime) +/// of every value assigned to a field. This ensures that data is never ac= cidentally truncated. +/// +/// The macro generates the bitfield type, [`From`] and [`Into`] implement= ations for its +/// storage type, and [`Default`] and [`Debug`] implementations. +/// +/// For each field, it also generates: +/// - `field()` - getter returning a [`Bounded`] (or converted type) for t= he field, +/// - `set_field(value)` - setter with compile-time bounds checking, +/// - `try_set_field(value)` - setter with runtime bounds checking (for fi= elds without type +/// conversion), +/// - `FIELD_MASK`, `FIELD_SHIFT`, `FIELD_RANGE` - constants for manual bi= t manipulation. +/// +/// # Implicit conversions +/// +/// Types that fit entirely within a field's bit width can be used directl= y with setters. +/// For example, `bool` works with single-bit fields, and `u8` works with = 8-bit fields: +/// +/// ```rust +/// use kernel::bitfield; +/// +/// bitfield! { +/// pub struct Flags(u32) { +/// 15:8 byte_field; +/// 0:0 flag; +/// } +/// } +/// +/// let flags =3D Flags::default() +/// .set_byte_field(0x42_u8) +/// .set_flag(true); +/// +/// assert_eq!(flags.as_raw(), (0x42 << Flags::BYTE_FIELD_SHIFT) | 1); +/// ``` +/// +/// # Runtime bounds checking +/// +/// When a value is not known at compile time, use `try_set_field()` to ch= eck bounds at runtime: +/// +/// ```rust +/// use kernel::bitfield; +/// +/// bitfield! { +/// pub struct Config(u8) { +/// 3:0 nibble; +/// } +/// } +/// +/// fn set_nibble(config: Config, value: u8) -> Result { +/// // Returns `EOVERFLOW` if `value > 0xf`. +/// config.try_set_nibble(value) +/// } +/// # Ok::<(), Error>(()) +/// ``` +/// +/// # Type conversion +/// +/// Fields can be automatically converted to/from a custom type using `=3D= >` (infallible) or +/// `?=3D>` (fallible). The custom type must implement the appropriate `Fr= om` or `TryFrom` traits +/// with [`Bounded`]. +/// +/// ## Infallible conversion (`=3D>`) +/// +/// Use when all bit patterns map to valid values: +/// +/// ```rust +/// use kernel::bitfield; +/// use kernel::num::Bounded; +/// +/// #[derive(Debug, Clone, Copy, Default, PartialEq)] +/// enum Power { +/// #[default] +/// Off, +/// On, +/// } +/// +/// impl From> for Power { +/// fn from(v: Bounded) -> Self { +/// match *v { +/// 0 =3D> Power::Off, +/// _ =3D> Power::On, +/// } +/// } +/// } +/// +/// impl From for Bounded { +/// fn from(p: Power) -> Self { +/// (p as u32 !=3D 0).into() +/// } +/// } +/// +/// bitfield! { +/// pub struct Control(u32) { +/// 0:0 power =3D> Power; +/// } +/// } +/// +/// let ctrl =3D Control::default().set_power(Power::On); +/// assert_eq!(ctrl.power(), Power::On); +/// ``` +/// +/// ## Fallible conversion (`?=3D>`) +/// +/// Use when some bit patterns are invalid. The getter returns a [`Result`= ]: +/// +/// ```rust +/// use kernel::bitfield; +/// use kernel::num::Bounded; +/// +/// #[derive(Debug, Clone, Copy, Default, PartialEq)] +/// enum Mode { +/// #[default] +/// Low =3D 0, +/// High =3D 1, +/// Auto =3D 2, +/// // 3 is invalid +/// } +/// +/// impl TryFrom> for Mode { +/// type Error =3D u32; +/// +/// fn try_from(v: Bounded) -> Result { +/// match *v { +/// 0 =3D> Ok(Mode::Low), +/// 1 =3D> Ok(Mode::High), +/// 2 =3D> Ok(Mode::Auto), +/// n =3D> Err(n), +/// } +/// } +/// } +/// +/// impl From for Bounded { +/// fn from(m: Mode) -> Self { +/// match m { +/// Mode::Low =3D> Bounded::::new::<0>(), +/// Mode::High =3D> Bounded::::new::<1>(), +/// Mode::Auto =3D> Bounded::::new::<2>(), +/// } +/// } +/// } +/// +/// bitfield! { +/// pub struct Config(u32) { +/// 1:0 mode ?=3D> Mode; +/// } +/// } +/// +/// let cfg =3D Config::default().set_mode(Mode::Auto); +/// assert_eq!(cfg.mode(), Ok(Mode::Auto)); +/// +/// // Invalid bit pattern returns an error. +/// assert_eq!(Config::from(0b11).mode(), Err(3)); +/// ``` +/// +/// [`Bounded`]: kernel::num::Bounded +#[macro_export] +macro_rules! bitfield { + // Entry point defining the bitfield struct, its implementations and i= ts field accessors. + ( + $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + ::kernel::bitfield!(@core $(#[$attr])* $vis $name $storage $(, $co= mment)?); + ::kernel::bitfield!(@fields $vis $name $storage { $($fields)* }); + }; + + // All rules below are helpers. + + // Defines the wrapper `$name` type and its conversions from/to the st= orage type. + (@core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty $(, $comment= :literal)?) =3D> { + $( + #[doc=3D$comment] + )? + $(#[$attr])* + #[repr(transparent)] + #[derive(Clone, Copy, PartialEq, Eq)] + $vis struct $name($storage); + + #[allow(dead_code)] + impl $name { + /// Returns the raw value of this bitfield. + /// + /// This is similar to the [`From`] implementation, but is sho= rter to invoke in + /// most cases. + $vis fn as_raw(self) -> $storage { + self.0 + } + } + + impl ::core::convert::From<$name> for $storage { + fn from(val: $name) -> $storage { + val.0 + } + } + + impl ::core::convert::From<$storage> for $name { + fn from(val: $storage) -> $name { + Self(val) + } + } + }; + + // Definitions requiring knowledge of individual fields: private and p= ublic field accessors, + // and `Debug` and `Default` implementations. + (@fields $vis:vis $name:ident $storage:ty { + $($hi:tt:$lo:tt $field:ident + $(?=3D> $try_into_type:ty)? + $(=3D> $into_type:ty)? + $(, $comment:literal)? + ; + )* + } + ) =3D> { + #[allow(dead_code)] + impl $name { + $( + ::kernel::bitfield!(@private_field_accessors $vis $name $storage := $hi:$lo $field); + ::kernel::bitfield!(@public_field_accessors $vis $name $storage : = $hi:$lo $field + $(?=3D> $try_into_type)? + $(=3D> $into_type)? + $(, $comment)? + ); + )* + } + + ::kernel::bitfield!(@debug $name { $($field;)* }); + ::kernel::bitfield!(@default $name { $($field;)* }); + }; + + // Private field accessors working with the correct `Bounded` type for= the field. + ( + @private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt= :$lo:tt $field:ident + ) =3D> { + ::kernel::macros::paste!( + $vis const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive =3D $lo..=3D$hi; + $vis const [<$field:upper _MASK>]: $storage =3D + ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1); + $vis const [<$field:upper _SHIFT>]: u32 =3D $lo; + ); + + ::kernel::macros::paste!( + fn [<__ $field>](self) -> + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> { + // Left shift to align the field's MSB with the storage MSB. + const ALIGN_TOP: u32 =3D $storage::BITS - ($hi + 1); + // Right shift to move the top-aligned field to bit 0 of the s= torage. + const ALIGN_BOTTOM: u32 =3D ALIGN_TOP + $lo; + + // Extract the field using two shifts. `Bounded::shr` produces= the correctly-sized + // output type. + let val =3D ::kernel::num::Bounded::<$storage, { $storage::BIT= S }>::from( + self.0 << ALIGN_TOP + ); + val.shr::() + } + + fn [<__set_ $field>]( + mut self, + value: ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>, + ) -> Self + { + const MASK: $storage =3D $name::[<$field:upper _MASK>]; + const SHIFT: u32 =3D $name::[<$field:upper _SHIFT>]; + + let value =3D value.get() << SHIFT; + self.0 =3D (self.0 & !MASK) | value; + + self + } + ); + }; + + // Public accessors for fields infallibly (`=3D>`) converted to a type. + ( + @public_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:= $lo:tt $field:ident + =3D> $into_type:ty $(, $comment:literal)? + ) =3D> { + ::kernel::macros::paste!( + + $( + #[doc=3D"Returns the value of this field:"] + #[doc=3D$comment] + )? + #[inline(always)] + $vis fn $field(self) -> $into_type + { + self.[<__ $field>]().into() + } + + $( + #[doc=3D"Sets the value of this field:"] + #[doc=3D$comment] + )? + #[inline(always)] + $vis fn [](self, value: $into_type) -> Self + { + self.[<__set_ $field>](value.into()) + } + + /// Private method, for use in the [`Default`] implementation. + fn [<$field _default>]() -> $into_type { + Default::default() + } + + ); + }; + + // Public accessors for fields fallibly (`?=3D>`) converted to a type. + ( + @public_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:= $lo:tt $field:ident + ?=3D> $try_into_type:ty $(, $comment:literal)? + ) =3D> { + ::kernel::macros::paste!( + + $( + #[doc=3D"Returns the value of this field:"] + #[doc=3D$comment] + )? + #[inline(always)] + $vis fn $field(self) -> + Result< + $try_into_type, + <$try_into_type as ::core::convert::TryFrom< + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> + >>::Error + > + { + self.[<__ $field>]().try_into() + } + + $( + #[doc=3D"Sets the value of this field:"] + #[doc=3D$comment] + )? + #[inline(always)] + $vis fn [](self, value: $try_into_type) -> Self + { + self.[<__set_ $field>](value.into()) + } + + /// Private method, for use in the [`Default`] implementation. + fn [<$field _default>]() -> $try_into_type { + Default::default() + } + + ); + }; + + // Public accessors for fields not converted to a type. + ( + @public_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:= $lo:tt $field:ident + $(, $comment:literal)? + ) =3D> { + ::kernel::macros::paste!( + + $( + #[doc=3D"Returns the value of this field:"] + #[doc=3D$comment] + )? + #[inline(always)] + $vis fn $field(self) -> + ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> + { + self.[<__ $field>]() + } + + $( + #[doc=3D"Sets the value of this field:"] + #[doc=3D$comment] + )? + #[inline(always)] + $vis fn []( + self, + value: T, + ) -> Self + where T: Into<::kernel::num::Bounded<$storage, { $hi + 1 - $lo= }>>, + { + self.[<__set_ $field>](value.into()) + } + + $( + #[doc=3D"Attempts to set the value of this field:"] + #[doc=3D$comment] + )? + #[inline(always)] + $vis fn []( + self, + value: T, + ) -> ::kernel::error::Result + where T: ::kernel::num::TryIntoBounded<$storage, { $hi + 1 - $= lo }>, + { + Ok( + self.[<__set_ $field>]( + value.try_into_bounded().ok_or(::kernel::error::code::= EOVERFLOW)? + ) + ) + } + + /// Private method, for use in the [`Default`] implementation. + fn [<$field _default>]() -> ::kernel::num::Bounded<$storage, { $hi= + 1 - $lo }> { + Default::default() + } + + ); + }; + + // `Debug` implementation. + (@debug $name:ident { $($field:ident;)* }) =3D> { + impl ::kernel::fmt::Debug for $name { + fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kerne= l::fmt::Result { + f.debug_struct(stringify!($name)) + .field("", &::kernel::prelude::fmt!("{:#x}", self= .0)) + $( + .field(stringify!($field), &self.$field()) + )* + .finish() + } + } + }; + + // `Default` implementation. + (@default $name:ident { $($field:ident;)* }) =3D> { + /// Returns a value for the bitfield where all fields are set to t= heir default value. + impl ::core::default::Default for $name { + fn default() -> Self { + #[allow(unused_mut)] + let mut value =3D Self(Default::default()); + + ::kernel::macros::paste!( + $( + value =3D value.[](Self::[<$field _default>](= )); + )* + ); + + value + } + } + }; +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index f812cf120042..66198e69d1f5 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -68,6 +68,7 @@ pub mod alloc; #[cfg(CONFIG_AUXILIARY_BUS)] pub mod auxiliary; +pub mod bitfield; pub mod bitmap; pub mod bits; #[cfg(CONFIG_BLOCK)] --=20 2.52.0 From nobody Sun Feb 8 03:57:15 2026 Received: from CY3PR05CU001.outbound.protection.outlook.com (mail-westcentralusazon11013067.outbound.protection.outlook.com [40.93.201.67]) (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 60B9C376BD0; Tue, 20 Jan 2026 06:23:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.93.201.67 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890233; cv=fail; b=CnR0+N1sTXgo+u2Ani6sa8Cx+LkrO7ThATt+RJESOL/uEEzE/Zr3XDlkvD8OU5h/CfuP/27wJWuWvC9rksNYYIiY5RqMUL7QJQRY/DrfnvohfoWFDhnyIosRH3jrWJ2ydFM81M5o8cBIjrNGPpsNxztSAGu9AxpGMNEPccSgQ1s= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890233; c=relaxed/simple; bh=YHkGbjCB5wf9wRTdBNctUErr/AMxFcgt7lnsHtXw0wc=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=uqLap0fV9gce1xmeDa/GZ+gRR/pSJa0gE9dq391rI6JiTpe81CYnlQSCyeycyz48/KucPxjpDF6DYSlTo1jzhK+gmkxX6VprwkfvlVJa/e20Y93ujScne5Uq3SNfZwEzD9eu4C8IT7bDCsLEt1CUZUEHjbJ+TW/OzpaHkIsLPNk= 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=CBgpnYpY; arc=fail smtp.client-ip=40.93.201.67 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="CBgpnYpY" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=uBjIxbbZEiA71bxh6NWDLHC/JU5C/5l3UkfRafCiuQ+DmHTp/BOJuKQ80d+P2TC4H6zpiZdcda9gHA3WNynyC/twCdWgPRjdhqYOuQPVtsk9CGmtyNr0R7umwjIiIZnNkShZSICdrDQXy0ATGmwIGZPniCqV62EAVTw9IZSaWm8jZkjiu8FzIg//MOCboZXSAqcWzAKXbU6VyEZTizxrlSlJ8EfVhrN2wSMKoNw2R9l3icmqWVIi7gBsJL5tcdF4gJZ6lfNr3q2NMF8NaaPA8cmOqShCgR2w55skDBXudxUodI8pGPnUcEjOH1QL9HhVS0NhfQIODwOXdCcQdabj4g== 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=ZzJPskV5pZcqFrrFf9ajOkWnx89K5oCQFm1vjfSOk9o=; b=VyE8zVd3rw/kOvPI1aqNWjunVS0IWSe9F564Vye8BQeCKpItEBfH7wHICWBPjqNYvj4j650rj9o6QxKx9jO1FxF9ObstGz3m+f/xPwr5dzR5yJHQPLYS1b6lfrcvv3HmqKtnN/8Z8RFrPEYviVEtIBlav4UdvbL1RUAFsdmyX6WCIcwrvz5mRCmiPXU1JTfsnGLl1Qax/xkATPg7b2fCyrCK+TbyacLs+7ernscxMYQPABRqtl2MSd0Fd74txlY+Jh8WbIyO9UiGHaajBqgQaNDviKuE0qSicL6fHLW9XI6c+BMZJ7CyuoTXfMlfvV3EAjODLhLUAU7zbepHFJTZ9w== 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=ZzJPskV5pZcqFrrFf9ajOkWnx89K5oCQFm1vjfSOk9o=; b=CBgpnYpYygscNwXfBCrsNgsuvSVfKYyxRKI9tgDrFYif13qx9tPR+aZ2abU4yw7BjYHGNsrGfEJQPcjNs4NCpznv2NOMI5XCz6DuDz+eNqiBI5V9IMKHdGW0SGyvcJO+4GGRNCtCQ0jJ+hJe1AJHzz0qeSd/2hzhBtLHPH1uxe5ChSocARH8obMIf/L3pN7feqxnYEMWA0eDSAsKp2EkUca7FLtG5WU7/gD2gvmc7Ql43SsuLAwuo6WGfw6D6WxDnCOVq3y5GkVcUTly4AJnHKz2hX7Q1frs6W6iWAa/9YNsKCRIK/1HaUTMX78pklG6XioDd4qEyjhvovgbzcjfOw== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) by PH0PR12MB5606.namprd12.prod.outlook.com (2603:10b6:510:141::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9520.12; Tue, 20 Jan 2026 06:23:47 +0000 Received: from CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989]) by CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989%6]) with mapi id 15.20.9520.011; Tue, 20 Jan 2026 06:23:47 +0000 From: Alexandre Courbot Date: Tue, 20 Jan 2026 15:17:57 +0900 Subject: [PATCH 4/6] rust: bitfield: Add KUNIT tests for bitfield Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260120-register-v1-4-723a1743b557@nvidia.com> References: <20260120-register-v1-0-723a1743b557@nvidia.com> In-Reply-To: <20260120-register-v1-0-723a1743b557@nvidia.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Yury Norov Cc: John Hubbard , Alistair Popple , Joel Fernandes , Timur Tabi , Edwin Peer , Eliot Courtney , Daniel Almeida , Dirk Behme , Steven Price , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Alexandre Courbot X-Mailer: b4 0.14.3 X-ClientProxiedBy: TYCPR01CA0137.jpnprd01.prod.outlook.com (2603:1096:400:2b7::13) To CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) 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: CH2PR12MB3990:EE_|PH0PR12MB5606:EE_ X-MS-Office365-Filtering-Correlation-Id: c3f57b37-42c6-4303-79cb-08de57ec7857 X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|376014|7416014|10070799003|1800799024|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?Yi9KOExLRjQ3azNjVnBBYTJMczRpYTE3ZWw0MlVMeXNXUldFbGMyUC91cCs3?= =?utf-8?B?OVI4RThNY2xMei9GNjFJNFo1UmNYTGlOOUlCcTlrV01tSWFmaGtPNGpNcVpZ?= =?utf-8?B?Y09IWUNrdUVlRzZqTloxN3VTNlJUdUxjdEZHZG9mbkxFY2VOcnoxNjRnZ3RE?= =?utf-8?B?cU5LVlNCbkZ1MXBIekRhT2hsSDFsN2hxQ3Y5VTFPQ2wyb0F3QnVpMEtiNkhY?= =?utf-8?B?QWx6Y3J1eEtqSXU2SjhGbHFxZlR6K1A0QzR1K3R1VGpHWUVpUzVWRlBPYmhz?= =?utf-8?B?aU1RSm94T20xYVpSZ3JsaVBBK09TWGtRK3lqSEJuTlRZcmRaVWZhV1krOVdV?= =?utf-8?B?SzVuRjQvSDN5dHpLZDlIZGh0VmZEUHVKc21nNzdWWDJOV28zcW9hTDNDY1Zw?= =?utf-8?B?SmRmVEg5V3NzNzNuaEhhamk5aXpEc3IzUndLeXNHK0RQQWFTVEd2akJmOTBy?= =?utf-8?B?SlVsZndGMHNKTkVBQnVmTm9HSmtOajZOWFV5ODBEUHZqVlFjTlhZS3BBVVBE?= =?utf-8?B?ZHBPS1k2bkJBaDZ1cXdvRWFKdTdvNVBXbGppRk9MYVlwcVBlSXhYS0J3UE1q?= =?utf-8?B?VVV5eGNwSDl0VlcrV1lxUG1wUHJxSFlidStsNTlnM2llWWl2eEhPR09STFg0?= =?utf-8?B?WmZ0Umg3SmRDTStzakZaeXhERFJJNnhRNXBwQnZsWnNjWFJLVFZNaVh6TktO?= =?utf-8?B?WDdqWHdrbmxBNjdJa2F5a2dqY3pVZ1ZVQUFjZHB4MFdNeWtjenlQaExEdVpk?= =?utf-8?B?MkhqSzRuTWhwQmhYQStGZlV0ODFCUm9XNTRpREJYRGQyL3RXUlVMWUpaWHFt?= =?utf-8?B?eEFDcjFuZENBeXZSRk9TWWxCb2RWdXN4Z2dVdDBhbVBvOUlwK09RYmorWkV0?= =?utf-8?B?aDRZcEowVVFwY3dIVW4yb1NTRGhLNHV3Mnk4U3ZsVjRJME1kdmNURUVaeUxR?= =?utf-8?B?YVNOZGtZV1BObVV4bnhpUW12ZXV4S1Q2TE41TDZ0ZGZ5Z3ZXUE9SWUx2bjdK?= =?utf-8?B?QnNEQlJIei9WZzk3SkV3UDlDRTVkUE95YndUYkRsMEsxTENkZ1U5dkZkZ1Jk?= =?utf-8?B?eG01MEdlak5jWHlSTTJUajFTKzNsTis2NitlS1RrWlVYcDMva1EwYWVnUkYw?= =?utf-8?B?cEhPZ1B5MXdtZ1lqN2lTUDFaNzJ3TTZyWDg2dityTTZnVGRLbmlXZ2pzT094?= =?utf-8?B?WDNLcTFxMXlLbnFvV2xRZ3FSd3dDQ2lJazl0RVdzQzBvNXhCOE5BU2NRZlZh?= =?utf-8?B?OUllMmQ2QWlvZmJSZ1h5UFo5TFZYV2Q3MW1CaVVOZG1VQW42KysxcEsva0kr?= =?utf-8?B?T1JDQzEwWTRTSnRhdHRsb1pLT3Jva1FhdzZlM3JOZkRQc3lvdzYwYVIwbG80?= =?utf-8?B?cG1JbkVaMmM1UmRMWXhLQzhOb2lLcDlyeXlhZU9nd3VMdnB1c2tBam5JdlNF?= =?utf-8?B?QkhsK1VIVUQwa0RERDhzdUFRUFoxKzNSS0o5OTZVb25JQ2VYY2xaSW1CZDZE?= =?utf-8?B?aHZVdGZ5dWpPS3gxZmVpRVRPTGpjQXJic0hDYmhFeElTcktSMVpYUHRkMndm?= =?utf-8?B?Yjhhd0NEZEg1OThQbGdBaGlndGc1U2laQnNIUjc1cVdkV3NZZisxZ2NkSDI1?= =?utf-8?B?UjBZcmJjVUMra1g4RytGcDIrY0dJR29JblQvWk5iaHJGMW1hKytkSzNUTVlN?= =?utf-8?B?Rjg1U0Vhd29wZkdpM1dvZjZxRTEwdHVuUm1rR1BLRXhwZVQ3b3NVMWNncng4?= =?utf-8?B?OGlDQnBkZVhXSHFaczc0eW5ZV2tQZHVReHNxRU1ka2FzYkkyMldMZndQQjZi?= =?utf-8?B?bGozeWlkenUraVhQVkg1VlE2V1hHajh2cFl6N2EvaXFqWDZWd29sREQzT3d2?= =?utf-8?B?NDlJcWtCK01HYVFmMTFESVFmQXRUTFNrSjIwUmFuUHFHZjAzUG1VWEdCbWsx?= =?utf-8?B?ZHZrcXZPcWlMM2VRbDdlb0tTajNuMjlVL3BwTThUais3U3YvUWtHbzVCcHhX?= =?utf-8?B?UjlUSzg1L3l4SDkwK0Yvb1huMHQwem5VcG5SbWdNQTlEVHJQOG4xTGQ0TERV?= =?utf-8?B?VUVQaVdIMDBWOGd5b2ZrMzFodlBidmV4TVk4STM1S1dLZVg1ejlEbzNCMGRp?= =?utf-8?B?S0xoOGpXVFRMbENER3V3NXh2djJHdC81aDJGT0hjaEZJSExMeVlndzJ3dEVm?= =?utf-8?B?eFE9PQ==?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:CH2PR12MB3990.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(376014)(7416014)(10070799003)(1800799024)(921020);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?NjIyYjdpTHdQWkJrdVR2dndSQVVKUmZTMUtNeXlMMkY3MHZhMGhTYWp6SWd2?= =?utf-8?B?WDFYTk5ZblFCeWNDZjczZFZZMm9kemh1MW0zbG93SVBxMEhVSXFrWUNqZm5N?= =?utf-8?B?K05Ud1BLa1lseUR4anJpbGVNWDFwRThwaGlIdW1FNG9xYUx5Zzc1VXorUlNs?= =?utf-8?B?KzVOUXdNeHMzcE1EeUd2R1lOQ2VyY3c1dExTTlNLcVp6NTN5Y1NtY2Y0dm8r?= =?utf-8?B?N3JJQ2VMU0xqYjlLYzBPQXcrTWUrQTh2YXBKRFlQMGZ1bUVRNFllSU82dysr?= =?utf-8?B?bFNQMzVObkwxZXdoZEtxc1E4MHFiV3c0V1pDa0ZrVzNXTVRWem1DVnhrenh4?= =?utf-8?B?dHZKa1BxRWtZWFNSSmxJK3kzaGtwUFR6NVAzRis2VHlVcW9tbnljbWFWbmt5?= =?utf-8?B?TUlzR0dJejZoRGJGMnBJNEllc1E4MW1Ocjc4WXhqTWNjbFF0a2czTXhvVERv?= =?utf-8?B?RGlvbnliQncwMEFGeG53NXdVakM3VUh2VkNkclp0WjJXdGhZTVc1ZmU3VXVt?= =?utf-8?B?SE5YWm1zeFVGb3JWVEhic2FEUnhScE5SZUNiZzdTL01MSVRTOWk3TEphUU5q?= =?utf-8?B?cVNkL2NlMDdyVm10WUQ3N2tMRU1NbVhCRDlkV2NKY2YwS3FFMkgxSUE5UFdQ?= =?utf-8?B?WWc0UjVHLzJGZ3F2c252V25xa0djMHFQd2ZDQ3YzU1I4VjlGbGRBdU84dHM3?= =?utf-8?B?bDQrSHorWTFwaVAwMWx5NzRuOXFPWXdMWXRpMkYzdGFCT1VqYVdJRFArbjNl?= =?utf-8?B?Q2JXakE0UDJEUGNyMjMzV1UzR2Q5VGEyT2loSjlUOXN6UG8vUjJLQ3hLcFpT?= =?utf-8?B?c3ZHRUZiVkpuK2d1TjE3eldKTEl2TzM3Sk1XMm1BNWo0d01zQzBSQlFhT3RT?= =?utf-8?B?R3R1NEJTRjhYbUptUXJjYTJzWUEyUmNHYjNTVDNNM2Nrdkt2WWFCcU8rNGFH?= =?utf-8?B?Z2xibmRKaFB3U1lWWDVFS0RBcDg4RXRLRTNMdG5GVkJyQ21NYXJZN3pHOHBj?= =?utf-8?B?cEZRRWtCZytiUXFrTTVubzVJYlZvaFdvU25vOVJrdGx2NmRuMkFEbEdiZ2tS?= =?utf-8?B?OXBGSFc3VEp4T2JTQWRsUXQvbW9ONFQ0Z1NvV0hacEFWVEN6dzVQRXMzU1I4?= =?utf-8?B?VW5tY2g0a3RIV1pMNjVYbkFYUmVXYXpETDY4Nk5YazlWall0Y0VZZFJUZGtM?= =?utf-8?B?M0xiYnU5WEVySjVqK1l6ZnVqcE4yTE5YSmVpZGRSVlU1d3MveEk1ekhqU2FW?= =?utf-8?B?cUxLOWg2SWZKVWlDVyt5djRWL0oralowOGZpOU9DZkRjRzAyLzI1T283YWMw?= =?utf-8?B?WUhKenVVR1FRS1FuSjhqSk1YRmx2dnR0aWJEWkFzWGxwMndpWm9hVnVhZXRl?= =?utf-8?B?aFdrcTh0OU82V0VndWtkbGdDa0VnUDltYVlhOXVYSy9TWjg3bnFGSmJ1dVZ3?= =?utf-8?B?cHpjZ3UyRE12aWFvZmUzZ1lScSs2aWVPT2g1OHRUUDZzR2g5Rkw1d1ViN2RM?= =?utf-8?B?V25XR29vOXlka3U2c2pHSXd4M005Uk92UHpueHNZNFhZdUo2NWMwSzRGZGZ1?= =?utf-8?B?KysvWW8wYlRIdG5UWHRZVVlBMWc0UTVPRjZ2SWI5cWxHcFlRVzZNcVVkTGU4?= =?utf-8?B?bkpncEJkaVJycCtKRU5PUUJtMzhRVDZWUWIveHN1K3lVUW1LRkI2blhwdmdJ?= =?utf-8?B?UGU4cE8wbzhTTFFkU3hXVXhXQ01wM1FzZGNVV202OHZ5dG9KOGZoRlJIeFc5?= =?utf-8?B?ampQTDEzOGdZaDl3THBvdU9RYkNVblhJaExMUERWVDAxS1phSXJvSXJ3bXNJ?= =?utf-8?B?eFRBaTlQeW14YVEycFlKalJ0emhydVdjK0grL1cwalhnTXlZQTlTQmFGK25J?= =?utf-8?B?SUpZZ0NnYW9kNTlueHNJR1JweDg2S1dxUlRqTnRnREpURXNqN2pRUmNvSXB2?= =?utf-8?B?dmZWaVRHTG1hTXV6RnFJV01JcDhuMVlmaUFFSUdQZE1WQThVWkltNVdhMm9E?= =?utf-8?B?YjVUOHBPM2JsRUpqb2pQbmRpOXZ5bUp4d3k2eXh0Qzc2MTd3SWhmaDZnRGh3?= =?utf-8?B?Um8yN0JMeGQwZWxVZjcxUkxnUHNhd2szZUpxUGtFbkFwcG0wQmtQNlZyK3Ri?= =?utf-8?B?MzdYdkNpSUVhRVJaNEhGeG5ySk9HU0FReGpmbGNQSklXMWpqeU9jZzdhRjRL?= =?utf-8?B?V1B1R3QydURpU0FleEp3U24rS0dIZ3lvOVNRd2xBc2R5aERZY1VadjVVMEl6?= =?utf-8?B?RzE5QlVTYUQ0end4YXZMblh2ZmpXNGxWVllMSHFHNHR1UEE0QzdUQ3BBNjdR?= =?utf-8?B?TnVGMUJiSVBvZFJmcEY2cXdOTDFrQW53YjBSKzMxc200U3VLWHJES3hKbDdj?= =?utf-8?Q?Ak5xnuSBkyDXhDRW/qMOGLCllexL5zEM44r5dOeyS2cnd?= X-MS-Exchange-AntiSpam-MessageData-1: LAeDKpKJlGjnSg== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: c3f57b37-42c6-4303-79cb-08de57ec7857 X-MS-Exchange-CrossTenant-AuthSource: CH2PR12MB3990.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Jan 2026 06:23:47.8458 (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: KCw2A7kWKSjp4HCYGNn2ItjksyP4dQyhFZT0ur56V3EwGqWTioSYKE9I2gxvha5oM2+LuzxQnvx+XCdd7oOolg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR12MB5606 From: Joel Fernandes Add KUNIT tests to make sure the macro is working correctly. Signed-off-by: Joel Fernandes [acourbot: update code to latest bitfield! macro.] Signed-off-by: Alexandre Courbot --- rust/kernel/bitfield.rs | 318 ++++++++++++++++++++++++++++++++++++++++++++= ++++ 1 file changed, 318 insertions(+) diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs index 2926ab802227..a1505816a590 100644 --- a/rust/kernel/bitfield.rs +++ b/rust/kernel/bitfield.rs @@ -501,3 +501,321 @@ fn default() -> Self { } }; } + +#[::kernel::macros::kunit_tests(kernel_bitfield)] +mod tests { + use core::convert::TryFrom; + + use kernel::num::Bounded; + + // Enum types for testing =3D> and ?=3D> conversions + #[derive(Debug, Default, Clone, Copy, PartialEq)] + enum MemoryType { + #[default] + Unmapped =3D 0, + Normal =3D 1, + Device =3D 2, + Reserved =3D 3, + } + + impl TryFrom> for MemoryType { + type Error =3D u64; + fn try_from(value: Bounded) -> Result { + match value.get() { + 0 =3D> Ok(MemoryType::Unmapped), + 1 =3D> Ok(MemoryType::Normal), + 2 =3D> Ok(MemoryType::Device), + 3 =3D> Ok(MemoryType::Reserved), + _ =3D> Err(value.get()), + } + } + } + + impl From for Bounded { + fn from(mt: MemoryType) -> Bounded { + Bounded::from_expr(mt as u64) + } + } + + #[derive(Debug, Default, Clone, Copy, PartialEq)] + enum Priority { + #[default] + Low =3D 0, + Medium =3D 1, + High =3D 2, + Critical =3D 3, + } + + impl From> for Priority { + fn from(value: Bounded) -> Self { + match value & 0x3 { + 0 =3D> Priority::Low, + 1 =3D> Priority::Medium, + 2 =3D> Priority::High, + _ =3D> Priority::Critical, + } + } + } + + impl From for Bounded { + fn from(p: Priority) -> Bounded { + Bounded::from_expr(p as u16) + } + } + + bitfield! { + struct TestPageTableEntry(u64) { + 0:0 present; + 1:1 writable; + 11:9 available; + 15:12 mem_type ?=3D> MemoryType; + 51:16 pfn; + 61:52 available2; + } + } + + bitfield! { + struct TestControlRegister(u16) { + 0:0 enable; + 3:1 mode; + 5:4 priority =3D> Priority; + 7:4 priority_nibble; + 15:8 channel; + } + } + + bitfield! { + struct TestStatusRegister(u8) { + 0:0 ready; + 1:1 error; + 3:2 state; + 7:4 reserved; + 7:0 full_byte; // For entire register + } + } + + #[test] + fn test_single_bits() { + let mut pte =3D TestPageTableEntry::default(); + + assert!(!pte.present().as_bool()); + assert!(!pte.writable().as_bool()); + assert_eq!(u64::from(pte), 0x0); + + pte =3D pte.set_present(true); + assert!(pte.present().as_bool()); + assert_eq!(u64::from(pte), 0x1); + + pte =3D pte.set_writable(true); + assert!(pte.writable().as_bool()); + assert_eq!(u64::from(pte), 0x3); + + pte =3D pte.set_writable(false); + assert!(!pte.writable().as_bool()); + assert_eq!(u64::from(pte), 0x1); + + assert_eq!(pte.available(), 0); + pte =3D pte.set_available(Bounded::::new::<0x5>()); + assert_eq!(pte.available(), 0x5); + assert_eq!(u64::from(pte), 0xA01); + } + + #[test] + fn test_range_fields() { + let mut pte =3D TestPageTableEntry::default(); + assert_eq!(u64::from(pte), 0x0); + + pte =3D pte.set_pfn(Bounded::::new::<0x123456>()); + assert_eq!(pte.pfn(), 0x123456); + assert_eq!(u64::from(pte), 0x1234560000); + + pte =3D pte.set_available(Bounded::::new::<0x7>()); + assert_eq!(pte.available(), 0x7); + assert_eq!(u64::from(pte), 0x1234560E00); + + pte =3D pte.set_available2(Bounded::::new::<0x3FF>()); + assert_eq!(pte.available2(), 0x3FF); + assert_eq!(u64::from(pte), 0x3FF0_0012_3456_0E00u64); + + // Test TryFrom with ?=3D> for MemoryType + pte =3D pte.set_mem_type(MemoryType::Device); + assert_eq!(pte.mem_type(), Ok(MemoryType::Device)); + assert_eq!(u64::from(pte), 0x3FF0_0012_3456_2E00u64); + + pte =3D pte.set_mem_type(MemoryType::Normal); + assert_eq!(pte.mem_type(), Ok(MemoryType::Normal)); + assert_eq!(u64::from(pte), 0x3FF0_0012_3456_1E00u64); + + // Test all valid values for mem_type + pte =3D pte.set_mem_type(MemoryType::Reserved); + assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved)); + assert_eq!(u64::from(pte), 0x3FF0_0012_3456_3E00u64); + + // Test failure case using mem_type field which has 4 bits (0-15) + // MemoryType only handles 0-3, so values 4-15 should return Err + let mut raw =3D pte.into(); + // Set bits 15:12 to 7 (invalid for MemoryType) + raw =3D (raw & !::kernel::bits::genmask_u64(12..=3D15)) | (0x7 << = 12); + let invalid_pte =3D TestPageTableEntry(raw); + // Should return Err with the invalid value + assert_eq!(invalid_pte.mem_type(), Err(0x7)); + + // Test a valid value after testing invalid to ensure both cases w= ork + // Set bits 15:12 to 2 (valid: Device) + raw =3D (raw & !::kernel::bits::genmask_u64(12..=3D15)) | (0x2 << = 12); + let valid_pte =3D TestPageTableEntry(raw); + assert_eq!(valid_pte.mem_type(), Ok(MemoryType::Device)); + + const MAX_PFN: u64 =3D ::kernel::bits::genmask_u64(0..=3D35); + pte =3D pte.set_pfn(Bounded::::new::<{ MAX_PFN }>()); + assert_eq!(pte.pfn(), MAX_PFN); + } + + #[test] + fn test_builder_pattern() { + let pte =3D TestPageTableEntry::default() + .set_present(true) + .set_writable(true) + .set_available(Bounded::::new::<0x7>()) + .set_pfn(Bounded::::new::<0xABCDEF>()) + .set_mem_type(MemoryType::Reserved) + .set_available2(Bounded::::new::<0x3FF>()); + + assert!(pte.present().as_bool()); + assert!(pte.writable().as_bool()); + assert_eq!(pte.available(), 0x7); + assert_eq!(pte.pfn(), 0xABCDEF); + assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved)); + assert_eq!(pte.available2(), 0x3FF); + } + + #[test] + fn test_raw_operations() { + let raw_value =3D 0x3FF0000031233E03u64; + + let pte =3D TestPageTableEntry(raw_value); + assert_eq!(u64::from(pte), raw_value); + + assert!(pte.present().as_bool()); + assert!(pte.writable().as_bool()); + assert_eq!(pte.available(), 0x7); + assert_eq!(pte.pfn(), 0x3123); + assert_eq!(pte.mem_type(), Ok(MemoryType::Reserved)); + assert_eq!(pte.available2(), 0x3FF); + + // Test using direct constructor syntax TestStruct(value) + let pte2 =3D TestPageTableEntry(raw_value); + assert_eq!(u64::from(pte2), raw_value); + } + + #[test] + fn test_u16_bitfield() { + let mut ctrl =3D TestControlRegister::default(); + + assert!(!ctrl.enable().as_bool()); + assert_eq!(ctrl.mode(), 0); + assert_eq!(ctrl.priority(), Priority::Low); + assert_eq!(ctrl.priority_nibble(), 0); + assert_eq!(ctrl.channel(), 0); + + ctrl =3D ctrl.set_enable(true); + assert!(ctrl.enable().as_bool()); + + ctrl =3D ctrl.set_mode(Bounded::::new::<0x5>()); + assert_eq!(ctrl.mode(), 0x5); + + // Test From conversion with =3D> + ctrl =3D ctrl.set_priority(Priority::High); + assert_eq!(ctrl.priority(), Priority::High); + assert_eq!(ctrl.priority_nibble(), 0x2); // High =3D 2 in bits 5:4 + + ctrl =3D ctrl.set_channel(0xAB); + assert_eq!(ctrl.channel(), 0xAB); + + // Test overlapping fields + ctrl =3D ctrl.set_priority_nibble(Bounded::::new::<0xF>()); + assert_eq!(ctrl.priority_nibble(), 0xF); + assert_eq!(ctrl.priority(), Priority::Critical); // bits 5:4 =3D 0= x3 + + let ctrl2 =3D TestControlRegister::default() + .set_enable(true) + .set_mode(Bounded::::new::<0x3>()) + .set_priority(Priority::Medium) + .set_channel(0x42); + + assert!(ctrl2.enable().as_bool()); + assert_eq!(ctrl2.mode(), 0x3); + assert_eq!(ctrl2.priority(), Priority::Medium); + assert_eq!(ctrl2.channel(), 0x42); + + let raw_value: u16 =3D 0x4217; + let ctrl3 =3D TestControlRegister(raw_value); + assert_eq!(u16::from(ctrl3), raw_value); + assert!(ctrl3.enable().as_bool()); + assert_eq!(ctrl3.priority(), Priority::Medium); + assert_eq!(ctrl3.priority_nibble(), 0x1); + assert_eq!(ctrl3.channel(), 0x42); + } + + #[test] + fn test_u8_bitfield() { + let mut status =3D TestStatusRegister::default(); + + assert!(!status.ready().as_bool()); + assert!(!status.error().as_bool()); + assert_eq!(status.state(), 0); + assert_eq!(status.reserved(), 0); + assert_eq!(status.full_byte(), 0); + + status =3D status.set_ready(true); + assert!(status.ready().as_bool()); + assert_eq!(status.full_byte(), 0x01); + + status =3D status.set_error(true); + assert!(status.error().as_bool()); + assert_eq!(status.full_byte(), 0x03); + + status =3D status.set_state(Bounded::::new::<0x3>()); + assert_eq!(status.state(), 0x3); + assert_eq!(status.full_byte(), 0x0F); + + status =3D status.set_reserved(Bounded::::new::<0xA>()); + assert_eq!(status.reserved(), 0xA); + assert_eq!(status.full_byte(), 0xAF); + + // Test overlapping field + status =3D status.set_full_byte(0x55); + assert_eq!(status.full_byte(), 0x55); + assert!(status.ready().as_bool()); + assert!(!status.error().as_bool()); + assert_eq!(status.state(), 0x1); + assert_eq!(status.reserved(), 0x5); + + let status2 =3D TestStatusRegister::default() + .set_ready(true) + .set_state(Bounded::::new::<0x2>()) + .set_reserved(Bounded::::new::<0x5>()); + + assert!(status2.ready().as_bool()); + assert!(!status2.error().as_bool()); + assert_eq!(status2.state(), 0x2); + assert_eq!(status2.reserved(), 0x5); + assert_eq!(status2.full_byte(), 0x59); + + let raw_value: u8 =3D 0x59; + let status3 =3D TestStatusRegister(raw_value); + assert_eq!(u8::from(status3), raw_value); + assert!(status3.ready().as_bool()); + assert!(!status3.error().as_bool()); + assert_eq!(status3.state(), 0x2); + assert_eq!(status3.reserved(), 0x5); + assert_eq!(status3.full_byte(), 0x59); + + let status4 =3D TestStatusRegister(0xFF); + assert!(status4.ready().as_bool()); + assert!(status4.error().as_bool()); + assert_eq!(status4.state(), 0x3); + assert_eq!(status4.reserved(), 0xF); + assert_eq!(status4.full_byte(), 0xFF); + } +} --=20 2.52.0 From nobody Sun Feb 8 03:57:15 2026 Received: from CY3PR05CU001.outbound.protection.outlook.com (mail-westcentralusazon11013049.outbound.protection.outlook.com [40.93.201.49]) (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 F2C3736C59B; Tue, 20 Jan 2026 06:23:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.93.201.49 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890241; cv=fail; b=qEpqKTluMFDTO5ND/mBGcowgdV17ODjEScQ7POj1ymsRRYvKnPFliuPPM/Xx/B4Nn8TMTDf4dHd6EuZKclsF1KHSUH7kKRDGKc4SR5ILRQ2JBmks1MZ90L8fhw+aYb6GYGmwq9Qj1B/FJevRaNzTiJVKqqxCQa+n81zhhf+IDAo= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890241; c=relaxed/simple; bh=5bUogOqiCGz16L4PeiKUjiVjYs0KU3OcuPxBR+8gbL0=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=hZFv0m4O/0hJqfi3FpjB1zgCNsF6WZNYy8baq+93G+Kv/A0bVE8HpLGUk3Q5nhoZA+QtSSZachYXn/M+4E3XmVAjVYdDe6oUKz4/W7sb6wzAOAm5Z6bdGqd9dFKQstZUMFbrWzii9c/lmEpmsVJw8Aj27IBVrtwh6quLT74NVMc= 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=MmbEbqva; arc=fail smtp.client-ip=40.93.201.49 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="MmbEbqva" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=kn1549Fbim0hKuu49wDpDCKaqjeB4W1DA8QUC2bGJYk2oxvwM8eex4zk7YioO+yIxGVSnYi6+G74pNDLyu2PaHtsdoJD83/cqqn/5661kk1MK2mqzAb7i67liN4j3aUMvzK45xn+S3SQVRbSK9SuCX/rtRZ8gRayIhdjP7oW3crswNnEEdAV+lkUP00+V2u3X8nmaVSSF3KVByhfq2ZYhFL0bWBPyCsNbsQCQnLl2lZ4oKAK0EhdyKSs2fsB0HAzI+e4TjcA4C4yn5HXkmzlx7SYTECL+y+/HoBtg+Jd++PCAZ91bGOL5XhsU2d2LwGmBVhzWXQEDQ0WjO3+3vmBgA== 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=Wkytn4gSWa9DaU8l5afIrLOfILjXoEKBV90GNilJCyA=; b=S0KqzSK01PcRA0FwXXkKwOvB2oZsdRHWMvsmBD9bZCM/ECO3JM+wciE2QmkXf66RSIh5uX/t7tnsBYm0hUlLyso96cmgyCP/Kf788fhVCBWtGjEvPLvQTegmBjonszL/MZOed4b92zrRNE0uyfVyEh1wWWNd858YiAvCdpmz7gHKX4xswbbbv/oi+KKvYO8Edvczg7FdhbgCIK4WNRnurSiOJhAs1d6mHB4cTcoCc2LehZGGbxuNVk16okScYgMQNv+k67XIKGXTFDHgbFBebikXxVmwzH/1ztyL2mR4m5zKPykC58kK9Yk/DBWA+Hqc8vwsOMHI6OFnStywDfqHjA== 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=Wkytn4gSWa9DaU8l5afIrLOfILjXoEKBV90GNilJCyA=; b=MmbEbqvav/dNlgKH2iOcoC04uZzVqnvfjCOSsZ4Rrd7E0e133CByABaaL9kBBPyrrzfroezRqG9JGGjxOOuqeR8o8Pml0/KttBI4ucVW1sghuSCE5XlTZvbgpaZAurZLJHOXwkFKMryyjFIXc0B+jlcQqpzcuLhoKUsyYd0whSM3DGNCqtpl8/01ubQ5eRfXrDC1v7bn+MGM9t5TvU3X1OCf/cVo1YB5jtct3gSBdUqMvQGXxLZEMJnAiKnTOqOw9MrCHqtJJ6kovKzGY7MOk3iu2sgvCFja8zLa1IYK8za7LGvP8XPYiqUYHFX08tBmDLuzoGZc7srPlbcV46V94A== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) by PH0PR12MB5606.namprd12.prod.outlook.com (2603:10b6:510:141::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9520.12; Tue, 20 Jan 2026 06:23:51 +0000 Received: from CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989]) by CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989%6]) with mapi id 15.20.9520.011; Tue, 20 Jan 2026 06:23:51 +0000 From: Alexandre Courbot Date: Tue, 20 Jan 2026 15:17:58 +0900 Subject: [PATCH 5/6] rust: io: add `register!` macro Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260120-register-v1-5-723a1743b557@nvidia.com> References: <20260120-register-v1-0-723a1743b557@nvidia.com> In-Reply-To: <20260120-register-v1-0-723a1743b557@nvidia.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Yury Norov Cc: John Hubbard , Alistair Popple , Joel Fernandes , Timur Tabi , Edwin Peer , Eliot Courtney , Daniel Almeida , Dirk Behme , Steven Price , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Alexandre Courbot X-Mailer: b4 0.14.3 X-ClientProxiedBy: TYCPR01CA0139.jpnprd01.prod.outlook.com (2603:1096:400:2b7::16) To CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) 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: CH2PR12MB3990:EE_|PH0PR12MB5606:EE_ X-MS-Office365-Filtering-Correlation-Id: 29753159-e9c8-40ec-8a05-08de57ec7a9f X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|376014|7416014|10070799003|1800799024|7142099003|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?cEJGaW0zenRmZE1xMHNnMGpObTZRUnh1dkRmY01LQlh5V3pBQ2tLbS9vMVFW?= =?utf-8?B?dGRDUW1pTlpUL21RR0dBQk5JTE5EYmNFU2tpUk1wWW1kRitBTmNJR1ZqVmhp?= =?utf-8?B?VVVxZ3lHUnBIQ1FwOEZjQ0UxVjQ1YUFLVVlMSU5yanNBeG1qYkU1WEtMR1dV?= =?utf-8?B?bFYxVlF2VFBhUTh6SCs4RXZrbzhDMDhnaFdwVzRFVmlPRlN6Q3QvMjVyY3dy?= =?utf-8?B?RFFYbTNjeEh1MTgrRHVoYmU5d25UWm9qTnd6RW4rb0tNd09zdmVWQnpJdTRi?= =?utf-8?B?Y0FGc3AwNmVTWEdCM290SXErT24vVmp1SWx0bjRFVUl1ZkdrbW5vNVVjbktD?= =?utf-8?B?Q2l5bTJUOENQVFJMamowYlZkblBLczZSdDhxR2tPdmI4MDNhM2NuTkZZaTJ4?= =?utf-8?B?SkNJczFMZ3RZRk9YMnd0WUV2cFlXN29zVmRhU1U5eE9uYVJkQW05OXZIVWRM?= =?utf-8?B?WkQyVFB0Q2VrRmxKaG9ONFloMHFUcnpya1habnVkekZXc3NXOXhIbHZjUXpY?= =?utf-8?B?Mkh1VmIwZGdKMGhvWWhLZUNJNmQ0WjBaeUx2TXR4dFpJOHFhSkhJK2JscHhz?= =?utf-8?B?WG8zZFQ2MmovZzBXaVp5TVdaWVFzcnRZSzQyMGJTb282REdSS1czTHIxSngr?= =?utf-8?B?aStuMCtKTGIrcmRyMEpHS0Z5S2toNnNkdW9NR29pY1ZxSk5FU2tYZms4Tmht?= =?utf-8?B?aHQ4ZlZPWUJ5RlJRNUh6ejQ5bVYyTE9XODVqNWlTMTlOSmZCTmM4YkVDWXZ4?= =?utf-8?B?V0hIejd4dUhtVVFnYmZwWGRweHEyTFZaQVRoK094YlJMQ2lVWmVkQ1JBeE40?= =?utf-8?B?ZytleEpoZ3V1RlI0dElVVW5Qc3dPWWszam5wOG9jbmFaWWJ5UnhzeGh4em1l?= =?utf-8?B?UTN5WEVOWFJSRlIvM1ErcWNtV1VxeGlTb0h0cEcxbDdSL0FKbmJZTzFDY09w?= =?utf-8?B?NnJGWkRmREl1V3FKVDlpck9nbVZMZXhvVVBwT2VjcTdIUThxdXNYK2N4cEFD?= =?utf-8?B?WE9Tcm54dmxmRUM3UUE0ejVtMjlGektucy93c2pjbkVEeFg3Q0pubTdHd3Nw?= =?utf-8?B?VDArdDJmZzdzYTh5bmIwWks4Nkd0NGtOYThZT0s0QUNxMXB1N3RDUVlNdlNo?= =?utf-8?B?Y3h5YmRvYytNR1BNd2VnVWtHWTNWV0RBZjFvUXlMdzBrUVU2VmNMTGNQakh4?= =?utf-8?B?SFJCc294bGJMcXFyNE03RGZFQnNVSkR5ZGZQQVIxVXhaNkliTi9jOUkyc2tu?= =?utf-8?B?cUVMSFhseW5mdVNHQmRFNDhiZnM5bUIxNWo1dlRVd3VWVVRLN0JVRWNUTS9a?= =?utf-8?B?QmVyOUYrOS8yckNyeStjSUJ5a2RaUjRaMG1XTWNaUFRXQ0theHgwS0RsekMz?= =?utf-8?B?dlNabGdDRENISi94R2kraDJOL2g3bkZXVTBVWlBSZThWOGxLSndpWTUrUkdY?= =?utf-8?B?S2VjN3N1TU5TWksvcUNRQWYweEUwVVlIT0E5SHEyMVR1SUE0ODNYanJSbmRm?= =?utf-8?B?R3VZbW1mR3JCelMrVFkrdWZtVFY0eG84UVBKT041TWFRWDIwclVoNk4xRHFq?= =?utf-8?B?elN1MDhJSXJqM1VGNjJYVkhZZ0ZReTFUaG02amMwNnZPRHRwbWFJcmJCRExh?= =?utf-8?B?cDBnSkkyeGdwQmtUK05Rc3VEajA4Yi9IbTMxRGZRVnk1OEl4MHk5bkVRYjFu?= =?utf-8?B?dmlxbDlaK0xFOXpFU2FtaUVmU0VGdzIraEczK0RZemlaK0NWYVpLT3M0ekth?= =?utf-8?B?amRxOUlEYTVlUUNkZUlZUWU0bWlWdElqdEhNS1RpWlVYdXZOY3JGNW13eWpX?= =?utf-8?B?WFZJQWNza0M1NWN3ZzJFTXhIVEJPZHNzazNhSDQ1ZnJjZmxVd1ZnNk1XN2Qv?= =?utf-8?B?UVlLWHVtRFNPenhXQ2ZLNFJVSFczZjd4TEV0TW82Q2h1ejU1Z0lZaE5HWUNX?= =?utf-8?B?OW1EdXpEUlRDWWtubUlRY2VBZjNabHQ4aU8wU0FwaitLekQvUDBvMTFWV1h4?= =?utf-8?B?eFk2SzBaTEhNQkFkRFNxOVNudUg2Smhxc0drWG1aQlpSby8xdCs2cFMrWVUv?= =?utf-8?B?NUlTdzBicktGQTJoTzVkVlNCOGNyNVljWXlpNkRuUUxpL2FuVWdnWndCWERB?= =?utf-8?B?Ym1vVjk5NWo0dU1zSmlValo0RWoxQXAxc0srTlNLV0Z4M1A4TlBiaVdqa1g1?= =?utf-8?B?NEE9PQ==?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:CH2PR12MB3990.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(376014)(7416014)(10070799003)(1800799024)(7142099003)(921020);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?MjFGbEorRGY5aXJ0ZStoV0VURHcwTFRTQ0FBMGJzMWZOTTZUNStObXI4UVV3?= =?utf-8?B?djVzYzFoaXdacjI5WW5LdEFRTnpwdjBCWURTbEhBUnNlcFJlZ2dkVTBGYW9l?= =?utf-8?B?WkVnblpkcGRyM2g0S2hhY3dQMVlwUWpQYnR1UW1zeEpXSE92bVBKbmlSekQ3?= =?utf-8?B?Z3JrL3dYUXFEbmdtOVZCWGhwRnNlT2JLQjFSY2FST2puQXBwWll3dThMSzM2?= =?utf-8?B?R0RwYUY0aU5Lamo3RHBZc2ZVbVlWWGorL2p2LytHOEF1T0RGZUFhK2x1dGp0?= =?utf-8?B?WWJUdGFjNTdZNUlIM3RHUGtuSzRjcUw5ZHdTMnR5UzYwTWZIaS9oRWM3NzN3?= =?utf-8?B?UDZUd2FhTVJYdC9wTTBjTTY2ejZWOEJjS3VPK0d1cEVvcCt2RmpiRFlQV0hv?= =?utf-8?B?V09mVThlTHZ6bDdIQkVIeXBUOERUVlhKbzVENEVNNnFPUTlCWSt2VFJ3UkNu?= =?utf-8?B?U090TzlUK0ZUZ2hPOElmTjNVN3A4anlHbURuNnc2dmxKdkQwQmlpaWwyRW5B?= =?utf-8?B?TEJhY2Z3Uk5jSTlZUlNWWXNFK1BTZ1QvS25TSWVHd1JUVDQ3bFQ3VTM0dWts?= =?utf-8?B?ZUowcjE0N01EUkdEWi9zdFhKZk5yUTZXQnhzWXIveVd2YjNKZlM3ZTFScFZ3?= =?utf-8?B?citoQ2loN3ZpM01pWDFVTngweFFYZml4QXlBV2hTWkRXSUprNDhldDhRdDgy?= =?utf-8?B?Q21LY1ZJZ1Rkck5jQlhob1h2eVVwK29pRjUwa21PVU1xVWtaK3dkTlc3cWNT?= =?utf-8?B?bk1JdnNDSGhlaHZNOUNvMVRnN0Rhc0hFakpialFMRWc3S3g0am5aY2RMVG82?= =?utf-8?B?WHVkcDJFdzFZSmxSdXIxa0tDMkJSVHJkYVgrQ2R6azBKeVZUM2lxSkg5N1M5?= =?utf-8?B?RXdYdWkrSENrc2JGN3VHSnN0NXhRN2t0ZkNKTzB2ZXVvdEYxWndNb012OUQy?= =?utf-8?B?dFZCQXJjdW5RUkp1eERhR0ZRblkrWUJZandRWXZLaGJVM0hidlJqbVYvYUcr?= =?utf-8?B?b2J5bGZhS2RYTVJWaE5ndDdoL05VVTVwTWw3ZzF3Qys3M0tDTnpLaFJoT3d0?= =?utf-8?B?VW1WOTJ3QmU3eERMejFJRDQ3MXplQVcrWUZPUzhoRFEyZ21WRi81VkttdTRV?= =?utf-8?B?a3VJSTUwTTlpLzlhaUVrTzIrSHVrNnBmTElsbzlOT21uZHBoMFJ4MDhXakZT?= =?utf-8?B?Nm5uOURjbmt5eDhQWHZ6ZVQ1blhiTWpPL2ZsYlpYYkJMZ004dE1BVlZlTUNn?= =?utf-8?B?VXU4YVIvd1Y3Ym9uTjk3VC8wSjhEMG9rOXY1VXl6c3hSWTdNakMzQ1FQMjB6?= =?utf-8?B?eHMzV1ZaV1lYb1hnVmszUU85ZDN6UHhkTmczRWsyTnFabzlFSVkrcnNQVC93?= =?utf-8?B?R2o5VDZIN0pEOEVSL2djd21ENUprYlBGZGYydy9vT0JCY292dW1waTI3c0hU?= =?utf-8?B?WC9iWDVpcFNpSUF3Wm5kMnhISlRqcldabVRCRjFWTWc4SkZvM1VaWEhHVXZK?= =?utf-8?B?WnNCUlFNbElTaDU5SlZ6T242RnRTdHVyano1RU5ScmNKMVJVTHcxdjBXczFi?= =?utf-8?B?TU0zQVN1UFRSblN0eTJhK040NUlWajdHYS9QcFpCZU1iRFhvT3FqRWhONVdt?= =?utf-8?B?aldTQldiblk2SlBIRE9QczNoS2tQT0ZhNDluK3k1Q3NaV2dZdHFWdDBuYmFP?= =?utf-8?B?L2JHRFA1YlNQeGxVbU9tTDBZTWQ2dzR2dGRGU05rNC9TSTYvNFR2eUdSSFdI?= =?utf-8?B?NjYwZjE2YWwweXdLZk5BdVhibVd2RHllMGFwNjA4QUhlWFJkUWQ3cUx0Yk5L?= =?utf-8?B?bmx4OFB5em5zenNxOTNnc0RNU2NtRmxuMTFYSTRJR012Q29WUjIzcEtaQVRv?= =?utf-8?B?M2phOHZOTnBCaExLVENRbTdBeVBNaEZtVnNTZWl0THhtcWtPNk03RG9WVjNk?= =?utf-8?B?dG5KTlRZOFp2azNac0JxRGxFTXBEaXh3b3BNQjU1cXVNRVpCTFpyVGFNSktU?= =?utf-8?B?MTdGVjN0UnpGRkFpTFFpZVcxWE9BMzhld2EvZzRBcHlCVUNGY3k4UGYwMTha?= =?utf-8?B?MEdwYkhVbGVJY3NzY2Z6Mis5L3NwYldWVVRlWVhJclFybThENUVWY09UOG1l?= =?utf-8?B?VjNyeFNDZFNrNklIT2prS2I2eitHN21rSHhDbE5RVisrQjZJWGZZZXVPVldC?= =?utf-8?B?U09CRlV0ckpRNXdDWmJEendqZDFFNmViZVUwNkFEUTJ2RVFadHdIdjZubFJ3?= =?utf-8?B?eFh6eVo4MmFrS1ZiTnZMZmJsWEhPaC9GeUJrTUtBM0l2WW5xUk90ODdJNGFE?= =?utf-8?B?VTNqK3ZheWVXWGVxa2Y1di8wQW1nK256NitCTElQUTRVdVhvWXczbmdGSW5k?= =?utf-8?Q?qc2uQyTahfn1M2zKoiEW+DeNqZ5wdlHGm2RHtHDuwXiNa?= X-MS-Exchange-AntiSpam-MessageData-1: dpB4K3KWp5ktRg== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: 29753159-e9c8-40ec-8a05-08de57ec7a9f X-MS-Exchange-CrossTenant-AuthSource: CH2PR12MB3990.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Jan 2026 06:23:51.7026 (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: +YT1F/7nSzbpCX5i9COoqB5uI74tVXa+XsbK0fWsiNXV6rLoD+s7Cgg3LYNh2QpggqVJBbeeYRHxsHviahX9fg== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR12MB5606 Add a macro for defining hardware register types with I/O accessors. Registers are essentially bitfields with an address and I/O accessors. The address of registers can be direct, relative, or indexed, supporting most of the patterns in which registers are arranged. Signed-off-by: Alexandre Courbot --- rust/kernel/io.rs | 1 + rust/kernel/io/register.rs | 926 +++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 927 insertions(+) diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs index 98e8b84e68d1..19b34a88b66c 100644 --- a/rust/kernel/io.rs +++ b/rust/kernel/io.rs @@ -11,6 +11,7 @@ =20 pub mod mem; pub mod poll; +pub mod register; pub mod resource; =20 pub use resource::Resource; diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs new file mode 100644 index 000000000000..f28dda146679 --- /dev/null +++ b/rust/kernel/io/register.rs @@ -0,0 +1,926 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! A macro to define register layout and accessors. +//! +//! A single register typically includes several fields, which are accesse= d through a combination +//! of bit-shift and mask operations that introduce a class of potential m= istakes, notably because +//! not all possible field values are necessarily valid. +//! +//! The [`register!`] macro in this module provides an intuitive and reada= ble syntax for defining a +//! dedicated type for each register. Each such type comes with its own fi= eld accessors that can +//! return an error if a field's value is invalid. Please look at the [`bi= tfield!`] macro for the +//! complete syntax of fields definitions. +//! +//! [`register!`]: kernel::register! +//! [`bitfield!`]: kernel::bitfield! + +/// Trait providing a base address to be added to the offset of a relative= register to obtain +/// its actual offset. +/// +/// The `T` generic argument is used to distinguish which base to use, in = case a type provides +/// several bases. It is given to the `register!` macro to restrict the us= e of the register to +/// implementors of this particular variant. +pub trait RegisterBase { + /// Base address to which register offsets are added. + const BASE: usize; +} + +/// Trait providing I/O read/write operations for register storage types. +/// +/// This trait is implemented for all integer types on which I/O can be pe= rformed, allowing the +/// `register!` macro to generate appropriate I/O accessor methods based o= n the register's storage +/// type. +pub trait RegisterIo: Sized { + /// Read a value from the given offset in the I/O region. + fn read(io: &T, offset: usize) -> Self + where + T: core::ops::Deref>; + + /// Write a value to the given offset in the I/O region. + fn write(self, io: &T, offset: usize) + where + T: core::ops::Deref>; +} + +impl RegisterIo for u8 { + #[inline(always)] + fn read(io: &T, offset: usize) -> Self + where + T: core::ops::Deref>, + { + io.read8(offset) + } + + #[inline(always)] + fn write(self, io: &T, offset: usize) + where + T: core::ops::Deref>, + { + io.write8(self, offset) + } +} + +impl RegisterIo for u16 { + #[inline(always)] + fn read(io: &T, offset: usize) -> Self + where + T: core::ops::Deref>, + { + io.read16(offset) + } + + #[inline(always)] + fn write(self, io: &T, offset: usize) + where + T: core::ops::Deref>, + { + io.write16(self, offset) + } +} + +impl RegisterIo for u32 { + #[inline(always)] + fn read(io: &T, offset: usize) -> Self + where + T: core::ops::Deref>, + { + io.read32(offset) + } + + #[inline(always)] + fn write(self, io: &T, offset: usize) + where + T: core::ops::Deref>, + { + io.write32(self, offset) + } +} + +#[cfg(CONFIG_64BIT)] +impl RegisterIo for u64 { + #[inline(always)] + fn read(io: &T, offset: usize) -> Self + where + T: core::ops::Deref>, + { + io.read64(offset) + } + + #[inline(always)] + fn write(self, io: &T, offset: usize) + where + T: core::ops::Deref>, + { + io.write64(self, offset) + } +} + +/// Defines a dedicated type for a register with an absolute offset, inclu= ding getter and setter +/// methods for its fields and methods to read and write it from an `Io` r= egion. +/// +/// A register is essentially a [`bitfield!`] with I/O capabilities. The s= yntax of the `register!` +/// macro reflects that fact, being essentially identical to that of [`bit= field!`] with the +/// addition of addressing information after the `@` token. +/// +/// Example: +/// +/// ``` +/// use kernel::register; +/// +/// register!(pub BOOT_0(u32) @ 0x00000100, "Basic revision information ab= out the chip" { +/// 7:4 major_revision, "Major revision of the chip"; +/// 3:0 minor_revision, "Minor revision of the chip"; +/// }); +/// ``` +/// +/// This defines a `BOOT_0` type which can be read or written from offset = `0x100` of an `Io` +/// region. For instance, `minor_revision` is made of the 4 least signific= ant bits of the +/// register. Each field can be accessed and modified using accessor +/// methods: +/// +/// ```no_run +/// use kernel::register; +/// use kernel::num::Bounded; +/// +/// # register!(pub BOOT_0(u32) @ 0x00000100, "Basic revision information = about the chip" { +/// # 7:4 major_revision, "Major revision of the chip"; +/// # 3:0 minor_revision, "Minor revision of the chip"; +/// # }); +/// # fn test(bar: &kernel::io::Io) { +/// // Read from the register's defined offset (0x100). +/// let boot0 =3D BOOT_0::read(&bar); +/// pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.m= inor_revision().get()); +/// +/// // Update some fields and write the value back. +/// boot0 +/// .set_major_revision(Bounded::::new::<3>()) +/// .set_minor_revision(Bounded::::new::<10>()) +/// .write(&bar); +/// +/// // Or, just read and update the register in a single step: +/// BOOT_0::update(&bar, |r| r +/// .set_major_revision(Bounded::::new::<3>()) +/// .set_minor_revision(Bounded::::new::<10>()) +/// ); +/// # } +/// ``` +/// +/// The documentation strings are optional. If present, they will be added= to the type's +/// definition, or the field getter and setter methods they are attached t= o. +/// +/// Attributes can be applied to the generated struct. The `#[allow(non_ca= mel_case_types)]` +/// attribute is automatically added since register names typically use SC= REAMING_CASE: +/// +/// ``` +/// use kernel::register; +/// +/// register! { +/// pub STATUS(u32) @ 0x00000000, "Status register" { +/// 0:0 ready, "Device ready flag"; +/// } +/// } +/// ``` +/// +/// It is also possible to create an alias register by using the `=3D> ALI= AS` syntax. This is useful +/// for cases where a register's interpretation depends on the context: +/// +/// ``` +/// use kernel::register; +/// +/// register!(pub SCRATCH(u32) @ 0x00000200, "Scratch register" { +/// 31:0 value, "Raw value"; +/// }); +/// +/// register!(pub SCRATCH_BOOT_STATUS(u32) =3D> SCRATCH, "Boot status of t= he firmware" { +/// 0:0 completed, "Whether the firmware has completed booting"; +/// }); +/// ``` +/// +/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `S= CRATCH`, while also +/// providing its own `completed` field. +/// +/// ## Relative registers +/// +/// A register can be defined as being accessible from a fixed offset of a= provided base. For +/// instance, imagine the following I/O space: +/// +/// ```text +/// +-----------------------------+ +/// | ... | +/// | | +/// 0x100--->+------------CPU0-------------+ +/// | | +/// 0x110--->+-----------------------------+ +/// | CPU_CTL | +/// +-----------------------------+ +/// | ... | +/// | | +/// | | +/// 0x200--->+------------CPU1-------------+ +/// | | +/// 0x210--->+-----------------------------+ +/// | CPU_CTL | +/// +-----------------------------+ +/// | ... | +/// +-----------------------------+ +/// ``` +/// +/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset= `0x10` of their I/O +/// space segment. Since both instances of `CPU_CTL` share the same layout= , we don't want to define +/// them twice and would prefer a way to select which one to use from a si= ngle definition +/// +/// This can be done using the `Base[Offset]` syntax when specifying the r= egister's address. +/// +/// `Base` is an arbitrary type (typically a ZST) to be used as a generic = parameter of the +/// [`RegisterBase`] trait to provide the base as a constant, i.e. each ty= pe providing a base for +/// this register needs to implement `RegisterBase`. Here is the abo= ve example translated +/// into code: +/// +/// ```no_run +/// use kernel::register; +/// use kernel::io::register::RegisterBase; +/// +/// // Type used to identify the base. +/// pub struct CpuCtlBase; +/// +/// // ZST describing `CPU0`. +/// struct Cpu0; +/// impl RegisterBase for Cpu0 { +/// const BASE: usize =3D 0x100; +/// } +/// // Singleton of `CPU0` used to identify it. +/// const CPU0: Cpu0 =3D Cpu0; +/// +/// // ZST describing `CPU1`. +/// struct Cpu1; +/// impl RegisterBase for Cpu1 { +/// const BASE: usize =3D 0x200; +/// } +/// // Singleton of `CPU1` used to identify it. +/// const CPU1: Cpu1 =3D Cpu1; +/// +/// # fn test(bar: &kernel::io::Io) { +/// // This makes `CPU_CTL` accessible from all implementors of `RegisterB= ase`. +/// register!(pub CPU_CTL(u32) @ CpuCtlBase[0x10], "CPU core control" { +/// 0:0 start, "Start the CPU core"; +/// }); +/// +/// // The `read`, `write` and `update` methods of relative registers take= an extra `base` argument +/// // that is used to resolve its final address by adding its `BASE` to t= he offset of the +/// // register. +/// +/// // Start `CPU0`. +/// CPU_CTL::update(&bar, &CPU0, |r| r.set_start(true)); +/// +/// // Start `CPU1`. +/// CPU_CTL::update(&bar, &CPU1, |r| r.set_start(true)); +/// +/// // Aliases can also be defined for relative register. +/// register!(pub CPU_CTL_ALIAS(u32) =3D> CpuCtlBase[CPU_CTL], "Alias to C= PU core control" { +/// 1:1 alias_start, "Start the aliased CPU core"; +/// }); +/// +/// // Start the aliased `CPU0`. +/// CPU_CTL_ALIAS::update(&bar, &CPU0, |r| r.set_alias_start(true)); +/// # } +/// ``` +/// +/// ## Arrays of registers +/// +/// Some I/O areas contain consecutive values that can be interpreted in t= he same way. These areas +/// can be defined as an array of identical registers, allowing them to be= accessed by index with +/// compile-time or runtime bound checking. Simply define their address as= `Address[Size]`, and add +/// an `idx` parameter to their `read`, `write` and `update` methods: +/// +/// ```no_run +/// use kernel::register; +/// +/// # fn no_run(bar: &kernel::io::Io) -> Result<(), Error> { +/// # fn get_scratch_idx() -> usize { +/// # 0x15 +/// # } +/// // Array of 64 consecutive registers with the same layout starting at = offset `0x80`. +/// register!(pub SCRATCH(u32) @ 0x00000080[64], "Scratch registers" { +/// 31:0 value; +/// }); +/// +/// // Read scratch register 0, i.e. I/O address `0x80`. +/// let scratch_0 =3D SCRATCH::read(&bar, 0).value(); +/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`. +/// let scratch_15 =3D SCRATCH::read(&bar, 15).value(); +/// +/// // This is out of bounds and won't build. +/// // let scratch_128 =3D SCRATCH::read(&bar, 128).value(); +/// +/// // Runtime-obtained array index. +/// let scratch_idx =3D get_scratch_idx(); +/// // Access on a runtime index returns an error if it is out-of-bounds. +/// let some_scratch =3D SCRATCH::try_read(&bar, scratch_idx)?.value(); +/// +/// // Alias to a particular register in an array. +/// // Here `SCRATCH[8]` is used to convey the firmware exit code. +/// register!(pub FIRMWARE_STATUS(u32) =3D> SCRATCH[8], "Firmware exit sta= tus code" { +/// 7:0 status; +/// }); +/// +/// let status =3D FIRMWARE_STATUS::read(&bar).status(); +/// +/// // Non-contiguous register arrays can be defined by adding a stride pa= rameter. +/// // Here, each of the 16 registers of the array are separated by 8 byte= s, meaning that the +/// // registers of the two declarations below are interleaved. +/// register!(pub SCRATCH_INTERLEAVED_0(u32) @ 0x000000c0[16 ; 8], "Scratc= h registers bank 0" { +/// 31:0 value; +/// }); +/// register!(pub SCRATCH_INTERLEAVED_1(u32) @ 0x000000c4[16 ; 8], "Scratc= h registers bank 1" { +/// 31:0 value; +/// }); +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Relative arrays of registers +/// +/// Combining the two features described in the sections above, arrays of = registers accessible from +/// a base can also be defined: +/// +/// ```no_run +/// use kernel::register; +/// use kernel::io::register::RegisterBase; +/// +/// # fn no_run(bar: &kernel::io::Io) -> Result<(), Error> { +/// # fn get_scratch_idx() -> usize { +/// # 0x15 +/// # } +/// // Type used as parameter of `RegisterBase` to specify the base. +/// pub struct CpuCtlBase; +/// +/// // ZST describing `CPU0`. +/// struct Cpu0; +/// impl RegisterBase for Cpu0 { +/// const BASE: usize =3D 0x100; +/// } +/// // Singleton of `CPU0` used to identify it. +/// const CPU0: Cpu0 =3D Cpu0; +/// +/// // ZST describing `CPU1`. +/// struct Cpu1; +/// impl RegisterBase for Cpu1 { +/// const BASE: usize =3D 0x200; +/// } +/// // Singleton of `CPU1` used to identify it. +/// const CPU1: Cpu1 =3D Cpu1; +/// +/// // 64 per-cpu scratch registers, arranged as a contiguous array. +/// register!(pub CPU_SCRATCH(u32) @ CpuCtlBase[0x00000080[64]], "Per-CPU = scratch registers" { +/// 31:0 value; +/// }); +/// +/// let cpu0_scratch_0 =3D CPU_SCRATCH::read(&bar, &Cpu0, 0).value(); +/// let cpu1_scratch_15 =3D CPU_SCRATCH::read(&bar, &Cpu1, 15).value(); +/// +/// // This won't build. +/// // let cpu0_scratch_128 =3D CPU_SCRATCH::read(&bar, &Cpu0, 128).value(= ); +/// +/// // Runtime-obtained array index. +/// let scratch_idx =3D get_scratch_idx(); +/// // Access on a runtime value returns an error if it is out-of-bounds. +/// let cpu0_some_scratch =3D CPU_SCRATCH::try_read(&bar, &Cpu0, scratch_i= dx)?.value(); +/// +/// // `SCRATCH[8]` is used to convey the firmware exit code. +/// register!(pub CPU_FIRMWARE_STATUS(u32) =3D> CpuCtlBase[CPU_SCRATCH[8]], +/// "Per-CPU firmware exit status code" { +/// 7:0 status; +/// }); +/// +/// let cpu0_status =3D CPU_FIRMWARE_STATUS::read(&bar, &Cpu0).status(); +/// +/// // Non-contiguous register arrays can be defined by adding a stride pa= rameter. +/// // Here, each of the 16 registers of the array are separated by 8 byte= s, meaning that the +/// // registers of the two declarations below are interleaved. +/// register!(pub CPU_SCRATCH_INTERLEAVED_0(u32) @ CpuCtlBase[0x00000d00[1= 6 ; 8]], +/// "Scratch registers bank 0" { +/// 31:0 value; +/// }); +/// register!(pub CPU_SCRATCH_INTERLEAVED_1(u32) @ CpuCtlBase[0x00000d04[1= 6 ; 8]], +/// "Scratch registers bank 1" { +/// 31:0 value; +/// }); +/// # Ok(()) +/// # } +/// ``` +/// [`bitfield!`]: kernel::bitfield! +#[macro_export] +macro_rules! register { + // Creates a register at a fixed offset of the MMIO space. + ( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:lit= eral + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + ::kernel::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) $(, $commen= t)? { $($fields)* } + ); + ::kernel::register!(@io_fixed $name($storage) @ $offset); + }; + + // Creates an alias register of fixed offset register `alias` with its= own fields. + ( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) =3D> $alias:i= dent + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + ::kernel::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) $(, $commen= t)? { $($fields)* } + ); + ::kernel::register!(@io_fixed $name($storage) @ $alias::OFFSET); + }; + + // Creates a register at a relative offset from a base address provide= r. + ( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ty [ = $offset:literal ] + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + ::kernel::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) $(, $commen= t)? { $($fields)* } + ); + ::kernel::register!(@io_relative $name($storage) @ $base [ $offset= ]); + }; + + // Creates an alias register of relative offset register `alias` with = its own fields. + ( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) =3D> $base:ty= [ $alias:ident ] + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + ::kernel::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) $(, $commen= t)? { $($fields)* } + ); + ::kernel::register!(@io_relative $name($storage) @ $base [ $alias:= :OFFSET ]); + }; + + // Creates an array of registers at a fixed offset of the MMIO space. + ( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + @ $offset:literal [ $size:expr ; $stride:expr ] + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + static_assert!(::core::mem::size_of::<$storage>() <=3D $stride); + + ::kernel::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) $(, $commen= t)? { $($fields)* } + ); + ::kernel::register!(@io_array $name($storage) @ $offset [ $size ; = $stride ]); + }; + + // Shortcut for contiguous array of registers (stride =3D=3D size of e= lement). + ( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:lit= eral [ $size:expr ] + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + ::kernel::register!( + $(#[$attr])* $vis $name($storage) + @ $offset [ $size ; ::core::mem::size_of::<$storage>() ] + $(, $comment)? { $($fields)* } + ); + }; + + // Creates an array of registers at a relative offset from a base addr= ess provider. + ( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + @ $base:ty [ $offset:literal [ $size:expr ; $stride:expr ] ] + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + static_assert!(::core::mem::size_of::<$storage>() <=3D $stride); + + ::kernel::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) $(, $commen= t)? { $($fields)* } + ); + ::kernel::register!( + @io_relative_array $name($storage) @ $base [ $offset [ $size ;= $stride ] ] + ); + }; + + // Shortcut for contiguous array of relative registers (stride =3D=3D = size of element). + ( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + @ $base:ty [ $offset:literal [ $size:expr ] ] + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + ::kernel::register!( + $(#[$attr])* $vis $name($storage) + @ $base [ $offset [ $size ; ::core::mem::size_of::<$storag= e>() ] ] + $(, $comment)? { $($fields)* } + ); + }; + + // Creates an alias of register `idx` of relative array of registers `= alias` with its own + // fields. + ( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) + =3D> $base:ty [ $alias:ident [ $idx:expr ] ] + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + static_assert!($idx < $alias::SIZE); + + ::kernel::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) $(, $commen= t)? { $($fields)* } + ); + ::kernel::register!( + @io_relative $name($storage) @ $base [ $alias::OFFSET + $idx *= $alias::STRIDE ] + ); + }; + + // Creates an alias of register `idx` of array of registers `alias` wi= th its own fields. + // This rule belongs to the (non-relative) register arrays set, but ne= eds to be put last + // to avoid it being interpreted in place of the relative register arr= ay alias rule. + ( + $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) =3D> $alias:i= dent [ $idx:expr ] + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + static_assert!($idx < $alias::SIZE); + + ::kernel::register!( + @bitfield $(#[$attr])* $vis struct $name($storage) $(, $commen= t)? { $($fields)* } + ); + ::kernel::register!(@io_fixed $name($storage) @ $alias::OFFSET + $= idx * $alias::STRIDE); + }; + + // All rules below are helpers. + + // Generates the bitfield for the register. + // + // `#[allow(non_camel_case_types)]` is added since register names typi= cally use SCREAMING_CASE. + ( + @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:t= y) + $(, $comment:literal)? { $($fields:tt)* } + ) =3D> { + ::kernel::bitfield!( + #[allow(non_camel_case_types)] + $(#[$attr])* $vis struct $name($storage) $(, $comment)? { $($f= ields)* } + ); + }; + + // Generates the IO accessors for a fixed offset register. + (@io_fixed $name:ident ($storage:ty) @ $offset:expr) =3D> { + #[allow(dead_code)] + impl $name { + pub const OFFSET: usize =3D $offset; + + /// Read the register from its address in `io`. + #[inline(always)] + pub fn read(io: &T) -> Self where + T: ::core::ops::Deref>, + { + Self(<$storage as $crate::io::register::RegisterIo>::read(= io, $offset)) + } + + /// Write the value contained in `self` to the register addres= s in `io`. + #[inline(always)] + pub fn write(self, io: &T) where + T: ::core::ops::Deref>, + { + <$storage as $crate::io::register::RegisterIo>::write(self= .0, io, $offset) + } + + /// Read the register from its address in `io` and run `f` on = its value to obtain a new + /// value to write back. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn update( + io: &T, + f: F, + ) where + T: ::core::ops::Deref>, + F: ::core::ops::FnOnce(Self) -> Self, + { + let reg =3D f(Self::read(io)); + reg.write(io); + } + } + }; + + // Generates the IO accessors for a relative offset register. + (@io_relative $name:ident ($storage:ty) @ $base:ty [ $offset:expr ]) = =3D> { + #[allow(dead_code)] + impl $name { + pub const OFFSET: usize =3D $offset; + + /// Read the register from `io`, using the base address provid= ed by `base` and adding + /// the register's offset to it. + #[inline(always)] + pub fn read( + io: &T, + #[allow(unused_variables)] + base: &B, + ) -> Self where + T: ::core::ops::Deref>, + B: $crate::io::register::RegisterBase<$base>, + { + let offset =3D >::BASE + $name::OFFSET; + + Self(<$storage as $crate::io::register::RegisterIo>::read(= io, offset)) + } + + /// Write the value contained in `self` to `io`, using the bas= e address provided by + /// `base` and adding the register's offset to it. + #[inline(always)] + pub fn write( + self, + io: &T, + #[allow(unused_variables)] + base: &B, + ) where + T: ::core::ops::Deref>, + B: $crate::io::register::RegisterBase<$base>, + { + let offset =3D >::BASE + $name::OFFSET; + + <$storage as $crate::io::register::RegisterIo>::write(self= .0, io, offset) + } + + /// Read the register from `io`, using the base address provid= ed by `base` and adding + /// the register's offset to it, then run `f` on its value to = obtain a new value to + /// write back. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn update( + io: &T, + base: &B, + f: F, + ) where + T: ::core::ops::Deref>, + B: $crate::io::register::RegisterBase<$base>, + F: ::core::ops::FnOnce(Self) -> Self, + { + let reg =3D f(Self::read(io, base)); + reg.write(io, base); + } + } + }; + + // Generates the IO accessors for an array of registers. + (@io_array $name:ident ($storage:ty) @ $offset:literal [ $size:expr ; = $stride:expr ]) =3D> { + #[allow(dead_code)] + impl $name { + pub const OFFSET: usize =3D $offset; + pub const SIZE: usize =3D $size; + pub const STRIDE: usize =3D $stride; + + /// Read the array register at index `idx` from its address in= `io`. + #[inline(always)] + pub fn read( + io: &T, + idx: usize, + ) -> Self where + T: ::core::ops::Deref>, + { + build_assert!(idx < Self::SIZE); + + let offset =3D Self::OFFSET + (idx * Self::STRIDE); + + Self(<$storage as $crate::io::register::RegisterIo>::read(= io, offset)) + } + + /// Write the value contained in `self` to the array register = with index `idx` in `io`. + #[inline(always)] + pub fn write( + self, + io: &T, + idx: usize + ) where + T: ::core::ops::Deref>, + { + build_assert!(idx < Self::SIZE); + + let offset =3D Self::OFFSET + (idx * Self::STRIDE); + + <$storage as $crate::io::register::RegisterIo>::write(self= .0, io, offset) + } + + /// Read the array register at index `idx` in `io` and run `f`= on its value to obtain a + /// new value to write back. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn update( + io: &T, + idx: usize, + f: F, + ) where + T: ::core::ops::Deref>, + F: ::core::ops::FnOnce(Self) -> Self, + { + let reg =3D f(Self::read(io, idx)); + reg.write(io, idx); + } + + /// Read the array register at index `idx` from its address in= `io`. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + #[inline(always)] + pub fn try_read( + io: &T, + idx: usize, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref>, + { + if idx < Self::SIZE { + Ok(Self::read(io, idx)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + + /// Write the value contained in `self` to the array register = with index `idx` in `io`. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + #[inline(always)] + pub fn try_write( + self, + io: &T, + idx: usize, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref>, + { + if idx < Self::SIZE { + Ok(self.write(io, idx)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + + /// Read the array register at index `idx` in `io` and run `f`= on its value to obtain a + /// new value to write back. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn try_update( + io: &T, + idx: usize, + f: F, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref>, + F: ::core::ops::FnOnce(Self) -> Self, + { + if idx < Self::SIZE { + Ok(Self::update(io, idx, f)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + } + }; + + // Generates the IO accessors for an array of relative registers. + ( + @io_relative_array $name:ident ($storage:ty) @ $base:ty + [ $offset:literal [ $size:expr ; $stride:expr ] ] + ) =3D> { + #[allow(dead_code)] + impl $name { + pub const OFFSET: usize =3D $offset; + pub const SIZE: usize =3D $size; + pub const STRIDE: usize =3D $stride; + + /// Read the array register at index `idx` from `io`, using th= e base address provided + /// by `base` and adding the register's offset to it. + #[inline(always)] + pub fn read( + io: &T, + #[allow(unused_variables)] + base: &B, + idx: usize, + ) -> Self where + T: ::core::ops::Deref>, + B: $crate::io::register::RegisterBase<$base>, + { + build_assert!(idx < Self::SIZE); + + let offset =3D >::BASE + + Self::OFFSET + (idx * Self::STRIDE); + + Self(<$storage as $crate::io::register::RegisterIo>::read(= io, offset)) + } + + /// Write the value contained in `self` to `io`, using the bas= e address provided by + /// `base` and adding the offset of array register `idx` to it. + #[inline(always)] + pub fn write( + self, + io: &T, + #[allow(unused_variables)] + base: &B, + idx: usize + ) where + T: ::core::ops::Deref>, + B: $crate::io::register::RegisterBase<$base>, + { + build_assert!(idx < Self::SIZE); + + let offset =3D >::BASE + + Self::OFFSET + (idx * Self::STRIDE); + + <$storage as $crate::io::register::RegisterIo>::write(self= .0, io, offset) + } + + /// Read the array register at index `idx` from `io`, using th= e base address provided + /// by `base` and adding the register's offset to it, then run= `f` on its value to + /// obtain a new value to write back. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn update( + io: &T, + base: &B, + idx: usize, + f: F, + ) where + T: ::core::ops::Deref>, + B: $crate::io::register::RegisterBase<$base>, + F: ::core::ops::FnOnce(Self) -> Self, + { + let reg =3D f(Self::read(io, base, idx)); + reg.write(io, base, idx); + } + + /// Read the array register at index `idx` from `io`, using th= e base address provided + /// by `base` and adding the register's offset to it. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + #[inline(always)] + pub fn try_read( + io: &T, + base: &B, + idx: usize, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref>, + B: $crate::io::register::RegisterBase<$base>, + { + if idx < Self::SIZE { + Ok(Self::read(io, base, idx)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + + /// Write the value contained in `self` to `io`, using the bas= e address provided by + /// `base` and adding the offset of array register `idx` to it. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + #[inline(always)] + pub fn try_write( + self, + io: &T, + base: &B, + idx: usize, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref>, + B: $crate::io::register::RegisterBase<$base>, + { + if idx < Self::SIZE { + Ok(self.write(io, base, idx)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + + /// Read the array register at index `idx` from `io`, using th= e base address provided + /// by `base` and adding the register's offset to it, then run= `f` on its value to + /// obtain a new value to write back. + /// + /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned if the + /// access was out-of-bounds. + /// + /// Note that this operation is not atomic. In concurrent cont= exts, external + /// synchronization may be required to prevent race conditions. + #[inline(always)] + pub fn try_update( + io: &T, + base: &B, + idx: usize, + f: F, + ) -> ::kernel::error::Result where + T: ::core::ops::Deref>, + B: $crate::io::register::RegisterBase<$base>, + F: ::core::ops::FnOnce(Self) -> Self, + { + if idx < Self::SIZE { + Ok(Self::update(io, base, idx, f)) + } else { + Err(::kernel::error::code::EINVAL) + } + } + } + }; +} --=20 2.52.0 From nobody Sun Feb 8 03:57:15 2026 Received: from CY3PR05CU001.outbound.protection.outlook.com (mail-westcentralusazon11013041.outbound.protection.outlook.com [40.93.201.41]) (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 B732A36CDF6; Tue, 20 Jan 2026 06:24:08 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=fail smtp.client-ip=40.93.201.41 ARC-Seal: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890253; cv=fail; b=uR5AqslFcSu+VwLK7gOSbRMhsd2WnFGcu8fKzuGuXIVDZaNKxvhcMyiY6w+bKtDbI/7Zc0EcqCaa0KwpIFLLle4p6vkMHZ3+GYTqy1MLt7162gjQjprWJxqs4qv4U3Y52S9bwKkMFJ78WvgMNuMIL8Ixy9WDLYEk6LhVxEwAwYE= ARC-Message-Signature: i=2; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768890253; c=relaxed/simple; bh=FCJ5mG5RgF+7ETn55G/AAaK7xTqXybeY10uZ0w/BfAY=; h=From:Date:Subject:Content-Type:Message-Id:References:In-Reply-To: To:Cc:MIME-Version; b=ZssCJAxQoxoVDJQZnrRdW+6ejSU0KxSlxY4KJnD7B/AmJRSl34r2jsO2yL1gEEFyVCt7+Y7jMhyz6qFnqyJkGTuENXHjWvY952CXATo+USHMyeYCZ64a6ZvkmURZ3GAvjRlH6B8murVXKBD/ChjYPUhPX5IZMpbXzJmFkX3VM7w= 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=QMvUmsTF; arc=fail smtp.client-ip=40.93.201.41 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="QMvUmsTF" ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=ZXZGBhHcJ66nfQmZtdGBhP+sCpmfsZbnDJyIM37JH0/KObRRy0Nf3/A9PNbSir6BsqCk4SvFEjGsgmlBeIMxhtxAFEGTAm6bvBnLvB8cLeCNx15Q5Ay19VqATAyUAF+7rHj2uf06nDA0H/9gchFsJYpQTJGn0tj8OFly6QCyKKvC5So1jgvK9nDaj9zeOZNzeCJ61JozD8gu/U9Op+tNqlSfqQKn9IguRK3+Sz9NH2Z1O/akldVC78Nse/ylWzviF5usMxe1W/rKuV67cSIGfd4O/FO8fWgtLNwbjvL1gSbu3rIvu530aHrDg+mK/Aow93l3IlHrAdYP5QlkPwSvxg== 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=zSGfhNt2gbhjmWhelEeCx277I3lh8pfXGDV5QQQpwPQ=; b=KdxP6s2ClJPprptAF2VCE2dBcmdyXQj9I0fZcwQPU5A+yol1RjOpGHuCDdAD87ECgh8gxsTt75YDWNuxQiTIl/7QP1KL1KyouYL/LXb3zbi8dcfWvr4KdJvtWNe4BPAgwxokhFzC2g6XOHoeGeQAhlbpBmKiGxob9wISXuoEvJJPt1ojRGK7M8wsDcgGsvtWRfATmOpBho2TkuUcQv4KvEJpg7kExq9la+eLj1roY4//8jq5xiHgm+uJU6KG6jKzh3s3rCXN2oHie6jYldFKqxu631k1HxqppQQO58D7C20Lt+jni9Dp91A+Ps6GNmIiccelCnuI9GnYhHlG1TDTeg== 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=zSGfhNt2gbhjmWhelEeCx277I3lh8pfXGDV5QQQpwPQ=; b=QMvUmsTF4NeTskjkvAznn5+9x8DJ+j0vcEQ09Gbd1uATKWFtwJ9xHuJx5uY21SzmEUeiUqvFz/EvHwdqywSz3ySz4HSyQhMcEt2VzxAqdHoJMiCNCRO6H6kAsyxw0p1BGxu5Z4jQGPgC8OccQHF48gMKEvXc4TQvIvP0fISdu0uOK37/J9+jlw49yxfD8JxzKU7x3zL0VdmzurSVyLgAisHiMGMZXSsKLRbM8NmKiQeGmV+GTROL78lG2+qhRhz0VAGYQ4WOiDUNi6HzXYRHQdsnEGb3SeXLYqiqqOPswhNjOUxNNezt82aQTh+rpLsua2UJ70Y+jPLBj8xvYdhteQ== Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=nvidia.com; Received: from CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) by PH0PR12MB5606.namprd12.prod.outlook.com (2603:10b6:510:141::16) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9520.12; Tue, 20 Jan 2026 06:23:55 +0000 Received: from CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989]) by CH2PR12MB3990.namprd12.prod.outlook.com ([fe80::7de1:4fe5:8ead:5989%6]) with mapi id 15.20.9520.011; Tue, 20 Jan 2026 06:23:55 +0000 From: Alexandre Courbot Date: Tue, 20 Jan 2026 15:17:59 +0900 Subject: [PATCH FOR REFERENCE 6/6] gpu: nova-core: use the kernel `register!` and `bitfield!` macros Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260120-register-v1-6-723a1743b557@nvidia.com> References: <20260120-register-v1-0-723a1743b557@nvidia.com> In-Reply-To: <20260120-register-v1-0-723a1743b557@nvidia.com> To: Miguel Ojeda , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Yury Norov Cc: John Hubbard , Alistair Popple , Joel Fernandes , Timur Tabi , Edwin Peer , Eliot Courtney , Daniel Almeida , Dirk Behme , Steven Price , rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org, Alexandre Courbot X-Mailer: b4 0.14.3 X-ClientProxiedBy: TYCPR01CA0183.jpnprd01.prod.outlook.com (2603:1096:400:2b0::19) To CH2PR12MB3990.namprd12.prod.outlook.com (2603:10b6:610:28::18) 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: CH2PR12MB3990:EE_|PH0PR12MB5606:EE_ X-MS-Office365-Filtering-Correlation-Id: f30adfa8-756e-437c-6bd6-08de57ec7c8c X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0;ARA:13230040|366016|376014|7416014|10070799003|1800799024|921020; X-Microsoft-Antispam-Message-Info: =?utf-8?B?azVNdW5CcUozejJPNTdqenhxVVJlaXFXTUFEdW04allUSDJHZk41aTdtQnJp?= =?utf-8?B?R0N6T2V1Y1FCM1h1czN3WTlEUDB2anhHUTdaZ0NJQ3pzMW1hWVE3d1ovU0dN?= =?utf-8?B?aGpkY3dxSk4rT2ZBVU14ZHluemE1M3huckV1dEJTT2hVaDlieSt2V0RYS2Y1?= =?utf-8?B?dmRYWXQ0dDBiZXh1cFBlaEY4REdFU2lSVGp1QmF4NGs5Nnlrb015UHNvQkRW?= =?utf-8?B?aklRK0t4Y1ZlZWFycXdnZXlRa010dTl4NWttUFJNd09pSm5HQzFPcWtFWkc2?= =?utf-8?B?dVU4M2g3T0JDU1dkMmQzMlJRdXdDbnNjMlhRekUwS2lJS0tmcDBDdFA0M0I3?= =?utf-8?B?a293UWN2Z2NrM3pRdFpoaHBZOFZyb2xTQkpLK21GbXBRbHRhWElVY3owTHVR?= =?utf-8?B?cWNaVEp4NitWekRzdXNYankzQ1lDMld5ZEZxUTNHSTFZL2RydG5sNm10eDZV?= =?utf-8?B?ZiszV3c4SHRMREJBcXdlamVJdHBTM3VJY05YQTlKUHVaZ3Vhc0x6SElzNFpZ?= =?utf-8?B?UWtyd3o1TEtPVGJDS1FnSmdsVmpNQTJWQ1RtTk5Ga2Iyc29XVk9wb25sMHgz?= =?utf-8?B?bjdNS2RoWkZBSW5ROTJMc1ZOa0lpV25lVzJhcCt2S0hmdkNvcC9yZ1VpaU8w?= =?utf-8?B?YWNLdDlHZG84SzM4TEt6L0loOUUyRW8xYnRuWk1xYmQ2WDgvSzhKRVc2Ukdx?= =?utf-8?B?NGJMTWUzR1g3N2hUUTdIOG5YZHZIMFFNcC8vZlM0N29mdVAzTWk2VkFrMXpD?= =?utf-8?B?M1p5dGptMHZsdjZPWE9NdFZsWUkrRVZrNjAxanNFUnhEM1hiVUFYVWpqQk52?= =?utf-8?B?MEtQRkQyWDBURmxTeW5qYVA1T05iKzVLQ2dkdkExNGpQeGgyQ0NqNVNGRDBE?= =?utf-8?B?K0ljMklub3JiQmEyQTFCaE1CNkhJSkpUR2N3LzRJWnFRdlpUNjJOZzZKc0Fr?= =?utf-8?B?UVlzd2U4Ykhwc0VHcDdvY2w2dG9tREhkQi9WQWlWQTRETGRFaE5LODc4SnAw?= =?utf-8?B?N1owbGpWcTJCOHdkN3lhYUh4aEExR0s1VWo4OUI4TlNSVmFpWmFoNUQ0eHJa?= =?utf-8?B?NnB5aFNFZmR3UlJzM3NJV2FFMERCZ2hhdXcwT2k2OGJESVlHV1VjS2RYdVJY?= =?utf-8?B?U2JYQVVrVXZtYjVGZFpiRm5rOU1NYXBjTkV0RGQ0QUsrb0M1UTFCUUNmMnMz?= =?utf-8?B?YzVQUHh4TWdpK3pJTGtzbmdEM1pId20yc2J6TE55ZGRWTTZDSVVZcWxYbDBv?= =?utf-8?B?RGNvSVl6d3Z2dllXWklZNzZyQXRGQXJRQXdwcGZ1aWxFcVcxOHMzdFdiSDVw?= =?utf-8?B?RXZwdExtY2d6TmtJamQ3Tmx3US9aaUY1VjBJMVczaTRNMDZXV3FDVisyWEpU?= =?utf-8?B?N2tvSWtQT25wOXNmVmFyNnpSN0NCbElHYk11UW1HQ2hJaEcyRUdnbEZNT0Q4?= =?utf-8?B?Y3ZSZkYxSVZoWGJDVnpqZWh2N0VUT0trZGZETHNhVjBXVHpnaWZmVWhlQUNy?= =?utf-8?B?dXYyNnBrUENpdGV5SVdLL0Y2RFdEZ09zeCtCNmx5blY5STQyQThYNFJPMDMv?= =?utf-8?B?SkhJZUo2cWZURXVvQ1lVMzNLbmpuMTNnMDEwVHh1OGc1T2FwelBmNGp5WEc1?= =?utf-8?B?TWRzL0NTRjV3N3lEWjZwWGNJQzQrdjdwcmpTS3orSEFyc2xMUlByWlkyR0dJ?= =?utf-8?B?dmdqeGlsWXVlMnpENkh0bVZlUEFMUUJYS1JkTmpadTd3WEZkZlBvM3JFZHAr?= =?utf-8?B?SjBvaDlZcnpWV3k2WDEvOWM3NVNZdi9SdDQvWU9BOTBIS01BSEdpYTFtcStN?= =?utf-8?B?SUFqQkFsWldCS0hEL3NmK3JrNTBXU3hSbkJ1emRmWUpQSmMwSklITkVZejdy?= =?utf-8?B?V0w2YXd3OThOMnlGNWtWOFJQRGlNVXZEUDVSRXpKNFp2Snk3Y2I3U2Q0R1RK?= =?utf-8?B?dnVtRTRFcWRvd0N4OFRmM3BYUmtxRytIZ0luK0gzcVhCaVBVV1VWR2RJL3ZW?= =?utf-8?B?ckJYUDdmSTNFbmN3aUM5a3V5YkRPdTVESFB2MGcvcDVQdE4rRzdVOXhmWXV0?= =?utf-8?B?ZVh0MWRHeGJPbjVyTXJGUXBzcTloZWRRcHA4OTJEaFk1WEdXbFE0RWRqQ0Zt?= =?utf-8?B?cW9mVGZKdzF4Z1ZUSnZ6Zzd2YWtsZ3pLak9sSXlUUjljaG5aV3RvL1puMHRC?= =?utf-8?Q?/30l8CXOrSEX4nBdeKA1+ao=3D?= X-Forefront-Antispam-Report: CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:CH2PR12MB3990.namprd12.prod.outlook.com;PTR:;CAT:NONE;SFS:(13230040)(366016)(376014)(7416014)(10070799003)(1800799024)(921020);DIR:OUT;SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 2 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?UGpiV20rb2JmMUhnektzY0lHQit1R2Y2RWlwK21kdFB4SGJ2Rmxaa1doKzI5?= =?utf-8?B?bkdQSWlpTTU5T2JnUjhrZCtVUFh4WERta1AycGtpaCtDYk9lMmZJVlRyMlk3?= =?utf-8?B?RnhWbk1naSszOXd4K2JVMERPSDN5anIvOURBcjZXNUxCaDlZaDc2MjhxWWND?= =?utf-8?B?RDVtcVdlNkh2MTFYT0Q3NkR1dzc2WVJ0ZS9sclVMaWlUb0hFMW8yVytnaFZx?= =?utf-8?B?bUszVm8zMXNzQ3RSRnk4eFRrZUdyTlRFYTlLWm01UHpjTk5mKy9zWCtWRG41?= =?utf-8?B?WVc3MDlKelZ1bXREN3R0ZG5TYmpKT25jOWNra2ZQeDE0K3I2V2ZEVUZLTUZu?= =?utf-8?B?YXpRT1lxRm5Qd1RmcUVXUmFVN2pLS21LcUUvWUJWbkRaWjBPRVI1dHVWZUJG?= =?utf-8?B?b1VPYkFWVk8xWmVvSytHclBjUE1YRWhmSDNkSEFraUZTR0RmMkpvbTQ1Z2FM?= =?utf-8?B?c2prSXA3Nm1HeGxURlRiRkFkbG03S29LYThVS1FXYUNmMFR5ZEZuSDU4RTdr?= =?utf-8?B?MFNtUFRTNHJkZ3pyVnhlUGJsdm5OcjdCUEhXNHN6M2FncDJ3K0htZmJxZ2Ni?= =?utf-8?B?b2s4ZHd2SFdaS2ZySHYxZzNVb25neWpjaFNJUmpRb3dtdmJkY2hWZTVBQm1K?= =?utf-8?B?SHYvcm11Wk5wTFBPQlZQSk5xb0VnZjBOcHQ4MElIT3JHVzRKa3FGUjJKSk1u?= =?utf-8?B?cHU3TWRpNXZWTFViazJycGhBYU9CVjk4eHFjeFUybXBlSmM0UE9Nbk9jUHVs?= =?utf-8?B?clkwZERGSmNWc3RDRWZ6blV3OTVpWUZwVGhjMGx6cjBxZHY2S2tzZWRzZk5K?= =?utf-8?B?YktpVk5Zd1YzczVidFNXak5OcUNSR1l3Rnd3bzhUS28wSXUwcTNSNzBJTVlN?= =?utf-8?B?VVF2ajlPR2Q3ZVowM3VIOWNBOXRUZzJFb0RLOTdLSitNNVlMcXUwNHZPMk1Q?= =?utf-8?B?STNUNXVBS3pYU0orRlV5Z2VSN2RUeVE0TDNhQVhEcm43N2dSam5idWhzRXg1?= =?utf-8?B?WlRDQXU1ZWs3b3NVak9JR1dkSzc3VWEzVU4xclJoenJQdHJkbSs1cGlrWGJC?= =?utf-8?B?YzB3VVllTnBsYnVqclFUNy95SXNTS0FkWXB0cU51RTdnZ0lXVmJpUkttOWty?= =?utf-8?B?NDl1NjM0d2xxcHhQTGx4K2Y4VjZQRzRYeFl3eGNWUGt4Tm5WVjZsY2hBS0J2?= =?utf-8?B?ZWZ0VjU1dnM1ZXZZOFRhaXNqckpEVy9jaEtKQU1BdmE3T2pmbVBLUHhMNGI5?= =?utf-8?B?am9HWGY4NzJQcEYweHB0R2c4OHlhZmFXT011dUpoR2x2K0dhb2JYSmpwc2lu?= =?utf-8?B?WXBOV09uOGRrWlMzQ1FDbEhPTDlOLzN4c2wwTmFRNTdNS3VIQi9zcnNURENm?= =?utf-8?B?WnB6NEwzRi9CRTdqYXZzMzhYNW1QZDh5eGUvZWZjWThaL0FNUlVNSEhZTHd5?= =?utf-8?B?MklrUytpV3BvWUxsNy9ZSzlUYUs3bUNSaDRaWVp0L3FZR3l5S2Mvak9JYWpD?= =?utf-8?B?QzRQbXRuNU5nckkvS1JIU1BIKzN6VnZIV0M1MG9YUkZNOG01VngyU2Vqd0Vy?= =?utf-8?B?S0tkdjVTaEFyc0FlSkVFVitNb1hoTGlURnptYlpKQnBYT2RvTTB2NmlnWThZ?= =?utf-8?B?dC9lM1djWnBNRFROeTZBbmtyNmYxRG95NVlJZ1hkdXFtQkF6REMvcnh5VkNL?= =?utf-8?B?WG9uOW5DQ2hibEhHeCtlMkxpbVNFanR0cDZzcGg4SUJhcVR0VlJ5dU0rNFA2?= =?utf-8?B?R1A3ZFQ3RWJQZ2U2T1Q1L250QkhnUjNJTEY0VS9tSW9neHM4MVlYUEs4WWli?= =?utf-8?B?d1V4c0ZTcUVrRFdRMjN0d0N5aHFmSVZkM2lXZVVwNk9wU05FWXR4QnVrY3Ax?= =?utf-8?B?Q0JNejJHZnU5NVJ1L0J4TGZ4THNJc1ZCY0lJVFRJaDZLVGdKMWZ0RHlaRGlV?= =?utf-8?B?UDFPakRqV055TnNWUjZvZTZ5QW5Ub3lsaHA1bHlhazZjdk5UeGpFQ0JvUTJi?= =?utf-8?B?dkU1bVNMdnlzTnJOdVNyM2ZPbGVGOTUwQXFmVEI0OXN4TFFreEp2aVVmd05O?= =?utf-8?B?eWpST2VaNDhVYnZJb1VFbkZLVnpUVWhwc2lESzlONGRxenFwcy9iaWo0ejVQ?= =?utf-8?B?ay9tZUdkaXVicGR0Nnhtc3hrUEY5d3lPSS9ldE5CQmNDdkdicjhYd1Y1S3B3?= =?utf-8?B?WUNSUkFxc2dzTWpFSUZrYjNRSFVCNFdMTDNSa2tWWnFtL2M0OFVxUWI2QURi?= =?utf-8?B?WWpHaGRHR1ppSlhJQUdLZnVDdUtCYUJiN2lkU09jM2ZBWHY2NFYyQnVLSmx4?= =?utf-8?B?dkhWMXMzOXBmSmpjTUtHVlg5TzlrSmV4U1ZpS0o0eUZMeVMxRU1MUkp2REVm?= =?utf-8?Q?hfnUab8hlO0TnlZ2bAyCzeOl9V5q8SqLJv+Hl8PDDkg63?= X-MS-Exchange-AntiSpam-MessageData-1: IEBIAiDrq3XwtA== X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-Network-Message-Id: f30adfa8-756e-437c-6bd6-08de57ec7c8c X-MS-Exchange-CrossTenant-AuthSource: CH2PR12MB3990.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 20 Jan 2026 06:23:55.0901 (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: Jlt4K3pEujA6jZqjRqPK7sNOHja0ojXpnlMly26xvp5WdToPACvPsEfWMKpE9h+1ucQslPNnpagAWmMo48arig== X-MS-Exchange-Transport-CrossTenantHeadersStamped: PH0PR12MB5606 Replace the nova-core internal `bitfield` and `register` macros by the ones defined in the `kernel` crate and remove our own private implementations. Signed-off-by: Alexandre Courbot --- drivers/gpu/nova-core/bitfield.rs | 330 -------------- drivers/gpu/nova-core/falcon.rs | 127 +++--- drivers/gpu/nova-core/falcon/gsp.rs | 10 +- drivers/gpu/nova-core/falcon/hal/ga102.rs | 5 +- drivers/gpu/nova-core/falcon/sec2.rs | 13 +- drivers/gpu/nova-core/fb/hal/ga100.rs | 9 +- drivers/gpu/nova-core/gpu.rs | 24 +- drivers/gpu/nova-core/gsp/cmdq.rs | 2 +- drivers/gpu/nova-core/gsp/fw.rs | 5 +- drivers/gpu/nova-core/nova_core.rs | 3 - drivers/gpu/nova-core/regs.rs | 265 +++++------ drivers/gpu/nova-core/regs/macros.rs | 721 --------------------------= ---- 12 files changed, 223 insertions(+), 1291 deletions(-) diff --git a/drivers/gpu/nova-core/bitfield.rs b/drivers/gpu/nova-core/bitf= ield.rs deleted file mode 100644 index 16e143658c51..000000000000 --- a/drivers/gpu/nova-core/bitfield.rs +++ /dev/null @@ -1,330 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -//! Bitfield library for Rust structures -//! -//! Support for defining bitfields in Rust structures. Also used by the [`= register!`] macro. - -/// Defines a struct with accessors to access bits within an inner unsigne= d integer. -/// -/// # Syntax -/// -/// ```rust -/// use nova_core::bitfield; -/// -/// #[derive(Debug, Clone, Copy, Default)] -/// enum Mode { -/// #[default] -/// Low =3D 0, -/// High =3D 1, -/// Auto =3D 2, -/// } -/// -/// impl TryFrom for Mode { -/// type Error =3D u8; -/// fn try_from(value: u8) -> Result { -/// match value { -/// 0 =3D> Ok(Mode::Low), -/// 1 =3D> Ok(Mode::High), -/// 2 =3D> Ok(Mode::Auto), -/// _ =3D> Err(value), -/// } -/// } -/// } -/// -/// impl From for u8 { -/// fn from(mode: Mode) -> u8 { -/// mode as u8 -/// } -/// } -/// -/// #[derive(Debug, Clone, Copy, Default)] -/// enum State { -/// #[default] -/// Inactive =3D 0, -/// Active =3D 1, -/// } -/// -/// impl From for State { -/// fn from(value: bool) -> Self { -/// if value { State::Active } else { State::Inactive } -/// } -/// } -/// -/// impl From for bool { -/// fn from(state: State) -> bool { -/// match state { -/// State::Inactive =3D> false, -/// State::Active =3D> true, -/// } -/// } -/// } -/// -/// bitfield! { -/// pub struct ControlReg(u32) { -/// 7:7 state as bool =3D> State; -/// 3:0 mode as u8 ?=3D> Mode; -/// } -/// } -/// ``` -/// -/// This generates a struct with: -/// - Field accessors: `mode()`, `state()`, etc. -/// - Field setters: `set_mode()`, `set_state()`, etc. (supports chaining = with builder pattern). -/// Note that the compiler will error out if the size of the setter's ar= g exceeds the -/// struct's storage size. -/// - Debug and Default implementations. -/// -/// Note: Field accessors and setters inherit the same visibility as the s= truct itself. -/// In the example above, both `mode()` and `set_mode()` methods will be `= pub`. -/// -/// Fields are defined as follows: -/// -/// - `as ` simply returns the field value casted to , typical= ly `u32`, `u16`, `u8` or -/// `bool`. Note that `bool` fields must have a range of 1 bit. -/// - `as =3D> ` calls ``'s `From::<>` = implementation and returns -/// the result. -/// - `as ?=3D> ` calls ``'s `TryFrom= ::<>` implementation -/// and returns the result. This is useful with fields for which not all= values are valid. -macro_rules! bitfield { - // Main entry point - defines the bitfield struct with fields - ($vis:vis struct $name:ident($storage:ty) $(, $comment:literal)? { $($= fields:tt)* }) =3D> { - bitfield!(@core $vis $name $storage $(, $comment)? { $($fields)* }= ); - }; - - // All rules below are helpers. - - // Defines the wrapper `$name` type, as well as its relevant implement= ations (`Debug`, - // `Default`, and conversion to the value type) and field accessor met= hods. - (@core $vis:vis $name:ident $storage:ty $(, $comment:literal)? { $($fi= elds:tt)* }) =3D> { - $( - #[doc=3D$comment] - )? - #[repr(transparent)] - #[derive(Clone, Copy)] - $vis struct $name($storage); - - impl ::core::convert::From<$name> for $storage { - fn from(val: $name) -> $storage { - val.0 - } - } - - bitfield!(@fields_dispatcher $vis $name $storage { $($fields)* }); - }; - - // Captures the fields and passes them to all the implementers that re= quire field information. - // - // Used to simplify the matching rules for implementers, so they don't= need to match the entire - // complex fields rule even though they only make use of part of it. - (@fields_dispatcher $vis:vis $name:ident $storage:ty { - $($hi:tt:$lo:tt $field:ident as $type:tt - $(?=3D> $try_into_type:ty)? - $(=3D> $into_type:ty)? - $(, $comment:literal)? - ; - )* - } - ) =3D> { - bitfield!(@field_accessors $vis $name $storage { - $( - $hi:$lo $field as $type - $(?=3D> $try_into_type)? - $(=3D> $into_type)? - $(, $comment)? - ; - )* - }); - bitfield!(@debug $name { $($field;)* }); - bitfield!(@default $name { $($field;)* }); - }; - - // Defines all the field getter/setter methods for `$name`. - ( - @field_accessors $vis:vis $name:ident $storage:ty { - $($hi:tt:$lo:tt $field:ident as $type:tt - $(?=3D> $try_into_type:ty)? - $(=3D> $into_type:ty)? - $(, $comment:literal)? - ; - )* - } - ) =3D> { - $( - bitfield!(@check_field_bounds $hi:$lo $field as $type); - )* - - #[allow(dead_code)] - impl $name { - $( - bitfield!(@field_accessor $vis $name $storage, $hi:$lo $field = as $type - $(?=3D> $try_into_type)? - $(=3D> $into_type)? - $(, $comment)? - ; - ); - )* - } - }; - - // Boolean fields must have `$hi =3D=3D $lo`. - (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) =3D> { - #[allow(clippy::eq_op)] - const _: () =3D { - ::kernel::build_assert!( - $hi =3D=3D $lo, - concat!("boolean field `", stringify!($field), "` covers m= ore than one bit") - ); - }; - }; - - // Non-boolean fields must have `$hi >=3D $lo`. - (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) =3D> { - #[allow(clippy::eq_op)] - const _: () =3D { - ::kernel::build_assert!( - $hi >=3D $lo, - concat!("field `", stringify!($field), "`'s MSB is smaller= than its LSB") - ); - }; - }; - - // Catches fields defined as `bool` and convert them into a boolean va= lue. - ( - @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $f= ield:ident as bool - =3D> $into_type:ty $(, $comment:literal)?; - ) =3D> { - bitfield!( - @leaf_accessor $vis $name $storage, $hi:$lo $field - { |f| <$into_type>::from(f !=3D 0) } - bool $into_type =3D> $into_type $(, $comment)?; - ); - }; - - // Shortcut for fields defined as `bool` without the `=3D>` syntax. - ( - @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $f= ield:ident as bool - $(, $comment:literal)?; - ) =3D> { - bitfield!( - @field_accessor $vis $name $storage, $hi:$lo $field as bool = =3D> bool $(, $comment)?; - ); - }; - - // Catches the `?=3D>` syntax for non-boolean fields. - ( - @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $f= ield:ident as $type:tt - ?=3D> $try_into_type:ty $(, $comment:literal)?; - ) =3D> { - bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field - { |f| <$try_into_type>::try_from(f as $type) } $type $try_into= _type =3D> - ::core::result::Result< - $try_into_type, - <$try_into_type as ::core::convert::TryFrom<$type>>::Error - > - $(, $comment)?;); - }; - - // Catches the `=3D>` syntax for non-boolean fields. - ( - @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $f= ield:ident as $type:tt - =3D> $into_type:ty $(, $comment:literal)?; - ) =3D> { - bitfield!(@leaf_accessor $vis $name $storage, $hi:$lo $field - { |f| <$into_type>::from(f as $type) } $type $into_type =3D> $= into_type $(, $comment)?;); - }; - - // Shortcut for non-boolean fields defined without the `=3D>` or `?=3D= >` syntax. - ( - @field_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $f= ield:ident as $type:tt - $(, $comment:literal)?; - ) =3D> { - bitfield!( - @field_accessor $vis $name $storage, $hi:$lo $field as $type = =3D> $type $(, $comment)?; - ); - }; - - // Generates the accessor methods for a single field. - ( - @leaf_accessor $vis:vis $name:ident $storage:ty, $hi:tt:$lo:tt $fi= eld:ident - { $process:expr } $prim_type:tt $to_type:ty =3D> $res_type:ty = $(, $comment:literal)?; - ) =3D> { - ::kernel::macros::paste!( - const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive =3D= $lo..=3D$hi; - const [<$field:upper _MASK>]: $storage =3D { - // Generate mask for shifting - match ::core::mem::size_of::<$storage>() { - 1 =3D> ::kernel::bits::genmask_u8($lo..=3D$hi) as $storage, - 2 =3D> ::kernel::bits::genmask_u16($lo..=3D$hi) as $storag= e, - 4 =3D> ::kernel::bits::genmask_u32($lo..=3D$hi) as $storag= e, - 8 =3D> ::kernel::bits::genmask_u64($lo..=3D$hi) as $storag= e, - _ =3D> ::kernel::build_error!("Unsupported storage type si= ze") - } - }; - const [<$field:upper _SHIFT>]: u32 =3D $lo; - ); - - $( - #[doc=3D"Returns the value of this field:"] - #[doc=3D$comment] - )? - #[inline(always)] - $vis fn $field(self) -> $res_type { - ::kernel::macros::paste!( - const MASK: $storage =3D $name::[<$field:upper _MASK>]; - const SHIFT: u32 =3D $name::[<$field:upper _SHIFT>]; - ); - let field =3D ((self.0 & MASK) >> SHIFT); - - $process(field) - } - - ::kernel::macros::paste!( - $( - #[doc=3D"Sets the value of this field:"] - #[doc=3D$comment] - )? - #[inline(always)] - $vis fn [](mut self, value: $to_type) -> Self { - const MASK: $storage =3D $name::[<$field:upper _MASK>]; - const SHIFT: u32 =3D $name::[<$field:upper _SHIFT>]; - let value =3D ($storage::from($prim_type::from(value)) << SHIF= T) & MASK; - self.0 =3D (self.0 & !MASK) | value; - - self - } - ); - }; - - // Generates the `Debug` implementation for `$name`. - (@debug $name:ident { $($field:ident;)* }) =3D> { - impl ::kernel::fmt::Debug for $name { - fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kerne= l::fmt::Result { - f.debug_struct(stringify!($name)) - .field("", &::kernel::prelude::fmt!("{:#x}", &sel= f.0)) - $( - .field(stringify!($field), &self.$field()) - )* - .finish() - } - } - }; - - // Generates the `Default` implementation for `$name`. - (@default $name:ident { $($field:ident;)* }) =3D> { - /// Returns a value for the bitfield where all fields are set to t= heir default value. - impl ::core::default::Default for $name { - fn default() -> Self { - #[allow(unused_mut)] - let mut value =3D Self(Default::default()); - - ::kernel::macros::paste!( - $( - value.[](Default::default()); - )* - ); - - value - } - } - }; -} diff --git a/drivers/gpu/nova-core/falcon.rs b/drivers/gpu/nova-core/falcon= .rs index 82c661aef594..2d0c4cbd9e67 100644 --- a/drivers/gpu/nova-core/falcon.rs +++ b/drivers/gpu/nova-core/falcon.rs @@ -9,12 +9,16 @@ use kernel::{ device, dma::DmaAddress, - io::poll::read_poll_timeout, + io::{ + poll::read_poll_timeout, + register::RegisterBase, // + }, + num::Bounded, prelude::*, sync::aref::ARef, time::{ - delay::fsleep, - Delta, // + delay::fsleep, // + Delta, }, }; =20 @@ -27,7 +31,6 @@ IntoSafeCast, // }, regs, - regs::macros::RegisterBase, // }; =20 pub(crate) mod gsp; @@ -35,11 +38,12 @@ pub(crate) mod sec2; =20 // TODO[FPRI]: Replace with `ToPrimitive`. -macro_rules! impl_from_enum_to_u8 { - ($enum_type:ty) =3D> { - impl From<$enum_type> for u8 { +// TODO: macro that defines the struct and impls, like for Chipset. +macro_rules! impl_from_enum_to_bounded { + ($enum_type:ty, $length:literal) =3D> { + impl From<$enum_type> for Bounded { fn from(value: $enum_type) -> Self { - value as u8 + Bounded::from_expr(value as u32) } } }; @@ -47,7 +51,6 @@ fn from(value: $enum_type) -> Self { =20 /// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCO= N_FALCON_HWCFG1`] /// register. -#[repr(u8)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub(crate) enum FalconCoreRev { #[default] @@ -59,16 +62,16 @@ pub(crate) enum FalconCoreRev { Rev6 =3D 6, Rev7 =3D 7, } -impl_from_enum_to_u8!(FalconCoreRev); +impl_from_enum_to_bounded!(FalconCoreRev, 4); =20 // TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for FalconCoreRev { +impl TryFrom> for FalconCoreRev { type Error =3D Error; =20 - fn try_from(value: u8) -> Result { + fn try_from(value: Bounded) -> Result { use FalconCoreRev::*; =20 - let rev =3D match value { + let rev =3D match value.get() { 1 =3D> Rev1, 2 =3D> Rev2, 3 =3D> Rev3, @@ -85,7 +88,6 @@ fn try_from(value: u8) -> Result { =20 /// Revision subversion number of a falcon core, used in the /// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register. -#[repr(u8)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub(crate) enum FalconCoreRevSubversion { #[default] @@ -94,30 +96,27 @@ pub(crate) enum FalconCoreRevSubversion { Subversion2 =3D 2, Subversion3 =3D 3, } -impl_from_enum_to_u8!(FalconCoreRevSubversion); +impl_from_enum_to_bounded!(FalconCoreRevSubversion, 2); =20 // TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for FalconCoreRevSubversion { - type Error =3D Error; - - fn try_from(value: u8) -> Result { +impl From> for FalconCoreRevSubversion { + fn from(value: Bounded) -> Self { use FalconCoreRevSubversion::*; =20 - let sub_version =3D match value & 0b11 { + match value.get() { 0 =3D> Subversion0, 1 =3D> Subversion1, 2 =3D> Subversion2, 3 =3D> Subversion3, - _ =3D> return Err(EINVAL), - }; - - Ok(sub_version) + // SAFETY: `value` comes from a 2-bit `Bounded`, and we just c= hecked all possible + // values. + _ =3D> unsafe { core::hint::unreachable_unchecked() }, + } } } =20 /// Security model of a falcon core, used in the [`crate::regs::NV_PFALCON= _FALCON_HWCFG1`] /// register. -#[repr(u8)] #[derive(Debug, Default, Copy, Clone)] /// Security mode of the Falcon microprocessor. /// @@ -138,16 +137,16 @@ pub(crate) enum FalconSecurityModel { /// Also known as High-Secure, Privilege Level 3 or PL3. Heavy =3D 3, } -impl_from_enum_to_u8!(FalconSecurityModel); +impl_from_enum_to_bounded!(FalconSecurityModel, 2); =20 // TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for FalconSecurityModel { +impl TryFrom> for FalconSecurityModel { type Error =3D Error; =20 - fn try_from(value: u8) -> Result { + fn try_from(value: Bounded) -> Result { use FalconSecurityModel::*; =20 - let sec_model =3D match value { + let sec_model =3D match value.get() { 0 =3D> None, 2 =3D> Light, 3 =3D> Heavy, @@ -160,24 +159,23 @@ fn try_from(value: u8) -> Result { =20 /// Signing algorithm for a given firmware, used in the [`crate::regs::NV_= PFALCON2_FALCON_MOD_SEL`] /// register. It is passed to the Falcon Boot ROM (BROM) as a parameter. -#[repr(u8)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] pub(crate) enum FalconModSelAlgo { /// AES. - #[expect(dead_code)] Aes =3D 0, /// RSA3K. #[default] Rsa3k =3D 1, } -impl_from_enum_to_u8!(FalconModSelAlgo); +impl_from_enum_to_bounded!(FalconModSelAlgo, 8); =20 // TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for FalconModSelAlgo { +impl TryFrom> for FalconModSelAlgo { type Error =3D Error; =20 - fn try_from(value: u8) -> Result { - match value { + fn try_from(value: Bounded) -> Result { + match value.get() { + 0 =3D> Ok(FalconModSelAlgo::Aes), 1 =3D> Ok(FalconModSelAlgo::Rsa3k), _ =3D> Err(EINVAL), } @@ -185,21 +183,20 @@ fn try_from(value: u8) -> Result { } =20 /// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FAL= CON_DMATRFCMD`] register. -#[repr(u8)] #[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] pub(crate) enum DmaTrfCmdSize { /// 256 bytes transfer. #[default] Size256B =3D 0x6, } -impl_from_enum_to_u8!(DmaTrfCmdSize); +impl_from_enum_to_bounded!(DmaTrfCmdSize, 3); =20 // TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for DmaTrfCmdSize { +impl TryFrom> for DmaTrfCmdSize { type Error =3D Error; =20 - fn try_from(value: u8) -> Result { - match value { + fn try_from(value: Bounded) -> Result { + match value.get() { 0x6 =3D> Ok(Self::Size256B), _ =3D> Err(EINVAL), } @@ -215,25 +212,17 @@ pub(crate) enum PeregrineCoreSelect { /// RISC-V core is active. Riscv =3D 1, } +impl_from_enum_to_bounded!(PeregrineCoreSelect, 1); =20 -impl From for PeregrineCoreSelect { - fn from(value: bool) -> Self { - match value { +impl From> for PeregrineCoreSelect { + fn from(value: Bounded) -> Self { + match bool::from(value) { false =3D> PeregrineCoreSelect::Falcon, true =3D> PeregrineCoreSelect::Riscv, } } } =20 -impl From for bool { - fn from(value: PeregrineCoreSelect) -> Self { - match value { - PeregrineCoreSelect::Falcon =3D> false, - PeregrineCoreSelect::Riscv =3D> true, - } - } -} - /// Different types of memory present in a falcon core. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum FalconMem { @@ -257,14 +246,14 @@ pub(crate) enum FalconFbifTarget { /// Non-coherent system memory (System DRAM). NoncoherentSysmem =3D 2, } -impl_from_enum_to_u8!(FalconFbifTarget); +impl_from_enum_to_bounded!(FalconFbifTarget, 2); =20 // TODO[FPRI]: replace with `FromPrimitive`. -impl TryFrom for FalconFbifTarget { +impl TryFrom> for FalconFbifTarget { type Error =3D Error; =20 - fn try_from(value: u8) -> Result { - let res =3D match value { + fn try_from(value: Bounded) -> Result { + let res =3D match value.get() { 0 =3D> Self::LocalFb, 1 =3D> Self::CoherentSysmem, 2 =3D> Self::NoncoherentSysmem, @@ -284,26 +273,18 @@ pub(crate) enum FalconFbifMemType { /// Physical memory addresses. Physical =3D 1, } +impl_from_enum_to_bounded!(FalconFbifMemType, 1); =20 /// Conversion from a single-bit register field. -impl From for FalconFbifMemType { - fn from(value: bool) -> Self { - match value { +impl From> for FalconFbifMemType { + fn from(value: Bounded) -> Self { + match bool::from(value) { false =3D> Self::Virtual, true =3D> Self::Physical, } } } =20 -impl From for bool { - fn from(value: FalconFbifMemType) -> Self { - match value { - FalconFbifMemType::Virtual =3D> false, - FalconFbifMemType::Physical =3D> true, - } - } -} - /// Type used to represent the `PFALCON` registers address base for a give= n falcon engine. pub(crate) struct PFalconBase(()); =20 @@ -432,7 +413,7 @@ pub(crate) fn reset(&self, bar: &Bar0) -> Result { self.reset_wait_mem_scrubbing(bar)?; =20 regs::NV_PFALCON_FALCON_RM::default() - .set_value(regs::NV_PMC_BOOT_0::read(bar).into()) + .set_value(regs::NV_PMC_BOOT_0::read(bar).as_raw()) .write(bar, &E::ID); =20 Ok(()) @@ -501,20 +482,18 @@ fn dma_wr>( .set_base((dma_start >> 8) as u32) .write(bar, &E::ID); regs::NV_PFALCON_FALCON_DMATRFBASE1::default() - // CAST: `as u16` is used on purpose since the remaining bits = are guaranteed to fit - // within a `u16`. - .set_base((dma_start >> 40) as u16) + .try_set_base(dma_start >> 40)? .write(bar, &E::ID); =20 let cmd =3D regs::NV_PFALCON_FALCON_DMATRFCMD::default() .set_size(DmaTrfCmdSize::Size256B) .set_imem(target_mem =3D=3D FalconMem::Imem) - .set_sec(if sec { 1 } else { 0 }); + .set_sec(sec); =20 for pos in (0..num_transfers).map(|i| i * DMA_LEN) { // Perform a transfer of size `DMA_LEN`. regs::NV_PFALCON_FALCON_DMATRFMOFFS::default() - .set_offs(load_offsets.dst_start + pos) + .try_set_offs(load_offsets.dst_start + pos)? .write(bar, &E::ID); regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default() .set_offs(src_start + pos) diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/fa= lcon/gsp.rs index 67edef3636c1..dcdf3962ab0d 100644 --- a/drivers/gpu/nova-core/falcon/gsp.rs +++ b/drivers/gpu/nova-core/falcon/gsp.rs @@ -1,7 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 =20 use kernel::{ - io::poll::read_poll_timeout, + io::{ + poll::read_poll_timeout, + register::RegisterBase, // + }, prelude::*, time::Delta, // }; @@ -14,10 +17,7 @@ PFalcon2Base, PFalconBase, // }, - regs::{ - self, - macros::RegisterBase, // - }, + regs, }; =20 /// Type specifying the `Gsp` falcon engine. Cannot be instantiated. diff --git a/drivers/gpu/nova-core/falcon/hal/ga102.rs b/drivers/gpu/nova-c= ore/falcon/hal/ga102.rs index 69a7a95cac16..72afbd9101cf 100644 --- a/drivers/gpu/nova-core/falcon/hal/ga102.rs +++ b/drivers/gpu/nova-core/falcon/hal/ga102.rs @@ -59,7 +59,7 @@ fn signature_reg_fuse_version_ga102( =20 // `ucode_idx` is guaranteed to be in the range [0..15], making the `r= ead` calls provable valid // at build-time. - let reg_fuse_version =3D if engine_id_mask & 0x0001 !=3D 0 { + let reg_fuse_version: u16 =3D if engine_id_mask & 0x0001 !=3D 0 { regs::NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION::read(bar, ucode_idx).da= ta() } else if engine_id_mask & 0x0004 !=3D 0 { regs::NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION::read(bar, ucode_idx).d= ata() @@ -68,7 +68,8 @@ fn signature_reg_fuse_version_ga102( } else { dev_err!(dev, "unexpected engine_id_mask {:#x}", engine_id_mask); return Err(EINVAL); - }; + } + .into(); =20 // TODO[NUMM]: replace with `last_set_bit` once it lands. Ok(u16::BITS - reg_fuse_version.leading_zeros()) diff --git a/drivers/gpu/nova-core/falcon/sec2.rs b/drivers/gpu/nova-core/f= alcon/sec2.rs index b57d362e576a..5d836e2d17dd 100644 --- a/drivers/gpu/nova-core/falcon/sec2.rs +++ b/drivers/gpu/nova-core/falcon/sec2.rs @@ -1,12 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 =20 -use crate::{ - falcon::{ - FalconEngine, - PFalcon2Base, - PFalconBase, // - }, - regs::macros::RegisterBase, +use kernel::io::register::RegisterBase; + +use crate::falcon::{ + FalconEngine, + PFalcon2Base, + PFalconBase, // }; =20 /// Type specifying the `Sec2` falcon engine. Cannot be instantiated. diff --git a/drivers/gpu/nova-core/fb/hal/ga100.rs b/drivers/gpu/nova-core/= fb/hal/ga100.rs index e0acc41aa7cd..acf46ad0dba1 100644 --- a/drivers/gpu/nova-core/fb/hal/ga100.rs +++ b/drivers/gpu/nova-core/fb/hal/ga100.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: GPL-2.0 =20 -use kernel::prelude::*; +use kernel::{ + num::Bounded, + prelude::*, // +}; =20 use crate::{ driver::Bar0, @@ -20,9 +23,7 @@ pub(super) fn read_sysmem_flush_page_ga100(bar: &Bar0) ->= u64 { =20 pub(super) fn write_sysmem_flush_page_ga100(bar: &Bar0, addr: u64) { regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI::default() - // CAST: `as u32` is used on purpose since the remaining bits are = guaranteed to fit within - // a `u32`. - .set_adr_63_40((addr >> FLUSH_SYSMEM_ADDR_SHIFT_HI) as u32) + .set_adr_63_40(Bounded::from_expr(addr >> FLUSH_SYSMEM_ADDR_SHIFT_= HI).cast()) .write(bar); regs::NV_PFB_NISO_FLUSH_SYSMEM_ADDR::default() // CAST: `as u32` is used on purpose since we want to strip the up= per bits that have been diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs index 629c9d2dc994..556b2454b5a6 100644 --- a/drivers/gpu/nova-core/gpu.rs +++ b/drivers/gpu/nova-core/gpu.rs @@ -4,6 +4,7 @@ device, devres::Devres, fmt, + num::Bounded, pci, prelude::*, sync::Arc, // @@ -135,11 +136,11 @@ pub(crate) enum Architecture { Ada =3D 0x19, } =20 -impl TryFrom for Architecture { +impl TryFrom> for Architecture { type Error =3D Error; =20 - fn try_from(value: u8) -> Result { - match value { + fn try_from(value: Bounded) -> Result { + match u8::from(value) { 0x16 =3D> Ok(Self::Turing), 0x17 =3D> Ok(Self::Ampere), 0x19 =3D> Ok(Self::Ada), @@ -148,23 +149,26 @@ fn try_from(value: u8) -> Result { } } =20 -impl From for u8 { +impl From for Bounded { fn from(value: Architecture) -> Self { - // CAST: `Architecture` is `repr(u8)`, so this cast is always loss= less. - value as u8 + match value { + Architecture::Turing =3D> Bounded::::new::<0x16>().cast= (), + Architecture::Ampere =3D> Bounded::::new::<0x17>().cast= (), + Architecture::Ada =3D> Bounded::::new::<0x19>().cast(), + } } } =20 pub(crate) struct Revision { - major: u8, - minor: u8, + major: Bounded, + minor: Bounded, } =20 impl From for Revision { fn from(boot0: regs::NV_PMC_BOOT_42) -> Self { Self { - major: boot0.major_revision(), - minor: boot0.minor_revision(), + major: boot0.major_revision().cast(), + minor: boot0.minor_revision().cast(), } } } diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/= cmdq.rs index 6f946d14868a..baa36139017b 100644 --- a/drivers/gpu/nova-core/gsp/cmdq.rs +++ b/drivers/gpu/nova-core/gsp/cmdq.rs @@ -476,7 +476,7 @@ fn calculate_checksum>(it: T) = -> u32 { /// Notifies the GSP that we have updated the command queue pointers. fn notify_gsp(bar: &Bar0) { regs::NV_PGSP_QUEUE_HEAD::default() - .set_address(0) + .set_address(0u32) .write(bar); } =20 diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw= .rs index abffd6beec65..91beeaca0e9e 100644 --- a/drivers/gpu/nova-core/gsp/fw.rs +++ b/drivers/gpu/nova-core/gsp/fw.rs @@ -9,6 +9,7 @@ use core::ops::Range; =20 use kernel::{ + bitfield, dma::CoherentAllocation, fmt, prelude::*, @@ -762,8 +763,8 @@ unsafe impl AsBytes for MsgqRxHeader {} =20 bitfield! { struct MsgHeaderVersion(u32) { - 31:24 major as u8; - 23:16 minor as u8; + 31:24 major; + 23:16 minor; } } =20 diff --git a/drivers/gpu/nova-core/nova_core.rs b/drivers/gpu/nova-core/nov= a_core.rs index b98a1c03f13d..92dd38e21c2c 100644 --- a/drivers/gpu/nova-core/nova_core.rs +++ b/drivers/gpu/nova-core/nova_core.rs @@ -2,9 +2,6 @@ =20 //! Nova Core GPU Driver =20 -#[macro_use] -mod bitfield; - mod dma; mod driver; mod falcon; diff --git a/drivers/gpu/nova-core/regs.rs b/drivers/gpu/nova-core/regs.rs index 82cc6c0790e5..794401122f06 100644 --- a/drivers/gpu/nova-core/regs.rs +++ b/drivers/gpu/nova-core/regs.rs @@ -1,12 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 =20 -// Required to retain the original register names used by OpenRM, which ar= e all capital snake case -// but are mapped to types. -#![allow(non_camel_case_types)] - -#[macro_use] -pub(crate) mod macros; - use kernel::prelude::*; =20 use crate::{ @@ -29,20 +22,28 @@ num::FromSafeCast, }; =20 +// All nova-core registers are 32-bit and `pub(crate)`. Wrap the `register= !` macro to avoid +// repeating this information for every register. +macro_rules! nv_reg { + ($(#[$attr:meta])* $name:ident $($tail:tt)*) =3D> { + ::kernel::register!($(#[$attr])* pub(crate) $name(u32) $($tail)*); + }; +} + // PMC =20 -register!(NV_PMC_BOOT_0 @ 0x00000000, "Basic revision information about th= e GPU" { - 3:0 minor_revision as u8, "Minor revision of the chip"; - 7:4 major_revision as u8, "Major revision of the chip"; - 8:8 architecture_1 as u8, "MSB of the architecture"; - 23:20 implementation as u8, "Implementation version of the architect= ure"; - 28:24 architecture_0 as u8, "Lower bits of the architecture"; +nv_reg!(NV_PMC_BOOT_0 @ 0x00000000, "Basic revision information about the = GPU" { + 3:0 minor_revision, "Minor revision of the chip"; + 7:4 major_revision, "Major revision of the chip"; + 8:8 architecture_1, "MSB of the architecture"; + 23:20 implementation, "Implementation version of the architecture"; + 28:24 architecture_0, "Lower bits of the architecture"; }); =20 impl NV_PMC_BOOT_0 { pub(crate) fn is_older_than_fermi(self) -> bool { // From https://github.com/NVIDIA/open-gpu-doc/tree/master/manuals= : - const NV_PMC_BOOT_0_ARCHITECTURE_GF100: u8 =3D 0xc; + const NV_PMC_BOOT_0_ARCHITECTURE_GF100: u32 =3D 0xc; =20 // Older chips left arch1 zeroed out. That, combined with an arch0= value that is less than // GF100, means "older than Fermi". @@ -50,11 +51,11 @@ pub(crate) fn is_older_than_fermi(self) -> bool { } } =20 -register!(NV_PMC_BOOT_42 @ 0x00000a00, "Extended architecture information"= { - 15:12 minor_revision as u8, "Minor revision of the chip"; - 19:16 major_revision as u8, "Major revision of the chip"; - 23:20 implementation as u8, "Implementation version of the architect= ure"; - 29:24 architecture as u8 ?=3D> Architecture, "Architecture value"; +nv_reg!(NV_PMC_BOOT_42 @ 0x00000a00, "Extended architecture information" { + 15:12 minor_revision, "Minor revision of the chip"; + 19:16 major_revision, "Major revision of the chip"; + 23:20 implementation, "Implementation version of the architecture"; + 29:24 architecture ?=3D> Architecture, "Architecture value"; }); =20 impl NV_PMC_BOOT_42 { @@ -89,11 +90,11 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> ke= rnel::fmt::Result { =20 // PBUS =20 -register!(NV_PBUS_SW_SCRATCH @ 0x00001400[64] {}); +nv_reg!(NV_PBUS_SW_SCRATCH @ 0x00001400[64] {}); =20 -register!(NV_PBUS_SW_SCRATCH_0E_FRTS_ERR =3D> NV_PBUS_SW_SCRATCH[0xe], +nv_reg!(NV_PBUS_SW_SCRATCH_0E_FRTS_ERR =3D> NV_PBUS_SW_SCRATCH[0xe], "scratch register 0xe used as FRTS firmware error code" { - 31:16 frts_err_code as u16; + 31:16 frts_err_code; }); =20 // PFB @@ -101,22 +102,22 @@ fn fmt(&self, f: &mut kernel::fmt::Formatter<'_>) -> = kernel::fmt::Result { // The following two registers together hold the physical system memory ad= dress that is used by the // GPU to perform sysmembar operations (see `fb::SysmemFlush`). =20 -register!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR @ 0x00100c10 { - 31:0 adr_39_08 as u32; +nv_reg!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR @ 0x00100c10 { + 31:0 adr_39_08; }); =20 -register!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI @ 0x00100c40 { - 23:0 adr_63_40 as u32; +nv_reg!(NV_PFB_NISO_FLUSH_SYSMEM_ADDR_HI @ 0x00100c40 { + 23:0 adr_63_40; }); =20 -register!(NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE @ 0x00100ce0 { - 3:0 lower_scale as u8; - 9:4 lower_mag as u8; - 30:30 ecc_mode_enabled as bool; +nv_reg!(NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE @ 0x00100ce0 { + 3:0 lower_scale; + 9:4 lower_mag; + 30:30 ecc_mode_enabled =3D> bool; }); =20 -register!(NV_PGSP_QUEUE_HEAD @ 0x00110c00 { - 31:0 address as u32; +nv_reg!(NV_PGSP_QUEUE_HEAD @ 0x00110c00 { + 31:0 address; }); =20 impl NV_PFB_PRI_MMU_LOCAL_MEMORY_RANGE { @@ -134,8 +135,8 @@ pub(crate) fn usable_fb_size(self) -> u64 { } } =20 -register!(NV_PFB_PRI_MMU_WPR2_ADDR_LO@0x001fa824 { - 31:4 lo_val as u32, "Bits 12..40 of the lower (inclusive) bound of = the WPR2 region"; +nv_reg!(NV_PFB_PRI_MMU_WPR2_ADDR_LO@0x001fa824 { + 31:4 lo_val, "Bits 12..40 of the lower (inclusive) bound of the WPR= 2 region"; }); =20 impl NV_PFB_PRI_MMU_WPR2_ADDR_LO { @@ -145,8 +146,8 @@ pub(crate) fn lower_bound(self) -> u64 { } } =20 -register!(NV_PFB_PRI_MMU_WPR2_ADDR_HI@0x001fa828 { - 31:4 hi_val as u32, "Bits 12..40 of the higher (exclusive) bound of= the WPR2 region"; +nv_reg!(NV_PFB_PRI_MMU_WPR2_ADDR_HI@0x001fa828 { + 31:4 hi_val, "Bits 12..40 of the higher (exclusive) bound of the WP= R2 region"; }); =20 impl NV_PFB_PRI_MMU_WPR2_ADDR_HI { @@ -169,25 +170,25 @@ pub(crate) fn higher_bound(self) -> u64 { =20 // Boot Sequence Interface (BSI) register used to determine // if GSP reload/resume has completed during the boot process. -register!(NV_PGC6_BSI_SECURE_SCRATCH_14 @ 0x001180f8 { - 26:26 boot_stage_3_handoff as bool; +nv_reg!(NV_PGC6_BSI_SECURE_SCRATCH_14 @ 0x001180f8 { + 26:26 boot_stage_3_handoff =3D> bool; }); =20 // Privilege level mask register. It dictates whether the host CPU has pri= vilege to access the // `PGC6_AON_SECURE_SCRATCH_GROUP_05` register (which it needs to read GFW= _BOOT). -register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128, +nv_reg!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_PRIV_LEVEL_MASK @ 0x00118128, "Privilege level mask register" { - 0:0 read_protection_level0 as bool, "Set after FWSEC lowers its pr= otection level"; + 0:0 read_protection_level0 =3D> bool, "Set after FWSEC lowers its = protection level"; }); =20 // OpenRM defines this as a register array, but doesn't specify its size a= nd only uses its first // element. Be conservative until we know the actual size or need to use m= ore registers. -register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05 @ 0x00118234[1] {}); +nv_reg!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_05 @ 0x00118234[1] {}); =20 -register!( +nv_reg!( NV_PGC6_AON_SECURE_SCRATCH_GROUP_05_0_GFW_BOOT =3D> NV_PGC6_AON_SECURE= _SCRATCH_GROUP_05[0], "Scratch group 05 register 0 used as GFW boot progress indicator" { - 7:0 progress as u8, "Progress of GFW boot (0xff means completed= )"; + 7:0 progress, "Progress of GFW boot (0xff means completed)"; } ); =20 @@ -198,14 +199,14 @@ pub(crate) fn completed(self) -> bool { } } =20 -register!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_42 @ 0x001183a4 { - 31:0 value as u32; +nv_reg!(NV_PGC6_AON_SECURE_SCRATCH_GROUP_42 @ 0x001183a4 { + 31:0 value; }); =20 -register!( +nv_reg!( NV_USABLE_FB_SIZE_IN_MB =3D> NV_PGC6_AON_SECURE_SCRATCH_GROUP_42, "Scratch group 42 register used as framebuffer size" { - 31:0 value as u32, "Usable framebuffer size, in megabytes"; + 31:0 value, "Usable framebuffer size, in megabytes"; } ); =20 @@ -218,9 +219,9 @@ pub(crate) fn usable_fb_size(self) -> u64 { =20 // PDISP =20 -register!(NV_PDISP_VGA_WORKSPACE_BASE @ 0x00625f04 { - 3:3 status_valid as bool, "Set if the `addr` field is valid"; - 31:8 addr as u32, "VGA workspace base address divided by 0x10000"; +nv_reg!(NV_PDISP_VGA_WORKSPACE_BASE @ 0x00625f04 { + 3:3 status_valid =3D> bool, "Set if the `addr` field is valid"; + 31:8 addr, "VGA workspace base address divided by 0x10000"; }); =20 impl NV_PDISP_VGA_WORKSPACE_BASE { @@ -238,47 +239,47 @@ pub(crate) fn vga_workspace_addr(self) -> Option= { =20 pub(crate) const NV_FUSE_OPT_FPF_SIZE: usize =3D 16; =20 -register!(NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION @ 0x00824100[NV_FUSE_OPT_FP= F_SIZE] { - 15:0 data as u16; +nv_reg!(NV_FUSE_OPT_FPF_NVDEC_UCODE1_VERSION @ 0x00824100[NV_FUSE_OPT_FPF_= SIZE] { + 15:0 data; }); =20 -register!(NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION @ 0x00824140[NV_FUSE_OPT_FPF= _SIZE] { - 15:0 data as u16; +nv_reg!(NV_FUSE_OPT_FPF_SEC2_UCODE1_VERSION @ 0x00824140[NV_FUSE_OPT_FPF_S= IZE] { + 15:0 data; }); =20 -register!(NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION @ 0x008241c0[NV_FUSE_OPT_FPF_= SIZE] { - 15:0 data as u16; +nv_reg!(NV_FUSE_OPT_FPF_GSP_UCODE1_VERSION @ 0x008241c0[NV_FUSE_OPT_FPF_SI= ZE] { + 15:0 data; }); =20 // PFALCON =20 -register!(NV_PFALCON_FALCON_IRQSCLR @ PFalconBase[0x00000004] { - 4:4 halt as bool; - 6:6 swgen0 as bool; +nv_reg!(NV_PFALCON_FALCON_IRQSCLR @ PFalconBase[0x00000004] { + 4:4 halt =3D> bool; + 6:6 swgen0 =3D> bool; }); =20 -register!(NV_PFALCON_FALCON_MAILBOX0 @ PFalconBase[0x00000040] { - 31:0 value as u32; +nv_reg!(NV_PFALCON_FALCON_MAILBOX0 @ PFalconBase[0x00000040] { + 31:0 value =3D> u32; }); =20 -register!(NV_PFALCON_FALCON_MAILBOX1 @ PFalconBase[0x00000044] { - 31:0 value as u32; +nv_reg!(NV_PFALCON_FALCON_MAILBOX1 @ PFalconBase[0x00000044] { + 31:0 value =3D> u32; }); =20 // Used to store version information about the firmware running // on the Falcon processor. -register!(NV_PFALCON_FALCON_OS @ PFalconBase[0x00000080] { - 31:0 value as u32; +nv_reg!(NV_PFALCON_FALCON_OS @ PFalconBase[0x00000080] { + 31:0 value =3D> u32; }); =20 -register!(NV_PFALCON_FALCON_RM @ PFalconBase[0x00000084] { - 31:0 value as u32; +nv_reg!(NV_PFALCON_FALCON_RM @ PFalconBase[0x00000084] { + 31:0 value =3D> u32; }); =20 -register!(NV_PFALCON_FALCON_HWCFG2 @ PFalconBase[0x000000f4] { - 10:10 riscv as bool; - 12:12 mem_scrubbing as bool, "Set to 0 after memory scrubbing is com= pleted"; - 31:31 reset_ready as bool, "Signal indicating that reset is complete= d (GA102+)"; +nv_reg!(NV_PFALCON_FALCON_HWCFG2 @ PFalconBase[0x000000f4] { + 10:10 riscv =3D> bool; + 12:12 mem_scrubbing =3D> bool, "Set to 0 after memory scrubbing is c= ompleted"; + 31:31 reset_ready =3D> bool, "Signal indicating that reset is comple= ted (GA102+)"; }); =20 impl NV_PFALCON_FALCON_HWCFG2 { @@ -288,107 +289,107 @@ pub(crate) fn mem_scrubbing_done(self) -> bool { } } =20 -register!(NV_PFALCON_FALCON_CPUCTL @ PFalconBase[0x00000100] { - 1:1 startcpu as bool; - 4:4 halted as bool; - 6:6 alias_en as bool; +nv_reg!(NV_PFALCON_FALCON_CPUCTL @ PFalconBase[0x00000100] { + 1:1 startcpu =3D> bool; + 4:4 halted =3D> bool; + 6:6 alias_en =3D> bool; }); =20 -register!(NV_PFALCON_FALCON_BOOTVEC @ PFalconBase[0x00000104] { - 31:0 value as u32; +nv_reg!(NV_PFALCON_FALCON_BOOTVEC @ PFalconBase[0x00000104] { + 31:0 value =3D> u32; }); =20 -register!(NV_PFALCON_FALCON_DMACTL @ PFalconBase[0x0000010c] { - 0:0 require_ctx as bool; - 1:1 dmem_scrubbing as bool; - 2:2 imem_scrubbing as bool; - 6:3 dmaq_num as u8; - 7:7 secure_stat as bool; +nv_reg!(NV_PFALCON_FALCON_DMACTL @ PFalconBase[0x0000010c] { + 0:0 require_ctx =3D> bool; + 1:1 dmem_scrubbing =3D> bool; + 2:2 imem_scrubbing =3D> bool; + 6:3 dmaq_num; + 7:7 secure_stat =3D> bool; }); =20 -register!(NV_PFALCON_FALCON_DMATRFBASE @ PFalconBase[0x00000110] { - 31:0 base as u32; +nv_reg!(NV_PFALCON_FALCON_DMATRFBASE @ PFalconBase[0x00000110] { + 31:0 base =3D> u32; }); =20 -register!(NV_PFALCON_FALCON_DMATRFMOFFS @ PFalconBase[0x00000114] { - 23:0 offs as u32; +nv_reg!(NV_PFALCON_FALCON_DMATRFMOFFS @ PFalconBase[0x00000114] { + 23:0 offs; }); =20 -register!(NV_PFALCON_FALCON_DMATRFCMD @ PFalconBase[0x00000118] { - 0:0 full as bool; - 1:1 idle as bool; - 3:2 sec as u8; - 4:4 imem as bool; - 5:5 is_write as bool; - 10:8 size as u8 ?=3D> DmaTrfCmdSize; - 14:12 ctxdma as u8; - 16:16 set_dmtag as u8; +nv_reg!(NV_PFALCON_FALCON_DMATRFCMD @ PFalconBase[0x00000118] { + 0:0 full =3D> bool; + 1:1 idle =3D> bool; + 3:2 sec; + 4:4 imem =3D> bool; + 5:5 is_write =3D> bool; + 10:8 size ?=3D> DmaTrfCmdSize; + 14:12 ctxdma; + 16:16 set_dmtag; }); =20 -register!(NV_PFALCON_FALCON_DMATRFFBOFFS @ PFalconBase[0x0000011c] { - 31:0 offs as u32; +nv_reg!(NV_PFALCON_FALCON_DMATRFFBOFFS @ PFalconBase[0x0000011c] { + 31:0 offs =3D> u32; }); =20 -register!(NV_PFALCON_FALCON_DMATRFBASE1 @ PFalconBase[0x00000128] { - 8:0 base as u16; +nv_reg!(NV_PFALCON_FALCON_DMATRFBASE1 @ PFalconBase[0x00000128] { + 8:0 base; }); =20 -register!(NV_PFALCON_FALCON_HWCFG1 @ PFalconBase[0x0000012c] { - 3:0 core_rev as u8 ?=3D> FalconCoreRev, "Core revision"; - 5:4 security_model as u8 ?=3D> FalconSecurityModel, "Security mode= l"; - 7:6 core_rev_subversion as u8 ?=3D> FalconCoreRevSubversion, "Core= revision subversion"; +nv_reg!(NV_PFALCON_FALCON_HWCFG1 @ PFalconBase[0x0000012c] { + 3:0 core_rev ?=3D> FalconCoreRev, "Core revision"; + 5:4 security_model ?=3D> FalconSecurityModel, "Security model"; + 7:6 core_rev_subversion =3D> FalconCoreRevSubversion, "Core revisi= on subversion"; }); =20 -register!(NV_PFALCON_FALCON_CPUCTL_ALIAS @ PFalconBase[0x00000130] { - 1:1 startcpu as bool; +nv_reg!(NV_PFALCON_FALCON_CPUCTL_ALIAS @ PFalconBase[0x00000130] { + 1:1 startcpu =3D> bool; }); =20 // Actually known as `NV_PSEC_FALCON_ENGINE` and `NV_PGSP_FALCON_ENGINE` d= epending on the falcon // instance. -register!(NV_PFALCON_FALCON_ENGINE @ PFalconBase[0x000003c0] { - 0:0 reset as bool; +nv_reg!(NV_PFALCON_FALCON_ENGINE @ PFalconBase[0x000003c0] { + 0:0 reset =3D> bool; }); =20 -register!(NV_PFALCON_FBIF_TRANSCFG @ PFalconBase[0x00000600[8]] { - 1:0 target as u8 ?=3D> FalconFbifTarget; - 2:2 mem_type as bool =3D> FalconFbifMemType; +nv_reg!(NV_PFALCON_FBIF_TRANSCFG @ PFalconBase[0x00000600[8]] { + 1:0 target ?=3D> FalconFbifTarget; + 2:2 mem_type =3D> FalconFbifMemType; }); =20 -register!(NV_PFALCON_FBIF_CTL @ PFalconBase[0x00000624] { - 7:7 allow_phys_no_ctx as bool; +nv_reg!(NV_PFALCON_FBIF_CTL @ PFalconBase[0x00000624] { + 7:7 allow_phys_no_ctx =3D> bool; }); =20 /* PFALCON2 */ =20 -register!(NV_PFALCON2_FALCON_MOD_SEL @ PFalcon2Base[0x00000180] { - 7:0 algo as u8 ?=3D> FalconModSelAlgo; +nv_reg!(NV_PFALCON2_FALCON_MOD_SEL @ PFalcon2Base[0x00000180] { + 7:0 algo ?=3D> FalconModSelAlgo; }); =20 -register!(NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID @ PFalcon2Base[0x00000198]= { - 7:0 ucode_id as u8; +nv_reg!(NV_PFALCON2_FALCON_BROM_CURR_UCODE_ID @ PFalcon2Base[0x00000198] { + 7:0 ucode_id =3D> u8; }); =20 -register!(NV_PFALCON2_FALCON_BROM_ENGIDMASK @ PFalcon2Base[0x0000019c] { - 31:0 value as u32; +nv_reg!(NV_PFALCON2_FALCON_BROM_ENGIDMASK @ PFalcon2Base[0x0000019c] { + 31:0 value =3D> u32; }); =20 // OpenRM defines this as a register array, but doesn't specify its size a= nd only uses its first // element. Be conservative until we know the actual size or need to use m= ore registers. -register!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalcon2Base[0x00000210[1]] { - 31:0 value as u32; +nv_reg!(NV_PFALCON2_FALCON_BROM_PARAADDR @ PFalcon2Base[0x00000210[1]] { + 31:0 value =3D> u32; }); =20 // PRISCV =20 -register!(NV_PRISCV_RISCV_CPUCTL @ PFalcon2Base[0x00000388] { - 0:0 halted as bool; - 7:7 active_stat as bool; +nv_reg!(NV_PRISCV_RISCV_CPUCTL @ PFalcon2Base[0x00000388] { + 0:0 halted =3D> bool; + 7:7 active_stat =3D> bool; }); =20 -register!(NV_PRISCV_RISCV_BCR_CTRL @ PFalcon2Base[0x00000668] { - 0:0 valid as bool; - 4:4 core_select as bool =3D> PeregrineCoreSelect; - 8:8 br_fetch as bool; +nv_reg!(NV_PRISCV_RISCV_BCR_CTRL @ PFalconBase[0x00001668] { + 0:0 valid =3D> bool; + 4:4 core_select =3D> PeregrineCoreSelect; + 8:8 br_fetch =3D> bool; }); =20 // The modules below provide registers that are not identical on all suppo= rted chips. They should @@ -397,15 +398,15 @@ pub(crate) fn mem_scrubbing_done(self) -> bool { pub(crate) mod gm107 { // FUSE =20 - register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 { - 0:0 display_disabled as bool; + nv_reg!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00021c04 { + 0:0 display_disabled =3D> bool; }); } =20 pub(crate) mod ga100 { // FUSE =20 - register!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 { - 0:0 display_disabled as bool; + nv_reg!(NV_FUSE_STATUS_OPT_DISPLAY @ 0x00820c04 { + 0:0 display_disabled =3D> bool; }); } diff --git a/drivers/gpu/nova-core/regs/macros.rs b/drivers/gpu/nova-core/r= egs/macros.rs deleted file mode 100644 index fd1a815fa57d..000000000000 --- a/drivers/gpu/nova-core/regs/macros.rs +++ /dev/null @@ -1,721 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -//! `register!` macro to define register layout and accessors. -//! -//! A single register typically includes several fields, which are accesse= d through a combination -//! of bit-shift and mask operations that introduce a class of potential m= istakes, notably because -//! not all possible field values are necessarily valid. -//! -//! The `register!` macro in this module provides an intuitive and readabl= e syntax for defining a -//! dedicated type for each register. Each such type comes with its own fi= eld accessors that can -//! return an error if a field's value is invalid. Please look at the [`bi= tfield`] macro for the -//! complete syntax of fields definitions. - -/// Trait providing a base address to be added to the offset of a relative= register to obtain -/// its actual offset. -/// -/// The `T` generic argument is used to distinguish which base to use, in = case a type provides -/// several bases. It is given to the `register!` macro to restrict the us= e of the register to -/// implementors of this particular variant. -pub(crate) trait RegisterBase { - const BASE: usize; -} - -/// Defines a dedicated type for a register with an absolute offset, inclu= ding getter and setter -/// methods for its fields and methods to read and write it from an `Io` r= egion. -/// -/// Example: -/// -/// ```no_run -/// register!(BOOT_0 @ 0x00000100, "Basic revision information about the G= PU" { -/// 3:0 minor_revision as u8, "Minor revision of the chip"; -/// 7:4 major_revision as u8, "Major revision of the chip"; -/// 28:20 chipset as u32 ?=3D> Chipset, "Chipset model"; -/// }); -/// ``` -/// -/// This defines a `BOOT_0` type which can be read or written from offset = `0x100` of an `Io` -/// region. It is composed of 3 fields, for instance `minor_revision` is m= ade of the 4 least -/// significant bits of the register. Each field can be accessed and modif= ied using accessor -/// methods: -/// -/// ```no_run -/// // Read from the register's defined offset (0x100). -/// let boot0 =3D BOOT_0::read(&bar); -/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_r= evision()); -/// -/// // `Chipset::try_from` is called with the value of the `chipset` field= and returns an -/// // error if it is invalid. -/// let chipset =3D boot0.chipset()?; -/// -/// // Update some fields and write the value back. -/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar); -/// -/// // Or, just read and update the register in a single step: -/// BOOT_0::update(&bar, |r| r.set_major_revision(3).set_minor_revision(10= )); -/// ``` -/// -/// The documentation strings are optional. If present, they will be added= to the type's -/// definition, or the field getter and setter methods they are attached t= o. -/// -/// It is also possible to create a alias register by using the `=3D> ALIA= S` syntax. This is useful -/// for cases where a register's interpretation depends on the context: -/// -/// ```no_run -/// register!(SCRATCH @ 0x00000200, "Scratch register" { -/// 31:0 value as u32, "Raw value"; -/// }); -/// -/// register!(SCRATCH_BOOT_STATUS =3D> SCRATCH, "Boot status of the firmwa= re" { -/// 0:0 completed as bool, "Whether the firmware has completed boo= ting"; -/// }); -/// ``` -/// -/// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as = `SCRATCH`, while also -/// providing its own `completed` field. -/// -/// ## Relative registers -/// -/// A register can be defined as being accessible from a fixed offset of a= provided base. For -/// instance, imagine the following I/O space: -/// -/// ```text -/// +-----------------------------+ -/// | ... | -/// | | -/// 0x100--->+------------CPU0-------------+ -/// | | -/// 0x110--->+-----------------------------+ -/// | CPU_CTL | -/// +-----------------------------+ -/// | ... | -/// | | -/// | | -/// 0x200--->+------------CPU1-------------+ -/// | | -/// 0x210--->+-----------------------------+ -/// | CPU_CTL | -/// +-----------------------------+ -/// | ... | -/// +-----------------------------+ -/// ``` -/// -/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset= `0x10` of their I/O -/// space segment. Since both instances of `CPU_CTL` share the same layout= , we don't want to define -/// them twice and would prefer a way to select which one to use from a si= ngle definition -/// -/// This can be done using the `Base[Offset]` syntax when specifying the r= egister's address. -/// -/// `Base` is an arbitrary type (typically a ZST) to be used as a generic = parameter of the -/// [`RegisterBase`] trait to provide the base as a constant, i.e. each ty= pe providing a base for -/// this register needs to implement `RegisterBase`. Here is the abo= ve example translated -/// into code: -/// -/// ```no_run -/// // Type used to identify the base. -/// pub(crate) struct CpuCtlBase; -/// -/// // ZST describing `CPU0`. -/// struct Cpu0; -/// impl RegisterBase for Cpu0 { -/// const BASE: usize =3D 0x100; -/// } -/// // Singleton of `CPU0` used to identify it. -/// const CPU0: Cpu0 =3D Cpu0; -/// -/// // ZST describing `CPU1`. -/// struct Cpu1; -/// impl RegisterBase for Cpu1 { -/// const BASE: usize =3D 0x200; -/// } -/// // Singleton of `CPU1` used to identify it. -/// const CPU1: Cpu1 =3D Cpu1; -/// -/// // This makes `CPU_CTL` accessible from all implementors of `RegisterB= ase`. -/// register!(CPU_CTL @ CpuCtlBase[0x10], "CPU core control" { -/// 0:0 start as bool, "Start the CPU core"; -/// }); -/// -/// // The `read`, `write` and `update` methods of relative registers take= an extra `base` argument -/// // that is used to resolve its final address by adding its `BASE` to t= he offset of the -/// // register. -/// -/// // Start `CPU0`. -/// CPU_CTL::update(bar, &CPU0, |r| r.set_start(true)); -/// -/// // Start `CPU1`. -/// CPU_CTL::update(bar, &CPU1, |r| r.set_start(true)); -/// -/// // Aliases can also be defined for relative register. -/// register!(CPU_CTL_ALIAS =3D> CpuCtlBase[CPU_CTL], "Alias to CPU core c= ontrol" { -/// 1:1 alias_start as bool, "Start the aliased CPU core"; -/// }); -/// -/// // Start the aliased `CPU0`. -/// CPU_CTL_ALIAS::update(bar, &CPU0, |r| r.set_alias_start(true)); -/// ``` -/// -/// ## Arrays of registers -/// -/// Some I/O areas contain consecutive values that can be interpreted in t= he same way. These areas -/// can be defined as an array of identical registers, allowing them to be= accessed by index with -/// compile-time or runtime bound checking. Simply define their address as= `Address[Size]`, and add -/// an `idx` parameter to their `read`, `write` and `update` methods: -/// -/// ```no_run -/// # fn no_run() -> Result<(), Error> { -/// # fn get_scratch_idx() -> usize { -/// # 0x15 -/// # } -/// // Array of 64 consecutive registers with the same layout starting at = offset `0x80`. -/// register!(SCRATCH @ 0x00000080[64], "Scratch registers" { -/// 31:0 value as u32; -/// }); -/// -/// // Read scratch register 0, i.e. I/O address `0x80`. -/// let scratch_0 =3D SCRATCH::read(bar, 0).value(); -/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`. -/// let scratch_15 =3D SCRATCH::read(bar, 15).value(); -/// -/// // This is out of bounds and won't build. -/// // let scratch_128 =3D SCRATCH::read(bar, 128).value(); -/// -/// // Runtime-obtained array index. -/// let scratch_idx =3D get_scratch_idx(); -/// // Access on a runtime index returns an error if it is out-of-bounds. -/// let some_scratch =3D SCRATCH::try_read(bar, scratch_idx)?.value(); -/// -/// // Alias to a particular register in an array. -/// // Here `SCRATCH[8]` is used to convey the firmware exit code. -/// register!(FIRMWARE_STATUS =3D> SCRATCH[8], "Firmware exit status code"= { -/// 7:0 status as u8; -/// }); -/// -/// let status =3D FIRMWARE_STATUS::read(bar).status(); -/// -/// // Non-contiguous register arrays can be defined by adding a stride pa= rameter. -/// // Here, each of the 16 registers of the array are separated by 8 byte= s, meaning that the -/// // registers of the two declarations below are interleaved. -/// register!(SCRATCH_INTERLEAVED_0 @ 0x000000c0[16 ; 8], "Scratch registe= rs bank 0" { -/// 31:0 value as u32; -/// }); -/// register!(SCRATCH_INTERLEAVED_1 @ 0x000000c4[16 ; 8], "Scratch registe= rs bank 1" { -/// 31:0 value as u32; -/// }); -/// # Ok(()) -/// # } -/// ``` -/// -/// ## Relative arrays of registers -/// -/// Combining the two features described in the sections above, arrays of = registers accessible from -/// a base can also be defined: -/// -/// ```no_run -/// # fn no_run() -> Result<(), Error> { -/// # fn get_scratch_idx() -> usize { -/// # 0x15 -/// # } -/// // Type used as parameter of `RegisterBase` to specify the base. -/// pub(crate) struct CpuCtlBase; -/// -/// // ZST describing `CPU0`. -/// struct Cpu0; -/// impl RegisterBase for Cpu0 { -/// const BASE: usize =3D 0x100; -/// } -/// // Singleton of `CPU0` used to identify it. -/// const CPU0: Cpu0 =3D Cpu0; -/// -/// // ZST describing `CPU1`. -/// struct Cpu1; -/// impl RegisterBase for Cpu1 { -/// const BASE: usize =3D 0x200; -/// } -/// // Singleton of `CPU1` used to identify it. -/// const CPU1: Cpu1 =3D Cpu1; -/// -/// // 64 per-cpu scratch registers, arranged as an contiguous array. -/// register!(CPU_SCRATCH @ CpuCtlBase[0x00000080[64]], "Per-CPU scratch r= egisters" { -/// 31:0 value as u32; -/// }); -/// -/// let cpu0_scratch_0 =3D CPU_SCRATCH::read(bar, &Cpu0, 0).value(); -/// let cpu1_scratch_15 =3D CPU_SCRATCH::read(bar, &Cpu1, 15).value(); -/// -/// // This won't build. -/// // let cpu0_scratch_128 =3D CPU_SCRATCH::read(bar, &Cpu0, 128).value(); -/// -/// // Runtime-obtained array index. -/// let scratch_idx =3D get_scratch_idx(); -/// // Access on a runtime value returns an error if it is out-of-bounds. -/// let cpu0_some_scratch =3D CPU_SCRATCH::try_read(bar, &Cpu0, scratch_id= x)?.value(); -/// -/// // `SCRATCH[8]` is used to convey the firmware exit code. -/// register!(CPU_FIRMWARE_STATUS =3D> CpuCtlBase[CPU_SCRATCH[8]], -/// "Per-CPU firmware exit status code" { -/// 7:0 status as u8; -/// }); -/// -/// let cpu0_status =3D CPU_FIRMWARE_STATUS::read(bar, &Cpu0).status(); -/// -/// // Non-contiguous register arrays can be defined by adding a stride pa= rameter. -/// // Here, each of the 16 registers of the array are separated by 8 byte= s, meaning that the -/// // registers of the two declarations below are interleaved. -/// register!(CPU_SCRATCH_INTERLEAVED_0 @ CpuCtlBase[0x00000d00[16 ; 8]], -/// "Scratch registers bank 0" { -/// 31:0 value as u32; -/// }); -/// register!(CPU_SCRATCH_INTERLEAVED_1 @ CpuCtlBase[0x00000d04[16 ; 8]], -/// "Scratch registers bank 1" { -/// 31:0 value as u32; -/// }); -/// # Ok(()) -/// # } -/// ``` -macro_rules! register { - // Creates a register at a fixed offset of the MMIO space. - ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)*= } ) =3D> { - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)= * } ); - register!(@io_fixed $name @ $offset); - }; - - // Creates an alias register of fixed offset register `alias` with its= own fields. - ($name:ident =3D> $alias:ident $(, $comment:literal)? { $($fields:tt)*= } ) =3D> { - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)= * } ); - register!(@io_fixed $name @ $alias::OFFSET); - }; - - // Creates a register at a relative offset from a base address provide= r. - ($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $= ($fields:tt)* } ) =3D> { - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)= * } ); - register!(@io_relative $name @ $base [ $offset ]); - }; - - // Creates an alias register of relative offset register `alias` with = its own fields. - ($name:ident =3D> $base:ty [ $alias:ident ] $(, $comment:literal)? { $= ($fields:tt)* }) =3D> { - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)= * } ); - register!(@io_relative $name @ $base [ $alias::OFFSET ]); - }; - - // Creates an array of registers at a fixed offset of the MMIO space. - ( - $name:ident @ $offset:literal [ $size:expr ; $stride:expr ] $(, $c= omment:literal)? { - $($fields:tt)* - } - ) =3D> { - static_assert!(::core::mem::size_of::() <=3D $stride); - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)= * } ); - register!(@io_array $name @ $offset [ $size ; $stride ]); - }; - - // Shortcut for contiguous array of registers (stride =3D=3D size of e= lement). - ( - $name:ident @ $offset:literal [ $size:expr ] $(, $comment:literal)= ? { - $($fields:tt)* - } - ) =3D> { - register!($name @ $offset [ $size ; ::core::mem::size_of::() = ] $(, $comment)? { - $($fields)* - } ); - }; - - // Creates an array of registers at a relative offset from a base addr= ess provider. - ( - $name:ident @ $base:ty [ $offset:literal [ $size:expr ; $stride:ex= pr ] ] - $(, $comment:literal)? { $($fields:tt)* } - ) =3D> { - static_assert!(::core::mem::size_of::() <=3D $stride); - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)= * } ); - register!(@io_relative_array $name @ $base [ $offset [ $size ; $st= ride ] ]); - }; - - // Shortcut for contiguous array of relative registers (stride =3D=3D = size of element). - ( - $name:ident @ $base:ty [ $offset:literal [ $size:expr ] ] $(, $com= ment:literal)? { - $($fields:tt)* - } - ) =3D> { - register!($name @ $base [ $offset [ $size ; ::core::mem::size_of::= () ] ] - $(, $comment)? { $($fields)* } ); - }; - - // Creates an alias of register `idx` of relative array of registers `= alias` with its own - // fields. - ( - $name:ident =3D> $base:ty [ $alias:ident [ $idx:expr ] ] $(, $comm= ent:literal)? { - $($fields:tt)* - } - ) =3D> { - static_assert!($idx < $alias::SIZE); - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)= * } ); - register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $al= ias::STRIDE ] ); - }; - - // Creates an alias of register `idx` of array of registers `alias` wi= th its own fields. - // This rule belongs to the (non-relative) register arrays set, but ne= eds to be put last - // to avoid it being interpreted in place of the relative register arr= ay alias rule. - ($name:ident =3D> $alias:ident [ $idx:expr ] $(, $comment:literal)? { = $($fields:tt)* }) =3D> { - static_assert!($idx < $alias::SIZE); - bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)= * } ); - register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE= ); - }; - - // Generates the IO accessors for a fixed offset register. - (@io_fixed $name:ident @ $offset:expr) =3D> { - #[allow(dead_code)] - impl $name { - pub(crate) const OFFSET: usize =3D $offset; - - /// Read the register from its address in `io`. - #[inline(always)] - pub(crate) fn read(io: &T) -> Self where - T: ::core::ops::Deref>, - { - Self(io.read32($offset)) - } - - /// Write the value contained in `self` to the register addres= s in `io`. - #[inline(always)] - pub(crate) fn write(self, io: &T) where - T: ::core::ops::Deref>, - { - io.write32(self.0, $offset) - } - - /// Read the register from its address in `io` and run `f` on = its value to obtain a new - /// value to write back. - #[inline(always)] - pub(crate) fn update( - io: &T, - f: F, - ) where - T: ::core::ops::Deref>, - F: ::core::ops::FnOnce(Self) -> Self, - { - let reg =3D f(Self::read(io)); - reg.write(io); - } - } - }; - - // Generates the IO accessors for a relative offset register. - (@io_relative $name:ident @ $base:ty [ $offset:expr ]) =3D> { - #[allow(dead_code)] - impl $name { - pub(crate) const OFFSET: usize =3D $offset; - - /// Read the register from `io`, using the base address provid= ed by `base` and adding - /// the register's offset to it. - #[inline(always)] - pub(crate) fn read( - io: &T, - #[allow(unused_variables)] - base: &B, - ) -> Self where - T: ::core::ops::Deref>, - B: crate::regs::macros::RegisterBase<$base>, - { - const OFFSET: usize =3D $name::OFFSET; - - let value =3D io.read32( - >::BASE = + OFFSET - ); - - Self(value) - } - - /// Write the value contained in `self` to `io`, using the bas= e address provided by - /// `base` and adding the register's offset to it. - #[inline(always)] - pub(crate) fn write( - self, - io: &T, - #[allow(unused_variables)] - base: &B, - ) where - T: ::core::ops::Deref>, - B: crate::regs::macros::RegisterBase<$base>, - { - const OFFSET: usize =3D $name::OFFSET; - - io.write32( - self.0, - >::BASE = + OFFSET - ); - } - - /// Read the register from `io`, using the base address provid= ed by `base` and adding - /// the register's offset to it, then run `f` on its value to = obtain a new value to - /// write back. - #[inline(always)] - pub(crate) fn update( - io: &T, - base: &B, - f: F, - ) where - T: ::core::ops::Deref>, - B: crate::regs::macros::RegisterBase<$base>, - F: ::core::ops::FnOnce(Self) -> Self, - { - let reg =3D f(Self::read(io, base)); - reg.write(io, base); - } - } - }; - - // Generates the IO accessors for an array of registers. - (@io_array $name:ident @ $offset:literal [ $size:expr ; $stride:expr ]= ) =3D> { - #[allow(dead_code)] - impl $name { - pub(crate) const OFFSET: usize =3D $offset; - pub(crate) const SIZE: usize =3D $size; - pub(crate) const STRIDE: usize =3D $stride; - - /// Read the array register at index `idx` from its address in= `io`. - #[inline(always)] - pub(crate) fn read( - io: &T, - idx: usize, - ) -> Self where - T: ::core::ops::Deref>, - { - build_assert!(idx < Self::SIZE); - - let offset =3D Self::OFFSET + (idx * Self::STRIDE); - let value =3D io.read32(offset); - - Self(value) - } - - /// Write the value contained in `self` to the array register = with index `idx` in `io`. - #[inline(always)] - pub(crate) fn write( - self, - io: &T, - idx: usize - ) where - T: ::core::ops::Deref>, - { - build_assert!(idx < Self::SIZE); - - let offset =3D Self::OFFSET + (idx * Self::STRIDE); - - io.write32(self.0, offset); - } - - /// Read the array register at index `idx` in `io` and run `f`= on its value to obtain a - /// new value to write back. - #[inline(always)] - pub(crate) fn update( - io: &T, - idx: usize, - f: F, - ) where - T: ::core::ops::Deref>, - F: ::core::ops::FnOnce(Self) -> Self, - { - let reg =3D f(Self::read(io, idx)); - reg.write(io, idx); - } - - /// Read the array register at index `idx` from its address in= `io`. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_read( - io: &T, - idx: usize, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, - { - if idx < Self::SIZE { - Ok(Self::read(io, idx)) - } else { - Err(EINVAL) - } - } - - /// Write the value contained in `self` to the array register = with index `idx` in `io`. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_write( - self, - io: &T, - idx: usize, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, - { - if idx < Self::SIZE { - Ok(self.write(io, idx)) - } else { - Err(EINVAL) - } - } - - /// Read the array register at index `idx` in `io` and run `f`= on its value to obtain a - /// new value to write back. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_update( - io: &T, - idx: usize, - f: F, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, - F: ::core::ops::FnOnce(Self) -> Self, - { - if idx < Self::SIZE { - Ok(Self::update(io, idx, f)) - } else { - Err(EINVAL) - } - } - } - }; - - // Generates the IO accessors for an array of relative registers. - ( - @io_relative_array $name:ident @ $base:ty - [ $offset:literal [ $size:expr ; $stride:expr ] ] - ) =3D> { - #[allow(dead_code)] - impl $name { - pub(crate) const OFFSET: usize =3D $offset; - pub(crate) const SIZE: usize =3D $size; - pub(crate) const STRIDE: usize =3D $stride; - - /// Read the array register at index `idx` from `io`, using th= e base address provided - /// by `base` and adding the register's offset to it. - #[inline(always)] - pub(crate) fn read( - io: &T, - #[allow(unused_variables)] - base: &B, - idx: usize, - ) -> Self where - T: ::core::ops::Deref>, - B: crate::regs::macros::RegisterBase<$base>, - { - build_assert!(idx < Self::SIZE); - - let offset =3D >::BASE + - Self::OFFSET + (idx * Self::STRIDE); - let value =3D io.read32(offset); - - Self(value) - } - - /// Write the value contained in `self` to `io`, using the bas= e address provided by - /// `base` and adding the offset of array register `idx` to it. - #[inline(always)] - pub(crate) fn write( - self, - io: &T, - #[allow(unused_variables)] - base: &B, - idx: usize - ) where - T: ::core::ops::Deref>, - B: crate::regs::macros::RegisterBase<$base>, - { - build_assert!(idx < Self::SIZE); - - let offset =3D >::BASE + - Self::OFFSET + (idx * Self::STRIDE); - - io.write32(self.0, offset); - } - - /// Read the array register at index `idx` from `io`, using th= e base address provided - /// by `base` and adding the register's offset to it, then run= `f` on its value to - /// obtain a new value to write back. - #[inline(always)] - pub(crate) fn update( - io: &T, - base: &B, - idx: usize, - f: F, - ) where - T: ::core::ops::Deref>, - B: crate::regs::macros::RegisterBase<$base>, - F: ::core::ops::FnOnce(Self) -> Self, - { - let reg =3D f(Self::read(io, base, idx)); - reg.write(io, base, idx); - } - - /// Read the array register at index `idx` from `io`, using th= e base address provided - /// by `base` and adding the register's offset to it. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_read( - io: &T, - base: &B, - idx: usize, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, - B: crate::regs::macros::RegisterBase<$base>, - { - if idx < Self::SIZE { - Ok(Self::read(io, base, idx)) - } else { - Err(EINVAL) - } - } - - /// Write the value contained in `self` to `io`, using the bas= e address provided by - /// `base` and adding the offset of array register `idx` to it. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_write( - self, - io: &T, - base: &B, - idx: usize, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, - B: crate::regs::macros::RegisterBase<$base>, - { - if idx < Self::SIZE { - Ok(self.write(io, base, idx)) - } else { - Err(EINVAL) - } - } - - /// Read the array register at index `idx` from `io`, using th= e base address provided - /// by `base` and adding the register's offset to it, then run= `f` on its value to - /// obtain a new value to write back. - /// - /// The validity of `idx` is checked at run-time, and `EINVAL`= is returned is the - /// access was out-of-bounds. - #[inline(always)] - pub(crate) fn try_update( - io: &T, - base: &B, - idx: usize, - f: F, - ) -> ::kernel::error::Result where - T: ::core::ops::Deref>, - B: crate::regs::macros::RegisterBase<$base>, - F: ::core::ops::FnOnce(Self) -> Self, - { - if idx < Self::SIZE { - Ok(Self::update(io, base, idx, f)) - } else { - Err(EINVAL) - } - } - } - }; -} --=20 2.52.0