[PATCH v3 17/19] Documentation: ioctl: add CMH ioctl documentation and register 'J'

Saravanakrishnan Krishnamoorthy <[email protected]>
Newsgroups gmane.linux.ports.riscv,gmane.linux.drivers.devicetree,gmane.linux.kernel.api,gmane.linux.kernel.cryptoapi,gmane.linux.documentation,gmane.linux.kernel
Message-ID <[email protected]>
From: Alex Ousherovitch <[email protected]>

Add Documentation/userspace-api/ioctl/cmh_mgmt.rst documenting the
ioctl commands on the /dev/cmh_mgmt misc device for the Rambus
CryptoManager Hub (CMH) hardware crypto accelerator driver.  Covers
key management, KIC key derivation, PKE (RSA, ECDSA, ECDH, EdDSA),
PQC (ML-KEM, ML-DSA, SLH-DSA), SM2, EAC, and DRBG.  Link the page
into the userspace-api/ioctl index toctree.

Register ioctl magic number 'J' (0x4A) in ioctl-number.rst.  The
driver uses ioctls 0x01-0x40.

Co-developed-by: Saravanakrishnan Krishnamoorthy <[email protected]>
Signed-off-by: Saravanakrishnan Krishnamoorthy <[email protected]>
Signed-off-by: Alex Ousherovitch <[email protected]>
Reviewed-by: Joel Wittenauer <[email protected]>
Reviewed-by: Thi Nguyen <[email protected]>
---
 .../userspace-api/ioctl/cmh_mgmt.rst          | 1295 +++++++++++++++++
 Documentation/userspace-api/ioctl/index.rst   |    1 +
 .../userspace-api/ioctl/ioctl-number.rst      |    1 +
 3 files changed, 1297 insertions(+)
 create mode 100644 Documentation/userspace-api/ioctl/cmh_mgmt.rst

diff --git a/Documentation/userspace-api/ioctl/cmh_mgmt.rst b/Documentation/userspace-api/ioctl/cmh_mgmt.rst
new file mode 100644
index 000000000000..d17a77975df6
--- /dev/null
+++ b/Documentation/userspace-api/ioctl/cmh_mgmt.rst
@@ -0,0 +1,1295 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=============================================
+CMH Key Management ioctl Interface (cmh_mgmt)
+=============================================
+
+:Author: Cryptography Research, Inc. (CRI)
+:Maintainer: [email protected]
+
+Introduction
+============
+
+The ``/dev/cmh_mgmt`` character device provides user-space access to key
+management, key derivation, public-key, and post-quantum cryptographic
+operations on the CryptoManager Hub (CMH) hardware accelerator.
+
+The device is created by the ``cmh`` kernel module as a ``misc_device``.
+All operations are synchronous -- the ioctl blocks until the hardware
+completes.  Opening the device requires ``CAP_SYS_ADMIN``.
+
+All ioctl argument structures are versioned: user space sets the
+``version`` field to ``CMH_MGMT_V1`` (currently 1).  This allows the
+driver to extend structures in the future without breaking the ABI.
+
+Data types and ioctl numbers are defined in
+``<uapi/linux/cmh_mgmt_ioctl.h>``.  The ioctl type letter is ``'J'``
+(0x4A).
+
+Error Handling
+==============
+
+Unless otherwise noted, all ioctls return 0 on success and a negative
+errno on failure.  Common error codes:
+
+========== =============================================================
+``EINVAL`` Invalid ``version`` field, unsupported parameter, or
+           out-of-range length.
+``EFAULT`` Failed to copy data to/from user space.
+``ENOMEM`` Kernel memory allocation failed.
+``EIO``    Hardware returned an error (eSW command failure).
+``ENOENT`` Key not found (``KEY_FIND``, ``KEY_LIST``).
+========== =============================================================
+
+Datastore Concepts
+==================
+
+The CMH hardware maintains an embedded datastore managed by the eSW
+firmware.  Objects in the datastore are identified by a 64-bit reference
+(``ref``) and optionally by a 64-bit Content ID (``cid``).
+
+Two storage classes exist:
+
+**Temporary (SYS_REF_TEMP)**
+  Lifetime is scoped to a single mailbox slot.  The eSW firmware
+  reclaims the object when the slot is reused.  Used for raw-key
+  provisioning via ``KEY_NEW`` + ``KEY_WRITE``.
+
+**Persistent (SYS_REF_PERSIST)**
+  Survives across mailbox slots.  Requires explicit deletion via
+  ``KEY_DELETE``.  Identified by CID; resolved to a per-mailbox ref
+  via ``KEY_FIND``.
+
+Mailbox Dispatch
+================
+
+All ``/dev/cmh_mgmt`` ioctls are submitted on a single management
+mailbox.  This is a structural requirement of the eSW datastore model,
+not a tunable:
+
+* Datastore access control is **per-mailbox**.  ``KEY_NEW`` grants the
+  creating mailbox read/write/execute access; other mailboxes have none
+  until granted.  The returned 64-bit ``ref`` encodes a randomised
+  offset and does **not** carry the owning mailbox, so an operation that
+  receives only a ``ref`` (``KEY_GRANT``, ``KEY_READ``, ``KEY_DELETE``,
+  ``DS_EXPORT``) cannot itself determine which mailbox owns the object.
+  Using one fixed management mailbox guarantees that a key's create,
+  modify, grant, read and hardware-held-key compute steps all share the
+  mailbox that holds its access rights, without exposing mailbox
+  identity in the UABI.  User space may still widen a key's access to
+  additional mailboxes via ``KEY_GRANT``.
+
+* The eSW ``SYS_REF_TEMP`` scratch store is per-mailbox and persists
+  across ioctl calls, so a multi-step flow that derives into
+  ``SYS_REF_TEMP`` (for example a ``KIC_*`` derivation) and later
+  consumes it (``DS_EXPORT`` with ``wrap_key = SYS_REF_TEMP``) requires
+  both calls to use the same mailbox.
+
+Per-mailbox ``rambus,cores`` device-tree affinity applies to the *stateless*
+in-kernel crypto API path, which carries no datastore state between
+requests and is balanced across mailboxes by the driver.
+
+Key Types
+=========
+
+The ``ds_type`` field in ``KEY_NEW`` and ``KEY_WRITE`` selects the
+datastore object type.  Values are defined as ``CMH_DS_*`` constants:
+
+=================================  =====  ==============================
+Constant                           Value  Description
+=================================  =====  ==============================
+``CMH_DS_RAW_VALUE``               1      Raw byte array
+``CMH_DS_AES_KEY``                 2      AES key (128/192/256-bit)
+``CMH_DS_AES_XTS_KEY``             3      AES-XTS key (256/512-bit)
+``CMH_DS_HMAC_KEY``                4      HMAC key
+``CMH_DS_KMAC_KEY``                5      KMAC key
+``CMH_DS_SM4_KEY``                 6      SM4 key (128-bit)
+``CMH_DS_CHACHA20_KEY``            7      ChaCha20 key (256-bit)
+``CMH_DS_RSA_PRIV_KEY``            10     RSA private key
+``CMH_DS_RSA_PUB_KEY``             11     RSA public key
+``CMH_DS_RSA_CRT_KEY``             12     RSA CRT private key
+``CMH_DS_ECDSA_PRIV_KEY``          13     ECDSA private key
+``CMH_DS_ECDSA_PUB_KEY``           14     ECDSA public key
+``CMH_DS_ECDH_PRIV_KEY``           15     ECDH private key
+``CMH_DS_EDDSA_PRIV_KEY``          16     EdDSA private key
+``CMH_DS_SHARED_SECRET``           17     Shared secret
+``CMH_DS_SM2_PRIV_KEY``            18     SM2 private key
+``CMH_DS_ML_KEM_DK``               20     ML-KEM decapsulation key
+``CMH_DS_ML_DSA_SK``               21     ML-DSA secret key
+``CMH_DS_SLHDSA_SK``               25     SLH-DSA secret key
+=================================  =====  ==============================
+
+Key Flags
+=========
+
+The ``flags`` field in ``KEY_NEW`` and ``KEY_WRITE`` is a bitmask:
+
+==================  ===========  ========================================
+Flag                Bit          Description
+==================  ===========  ========================================
+``CMH_FLAG_PT``     16           Key can be read as plaintext
+``CMH_FLAG_XC``     17           Key can be exported over XC bus
+``CMH_FLAG_SCA``    18           SCA key stored in 2 shares
+==================  ===========  ========================================
+
+Elliptic Curve IDs
+==================
+
+Curve identifiers for PKE operations (``curve`` field):
+
+==========================  =====
+Constant                    Value
+==========================  =====
+``CMH_CURVE_P192``          0x01
+``CMH_CURVE_P224``          0x02
+``CMH_CURVE_P256``          0x03
+``CMH_CURVE_P384``          0x04
+``CMH_CURVE_P521``          0x05
+``CMH_CURVE_SECP256K1``     0x07
+``CMH_CURVE_BP192R1``       0x11
+``CMH_CURVE_BP224R1``       0x12
+``CMH_CURVE_BP256R1``       0x13
+``CMH_CURVE_BP320R1``       0x14
+``CMH_CURVE_BP384R1``       0x15
+``CMH_CURVE_BP512R1``       0x16
+``CMH_CURVE_SM2``           0x18
+``CMH_CURVE_25519``         0x21
+``CMH_CURVE_448``           0x22
+==========================  =====
+
+Key Management ioctls
+=====================
+
+CMH_IOCTL_KEY_NEW
+-----------------
+
+Create a new empty datastore object.
+
+:Direction: ``_IOWR``
+:Number: 0x01
+:Argument: ``struct cmh_ioctl_key_new``
+
+::
+
+  struct cmh_ioctl_key_new {
+      __u32 version;     /* must be CMH_MGMT_V1 */
+      __u32 ds_type;     /* CMH_DS_* key type */
+      __u32 len;         /* key length in bytes */
+      __u32 flags;       /* CMH_FLAG_* */
+      __u64 cid;         /* caller ID (name) for the key */
+      __u64 ref;         /* [out] key reference */
+  };
+
+The returned ``ref`` is used in subsequent ``KEY_WRITE``, ``KEY_READ``,
+and crypto operation ioctls.
+
+CMH_IOCTL_KEY_NEW_RANDOM
+------------------------
+
+Create a new datastore object filled with hardware-generated random data.
+
+:Direction: ``_IOWR``
+:Number: 0x0B
+:Argument: ``struct cmh_ioctl_key_new``
+
+Same structure as ``KEY_NEW``.  The hardware DRBG fills the object with
+``len`` random bytes.
+
+CMH_IOCTL_KEY_WRITE
+-------------------
+
+Write key material into a previously created datastore object.
+
+:Direction: ``_IOW``
+:Number: 0x02
+:Argument: ``struct cmh_ioctl_key_write``
+
+::
+
+  struct cmh_ioctl_key_write {
+      __u32 version;
+      __u32 len;         /* key data length */
+      __u32 ds_type;     /* CMH_DS_* key type */
+      __u32 flags;       /* CMH_FLAG_* */
+      __u64 ref;         /* key reference from KEY_NEW */
+      __u64 wrap_key;    /* wrapping key ref (CMH_REF_NONE = plaintext) */
+      __u64 data;        /* user-space pointer to key material */
+  };
+
+If ``wrap_key`` is ``CMH_REF_NONE`` (0), key material is written in
+plaintext.  Otherwise, the data is unwrapped using the specified
+wrapping key.
+
+CMH_IOCTL_KEY_READ
+------------------
+
+Read key material from a datastore object.
+
+:Direction: ``_IOWR``
+:Number: 0x03
+:Argument: ``struct cmh_ioctl_key_read``
+
+::
+
+  struct cmh_ioctl_key_read {
+      __u32 version;
+      __u32 len;         /* buffer length */
+      __u64 ref;         /* key reference */
+      __u64 wrap_key;    /* wrapping key ref (CMH_REF_NONE = plaintext) */
+      __u64 data;        /* user-space pointer to output buffer */
+      __u32 out_len;     /* [out] actual bytes written */
+      __u32 __reserved;
+  };
+
+Plaintext reads require the ``CMH_FLAG_PT`` attribute on the key.
+The eSW prepends a 16-byte header (``CMH_SYS_WRAP_HDR_SIZE``) even
+for plaintext reads; the output buffer must accommodate this.  The
+output overhead is ``CMH_DS_EXPORT_OVERHEAD_PLAIN`` (16 bytes) for
+plaintext reads and ``CMH_DS_EXPORT_OVERHEAD_WRAPPED`` (48 bytes:
+16-byte header + 16-byte nonce + 16-byte tag) for wrapped reads.
+
+CMH_IOCTL_KEY_FIND
+------------------
+
+Resolve a Content ID to a datastore reference.
+
+:Direction: ``_IOWR``
+:Number: 0x04
+:Argument: ``struct cmh_ioctl_key_find``
+
+::
+
+  struct cmh_ioctl_key_find {
+      __u32 version;
+      __u32 __reserved;
+      __u64 cid;         /* caller ID to search for */
+      __u64 ref;         /* [out] resolved key reference */
+      __u32 len;         /* [out] key length */
+      __u32 type;        /* [out] key type */
+  };
+
+Returns ``-ENOENT`` if no object with the given CID exists.
+
+CMH_IOCTL_KEY_LIST
+------------------
+
+Iterate datastore objects.
+
+:Direction: ``_IOWR``
+:Number: 0x0E
+:Argument: ``struct cmh_ioctl_key_list``
+
+::
+
+  struct cmh_ioctl_key_list {
+      __u32 version;
+      __u32 __reserved;
+      __u64 start_ref;   /* starting DS reference (0 = first) */
+      __u64 ref;         /* [out] object reference */
+      __u64 cid;         /* [out] caller ID */
+      __u32 len;         /* [out] object length */
+      __u32 type;        /* [out] object type */
+  };
+
+Pass ``start_ref=0`` to begin from the first object.  On return, pass
+the returned ``ref`` as ``start_ref`` in the next call.  Iteration ends
+when ``ref == 0``.
+
+CMH_IOCTL_KEY_GRANT
+-------------------
+
+Set per-mailbox access permissions on a datastore object.
+
+:Direction: ``_IOW``
+:Number: 0x05
+:Argument: ``struct cmh_ioctl_key_grant``
+
+::
+
+  struct cmh_ioctl_key_grant {
+      __u32 version;
+      __u32 __reserved;
+      __u64 ref;         /* key reference */
+      __u64 read;        /* per-MBX read permission bitfield */
+      __u64 write;       /* per-MBX write permission bitfield */
+      __u64 execute;     /* per-MBX execute permission bitfield */
+  };
+
+CMH_IOCTL_KEY_DELETE
+--------------------
+
+Delete a datastore object (persistent keys only).
+
+:Direction: ``_IOW``
+:Number: 0x06
+:Argument: ``struct cmh_ioctl_key_grant``
+
+Uses the same structure as ``KEY_GRANT``; only the ``ref`` field is
+used.
+
+Datastore Export/Import ioctls
+==============================
+
+CMH_IOCTL_DS_EXPORT
+-------------------
+
+Export the entire datastore as an encrypted blob.
+
+:Direction: ``_IOWR``
+:Number: 0x07
+:Argument: ``struct cmh_ioctl_ds_export``
+
+::
+
+  struct cmh_ioctl_ds_export {
+      __u32 version;
+      __u32 len;         /* buffer length */
+      __u64 cid;         /* caller ID for response tagging */
+      __u64 wrap_key;    /* wrapping key ref (CMH_REF_NONE = plaintext) */
+      __u64 data;        /* user-space pointer to output buffer */
+      __u32 out_len;     /* [out] actual bytes written */
+      __u32 __reserved;
+  };
+
+CMH_IOCTL_DS_IMPORT
+-------------------
+
+Import a previously exported datastore blob.
+
+:Direction: ``_IOW``
+:Number: 0x08
+:Argument: ``struct cmh_ioctl_ds_import``
+
+::
+
+  struct cmh_ioctl_ds_import {
+      __u32 version;
+      __u32 len;         /* blob length */
+      __u64 wrap_key;    /* wrapping key ref (CMH_REF_NONE = plaintext) */
+      __u64 data;        /* user-space pointer to import blob */
+  };
+
+Key Derivation ioctls (KIC)
+===========================
+
+The Key Initialization Core (KIC) provides hardware key derivation from
+OTP-provisioned base keys.  Up to 8 base keys are available
+(``CMH_KIC_KEY1`` through ``CMH_KIC_KEY8``).
+
+CMH_IOCTL_KIC_HKDF1
+--------------------
+
+HKDF-based key derivation (single-step, label only).
+
+:Direction: ``_IOWR``
+:Number: 0x09
+:Argument: ``struct cmh_ioctl_kic_hkdf1``
+
+::
+
+  struct cmh_ioctl_kic_hkdf1 {
+      __u32 version;
+      __u32 key_len;     /* output key length */
+      __u64 base_key;    /* KIC base key reference */
+      __u64 cid;         /* CID for the new DS entry */
+      __u64 label;       /* user-space pointer to label data */
+      __u32 label_len;   /* label length in bytes */
+      __u32 flags;       /* CMH_KIC_FLAG_* */
+      __u64 ref;         /* [out] derived key reference */
+  };
+
+If ``CMH_KIC_FLAG_TEMP`` is set, the result is stored in the temporary
+datastore (not persistent).
+
+CMH_IOCTL_KIC_HKDF2
+--------------------
+
+HKDF-based key derivation (two-step, with salt key).
+
+:Direction: ``_IOWR``
+:Number: 0x0A
+:Argument: ``struct cmh_ioctl_kic_hkdf2``
+
+::
+
+  struct cmh_ioctl_kic_hkdf2 {
+      __u32 version;
+      __u32 key_len;
+      __u64 base_key;
+      __u64 salt_key;    /* salt key reference (CMH_REF_NONE = no salt) */
+      __u64 cid;
+      __u64 label;
+      __u32 label_len;
+      __u32 flags;
+      __u64 ref;         /* [out] derived key reference */
+  };
+
+CMH_IOCTL_KIC_AES_CMAC_KDF
+---------------------------
+
+AES-CMAC-based key derivation (NIST SP 800-108).
+
+:Direction: ``_IOWR``
+:Number: 0x0C
+:Argument: ``struct cmh_ioctl_kic_aes_cmac_kdf``
+
+::
+
+  struct cmh_ioctl_kic_aes_cmac_kdf {
+      __u32 version;
+      __u32 key_len;     /* base & output key length (must be 32) */
+      __u64 base_key;
+      __u64 cid;
+      __u64 label;
+      __u32 label_len;
+      __u32 flags;
+      __u64 ref;         /* [out] derived key reference */
+  };
+
+CMH_IOCTL_KIC_DKEK_DERIVE
+--------------------------
+
+Derive a Device Key Encryption Key (DKEK) for secure key export.
+
+:Direction: ``_IOWR``
+:Number: 0x0D
+:Argument: ``struct cmh_ioctl_kic_dkek_derive``
+
+::
+
+  struct cmh_ioctl_kic_dkek_derive {
+      __u32 version;
+      __u32 host_id;     /* target host ID (0 = caller's own) */
+      __u64 base_key;
+      __u64 cid;
+      __u64 metadata;    /* user-space pointer to metadata */
+      __u32 metadata_len;
+      __u32 flags;
+      __u64 ref;         /* [out] derived KEK reference */
+  };
+
+PKE (Public Key Engine) ioctls
+==============================
+
+RSA Operations
+--------------
+
+CMH_IOCTL_PKE_RSA_ENC
+~~~~~~~~~~~~~~~~~~~~~~
+
+RSA public-key encryption.
+
+:Direction: ``_IOWR``
+:Number: 0x10
+:Argument: ``struct cmh_ioctl_pke_rsa_enc``
+
+::
+
+  struct cmh_ioctl_pke_rsa_enc {
+      __u32 version;
+      __u32 bits;        /* RSA key size in bits (512-4096) */
+      __u64 e;           /* user-space pointer to public exponent */
+      __u32 e_len;       /* exponent length in bytes */
+      __u32 __reserved;
+      __u64 n;           /* user-space pointer to modulus */
+      __u64 input;       /* user-space pointer to input data */
+      __u64 output;      /* user-space pointer to output buffer */
+  };
+
+The public key (e, n) is passed as raw user-space buffers.
+
+CMH_IOCTL_PKE_RSA_DEC
+~~~~~~~~~~~~~~~~~~~~~~
+
+RSA private-key decryption using a datastore key reference.
+
+:Direction: ``_IOWR``
+:Number: 0x11
+:Argument: ``struct cmh_ioctl_pke_rsa_dec``
+
+::
+
+  struct cmh_ioctl_pke_rsa_dec {
+      __u32 version;
+      __u32 bits;
+      __u64 e;           /* public exponent */
+      __u32 e_len;
+      __u32 __reserved;
+      __u64 n;           /* modulus */
+      __u64 input;       /* ciphertext */
+      __u64 output;      /* plaintext output */
+      __u64 key_ref;     /* private key DS reference */
+  };
+
+CMH_IOCTL_PKE_RSA_CRT_DEC
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+RSA CRT private-key decryption (faster, uses CRT key format).
+
+:Direction: ``_IOWR``
+:Number: 0x12
+:Argument: ``struct cmh_ioctl_pke_rsa_crt_dec``
+
+::
+
+  struct cmh_ioctl_pke_rsa_crt_dec {
+      __u32 version;
+      __u32 bits;
+      __u64 e;
+      __u32 e_len;
+      __u32 __reserved;
+      __u64 n;
+      __u64 input;
+      __u64 output;
+      __u64 crt_ref;     /* CRT key DS reference */
+  };
+
+CMH_IOCTL_PKE_RSA_KEYGEN
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Generate an RSA key pair in hardware.
+
+:Direction: ``_IOWR``
+:Number: 0x13
+:Argument: ``struct cmh_ioctl_pke_rsa_keygen``
+
+::
+
+  struct cmh_ioctl_pke_rsa_keygen {
+      __u32 version;
+      __u32 bits;        /* key size in bits */
+      __u64 e;           /* user-space pointer to public exponent */
+      __u32 e_len;
+      __u32 flags;       /* CMH_FLAG_* */
+      __u64 n;           /* [out] user-space pointer to modulus buffer */
+      __u64 d_cid;       /* CID for private key DS entry */
+      __u64 d_ref;       /* [out] private key reference */
+      __u64 crt_cid;     /* CID for CRT key DS entry (0 = skip CRT) */
+      __u64 crt_ref;     /* [out] CRT key reference */
+  };
+
+Returns private key and optional CRT key as datastore references.
+The modulus is written back to user space.
+
+ECDSA Operations
+----------------
+
+CMH_IOCTL_PKE_ECDSA_SIGN
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ECDSA signature generation using a datastore private key.
+
+:Direction: ``_IOWR``
+:Number: 0x14
+:Argument: ``struct cmh_ioctl_pke_ecdsa_sign``
+
+::
+
+  struct cmh_ioctl_pke_ecdsa_sign {
+      __u32 version;
+      __u32 curve;       /* ABI curve ID (e.g. 0x03 = P-256) */
+      __u64 digest;      /* user-space pointer to hash digest */
+      __u32 digest_len;  /* digest length in bytes */
+      __u32 __reserved;
+      __u64 signature;   /* [out] user-space pointer to (r,s) */
+      __u64 key_ref;     /* private key DS reference */
+  };
+
+CMH_IOCTL_PKE_ECDH
+~~~~~~~~~~~~~~~~~~~
+
+Compute ECDH shared secret from a peer public key and a datastore
+private key.
+
+:Direction: ``_IOWR``
+:Number: 0x16
+:Argument: ``struct cmh_ioctl_pke_ecdh``
+
+::
+
+  struct cmh_ioctl_pke_ecdh {
+      __u32 version;
+      __u32 curve;
+      __u64 peer_key_x;  /* user-space pointer to peer public key X */
+      __u64 key_ref;     /* private key DS reference */
+      __u32 flags;       /* CMH_PKE_FLAG_DS_RESULT */
+      __u32 __reserved;
+      __u64 result_cid;  /* CID for DS result (if FLAG_DS_RESULT) */
+      __u64 output;      /* [out] raw shared secret or DS ref */
+  };
+
+If ``CMH_PKE_FLAG_DS_RESULT`` is set, the shared secret is stored in
+the datastore and a reference is returned instead of raw bytes.
+
+CMH_IOCTL_PKE_ECDH_KEYGEN
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Derive a public key from a datastore private key.
+
+:Direction: ``_IOWR``
+:Number: 0x17
+:Argument: ``struct cmh_ioctl_pke_ecdh_keygen``
+
+::
+
+  struct cmh_ioctl_pke_ecdh_keygen {
+      __u32 version;
+      __u32 curve;
+      __u64 key_ref;       /* private key DS reference */
+      __u64 public_key_x;  /* [out] user-space pointer to public key X */
+  };
+
+EdDSA Operations
+----------------
+
+CMH_IOCTL_PKE_EDDSA_SIGN
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+EdDSA (Ed25519/Ed448) signature generation.
+
+:Direction: ``_IOWR``
+:Number: 0x18
+:Argument: ``struct cmh_ioctl_pke_eddsa_sign``
+
+::
+
+  struct cmh_ioctl_pke_eddsa_sign {
+      __u32 version;
+      __u32 curve;       /* CURVE_25519 or CURVE_448 */
+      __u64 digest;      /* user-space ptr to message (not digest) */
+      __u32 digest_len;
+      __u32 __reserved;
+      __u64 signature;   /* [out] user-space pointer to signature */
+      __u64 key_ref;     /* private key DS reference */
+  };
+
+Note: the ``digest`` field is the full message (pure EdDSA), not a
+pre-computed hash.
+
+CMH_IOCTL_PKE_EDDSA_VERIFY
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+EdDSA signature verification.
+
+:Direction: ``_IOW``
+:Number: 0x19
+:Argument: ``struct cmh_ioctl_pke_eddsa_verify``
+
+::
+
+  struct cmh_ioctl_pke_eddsa_verify {
+      __u32 version;
+      __u32 curve;
+      __u64 digest;
+      __u32 digest_len;
+      __u32 __reserved;
+      __u64 signature;
+      __u64 public_key_y;  /* user-space pointer to public key Y */
+  };
+
+EC Key Management
+-----------------
+
+CMH_IOCTL_PKE_EC_KEYGEN
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Generate an EC private key in the hardware datastore.
+
+:Direction: ``_IOWR``
+:Number: 0x1A
+:Argument: ``struct cmh_ioctl_pke_ec_keygen``
+
+::
+
+  struct cmh_ioctl_pke_ec_keygen {
+      __u32 version;
+      __u32 curve;
+      __u32 flags;       /* CMH_FLAG_* */
+      __u32 __reserved;
+      __u64 cid;         /* CID for the new key DS entry */
+      __u64 ref;         /* [out] private key reference */
+  };
+
+CMH_IOCTL_PKE_EC_PUBGEN
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Derive the public key from a datastore private key.
+
+:Direction: ``_IOWR``
+:Number: 0x1B
+:Argument: ``struct cmh_ioctl_pke_ec_pubgen``
+
+::
+
+  struct cmh_ioctl_pke_ec_pubgen {
+      __u32 version;
+      __u32 curve;
+      __u64 key_ref;     /* private key DS reference */
+      __u64 public_key;  /* [out] user-space pointer to public key */
+  };
+
+CMH_IOCTL_PKE_EDDSA_KEYGEN_SCA
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Generate a 2-share SCA-protected Ed448 private key.
+
+:Direction: ``_IOWR``
+:Number: 0x1C
+:Argument: ``struct cmh_ioctl_pke_eddsa_keygen_sca``
+
+::
+
+  struct cmh_ioctl_pke_eddsa_keygen_sca {
+      __u32 version;
+      __u32 curve;       /* must be CURVE_448 */
+      __u64 key_ref;     /* input: normal Ed448 private key DS ref */
+      __u64 cid;         /* CID for the new SCA key DS entry */
+      __u64 sca_ref;     /* [out] SCA private key reference */
+  };
+
+Post-Quantum Cryptography (PQC) ioctls
+=======================================
+
+PQC operations support the following flags in the ``flags`` field:
+
+============================  ====  ====================================
+Flag                          Bit   Description
+============================  ====  ====================================
+``CMH_QSE_FLAG_MASKED``       0     Use masked (SCA-resistant) HW path
+``CMH_QSE_FLAG_DS_REF``       1     Store key output in DS, return ref
+``CMH_QSE_FLAG_HW_RNG``       2     Use HW RNG for seed/randomness
+============================  ====  ====================================
+
+ML-KEM (FIPS 203)
+-----------------
+
+CMH_IOCTL_ML_KEM_KEYGEN
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Generate an ML-KEM key pair.
+
+:Direction: ``_IOWR``
+:Number: 0x20
+:Argument: ``struct cmh_ioctl_ml_kem_keygen``
+
+::
+
+  struct cmh_ioctl_ml_kem_keygen {
+      __u32 version;
+      __u32 k;           /* security parameter: 2/3/4 */
+      __u32 flags;       /* CMH_QSE_FLAG_* */
+      __u32 __reserved;
+      __u64 seed;        /* user-space pointer to seed (or 0 for HW RNG) */
+      __u64 z;           /* user-space pointer to z (or 0 for HW RNG) */
+      __u64 ek;          /* [out] user-space pointer to encapsulation key */
+      __u64 dk;          /* [out] user-space pointer to decapsulation key,
+                          *       or [out] DS ref if CMH_QSE_FLAG_DS_REF */
+      __u64 dk_cid;      /* CID for DS entry (if DS_REF) */
+      __u64 dk_ref;      /* [out] dk DS reference (if DS_REF) */
+  };
+
+Security parameter ``k`` selects the strength: 2 (ML-KEM-512),
+3 (ML-KEM-768), or 4 (ML-KEM-1024).
+
+CMH_IOCTL_ML_KEM_ENC
+~~~~~~~~~~~~~~~~~~~~~
+
+ML-KEM encapsulation.  Produces ciphertext and shared secret.
+
+:Direction: ``_IOWR``
+:Number: 0x21
+:Argument: ``struct cmh_ioctl_ml_kem_enc``
+
+::
+
+  struct cmh_ioctl_ml_kem_enc {
+      __u32 version;
+      __u32 k;
+      __u32 flags;       /* CMH_QSE_FLAG_* */
+      __u32 __reserved;
+      __u64 coin;        /* user-space pointer to random coin (or 0) */
+      __u64 ek;          /* user-space pointer to encapsulation key */
+      __u64 ct;          /* [out] user-space pointer to ciphertext */
+      __u64 ss;          /* [out] user-space pointer to shared secret */
+      __u64 __reserved2[2];  /* reserved for future use */
+  };
+
+CMH_IOCTL_ML_KEM_DEC
+~~~~~~~~~~~~~~~~~~~~~
+
+ML-KEM decapsulation.  Recovers shared secret from ciphertext.
+
+:Direction: ``_IOWR``
+:Number: 0x22
+:Argument: ``struct cmh_ioctl_ml_kem_dec``
+
+::
+
+  struct cmh_ioctl_ml_kem_dec {
+      __u32 version;
+      __u32 k;
+      __u32 flags;       /* CMH_QSE_FLAG_* */
+      __u32 __reserved;
+      __u64 ct;          /* user-space pointer to ciphertext */
+      __u64 dk;          /* user-space pointer to dk or DS ref */
+      __u64 ss;          /* [out] user-space pointer to shared secret */
+      __u64 __reserved2[2];  /* reserved for future use */
+  };
+
+ML-DSA (FIPS 204)
+-----------------
+
+CMH_IOCTL_ML_DSA_KEYGEN
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Generate an ML-DSA key pair.
+
+:Direction: ``_IOWR``
+:Number: 0x23
+:Argument: ``struct cmh_ioctl_ml_dsa_keygen``
+
+::
+
+  struct cmh_ioctl_ml_dsa_keygen {
+      __u32 version;
+      __u32 mode;        /* security parameter: 2/3/5 */
+      __u32 flags;       /* CMH_QSE_FLAG_* */
+      __u32 __reserved;
+      __u64 seed;        /* user-space pointer to seed (or 0 for HW RNG) */
+      __u64 pk;          /* [out] user-space pointer to public key */
+      __u64 sk;          /* [out] user-space pointer to secret key,
+                          *       or [out] DS ref if CMH_QSE_FLAG_DS_REF */
+      __u64 sk_cid;      /* CID for DS entry (if DS_REF) */
+      __u64 sk_ref;      /* [out] sk DS reference (if DS_REF) */
+  };
+
+Security parameter ``mode`` selects the strength: 2 (ML-DSA-44),
+3 (ML-DSA-65), or 5 (ML-DSA-87).
+
+.. note::
+
+   When ``CMH_QSE_FLAG_DS_REF`` keeps the secret key in the datastore,
+   the public key returned in ``pk`` is the only copy: there is no
+   operation to derive the public key from the secret-key reference
+   for ML-DSA.  User space must persist ``pk`` at keygen time.
+
+CMH_IOCTL_ML_DSA_SIGN
+~~~~~~~~~~~~~~~~~~~~~~
+
+ML-DSA signature generation.
+
+:Direction: ``_IOWR``
+:Number: 0x24
+:Argument: ``struct cmh_ioctl_ml_dsa_sign``
+
+::
+
+  struct cmh_ioctl_ml_dsa_sign {
+      __u32 version;
+      __u32 mode;
+      __u32 flags;       /* CMH_QSE_FLAG_* */
+      __u32 mlen;        /* message length in bytes */
+      __u64 m;           /* user-space pointer to message */
+      __u64 sk;          /* user-space pointer to sk or DS ref */
+      __u64 sig;         /* [out] user-space pointer to signature */
+      __u64 rnd;         /* user-space pointer to randomness (or 0) */
+  };
+
+If ``mlen`` is set to ``CMH_ML_DSA_MLEN_EXTERNAL_MU`` (0xFFFFFFFF),
+the ``m`` pointer is interpreted as a 64-byte pre-hashed mu value
+(ExternalMu mode).
+
+CMH_IOCTL_SLHDSA_KEYGEN
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Generate an SLH-DSA key pair.
+
+:Direction: ``_IOWR``
+:Number: 0x28
+:Argument: ``struct cmh_ioctl_slhdsa_keygen``
+
+::
+
+  struct cmh_ioctl_slhdsa_keygen {
+      __u32 version;
+      __u32 parameter_set;  /* HCQ_SLHDSA_SHAKE_128S .. SHA2_256F */
+      __u32 flags;          /* CMH_QSE_FLAG_DS_REF */
+      __u32 __reserved;
+      __u64 seed;           /* user-space pointer to seed */
+      __u64 pk;             /* [out] user-space pointer to public key */
+      __u64 sk;             /* [out] user-space pointer to secret key,
+                             *       or [out] DS ref if CMH_QSE_FLAG_DS_REF */
+      __u64 sk_cid;         /* CID for DS entry (if DS_REF) */
+      __u64 sk_ref;         /* [out] sk DS reference (if DS_REF) */
+  };
+
+.. note::
+
+   When ``CMH_QSE_FLAG_DS_REF`` keeps the secret key in the datastore,
+   the public key returned in ``pk`` is the only copy: there is no
+   operation to derive the public key from the secret-key reference
+   for SLH-DSA.  User space must persist ``pk`` at keygen time.
+
+CMH_IOCTL_SLHDSA_SIGN
+~~~~~~~~~~~~~~~~~~~~~~
+
+SLH-DSA signature generation (pure mode).
+
+:Direction: ``_IOWR``
+:Number: 0x29
+:Argument: ``struct cmh_ioctl_slhdsa_sign``
+
+::
+
+  struct cmh_ioctl_slhdsa_sign {
+      __u32 version;
+      __u32 parameter_set;
+      __u32 msg_len;
+      __u32 ctx_len;
+      __u64 msg;         /* user-space pointer to message */
+      __u64 ctx;         /* user-space pointer to context (or 0) */
+      __u64 sk;          /* DS ref for secret key */
+      __u64 sig;         /* [out] user-space pointer to signature */
+      __u64 add_random;  /* user-space pointer to addl. randomness (or 0) */
+  };
+
+CMH_IOCTL_SLHDSA_SIGN_PREHASH
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+SLH-DSA pre-hash signature generation.
+
+:Direction: ``_IOWR``
+:Number: 0x2D
+:Argument: ``struct cmh_ioctl_slhdsa_sign_prehash``
+
+::
+
+  struct cmh_ioctl_slhdsa_sign_prehash {
+      __u32 version;
+      __u32 parameter_set;
+      __u32 prehash_algo;  /* CMH_SLHDSA_PREHASH_* */
+      __u32 digest;        /* 0 = raw msg (eSW hashes), 1 = pre-computed */
+      __u32 msg_len;
+      __u32 ctx_len;
+      __u64 msg;           /* user-space pointer to message/digest */
+      __u64 ctx;           /* user-space pointer to context (or 0) */
+      __u64 sk;            /* DS ref for secret key */
+      __u64 sig;           /* [out] user-space pointer to signature */
+      __u64 add_random;    /* user-space pointer to addl. randomness (or 0) */
+  };
+
+The ``prehash_algo`` field selects the hash algorithm
+(``CMH_SLHDSA_PREHASH_SHA256``, etc.).
+
+CMH_IOCTL_SM2_ENC_POINT
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Direction: ``_IOWR``
+:Number: 0x33
+:Argument: ``struct cmh_ioctl_sm2_enc_point``
+
+::
+
+  struct cmh_ioctl_sm2_enc_point {
+      __u32 version;
+      __u32 nonce_len;   /* 0 = HW generates, 32 = caller provides */
+      __u64 nonce;       /* user-space pointer to nonce (or 0) */
+      __u64 public_key;  /* user-space pointer to public key (64B) */
+      __u64 ciphertext;  /* [out] user-space pointer to C1 (64B) */
+      __u64 enc_point;   /* [out] user-space pointer to enc point (64B) */
+  };
+
+CMH_IOCTL_SM2_ENC_HASH
+~~~~~~~~~~~~~~~~~~~~~~~
+
+:Direction: ``_IOWR``
+:Number: 0x37
+:Argument: ``struct cmh_ioctl_sm2_enc_hash``
+
+::
+
+  struct cmh_ioctl_sm2_enc_hash {
+      __u32 version;
+      __u32 message_len;  /* message length (1..32) */
+      __u64 message;      /* user-space pointer to plaintext */
+      __u64 enc_point;    /* user-space pointer to enc point (64B) */
+      __u64 ciphertext;   /* [out] user-space pointer to ciphertext */
+  };
+
+CMH_IOCTL_SM2_DEC_POINT
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Direction: ``_IOWR``
+:Number: 0x32
+:Argument: ``struct cmh_ioctl_sm2_dec_point``
+
+::
+
+  struct cmh_ioctl_sm2_dec_point {
+      __u32 version;
+      __u32 ciphertext_len;  /* total ciphertext length (97..128) */
+      __u64 ciphertext;      /* user-space pointer to ciphertext (64B: C1) */
+      __u64 dec_point;       /* [out] user-space pointer to dec point (64B) */
+      __u64 key_ref;         /* private key DS reference */
+  };
+
+CMH_IOCTL_SM2_DEC_HASH
+~~~~~~~~~~~~~~~~~~~~~~~
+
+:Direction: ``_IOWR``
+:Number: 0x36
+:Argument: ``struct cmh_ioctl_sm2_dec_hash``
+
+::
+
+  struct cmh_ioctl_sm2_dec_hash {
+      __u32 version;
+      __u32 ciphertext_len;  /* ciphertext length (97..128) */
+      __u64 ciphertext;      /* user-space pointer to full ciphertext */
+      __u64 dec_point;       /* user-space pointer to dec point (64B) */
+      __u64 plaintext;       /* [out] user-space pointer to plaintext */
+  };
+
+SM2 Key Exchange (GM/T 0003.3)
+------------------------------
+
+The key exchange protocol is a multi-step flow:
+
+1. ``EC_KEYGEN(CMH_CURVE_SM2)`` -- generate a long-lived private key.
+2. ``EC_PUBGEN`` -- derive the public key.
+3. ``SM2_ID_DIGEST`` -- compute the SM3 identity digest (ZA).
+4. ``SM2_ECDH_KEYGEN`` -- generate an ephemeral session key.
+5. Exchange session keys with the peer.
+6. ``SM2_ECDH`` -- compute the shared point.
+7. ``SM2_ECDH_HASH`` -- derive the shared key from the shared point
+   and both parties' ZA digests.
+
+CMH_IOCTL_SM2_ECDH_KEYGEN
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+:Direction: ``_IOWR``
+:Number: 0x30
+:Argument: ``struct cmh_ioctl_sm2_ecdh_keygen``
+
+::
+
+  struct cmh_ioctl_sm2_ecdh_keygen {
+      __u32 version;
+      __u32 nonce_len;    /* 0 = HW generates r (written back), 32 = caller */
+      __u64 nonce;        /* [in/out] user-space pointer to nonce buffer (32B) */
+      __u64 session_key;  /* [out] user-space pointer to R=r*G (64B) */
+  };
+
+``nonce_len`` must be 0 or 32.  If ``nonce_len=0``, the hardware
+generates the ephemeral scalar and writes it back to the ``nonce``
+buffer.
+
+CMH_IOCTL_SM2_ECDH
+~~~~~~~~~~~~~~~~~~~
+
+:Direction: ``_IOWR``
+:Number: 0x31
+:Argument: ``struct cmh_ioctl_sm2_ecdh``
+
+::
+
+  struct cmh_ioctl_sm2_ecdh {
+      __u32 version;
+      __u32 nonce_len;         /* 0 = HW generates, 32 = caller provides */
+      __u64 nonce;             /* [in/out] user-space pointer to nonce r (32B) */
+      __u64 peer_public_key;   /* user-space pointer to peer pub key (64B) */
+      __u64 peer_session_key;  /* user-space pointer to peer session key (64B) */
+      __u64 key_ref;           /* private key DS reference */
+      __u64 shared_point;      /* [out] user-space pointer to shared point (64B) */
+      __u64 shared_point_ref;  /* [in/out] 0 = read-back; &ref = keep DS */
+  };
+
+If ``shared_point_ref`` points to a non-zero value, the shared point
+is kept in the datastore for use by ``SM2_ECDH_HASH``.
+
+CMH_IOCTL_SM2_ID_DIGEST
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Compute the SM3 identity digest (ZA) for a public key and identity
+string.
+
+:Direction: ``_IOWR``
+:Number: 0x34
+:Argument: ``struct cmh_ioctl_sm2_id_digest``
+
+::
+
+  struct cmh_ioctl_sm2_id_digest {
+      __u32 version;
+      __u32 id_len;      /* identity length in bytes (<=32) */
+      __u64 id;          /* user-space pointer to identity string */
+      __u64 public_key;  /* user-space pointer to public key (64B) */
+      __u64 digest;      /* [out] user-space pointer to ZA digest (32B) */
+  };
+
+CMH_IOCTL_SM2_ECDH_HASH
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Derive the shared key from the shared point and ZA digests.
+
+:Direction: ``_IOWR``
+:Number: 0x35
+:Argument: ``struct cmh_ioctl_sm2_ecdh_hash``
+
+::
+
+  struct cmh_ioctl_sm2_ecdh_hash {
+      __u32 version;
+      __u32 __reserved;
+      __u64 peer_id_digest;    /* ptr to Z_A -- initiator's digest (32B) */
+      __u64 id_digest;         /* ptr to Z_B -- responder's digest (32B) */
+      __u64 shared_point_ref;  /* DS reference from SM2_ECDH */
+      __u64 shared_key;        /* [out] ptr to shared key (16B) */
+  };
+
+.. important::
+
+   The digest fields use **absolute** ordering per GM/T 0003.3, not
+   relative own/peer ordering.  Both parties must pass:
+
+   - ``peer_id_digest`` = Z_A (initiator's digest) -- hashed first
+   - ``id_digest`` = Z_B (responder's digest) -- hashed second
+
+Hardware Management ioctls
+==========================
+
+CMH_IOCTL_EAC_READ
+-------------------
+
+Read and clear the hardware Error and Alarm Controller registers.
+
+:Direction: ``_IOWR``
+:Number: 0x0F
+:Argument: ``struct cmh_ioctl_eac_read``
+
+::
+
+  struct cmh_ioctl_eac_read {
+      __u32 version;
+      __u32 __reserved;
+      __u64 mailbox_notification;
+      __u32 hw_error;
+      __u32 hw_nmi;
+      __u32 hw_panic;
+      __u32 safety_fatal;
+      __u32 safety_notification;
+      __u32 sw_info0;
+      __u32 sw_info1;
+      __u32 sram_bank_errors[4];
+      __u32 __pad;
+  };
+
+The eSW atomically reads and clears the registers on each call.
+Successive reads show only new events since the last read.
+
+CMH_IOCTL_DRBG_CONFIG
+----------------------
+
+Configure the hardware DRBG before first use.
+
+:Direction: ``_IOW``
+:Number: 0x40
+:Argument: ``struct cmh_ioctl_drbg_config``
+
+::
+
+  struct cmh_ioctl_drbg_config {
+      __u32 version;
+      __u32 entropy_ratio;       /* CMH_DRBG_RATIO_* */
+      __u32 security_strength;   /* CMH_DRBG_STRENGTH_* */
+      __u32 __reserved;
+  };
+
+This is a management operation normally performed once at system
+startup.  Must be called before any ``hwrng`` reads or DRBG generate
+operations.
+
+ioctl Number Summary
+====================
+
+======================================  ====  ====  =========================================
+ioctl                                   Dir   Seq   Argument
+======================================  ====  ====  =========================================
+``CMH_IOCTL_KEY_NEW``                   IOWR  0x01  ``cmh_ioctl_key_new``
+``CMH_IOCTL_KEY_WRITE``                 IOW   0x02  ``cmh_ioctl_key_write``
+``CMH_IOCTL_KEY_READ``                  IOWR  0x03  ``cmh_ioctl_key_read``
+``CMH_IOCTL_KEY_FIND``                  IOWR  0x04  ``cmh_ioctl_key_find``
+``CMH_IOCTL_KEY_GRANT``                 IOW   0x05  ``cmh_ioctl_key_grant``
+``CMH_IOCTL_KEY_DELETE``                IOW   0x06  ``cmh_ioctl_key_grant``
+``CMH_IOCTL_DS_EXPORT``                 IOWR  0x07  ``cmh_ioctl_ds_export``
+``CMH_IOCTL_DS_IMPORT``                 IOW   0x08  ``cmh_ioctl_ds_import``
+``CMH_IOCTL_KIC_HKDF1``                 IOWR  0x09  ``cmh_ioctl_kic_hkdf1``
+``CMH_IOCTL_KIC_HKDF2``                 IOWR  0x0A  ``cmh_ioctl_kic_hkdf2``
+``CMH_IOCTL_KEY_NEW_RANDOM``            IOWR  0x0B  ``cmh_ioctl_key_new``
+``CMH_IOCTL_KIC_AES_CMAC_KDF``          IOWR  0x0C  ``cmh_ioctl_kic_aes_cmac_kdf``
+``CMH_IOCTL_KIC_DKEK_DERIVE``           IOWR  0x0D  ``cmh_ioctl_kic_dkek_derive``
+``CMH_IOCTL_KEY_LIST``                  IOWR  0x0E  ``cmh_ioctl_key_list``
+``CMH_IOCTL_EAC_READ``                  IOWR  0x0F  ``cmh_ioctl_eac_read``
+``CMH_IOCTL_PKE_RSA_ENC``               IOWR  0x10  ``cmh_ioctl_pke_rsa_enc``
+``CMH_IOCTL_PKE_RSA_DEC``               IOWR  0x11  ``cmh_ioctl_pke_rsa_dec``
+``CMH_IOCTL_PKE_RSA_CRT_DEC``           IOWR  0x12  ``cmh_ioctl_pke_rsa_crt_dec``
+``CMH_IOCTL_PKE_RSA_KEYGEN``            IOWR  0x13  ``cmh_ioctl_pke_rsa_keygen``
+``CMH_IOCTL_PKE_ECDSA_SIGN``            IOWR  0x14  ``cmh_ioctl_pke_ecdsa_sign``
+``CMH_IOCTL_PKE_ECDH``                  IOWR  0x16  ``cmh_ioctl_pke_ecdh``
+``CMH_IOCTL_PKE_ECDH_KEYGEN``           IOWR  0x17  ``cmh_ioctl_pke_ecdh_keygen``
+``CMH_IOCTL_PKE_EDDSA_SIGN``            IOWR  0x18  ``cmh_ioctl_pke_eddsa_sign``
+``CMH_IOCTL_PKE_EDDSA_VERIFY``          IOW   0x19  ``cmh_ioctl_pke_eddsa_verify``
+``CMH_IOCTL_PKE_EC_KEYGEN``             IOWR  0x1A  ``cmh_ioctl_pke_ec_keygen``
+``CMH_IOCTL_PKE_EC_PUBGEN``             IOWR  0x1B  ``cmh_ioctl_pke_ec_pubgen``
+``CMH_IOCTL_PKE_EDDSA_KEYGEN_SCA``      IOWR  0x1C  ``cmh_ioctl_pke_eddsa_keygen_sca``
+``CMH_IOCTL_ML_KEM_KEYGEN``             IOWR  0x20  ``cmh_ioctl_ml_kem_keygen``
+``CMH_IOCTL_ML_KEM_ENC``                IOWR  0x21  ``cmh_ioctl_ml_kem_enc``
+``CMH_IOCTL_ML_KEM_DEC``                IOWR  0x22  ``cmh_ioctl_ml_kem_dec``
+``CMH_IOCTL_ML_DSA_KEYGEN``             IOWR  0x23  ``cmh_ioctl_ml_dsa_keygen``
+``CMH_IOCTL_ML_DSA_SIGN``               IOWR  0x24  ``cmh_ioctl_ml_dsa_sign``
+``CMH_IOCTL_SLHDSA_KEYGEN``             IOWR  0x28  ``cmh_ioctl_slhdsa_keygen``
+``CMH_IOCTL_SLHDSA_SIGN``               IOWR  0x29  ``cmh_ioctl_slhdsa_sign``
+``CMH_IOCTL_SLHDSA_SIGN_PREHASH``       IOWR  0x2D  ``cmh_ioctl_slhdsa_sign_prehash``
+``CMH_IOCTL_SM2_ECDH_KEYGEN``           IOWR  0x30  ``cmh_ioctl_sm2_ecdh_keygen``
+``CMH_IOCTL_SM2_ECDH``                  IOWR  0x31  ``cmh_ioctl_sm2_ecdh``
+``CMH_IOCTL_SM2_DEC_POINT``             IOWR  0x32  ``cmh_ioctl_sm2_dec_point``
+``CMH_IOCTL_SM2_ENC_POINT``             IOWR  0x33  ``cmh_ioctl_sm2_enc_point``
+``CMH_IOCTL_SM2_ID_DIGEST``             IOWR  0x34  ``cmh_ioctl_sm2_id_digest``
+``CMH_IOCTL_SM2_ECDH_HASH``             IOWR  0x35  ``cmh_ioctl_sm2_ecdh_hash``
+``CMH_IOCTL_SM2_DEC_HASH``              IOWR  0x36  ``cmh_ioctl_sm2_dec_hash``
+``CMH_IOCTL_SM2_ENC_HASH``              IOWR  0x37  ``cmh_ioctl_sm2_enc_hash``
+``CMH_IOCTL_DRBG_CONFIG``               IOW   0x40  ``cmh_ioctl_drbg_config``
+======================================  ====  ====  =========================================
+
+Relationship to the in-kernel crypto API
+=========================================
+
+The main reason these operations are exposed as ioctls, rather than
+through the standard in-kernel crypto API, is the CMH datastore key
+model: an ioctl can operate on a *datastore-referenced* (hardware-held)
+key, identified only by a ``ref`` or CID, whose raw bytes the CPU never
+sees.  The standard crypto API cannot express this -- every
+``.setkey()`` takes raw key material -- so hardware key lifecycle
+(create, import, derive, grant, destroy) and compute-on-hardware-held-key
+operations have no crypto API equivalent and are only reachable here.
+
+These ioctls remain supported.  Where an operation can *also* be
+expressed through a standard kernel abstraction, additional in-kernel
+crypto API bindings may be added over time; when such a binding lands the
+driver registers through it, and the ioctl continues to be maintained for
+backward compatibility:
+
+- **EdDSA** (``CMH_IOCTL_PKE_EDDSA_*``): a kernel ``sig`` binding may be
+  added once ed25519/ed448 algorithm types are accepted upstream.
+
+- **ML-KEM** (``CMH_IOCTL_ML_KEM_*``): a kernel KEM binding may be added
+  once the in-flight KEM subsystem series lands.
+
+- **Key lifecycle** (``CMH_IOCTL_KEY_*``): integration with the kernel
+  KEYS subsystem (trusted-keys / encrypted-keys) may be evaluated as a
+  follow-up series.
+
+Operations that are inherently vendor-specific (EAC Chip Authentication,
+KIC key derivation, SM2 key exchange, DRBG configuration, datastore
+export/import) have no corresponding kernel abstraction and are expected
+to remain ioctl-only.
diff --git a/Documentation/userspace-api/ioctl/index.rst b/Documentation/userspace-api/ioctl/index.rst
index 475675eae086..bf88bb6b9a6f 100644
--- a/Documentation/userspace-api/ioctl/index.rst
+++ b/Documentation/userspace-api/ioctl/index.rst
@@ -12,4 +12,5 @@ IOCTLs
    ioctl-decoding
 
    cdrom
+   cmh_mgmt
    hdio
diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst
index 3f0ef1e27eb0..83fd74b6f396 100644
--- a/Documentation/userspace-api/ioctl/ioctl-number.rst
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst
@@ -170,6 +170,7 @@ Code  Seq#    Include File                                             Comments
 'I'   all    linux/isdn.h                                              conflict!
 'I'   00-0F  drivers/isdn/divert/isdn_divert.h                         conflict!
 'I'   40-4F  linux/mISDNif.h                                           conflict!
+'J'   01-40  uapi/linux/cmh_mgmt_ioctl.h                               Rambus CryptoManager Hub (CMH)
 'K'   all    linux/kd.h
 'L'   00-1F  linux/loop.h                                              conflict!
 'L'   10-1F  drivers/scsi/mpt3sas/mpt3sas_ctl.h                        conflict!
-- 
2.43.7
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.