第三篇------Virtual I/O Device (VIRTIO) Version 1.1

上一篇文章链接https://blog.csdn.net/Phoenix_zxk/article/details/132921821

接下来续上

5.9.4 支持的加密服务

以下加密服务已定义:

/* CIPHER 服务:用于加密解密操作 */
#define VIRTIO_CRYPTO_SERVICE_CIPHER 0
/* HASH 服务:用于哈希运算 */
#define VIRTIO_CRYPTO_SERVICE_HASH 1
/* MAC(消息认证码)服务:用于消息验证 */
#define VIRTIO_CRYPTO_SERVICE_MAC 2
/* AEAD(带相关数据的认证加密)服务:用于加密并验证带有相关数据的消息 */
#define VIRTIO_CRYPTO_SERVICE_AEAD 3

上述常量用于指示设备提供的加密服务,如 5.9.5 中所述。

5.9.4.1 加密服务 - CIPHER

以下CIPHER算法已定义:

/* 不使用加密 */
#define VIRTIO_CRYPTO_NO_CIPHER 0
/* ARC4 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_ARC4 1
/* AES ECB 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_AES_ECB 2
/* AES CBC 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_AES_CBC 3
/* AES CTR 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_AES_CTR 4
/* DES ECB 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_DES_ECB 5
/* DES CBC 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_DES_CBC 6
/* 3DES ECB 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_3DES_ECB 7
/* 3DES CBC 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_3DES_CBC 8
/* 3DES CTR 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_3DES_CTR 9
/* KASUMI F8 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_KASUMI_F8 10
/* SNOW3G UEA2 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_SNOW3G_UEA2 11
/* AES F8 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_AES_F8 12
/* AES XTS 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_AES_XTS 13
/* ZUC EEA3 加密算法 */
#define VIRTIO_CRYPTO_CIPHER_ZUC_EEA3 14

上述常量有两种用途:

  1. 作为位数,用于告诉驱动程序设备支持哪些CIPHER算法,详见 5.9.5。
  2. 作为值,用于在(CIPHER类型)加密操作请求中指定算法,详见 5.9.7.2.1。

5.9.4.2 HASH 服务

以下HASH算法已定义:

/* 不使用哈希算法 */
#define VIRTIO_CRYPTO_NO_HASH 0
/* MD5 哈希算法 */
#define VIRTIO_CRYPTO_HASH_MD5 1
/* SHA1 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA1 2
/* SHA-224 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA_224 3
/* SHA-256 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA_256 4
/* SHA-384 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA_384 5
/* SHA-512 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA_512 6
/* SHA3-224 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA3_224 7
/* SHA3-256 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA3_256 8
/* SHA3-384 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA3_384 9
/* SHA3-512 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA3_512 10
/* SHA3 SHAKE128 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA3_SHAKE128 11
/* SHA3 SHAKE256 哈希算法 */
#define VIRTIO_CRYPTO_HASH_SHA3_SHAKE256 12

上述常量有两种用途:

  1. 作为位数,用于告诉驱动程序设备支持哪些HASH算法,详见 5.9.5。
  2. 作为值,用于在(HASH类型)加密操作请求中指定算法,详见 5.9.7.2.1。

5.9.4.3 MAC 服务

以下MAC算法已定义:

/* 不使用消息认证码(MAC) */
#define VIRTIO_CRYPTO_NO_MAC 0
/* HMAC-MD5 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_HMAC_MD5 1
/* HMAC-SHA1 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_HMAC_SHA1 2
/* HMAC-SHA-224 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_HMAC_SHA_224 3
/* HMAC-SHA-256 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_HMAC_SHA_256 4
/* HMAC-SHA-384 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_HMAC_SHA_384 5
/* HMAC-SHA-512 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_HMAC_SHA_512 6
/* CMAC-3DES 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_CMAC_3DES 25
/* CMAC-AES 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_CMAC_AES 26
/* KASUMI F9 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_KASUMI_F9 27
/* SNOW3G UIA2 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_SNOW3G_UIA2 28
/* GMAC-AES 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_GMAC_AES 41
/* GMAC-TWOFISH 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_GMAC_TWOFISH 42
/* CBC-MAC-AES 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_CBCMAC_AES 49
/* CBC-MAC-KASUMI F9 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_CBCMAC_KASUMI_F9 50
/* XCBC-AES 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC_XCBC_AES 53
/* ZUC EIA3 消息认证码(MAC) */
#define VIRTIO_CRYPTO_MAC	54

上述常量有两种用途:

  1. 作为位数,用于告诉驱动程序设备支持哪些MAC算法,详见 5.9.5。
  2. 作为值,用于在(MAC类型)加密操作请求中指定算法,详见 5.9.7.2.1。

5.9.4.4 AEAD 服务

以下AEAD算法已定义:

/* 不使用认证加密相关数据(AEAD) */
#define VIRTIO_CRYPTO_NO_AEAD 0
/* GCM AEAD 算法 */
#define VIRTIO_CRYPTO_AEAD_GCM 1
/* CCM AEAD 算法 */
#define VIRTIO_CRYPTO_AEAD_CCM 2
/* ChaCha20-Poly1305 AEAD 算法 */
#define VIRTIO_CRYPTO_AEAD_CHACHA20_POLY1305 3

上述常量有两种用途:

  1. 作为位数,用于告诉驱动程序设备支持哪些AEAD算法,详见 5.9.5。
  2. 作为值,用于在(DEAD类型)加密操作请求中指定算法,详见 5.9.7.2.1。

5.9.5 设备配置布局

加密设备配置采用以下布局结构:

/* Virtio 加密配置信息结构体 */
struct virtio_crypto_config {
    le32 status;                // 加密状态
    le32 max_dataqueues;        // 最大数据队列数
    le32 crypto_services;       // 加密服务支持的服务类型

    /* 详细算法掩码 */
    le32 cipher_algo_l;         // 加密算法低位
    le32 cipher_algo_h;         // 加密算法高位
    le32 hash_algo;             // 哈希算法
    le32 mac_algo_l;            // MAC(消息认证码)算法低位
    le32 mac_algo_h;            // MAC(消息认证码)算法高位
    le32 aead_algo;             // AEAD(认证加密相关数据)算法

    /* 最大密钥长度(以字节为单位) */
    le32 max_cipher_key_len;    // 加密密钥的最大长度
    le32 max_auth_key_len;      // 认证密钥的最大长度
    le32 reserved;              // 保留字段

    /* 每个加密请求内容的最大大小(以字节为单位) */
    le64 max_size;
};

目前,只定义了一个状态位:设置 VIRTIO_CRYPTO_S_HW_READY 表示设备已准备好处理请求,对于驱动程序来说,这个位是只读的。

/* Virtio 加密设备状态标志:硬件准备就绪 */
#define VIRTIO_CRYPTO_S_HW_READY (1 << 0)

max_dataqueues 是设备可以配置的最大数据虚拟队列数。驱动程序可以只使用一个数据队列,或者可以使用多个以实现更好的性能。
crypto_services 是提供的加密服务,详见 5.9.4。
cipher_algo_l 是 CIPHER 算法的位 0-31,详见 5.9.4.1。
cipher_algo_h 是 CIPHER 算法的位 32-63,详见 5.9.4.1。
hash_algo 是 HASH 算法的位,详见 5.9.4.2。
mac_algo_l 是 MAC 算法的位 0-31,详见 5.9.4.3。
mac_algo_h 是 MAC 算法的位 32-63,详见 5.9.4.3。
aead_algo 是 AEAD 算法的位,详见 5.9.4.4。
max_cipher_key_len 是设备支持的密码密钥的最大长度。
max_auth_key_len 是设备支持的身份验证密钥的最大长度。
reserved 保留供将来使用。
max_size 是设备支持的每个加密请求内容的可变长度参数的最大大小。
注意:除非另有明确说明,所有长度和大小都以字节为单位。

5.9.5.1 设备要求:设备配置布局

• 设备必须将 max_dataqueues 设置为 1 到 65535(含)之间的值。
• 设备必须使用有效的标志设置状态,不得设置未定义的标志。
• 设备必须在状态设置为 VIRTIO_CRYPTO_S_HW_READY 后接受并处理请求。
• 设备必须根据设备提供的加密服务设置 crypto_services。
• 设备必须为 crypto_services 中每个广告服务设置详细算法掩码。设备不得设置未定义的算法位。
• 设备必须设置 max_size 以显示设备支持的加密请求的最大大小。
• 设备必须设置 max_cipher_key_len 以显示如果设备支持 CIPHER 服务,则密码密钥的最大长度。
• 设备必须设置 max_auth_key_len 以显示如果设备支持 MAC 服务,则认证密钥的最大长度。

5.9.5.2 驱动程序要求:设备配置布局

• 驱动程序必须从状态的最低位读取状态,以检查是否设置了 VIRTIO_CRYPTO_S_HW_READY,驱动程序必须在设备复位后重新读取它。
• 如果 VIRTIO_CRYPTO_S_HW_READY 没有设置,驱动程序不能向设备发送任何请求。
• 驱动程序必须读取 max_dataqueues 字段以发现设备支持的数据队列数。
• 驱动程序必须读取 crypto_services 字段以发现设备能够提供哪些服务。
• 驱动程序应忽略未定义的算法位。
• 驱动程序必须根据 crypto_services 字段读取详细的算法字段。
• 驱动程序应读取 max_size 以发现设备支持的每个加密请求内容的可变长度参数的最大大小,并且必须保证每个加密请求内容的大小在 max_size 范围内,否则请求将失败,驱动程序必须重置设备。
• 驱动程序应读取 max_cipher_key_len 以发现设备支持的密码密钥的最大长度,并且必须保证 key_len(CIPHER 服务或 AEAD 服务)在设备配置的 max_cipher_key_len 范围内,否则请求将失败。
• 驱动程序应读取 max_auth_key_len 以发现设备支持的认证密钥的最大长度,并且必须保证 auth_key_len(MAC 服务)在设备配置的 max_auth_key_len 范围内,否则请求将失败。

5.9.6 设备初始化

5.9.6.1 驱动程序要求:设备初始化

• 驱动程序必须配置和初始化所有虚拟队列。
• 驱动程序必须从 crypto_services 的位中读取支持的加密服务。
• 驱动程序必须根据 crypto_services 字段读取支持的算法。

5.9.7 设备操作

virtio 加密设备的操作是由放置在虚拟队列上的请求驱动的。请求包括队列类型特定的头部(指定操作等)和特定操作的有效载荷。
如果已经协商了 VIRTIO_CRYPTO_F_REVISION_1,则设备可以支持会话模式(见 5.9.7.2.1)和无状态模式操作请求。在无状态模式下,所有操作参数都作为每个请求的一部分提供,而在会话模式下,一些或所有操作参数在会话中管理。
无状态模式由特征位 0-4 在服务级别上保护。如果为服务协商了无状态模式,则该服务接受会话模式和无状态请求;否则,将拒绝无状态模式请求(通过操作状态)。

5.9.7.1 操作状态

设备必须返回一个状态代码作为操作(包括会话操作和服务操作)的一部分结果。有效的操作状态如下:
• VIRTIO_CRYPTO_OK:成功。
• VIRTIO_CRYPTO_BADMSG:身份验证失败(仅在 AEAD 解密时)。
• VIRTIO_CRYPTO_NOTSUPP:不支持的操作或算法。
• VIRTIO_CRYPTO_INVSESS:在执行加密操作时会话 ID 无效。
• VIRTIO_CRYPTO_NOSPC:没有空闲的会话 ID(仅在协商了 VIRTIO_CRYPTO_F_REVISION_1 特性位时)。
• VIRTIO_CRYPTO_ERR:发生未提及的任何故障。

/* Virtio 加密设备状态枚举 */
enum VIRTIO_CRYPTO_STATUS {
    VIRTIO_CRYPTO_OK = 0,         // 操作成功
    VIRTIO_CRYPTO_ERR = 1,        // 通用错误
    VIRTIO_CRYPTO_BADMSG = 2,     // 无效的消息
    VIRTIO_CRYPTO_NOTSUPP = 3,    // 不支持的操作
    VIRTIO_CRYPTO_INVSESS = 4,    // 无效的会话
    VIRTIO_CRYPTO_NOSPC = 5,      // 空间不足
    VIRTIO_CRYPTO_MAX              // 状态枚举的最大值
};

5.9.7.2 控制 Virtqueue

驱动程序使用控制 Virtqueue 向设备发送控制命令,例如会话操作(请参见 5.9.7.2.1)。
控制队列(controlq)的标头具有以下格式:

/* Virtio 加密服务类型宏 */
#define VIRTIO_CRYPTO_SERVICE_CIPHER 0  // 加密服务
#define VIRTIO_CRYPTO_SERVICE_HASH 1    // 哈希服务
#define VIRTIO_CRYPTO_SERVICE_MAC 2     // MAC(消息认证码)服务
#define VIRTIO_CRYPTO_SERVICE_AEAD 3    // AEAD(带有相关数据的身份验证加密)服务

/* Virtio 加密操作码宏 */
#define VIRTIO_CRYPTO_OPCODE(service, op) (((service) << 8) | (op))

/* Virtio 加密控制头操作码 */
#define VIRTIO_CRYPTO_CIPHER_CREATE_SESSION \
    VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_CIPHER, 0x02)  // 创建加密会话
#define VIRTIO_CRYPTO_CIPHER_DESTROY_SESSION \
    VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_CIPHER, 0x03)  // 销毁加密会话
#define VIRTIO_CRYPTO_HASH_CREATE_SESSION \
    VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_HASH, 0x02)    // 创建哈希会话
#define VIRTIO_CRYPTO_HASH_DESTROY_SESSION \
    VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_HASH, 0x03)    // 销毁哈希会话
#define VIRTIO_CRYPTO_MAC_CREATE_SESSION \
    VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_MAC, 0x02)     // 创建 MAC 会话
#define VIRTIO_CRYPTO_MAC_DESTROY_SESSION \
    VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_MAC, 0x03)     // 销毁 MAC 会话
#define VIRTIO_CRYPTO_AEAD_CREATE_SESSION \
    VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x02)    // 创建 AEAD 会话
#define VIRTIO_CRYPTO_AEAD_DESTROY_SESSION \
    VIRTIO_CRYPTO_OPCODE(VIRTIO_CRYPTO_SERVICE_AEAD, 0x03)    // 销毁 AEAD 会话
	le32 opcode;
	/* algo should be service-specific algorithms */
	le32 algo;
	le32 flag;
	le32 reserved;

控制队列请求由四个部分组成:

struct virtio_crypto_op_ctrl_req {
    /* 设备只读部分 */
    struct virtio_crypto_ctrl_header header;  // 加密控制头

    /* 固定长度字段,操作码特定 */
    u8 op_flf[flf_len];  // 操作固定长度字段
    /* 可变长度字段,操作码特定 */
    u8 op_vlf[vlf_len];  // 操作可变长度字段

    /* 设备只写部分 */
    /* 操作结果或完成状态 */
    u8 op_outcome[outcome_len];  // 操作结果或完成状态
};

  • header(标头):通用标头(请参阅上文)。

  • op_flf(操作码特定的固定长度参数):这些参数依赖于标头中的操作码。

  • flf_len:取决于是否协商了特性位VIRTIO_CRYPTO_F_REVISION_1(请参阅下文)。

  • op_vlf(操作码特定的可变长度参数):这些参数的大小也依赖于标头中的操作码。

  • vlf_len:表示所使用特定结构的大小。

  • 请注意:session-destroy 操作和 hash-session-create 操作的 vlf_len 值为零。

  • 如果操作码(在标头中)为 VIRTIO_CRYPTO_CIPHER_CREATE_SESSION,那么 op_flf 应为 struct virtio_crypto_sym_create_session_flf,如果协商了特性位 VIRTIO_CRYPTO_F_REVISION_1,则 struct virtio_crypto_sym_create_session_flf 不需要填充到 56 字节,同时 op_vlf 应为 struct virtio_crypto_sym_create_session_vlf

  • 如果操作码为 VIRTIO_CRYPTO_HASH_CREATE_SESSION,那么 op_flf 应为 struct virtio_crypto_hash_create_session_flf,如果协商了特性位 VIRTIO_CRYPTO_F_REVISION_1,则 struct virtio_crypto_hash_create_session_flf 不需要填充到 56 字节。

  • 如果操作码为 VIRTIO_CRYPTO_MAC_CREATE_SESSION,那么 op_flf 应为 struct virtio_crypto_mac_create_session_flf,如果协商了特性位 VIRTIO_CRYPTO_F_REVISION_1,则 struct virtio_crypto_mac_create_session_flf 不需要填充到 56 字节,同时 op_vlf 应为 struct virtio_crypto_mac_create_session_vlf

  • 如果操作码为 VIRTIO_CRYPTO_AEAD_CREATE_SESSION,那么 op_flf 应为 struct virtio_crypto_aead_create_session_flf,如果协商了特性位 VIRTIO_CRYPTO_F_REVISION_1,则 struct virtio_crypto_aead_create_session_flf 不需要填充到 56 字节,同时 op_vlf 应为 struct virtio_crypto_aead_create_session_vlf

  • 如果操作码为 VIRTIO_CRYPTO_CIPHER_DESTROY_SESSIONVIRTIO_CRYPTO_HASH_DESTROY_SESSIONVIRTIO_CRYPTO_MAC_DESTROY_SESSIONVIRTIO_CRYPTO_AEAD_DESTROY_SESSION,那么 op_flf 应为 struct virtio_crypto_destroy_session_flf,如果协商了特性位 VIRTIO_CRYPTO_F_REVISION_1,则 struct virtio_crypto_destroy_session_flf 不需要填充到 56 字节。

  • op_outcome 用于存储操作结果,对于 destroy session 操作,应使用 struct virtio_crypto_destroy_session_input,对于 create session 操作,应使用 struct virtio_crypto_create_session_input

  • outcome_len 是所使用结构的大小。

5.9.7.2.1 会话操作

会话是一个句柄,用于描述要应用于多个缓冲区的加密参数。以下结构存储了由设备设置的会话创建的结果:

struct virtio_crypto_create_session_input {
    le64 session_id;  // 会话ID
    le32 status;      // 状态
    le32 padding;     // 填充字段
};

销毁会话的请求包括以下信息:

//这个结构体的作用是告诉 Virtio 加密设备要销毁的会话的ID。
struct virtio_crypto_destroy_session_flf {
    le64 session_id;  // 会话ID
};
//这个结构体的作用是向 Virtio 加密设备发送销毁会话的请求,并包含了销毁操作的状态信息
struct virtio_crypto_destroy_session_input {
    u8 status;  // 状态
};

5.9.7.2.1.1 会话操作:HASH 会话

HASH 会话请求的固定长度参数如下:

struct virtio_crypto_hash_create_session_flf {
    /* 设备只读部分 */
    
    // 哈希算法的类型,具体取值参考 VIRTIO_CRYPTO_HASH_* 枚举
    le32 algo;
    
    // 哈希结果的长度
    le32 hash_result_len;
};

5.9.7.2.1.2 会话操作:MAC 会话

MAC 会话请求的固定长度和可变长度参数如下:

// virtio_crypto_mac_create_session_flf 结构体的注释
struct virtio_crypto_mac_create_session_flf {
    /* 设备只读部分 */

    // MAC 算法的类型,具体取值参考 VIRTIO_CRYPTO_MAC_* 枚举
    le32 algo;

    // 哈希结果的长度
    le32 hash_result_len;

    // 验证密钥的长度
    le32 auth_key_len;

    // 填充字段
    le32 padding;
};

// virtio_crypto_mac_create_session_vlf 结构体的注释
struct virtio_crypto_mac_create_session_vlf {
    /* 设备只读部分 */

    // 认证密钥
    u8 auth_key[auth_key_len];
};

在结构体 virtio_crypto_mac_create_session_flf 中,auth_key 的长度由 auth_key_len 指定。

5.9.7.2.1.3 会话操作:对称算法会话

对称算法会话的请求可以是 CIPHER 算法请求或链式算法(链接 CIPHER 和 HASH/MAC)请求。
CIPHER 会话请求的固定长度和可变长度参数如下:

// virtio_crypto_cipher_session_flf 结构体的注释
struct virtio_crypto_cipher_session_flf {
    /* 设备只读部分 */

    // 密码算法的类型,具体取值参考 VIRTIO_CRYPTO_CIPHER* 枚举
    le32 algo;

    // 密钥的长度
    le32 key_len;

    // 操作类型,可以是加密或解密,具体取值参考 VIRTIO_CRYPTO_OP_* 枚举
    le32 op;

    // 填充字段
    le32 padding;
};

// virtio_crypto_cipher_session_vlf 结构体的注释
struct virtio_crypto_cipher_session_vlf {
    /* 设备只读部分 */

    // 密码密钥
    u8 cipher_key[key_len];
};

在结构体 virtio_crypto_cipher_session_flf 中,cipher_key 的长度由 key_len 指定。
链式会话请求的固定长度和可变长度参数如下:

// virtio_crypto_alg_chain_session_flf 结构体的注释
struct virtio_crypto_alg_chain_session_flf {
    /* 设备只读部分 */

    // 算法链的顺序,可以是哈希然后加密或加密然后哈希,具体取值参考 VIRTIO_CRYPTO_SYM_ALG_CHAIN_ORDER_* 枚举
    le32 alg_chain_order;

    // 哈希模式,可以是普通哈希、认证哈希(MAC)或嵌套哈希,具体取值参考 VIRTIO_CRYPTO_SYM_HASH_MODE_* 枚举
    le32 hash_mode;

    // 密码会话的固定部分
    struct virtio_crypto_cipher_session_flf cipher_hdr;

    // 算法特定头部的大小
    #define VIRTIO_CRYPTO_ALG_CHAIN_SESS_OP_SPEC_HDR_SIZE 16

    // 算法特定头部的固定字段,根据不同算法的需要而定
    u8 algo_flf[VIRTIO_CRYPTO_ALG_CHAIN_SESS_OP_SPEC_HDR_SIZE];

    // 附加认证数据(AAD)的长度,以字节为单位
    le32 aad_len;

    // 填充字段
    le32 padding;
};

// virtio_crypto_alg_chain_session_vlf 结构体的注释
struct virtio_crypto_alg_chain_session_vlf {
    /* 设备只读部分 */

    // 加密密钥
    u8 cipher_key[key_len];

    // 认证密钥
    u8 auth_key[auth_key_len];
};

hash_mode 决定了 algo_flf 使用的类型。
algo_flf 固定为 16 字节,并且必须包含或是以下类型之一:
• struct virtio_crypto_hash_create_session_flf
• struct virtio_crypto_mac_create_session_flf
未使用部分的数据(如果有的话)将被忽略。
cipher_key 的长度由 cipher_hdr 中的 key_len 指定。
auth_key 的长度由 struct virtio_crypto_mac_create_session_flf 中的 auth_key_len 指定。
对称会话请求的固定长度参数如下:

// virtio_crypto_sym_create_session_flf 结构体的注释
struct virtio_crypto_sym_create_session_flf {
    /* 设备只读部分 */

    // 算法会话操作特定头部的大小
    #define VIRTIO_CRYPTO_SYM_SESS_OP_SPEC_HDR_SIZE 48

    // 算法特定头部的固定字段,根据不同算法的需要而定
    u8 op_flf[VIRTIO_CRYPTO_SYM_SESS_OP_SPEC_HDR_SIZE];

    // 操作类型,可以是无操作、仅对数据进行加密或进行算法链操作(具体操作由 op_flf 决定)
    // 具体取值参考 VIRTIO_CRYPTO_SYM_OP_* 枚举
    le32 op_type;

    // 填充字段
    le32 padding;
};

op_flf 固定为 48 字节,必须包含或是以下类型之一:
• struct virtio_crypto_cipher_session_flf
• struct virtio_crypto_alg_chain_session_flf
未使用部分的数据(如果有的话)在 op_flf 中将被忽略。
op_type 决定了 op_flf 使用的类型。
对称会话请求的可变长度参数如下:

// virtio_crypto_sym_create_session_vlf 结构体的注释
struct virtio_crypto_sym_create_session_vlf {
    /* 设备只读部分 */

    // 操作码特定的可变长度字段,字段大小由 vlf_len 决定

    // 这些字段的具体内容和含义取决于所选操作码

    // 示例:对于操作码 VIRTIO_CRYPTO_SYM_OP_CIPHER,这些字段可能包含密钥等数据

    u8 op_vlf[vlf_len];
};

op_vlf 必须包含或是以下类型之一:
• struct virtio_crypto_cipher_session_vlf
• struct virtio_crypto_alg_chain_session_vlf
struct virtio_crypto_sym_create_session_flf 中的 op_type 决定了 op_vlf 使用的类型。
vlf_len 是所使用特定结构的大小。

5.9.7.2.1.4 会话操作:AEAD 会话

AEAD 会话请求的固定长度和可变长度参数如下:

// virtio_crypto_aead_create_session_flf 结构体的注释
struct virtio_crypto_aead_create_session_flf {
    /* 设备只读部分 */

    // 查看 VIRTIO_CRYPTO_AEAD_* 上面的注释以获取详细信息
    le32 algo;        // 算法类型
    le32 key_len;     // 密钥长度
    le32 tag_len;     // 鉴别标签长度
    le32 aad_len;     // 附加认证数据 (AAD) 的长度,以字节为单位
    le32 op;          // 加密或解密操作,参见 VIRTIO_CRYPTO_OP_* 上面的注释
    le32 padding;
};
// virtio_crypto_aead_create_session_vlf 结构体的注释
struct virtio_crypto_aead_create_session_vlf {
    /* 设备只读部分 */

    // 密钥,长度由 key_len 决定

    // 示例:用于 AEAD 加密和解密操作的密钥数据

    u8 key[key_len];
};

key 的长度由 struct virtio_crypto_aead_create_session_flf 中的 key_len 指定。

5.9.7.2.1.5 驱动程序要求:会话操作:创建会话

• 驱动程序必须根据服务类型设置操作码字段:CIPHER、HASH、MAC 或 AEAD。
• 驱动程序必须按顺序设置控制通用标头、操作码特定标头、操作码特定的额外参数和操作码特定的结果缓冲区。参见5.9.7.2。
• 驱动程序必须将 reversed 字段设置为零。

5.9.7.2.1.6 设备要求:会话操作:创建会话

• 设备必须根据控制通用标头中的操作码使用相应的操作码特定结构。
• 设备必须根据使用的结构提取额外参数。
• 在完成会话创建后,设备必须将 status 字段设置为以下 enum VIRTIO_CRYPTO_STATUS 中的一个值:
– 如果成功创建会话,则设置为 VIRTIO_CRYPTO_OK。
– 如果请求的算法或操作不受支持,则设置为 VIRTIO_CRYPTO_NOTSUPP。
– 如果没有可用的会话ID(仅当协商了 VIRTIO_CRYPTO_F_REVISION_-1 特性位时),则设置为 VIRTIO_CRYPTO_NOSPC。
– 如果发生未在上述提及的失败,则设置为 VIRTIO_CRYPTO_ERR。
• 仅当状态设置为 VIRTIO_CRYPTO_OK 时,设备必须将 session_id 字段设置为唯一的会话标识符。

5.9.7.2.1.7 驱动程序要求:会话操作:销毁会话

• 驱动程序必须根据服务类型设置操作码字段:CIPHER、HASH、MAC 或 AEAD。
• 驱动程序必须将 session_id 设置为设备在创建会话时分配的有效值。

5.9.7.2.1.8 设备要求:会话操作:销毁会话

• 设备必须将 status 字段设置为 enum VIRTIO_CRYPTO_STATUS 中的以下值之一。
– 如果成功创建会话,则设置为 VIRTIO_CRYPTO_OK。
– 如果发生任何失败,则设置为 VIRTIO_CRYPTO_ERR。

5.9.7.3 数据虚拟队列

驱动程序使用数据虚拟队列向设备传输加密操作请求,并完成加密操作。
数据队列的标头如下:

// virtio_crypto_op_header 结构体定义了虚拟加密设备的操作头部信息。

struct virtio_crypto_op_header {
    // opcode 字段用于指定操作的类型,可以使用以下宏来设置不同类型的操作。

    // VIRTIO_CRYPTO_CIPHER_ENCRYPT 宏表示加密操作,用于加密数据。
    // VIRTIO_CRYPTO_CIPHER_DECRYPT 宏表示解密操作,用于解密数据。
    // VIRTIO_CRYPTO_HASH 宏表示哈希操作,用于生成哈希值。
    // VIRTIO_CRYPTO_MAC 宏表示消息认证码(MAC)操作,用于验证消息完整性。
    // VIRTIO_CRYPTO_AEAD_ENCRYPT 宏表示 AEAD 加密操作,同时提供加密和完整性验证。
    // VIRTIO_CRYPTO_AEAD_DECRYPT 宏表示 AEAD 解密操作,用于解密和验证数据完整性。
    
    le32 opcode;

    // algo 字段用于指定算法类型,具体算法类型应该是与服务相关的算法。
    // 不同服务可能支持不同的算法,这里的 algo 字段可以用于指定特定的算法。
    
    le32 algo;

    // session_id 字段用于指定与操作关联的会话 ID。
    // 操作可能需要在特定的会话中执行,session_id 字段用于指定会话的唯一标识符。

    le64 session_id;

    // flag 字段用于设置控制标志,可以影响操作的行为。
    // VIRTIO_CRYPTO_FLAG_SESSION_MODE 表示会话模式,指示操作将在会话中执行。
    // 具体控制标志的含义和用法可能取决于具体的操作和服务。

    le32 flag;

    // padding 用于填充结构体的字节,以确保整个结构体的大小符合对齐要求。
    
    le32 padding;
};

注意: 如果未协商 VIRTIO_CRYPTO_F_REVISION_1,则忽略该标志。
如果协商了 VIRTIO_CRYPTO_F_REVISION_1,但未协商 VIRTIO_CRYPTO_F__STATELESS_MODE,则设备应拒绝 请求,如果 flag 中未设置 VIRTIO_CRYPTO_FLAG_SESSION_MODE。

数据队列请求由四个部分组成:

// virtio_crypto_op_data_req 结构体定义了虚拟加密设备的数据操作请求。

struct virtio_crypto_op_data_req {
    // header 字段包含了操作的头部信息,它描述了操作的类型、算法等信息。
    struct virtio_crypto_op_header header;

    // op_flf 字段是操作特定的固定长度字段,其长度由 flf_len 决定。
    // 这些字段用于传递操作所需的固定信息。

    u8 op_flf[flf_len];

    // op_vlf 字段是操作特定的可变长度字段,其长度由 vlf_len 决定。
    // 这些字段用于传递操作所需的可变信息,具体含义和用法取决于操作类型和算法。

    u8 op_vlf[vlf_len];

    // inhdr 字段包含了输入头部信息,它描述了输入数据的属性和特征。
    // 输入头部信息可能包括数据长度、数据类型等信息,具体取决于操作类型和算法。

    struct virtio_crypto_inhdr inhdr;
};

  • header 是通用标头(见上文)。
  • op_flf 是操作码(在标头中)特定的标头。
  • flf_len 取决于 VIRTIO_CRYPTO_F_REVISION_1 特性位(见下文)。
  • op_vlf 是操作码(在标头中)特定的参数。
  • vlf_len 是所使用特定结构的大小。
    • 如果操作码(在标头中)为 VIRTIO_CRYPTO_CIPHER_ENCRYPT 或 VIRTIO_CRYPTO_CIPHER_DECRYPT,则:
    – 如果已协商 VIRTIO_CRYPTO_F_CIPHER_STATELESS_MODE,则 op_flf 为 struct virtio_crypto_-
    sym_data_flf_stateless,op_vlf 为 struct virtio_crypto_sym_data_vlf_stateless。
    – 如果未协商 VIRTIO_CRYPTO_F_CIPHER_STATELESS_MODE,则 op_flf 为 struct virtio_crypto_-
    sym_data_flf,如果已协商 VIRTIO_CRYPTO_F_REVISION_1,而未协商 VIRTIO_CRYPTO_F_
    CIPHER_STATELESS_MODE,则 struct virtio_crypto_sym_data_flf 会填充至 48 字节,op_vlf 为 struct virtio_crypto_sym_data_vlf。
    • 如果操作码(在标头中)为 VIRTIO_CRYPTO_HASH,则:
    – 如果已协商 VIRTIO_CRYPTO_F_HASH_STATELESS_MODE,则 op_flf 为 struct virtio_crypto_-
    hash_data_flf_stateless,op_vlf 为 struct virtio_crypto_hash_data_vlf_stateless。
    – 如果未协商 VIRTIO_CRYPTO_F_HASH_STATELESS_MODE,则 op_flf 为 struct virtio_crypto_-
    hash_data_flf,如果已协商 VIRTIO_CRYPTO_F_REVISION_1,而未协商 VIRTIO_CRYPTO_F_
    HASH_STATELESS_MODE,则 struct virtio_crypto_hash_data_flf 会填充至 48 字节,op_vlf 为 struct virtio_crypto_hash_data_vlf。
    • 如果操作码(在标头中)为 VIRTIO_CRYPTO_MAC,则:
    – 如果已协商 VIRTIO_CRYPTO_F_MAC_STATELESS_MODE,则 op_flf 为 struct virtio_crypto_-
    mac_data_flf_stateless,op_vlf 为 struct virtio_crypto_mac_data_vlf_stateless。
    – 如果未协商 VIRTIO_CRYPTO_F_MAC_STATELESS_MODE,则 op_flf 为 struct virtio_crypto_-
    mac_data_flf,如果已协商 VIRTIO_CRYPTO_F_REVISION_1,而未协商 VIRTIO_CRYPTO_F_
    MAC_STATELESS_MODE,则 struct virtio_crypto_mac_data_flf 会填充至 48 字节,op_vlf 为 struct virtio_crypto_mac_data_vlf。
    • 如果操作码(在标头中)为 VIRTIO_CRYPTO_AEAD_ENCRYPT 或 VIRTIO_CRYPTO_AEAD_DECRYPT,则:
    – 如果已协商 VIRTIO_CRYPTO_F_AEAD_STATELESS_MODE,则 op_flf 为 struct virtio_crypto_-
    aead_data_flf_stateless,op_vlf 为 struct virtio_crypto_aead_data_vlf_stateless。
    – 如果未协商 VIRTIO_CRYPTO_F_AEAD_STATELESS_MODE,则 op_flf 为 struct virtio_crypto_-
    aead_data_flf,如果已协商 VIRTIO_CRYPTO_F_REVISION_1,而未协商 VIRTIO_CRYPTO_F_
    AEAD_STATELESS_MODE,则 struct virtio_crypto_aead_data_flf 会填充至 48 字节,op_vlf 为 struct virtio_crypto_aead_data_vlf。
  • inhdr 是一个统一的输入标头,用于返回操作的状态,定义如下:
// virtio_crypto_inhdr 结构体定义了虚拟加密设备的输入头部信息。

struct virtio_crypto_inhdr {
    // status 字段包含了输入数据的状态信息。
    // 具体含义和用法取决于操作类型和算法,通常用于表示数据的有效性或其他特征。

    u8 status;
};

5.9.7.4 HASH 服务操作

会话模式 HASH 服务请求如下:

// virtio_crypto_hash_data_flf 结构体定义了虚拟加密设备哈希操作的固定长度字段。

struct virtio_crypto_hash_data_flf {
    // src_data_len 字段表示源数据的长度,以字节为单位。
    le32 src_data_len;
    
    // hash_result_len 字段表示哈希结果的长度,以字节为单位。
    le32 hash_result_len;
};

// virtio_crypto_hash_data_vlf 结构体定义了虚拟加密设备哈希操作的可变长度字段。

struct virtio_crypto_hash_data_vlf {
    // src_data 字段表示源数据,即待哈希的数据。
    // 这是一个设备只读部分,包含了源数据的内容。
    u8 src_data[src_data_len];
    
    // hash_result 字段表示哈希操作的结果数据。
    // 这是一个设备可写部分,用于存储哈希操作的结果。
    u8 hash_result[hash_result_len];
};

每个数据请求使用 virtio_crypto_hash_data_flf 结构和 virtio_crypto_hash_data_vlf 结构来存储用于执行 HASH 操作的信息。
src_data 是将被处理的源数据,src_data_len 是源数据的长度。hash_result 是结果数据,hash_result_len 是其长度。
无状态模式 HASH 服务请求如下:

// virtio_crypto_hash_data_flf_stateless 结构体定义了虚拟加密设备哈希操作的有状态(stateless)版本的固定长度字段。

struct virtio_crypto_hash_data_flf_stateless {
    // sess_para 字段包含了哈希操作会话的参数。
    struct {
        // algo 字段表示哈希算法的类型,参考 VIRTIO_CRYPTO_HASH_*。
        le32 algo;
    } sess_para;
    
    // src_data_len 字段表示源数据的长度,以字节为单位。
    le32 src_data_len;
    
    // hash_result_len 字段表示哈希结果的长度,以字节为单位。
    le32 hash_result_len;
    
    // reserved 字段为保留字段,暂时未使用。
    le32 reserved;
};

// virtio_crypto_hash_data_vlf_stateless 结构体定义了虚拟加密设备哈希操作的有状态(stateless)版本的可变长度字段。

struct virtio_crypto_hash_data_vlf_stateless {
    // src_data 字段表示源数据,即待哈希的数据。
    // 这是一个设备只读部分,包含了源数据的内容。
    u8 src_data[src_data_len];
    
    // hash_result 字段表示哈希操作的结果数据。
    // 这是一个设备可写部分,用于存储哈希操作的结果。
    u8 hash_result[hash_result_len];
};

5.9.7.4.1 驱动程序要求:HASH 服务操作

• 如果驱动程序使用会话模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 session_id 设置为设备在创建会话时分配的有效值。
• 如果已协商 VIRTIO_CRYPTO_F_HASH_STATELESS_MODE 特性位,1) 如果驱动程序使用无状态模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 flag 字段设置为零,并且必须设置 struct virtio_crypto_hash_data_flf_stateless.sess_para 中的字段,2) 如果驱动程序使用会话模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 flag 字段设置为 VIRTIO_CRYPTO_FLAG_SESSION_MODE。
• 驱动程序必须将 struct virtio_crypto_op_header 中的 opcode 设置为 VIRTIO_CRYPTO_HASH。

5.9.7.4.2 设备要求:HASH 服务操作

• 设备必须根据数据通用标头中的操作码使用相应的结构。
• 如果已协商 VIRTIO_CRYPTO_F_HASH_STATELESS_MODE 特性位,则设备必须解析 struct virtio_crypto_op_header 中的 flag 字段,以决定驱动程序使用的模式。
• 如果 HASH 操作成功,设备必须将 HASH 操作的结果复制到 hash_result[] 中。
• 设备必须将 struct virtio_crypto_inhdr 中的 status 设置为 enum VIRTIO_CRYPTO_STATUS 中的以下值之一:
– 如果操作成功,则设置为 VIRTIO_CRYPTO_OK。
– 如果请求的算法或操作不受支持,则设置为 VIRTIO_CRYPTO_NOTSUPP。
– 如果在会话模式下会话 ID 无效,则设置为 VIRTIO_CRYPTO_INVSESS。
– 如果发生未在上述提及的任何失败,则设置为 VIRTIO_CRYPTO_ERR。

5.9.7.5 MAC 服务操作

会话模式 MAC 服务请求如下:

// virtio_crypto_mac_data_flf 结构体定义了虚拟加密设备MAC(消息认证码)操作的固定长度字段。

struct virtio_crypto_mac_data_flf {
    // hdr 字段是一个 virtio_crypto_hash_data_flf 结构体,
    // 用于存储 MAC 操作所需的哈希操作的固定长度字段。
    struct virtio_crypto_hash_data_flf hdr;
};

// virtio_crypto_mac_data_vlf 结构体定义了虚拟加密设备MAC(消息认证码)操作的可变长度字段。

struct virtio_crypto_mac_data_vlf {
    // src_data 字段表示源数据,即待进行 MAC 计算的数据。
    // 这是一个设备只读部分,包含了源数据的内容。
    u8 src_data[src_data_len];
    
    // hash_result 字段表示 MAC 操作的结果数据。
    // 这是一个设备可写部分,用于存储 MAC 操作的结果。
    u8 hash_result[hash_result_len];
};

每个请求使用 virtio_crypto_mac_data_flf 结构和 virtio_crypto_mac_data_vlf 结构来存储用于执行 MAC 操作的信息。
src_data 是将被处理的源数据,src_data_len 是源数据的长度。hash_result 是结果数据,hash_result_len 是其长度。
无状态模式 MAC 服务请求如下:

struct virtio_crypto_mac_data_flf_stateless {
    struct {
        /* 参见上面的 VIRTIO_CRYPTO_MAC_* */
        le32 algo;
        /* 认证密钥的长度 */
        le32 auth_key_len;
    } sess_para;

    /* 源数据的长度 */
    le32 src_data_len;

    /* 哈希结果的长度 */
    le32 hash_result_len;
};

struct virtio_crypto_mac_data_vlf_stateless {
    /* 设备只读部分 */
    
    /* 认证密钥 */
    u8 auth_key[auth_key_len];

    /* 源数据 */
    u8 src_data[src_data_len];

    /* 设备只写部分 */

    /* 哈希结果数据 */
    u8 hash_result[hash_result_len];
};

auth_key 是在过程中将要使用的身份验证密钥。auth_key_len 是密钥的长度。

5.9.7.5.1 驱动程序要求:MAC 服务操作

• 如果驱动程序使用会话模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 session_id 设置为设备在创建会话时分配的有效值。
• 如果已协商 VIRTIO_CRYPTO_F_MAC_STATELESS_MODE 特性位,1) 如果驱动程序使用无状态模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 flag 字段设置为零,并且必须设置 struct virtio_crypto_mac_data_flf_stateless.sess_para 中的字段,2) 如果驱动程序使用会话模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 flag 字段设置为 VIRTIO_CRYPTO_FLAG_SESSION_MODE。
• 驱动程序必须将 struct virtio_crypto_op_header 中的 opcode 设置为 VIRTIO_CRYPTO_MAC。

5.9.7.5.2 设备要求:MAC 服务操作

• 如果已协商 VIRTIO_CRYPTO_F_MAC_STATELESS_MODE 特性位,设备必须解析 struct virtio_crypto_op_header 中的 flag 字段,以确定驱动程序使用的模式。
• 如果 HASH 操作成功,设备必须将 HASH 操作的结果复制到 hash_result[] 中。
• 设备必须将 struct virtio_crypto_inhdr 中的 status 设置为 enum VIRTIO_CRYPTO_STATUS 中的以下值之一:
– 如果操作成功,则设置为 VIRTIO_CRYPTO_OK。
– 如果请求的算法或操作不受支持,则设置为 VIRTIO_CRYPTO_NOTSUPP。
– 如果在会话模式下会话 ID 无效,则设置为 VIRTIO_CRYPTO_INVSESS。
– 如果发生未在上述提及的任何失败,则设置为 VIRTIO_CRYPTO_ERR。

5.9.7.6 对称算法操作

会话模式 CIPHER 服务请求如下:

// virtio_crypto_cipher_data_flf 结构体定义了虚拟加密设备对数据进行加密或解密操作时的固定长度字段。

struct virtio_crypto_cipher_data_flf {
    // iv_len 字段表示有效的 IV(初始化向量)或计数器数据的字节长度。
    // 对于在CBC或F8模式下的块密码、Kasumi中的F8模式、SNOW3G中的UEA2模式,
    // 这是IV的长度(必须与密码的块长度相同)。
    // 对于在CTR模式下的块密码,这是计数器的长度(必须与密码的块长度相同)。
    le32 iv_len;

    // src_data_len 字段表示源数据的长度,即待加密或解密的数据的长度。
    le32 src_data_len;

    // dst_data_len 字段表示目标数据的长度,即加密或解密操作的结果数据的长度。
    le32 dst_data_len;

    le32 padding;
};

// virtio_crypto_cipher_data_vlf 结构体定义了虚拟加密设备对数据进行加密或解密操作时的可变长度字段。

struct virtio_crypto_cipher_data_vlf {
    // iv 字段表示初始化向量(IV)或计数器数据。
    // 对于在CBC或F8模式下的块密码、Kasumi中的F8模式、SNOW3G中的UEA2模式,
    // 这是IV的值。
    // 对于在CTR模式下的块密码,这是计数器。
    // 对于AES-XTS,这是IEEE Std 1619-2007中的128位tweak值。
    // IV/Counter在每次部分加密操作之后将被更新。
    u8 iv[iv_len];

    // src_data 字段表示源数据,即待加密或解密的数据。
    // 这是设备只读部分,包含了源数据的内容。
    u8 src_data[src_data_len];

    // dst_data 字段表示目标数据,即加密或解密操作的结果数据。
    // 这是设备可写部分,用于存储加密或解密操作的结果。
    u8 dst_data[dst_data_len];
};

算法链的会话模式请求如下:

// virtio_crypto_alg_chain_data_flf 结构体定义了虚拟加密设备执行算法链操作时的固定长度字段。

struct virtio_crypto_alg_chain_data_flf {
    // iv_len 字段表示有效的 IV(初始化向量)或计数器数据的字节长度。
    le32 iv_len;

    // src_data_len 字段表示源数据的总长度,即待处理的数据的总长度。
    le32 src_data_len;

    // dst_data_len 字段表示目标数据的长度,即算法链操作的结果数据的长度。
    le32 dst_data_len;

    // cipher_start_src_offset 字段表示源数据中用于密码算法处理的起始位置的偏移量。
    le32 cipher_start_src_offset;

    // len_to_cipher 字段表示密码算法将要处理的源数据的长度。
    le32 len_to_cipher;

    // hash_start_src_offset 字段表示源数据中用于哈希算法处理的起始位置的偏移量。
    le32 hash_start_src_offset;

    // len_to_hash 字段表示哈希算法将要处理的源数据的长度。
    le32 len_to_hash;

    // aad_len 字段表示附加认证数据(AAD)的长度。
    le32 aad_len;

    // hash_result_len 字段表示哈希结果数据的长度。
    le32 hash_result_len;

    le32 reserved;
};

// virtio_crypto_alg_chain_data_vlf 结构体定义了虚拟加密设备执行算法链操作时的可变长度字段。

struct virtio_crypto_alg_chain_data_vlf {
    // iv 字段表示初始化向量(IV)或计数器数据。
    u8 iv[iv_len];

    // src_data 字段表示源数据,即待处理的数据。
    // 这是设备只读部分,包含了源数据的内容。
    u8 src_data[src_data_len];

    // aad 字段表示附加认证数据(AAD),如果存在的话。
    u8 aad[aad_len];

    // dst_data 字段表示目标数据,即算法链操作的结果数据。
    // 这是设备可写部分,用于存储算法链操作的结果。
    u8 dst_data[dst_data_len];

    // hash_result 字段表示哈希结果数据。
    u8 hash_result[hash_result_len];
};

对称算法的会话模式请求如下:

// virtio_crypto_sym_data_flf 结构体定义了虚拟加密设备执行对称算法操作时的固定长度字段。

struct virtio_crypto_sym_data_flf {
    // op_type_flf 字段表示用于对称算法操作的固定长度字段,其大小为 VIRTIO_CRYPTO_SYM_DATA_REQ_HDR_SIZE 字节。
    u8 op_type_flf[VIRTIO_CRYPTO_SYM_DATA_REQ_HDR_SIZE];

    // op_type 字段表示对称算法操作的类型,具体取值参见 VIRTIO_CRYPTO_SYM_OP_* 宏定义。
    le32 op_type;

    le32 padding;
};

// virtio_crypto_sym_data_vlf 结构体定义了虚拟加密设备执行对称算法操作时的可变长度字段。

struct virtio_crypto_sym_data_vlf {
    // op_type_vlf 字段表示用于对称算法操作的可变长度字段,其大小为 sym_para_len 字节。
    u8 op_type_vlf[sym_para_len];
};

每个请求使用 virtio_crypto_sym_data_flf 结构和 virtio_crypto_sym_data_flf 结构来存储用于执行 CIPHER 操作的信息。
op_type_flf 是 op_type 特定的标头,它必须以以下结构之一开始或是其中之一:
• struct virtio_crypto_cipher_data_flf
• struct virtio_crypto_alg_chain_data_flf
op_type_flf 的长度固定为 40 字节,未使用部分的数据(如果有的话)将被忽略。
op_type_vlf 是 op_type 特定的参数,它必须以以下结构之一开始或是其中之一:
• struct virtio_crypto_cipher_data_vlf
• struct virtio_crypto_alg_chain_data_vlf
sym_para_len 是所使用特定结构的大小。
无状态模式 CIPHER 服务请求如下:

// virtio_crypto_cipher_data_flf_stateless 结构体定义了虚拟加密设备在无状态模式下执行对称密码算法操作时的固定长度字段。

struct virtio_crypto_cipher_data_flf_stateless {
    struct {
        // sess_para 结构体用于存储与算法会话相关的参数。
        // algo 字段表示所使用的对称密码算法,具体取值参见 VIRTIO_CRYPTO_CIPHER* 宏定义。
        le32 algo;
        // key_len 字段表示密钥的长度。
        le32 key_len;
        // op 字段表示对称密码算法操作的类型,具体取值参见 VIRTIO_CRYPTO_OP_* 宏定义。
        le32 op;
    } sess_para;

    // iv_len 字段表示有效的初始化向量(IV)或计数器数据的字节长度。
    le32 iv_len;
    // src_data_len 字段表示源数据的字节长度。
    le32 src_data_len;
    // dst_data_len 字段表示目标数据的字节长度。
    le32 dst_data_len;
};

// virtio_crypto_cipher_data_vlf_stateless 结构体定义了虚拟加密设备在无状态模式下执行对称密码算法操作时的可变长度字段。

struct virtio_crypto_cipher_data_vlf_stateless {
    // cipher_key 字段用于存储对称密码算法所需的密钥。
    u8 cipher_key[key_len];
    // iv 字段用于存储初始化向量(IV)或计数器数据。
    u8 iv[iv_len];
    // src_data 字段用于存储源数据。
    u8 src_data[src_data_len];
    // dst_data 字段用于存储目标数据。
    u8 dst_data[dst_data_len];
};

无状态模式算法链的请求如下:

// virtio_crypto_alg_chain_data_flf_stateless 结构体定义了虚拟加密设备在无状态模式下执行算法链操作时的固定长度字段。

struct virtio_crypto_alg_chain_data_flf_stateless {
    struct {
        // sess_para 结构体用于存储与算法链会话相关的参数。
        struct {
            // alg_chain_order 字段表示算法链的顺序,具体取值参见 VIRTIO_CRYPTO_SYM_ALG_CHAIN_ORDER_* 宏定义。
            le32 alg_chain_order;
            // aad_len 字段表示附加认证数据(AAD)的字节长度。
            le32 aad_len;
            struct {
                // algo 字段表示对称密码算法,具体取值参见 VIRTIO_CRYPTO_CIPHER* 宏定义。
                le32 algo;
                // key_len 字段表示密钥的长度。
                le32 key_len;
                // op 字段表示对称密码算法操作的类型,具体取值参见 VIRTIO_CRYPTO_OP_* 宏定义。
                le32 op;
            } cipher;
            struct {
                // algo 字段表示哈希或消息认证码算法,具体取值参见 VIRTIO_CRYPTO_HASH_* 或 VIRTIO_CRYPTO_MAC_* 宏定义。
                le32 algo;
                // auth_key_len 字段表示认证密钥的长度。
                le32 auth_key_len;
                // hash_mode 字段表示哈希模式,具体取值参见 VIRTIO_CRYPTO_SYM_HASH_MODE_* 宏定义。
                le32 hash_mode;
            } hash;
        } sess_para;

        // iv_len 字段表示有效的初始化向量(IV)或计数器数据的字节长度。
        le32 iv_len;
        // src_data_len 字段表示源数据的字节长度。
        le32 src_data_len;
        // dst_data_len 字段表示目标数据的字节长度。
        le32 dst_data_len;
        // cipher_start_src_offset 字段表示在源数据中开始进行对称密码处理的偏移量。
        le32 cipher_start_src_offset;
        // len_to_cipher 字段表示对称密码算法操作处理的源数据长度。
        le32 len_to_cipher;
        // hash_start_src_offset 字段表示在源数据中开始进行哈希处理的偏移量。
        le32 hash_start_src_offset;
        // len_to_hash 字段表示哈希算法操作处理的源数据长度。
        le32 len_to_hash;
        // aad_len 字段表示附加认证数据(AAD)的字节长度。
        le32 aad_len;
        // hash_result_len 字段表示哈希结果的字节长度。
        le32 hash_result_len;
        le32 reserved;
    } sess_para;

    // virtio_crypto_alg_chain_data_vlf_stateless 结构体定义了虚拟加密设备在无状态模式下执行算法链操作时的可变长度字段。

    struct virtio_crypto_alg_chain_data_vlf_stateless {
        // cipher_key 字段用于存储对称密码算法所需的密钥。
        u8 cipher_key[key_len];
        // auth_key 字段用于存储认证密钥。
        u8 auth_key[auth_key_len];
        // iv 字段用于存储初始化向量(IV)或计数器数据。
        u8 iv[iv_len];
        // aad 字段用于存储附加认证数据(AAD)。
        u8 aad[aad_len];
        // src_data 字段用于存储源数据。
        u8 src_data[src_data_len];
        // dst_data 字段用于存储目标数据。
        u8 dst_data[dst_data_len];
        // hash_result 字段用于存储哈希结果数据。
        u8 hash_result[hash_result_len];
    };
};

无状态模式对称算法的请求如下:

// virtio_crypto_sym_data_flf_stateless 结构体定义了虚拟加密设备在无状态模式下执行对称密码操作时的固定长度字段。

struct virtio_crypto_sym_data_flf_stateless {
    /* Device read only portion */
    #define VIRTIO_CRYPTO_SYM_DATE_REQ_HDR_STATELESS_SIZE 72
    // op_type_flf 字段用于存储虚拟加密设备执行对称密码操作的固定长度字段。
    u8 op_type_flf[VIRTIO_CRYPTO_SYM_DATE_REQ_HDR_STATELESS_SIZE];

    /* Device write only portion */
    // op_type 字段表示对称密码操作的类型,具体取值参见 VIRTIO_CRYPTO_SYM_OP_* 宏定义。
    le32 op_type;
};

// virtio_crypto_sym_data_vlf_stateless 结构体定义了虚拟加密设备在无状态模式下执行对称密码操作时的可变长度字段。

struct virtio_crypto_sym_data_vlf_stateless {
    // op_type_vlf 字段用于存储虚拟加密设备执行对称密码操作的可变长度字段。
    u8 op_type_vlf[sym_para_len];
};

  • op_type_flf 是 op_type 特定的标头,它必须以以下结构之一开始或是其中之一:
    • struct virtio_crypto_cipher_data_flf_stateless
    • struct virtio_crypto_alg_chain_data_flf_stateless
    op_type_flf 的长度固定为 72 字节,未使用部分的数据(如果有的话)将被忽略。
  • op_type_vlf 是 op_type 特定的参数,它必须以以下结构之一开始或是其中之一:
    • struct virtio_crypto_cipher_data_vlf_stateless
    • struct virtio_crypto_alg_chain_data_vlf_stateless
  • sym_para_len 是所使用特定结构的大小。
5.9.7.6.1 驱动程序要求:对称算法操作

• 如果驱动程序使用会话模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 session_id 设置为设备在创建会话时分配的有效值。
• 如果已协商 VIRTIO_CRYPTO_F_CIPHER_STATELESS_MODE 特性位,1) 如果驱动程序使用无状态模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 flag 字段设置为零,并且必须设置 struct virtio_crypto_cipher_data_flf_stateless.sess_para 或 struct virtio_crypto_alg_chain_data_flf_stateless.sess_para 中的字段,2) 如果驱动程序使用会话模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 flag 字段设置为 VIRTIO_CRYPTO_FLAG_SESSION_MODE。
• 驱动程序必须将 struct virtio_crypto_op_header 中的 opcode 字段设置为 VIRTIO_CRYPTO_CIPHER_ENCRYPT 或 VIRTIO_CRYPTO_CIPHER_DECRYPT。
• 如果请求基于 VIRTIO_CRYPTO_SYM_OP_CIPHER,则驱动程序必须在 struct virtio_crypto_sym_data_flf 中指定 struct virtio_crypto_cipher_data_flf 的字段,在 struct virtio_crypto_sym_data_vlf 中指定 struct virtio_crypto_cipher_data_vlf 的字段。
• 如果请求类型为 VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING,则驱动程序必须在 struct virtio_crypto_sym_data_flf 中指定 struct virtio_crypto_alg_chain_data_flf 的字段,在 struct virtio_crypto_sym_data_vlf 中指定 struct virtio_crypto_alg_chain_data_vlf 的字段。

5.9.7.6.2 设备要求:对称算法操作

• 如果已协商 VIRTIO_CRYPTO_F_CIPHER_STATELESS_MODE 特性位,则设备必须解析 struct virtio_crypto_op_header 中的 flag 字段,以确定驱动程序使用的模式。
• 设备必须根据通用标头中的操作码字段解析 virtio_crypto_sym_data_req。
• 如果请求基于 VIRTIO_CRYPTO_SYM_OP_CIPHER,则设备必须在 struct virtio_crypto_sym_data_flf 中解析 struct virtio_crypto_cipher_data_flf 的字段,在 struct virtio_crypto_sym_data_vlf 中解析 struct virtio_crypto_cipher_data_vlf 的字段。
• 如果请求类型为 VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING,则设备必须在 struct virtio_crypto_sym_data_flf 中解析 struct virtio_crypto_alg_chain_data_flf 的字段,在 struct virtio_crypto_sym_data_vlf 中解析 struct virtio_crypto_alg_chain_data_vlf 的字段。
• 设备必须将加密操作的结果复制到 plain CIPHER 模式和算法链模式中的 dst_data[] 中。
• 设备必须在解析 plain 算法链模式中的附加认证数据之前检查 para.add_len 是否大于 0。
• 设备必须将 HASH/MAC 操作的结果复制到 hash_result[] 中,如果请求类型为 VIRTIO_CRYPTO_SYM_OP_ALGORITHM_CHAINING。
• 设备必须将 struct virtio_crypto_inhdr 中的 status 字段设置为 enum VIRTIO_CRYPTO_STATUS 中的以下值之一:
– 如果操作成功,则设置为 VIRTIO_CRYPTO_OK。
– 如果请求的算法或操作不受支持,则设置为 VIRTIO_CRYPTO_NOTSUPP。
– 如果在会话模式下会话 ID 无效,则设置为 VIRTIO_CRYPTO_INVSESS。
– 如果发生未在上述提及的任何失败,则设置为 VIRTIO_CRYPTO_ERR。

5.9.7.7 AEAD 服务操作

对称算法的会话模式请求如下:

// virtio_crypto_aead_data_flf 结构体定义了虚拟加密设备在进行AEAD(Authenticated Encryption with Associated Data)操作时的固定长度字段。

struct virtio_crypto_aead_data_flf {
    /*
    * 有效 IV 数据的字节长度。
    *
    * 对于 GCM 模式,这可以是 12(用于 96 位 IV)或 16,此时 iv 指向 J0。
    * 对于 CCM 模式,这是 nonce 的长度,范围可以在 7 到 13 之间。
    */
    le32 iv_len;

    /* 附加认证数据的长度 */
    le32 aad_len;

    /* 源数据的长度 */
    le32 src_data_len;

    /* 目标数据的长度,至少应为 src_data_len + tag_len */
    le32 dst_data_len;

    /* 认证标签长度 */
    le32 tag_len;

    le32 reserved;
};

// virtio_crypto_aead_data_vlf 结构体定义了虚拟加密设备在进行AEAD(Authenticated Encryption with Associated Data)操作时的可变长度字段。

struct virtio_crypto_aead_data_vlf {
    /* 设备只读部分 */

    /*
    * 初始化向量数据。
    *
    * 对于 GCM 模式,这要么是 IV(如果长度为 96 位),要么是 J0
    * (对于其他长度),其中 J0 的定义由 NIST SP800-38D 规定。
    * 无论 IV 长度如何,都需要分配完整的 16 字节。
    * 对于 CCM 模式,第一个字节是保留的,随后的 nonce 应从 &iv[1] 开始写入
    * (以允许空间以写入第一个字节中的标志位)。请注意,应分配完整的 16 字节,
    * 即使 iv_len 字段的值较小。
    *
    * IV 将在每次部分加密操作后更新。
    */
    u8 iv[iv_len];

    /* 源数据 */
    u8 src_data[src_data_len];

    /* 如果存在,附加认证数据 */
    u8 aad[aad_len];

    /* 设备只写部分 */

    /* 指向输出数据的指针 */
    u8 dst_data[dst_data_len];
};

每个请求使用 virtio_crypto_aead_data_flf 结构和 virtio_crypto_aead_data_flf 结构来存储用于执行 AEAD 操作的信息。
无状态模式 AEAD 服务请求如下:

// virtio_crypto_aead_data_flf_stateless 结构体定义了虚拟加密设备在进行 AEAD(Authenticated Encryption with Associated Data)无状态操作时的固定长度字段。

struct virtio_crypto_aead_data_flf_stateless {
    struct {
        /* 请参见上述的 VIRTIO_CRYPTO_AEAD_* */
        le32 algo;

        /* 密钥长度 */
        le32 key_len;

        /* 加密或解密,请参见上述的 VIRTIO_CRYPTO_OP_* */
        le32 op;
    } sess_para;

    /* 有效 IV 数据的字节长度 */
    le32 iv_len;

    /* 认证标签长度 */
    le32 tag_len;

    /* 附加认证数据的长度 */
    le32 aad_len;

    /* 源数据的长度 */
    le32 src_data_len;

    /* 目标数据的长度,至少应为 src_data_len + tag_len */
    le32 dst_data_len;
};

// virtio_crypto_aead_data_vlf_stateless 结构体定义了虚拟加密设备在进行 AEAD(Authenticated Encryption with Associated Data)无状态操作时的可变长度字段。

struct virtio_crypto_aead_data_vlf_stateless {
    /* 设备只读部分 */

    /* 密钥 */
    u8 key[key_len];

    /* 初始化向量数据 */
    u8 iv[iv_len];

    /* 源数据 */
    u8 src_data[src_data_len];

    /* 如果存在,附加认证数据 */
    u8 aad[aad_len];

    /* 设备只写部分 */

    /* 指向输出数据的指针 */
    u8 dst_data[dst_data_len];
};

5.9.7.7.1 驱动程序要求:AEAD 服务操作

• 如果驱动程序使用会话模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 session_id 设置为设备在创建会话时分配的有效值。
• 如果已协商 VIRTIO_CRYPTO_F_AEAD_STATELESS_MODE 特性位,1) 如果驱动程序使用无状态模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 flag 字段设置为零,并且必须设置 struct virtio_crypto_aead_data_flf_stateless.sess_para 中的字段,2) 如果驱动程序使用会话模式,则驱动程序必须将 struct virtio_crypto_op_header 中的 flag 字段设置为 VIRTIO_CRYPTO_FLAG_SESSION_MODE。
• 驱动程序必须将 struct virtio_crypto_op_header 中的 opcode 字段设置为 VIRTIO_CRYPTO_AEAD_ENCRYPT 或 VIRTIO_CRYPTO_AEAD_DECRYPT。

5.9.7.7.2 设备要求:AEAD 服务操作

• 如果已协商 VIRTIO_CRYPTO_F_AEAD_STATELESS_MODE 特性位,设备必须根据通用标头中的 opcode 字段解析 virtio_crypto_aead_data_vlf_stateless。
• 设备必须将加密操作的结果复制到 dst_data[] 中。
• 设备必须将身份验证标签复制到 dst_data[] 偏移位置的加密结果中。
• 设备必须将 struct virtio_crypto_inhdr 中的 status 字段设置为 enum VIRTIO_CRYPTO_STATUS 中的以下值之一:
– 当 opcode 字段为 VIRTIO_CRYPTO_AEAD_DECRYPT 时,设备必须验证并将验证结果返回给驱动程序。
– 如果操作成功,则设置为 VIRTIO_CRYPTO_OK。
– 如果请求的算法或操作不受支持,则设置为 VIRTIO_CRYPTO_NOTSUPP。
– 如果验证结果不正确,则设置为 VIRTIO_CRYPTO_BADMSG。
– 如果在会话模式下会话 ID 无效,则设置为 VIRTIO_CRYPTO_INVSESS。
– 如果发生未在上述提及的任何失败,则设置为 VIRTIO_CRYPTO_ERR。

5.10 套接字设备

virtio 套接字设备是一种零配置的套接字通信设备。它通过不使用以太网或 IP 协议来促进了来宾和设备之间的数据传输。

5.10.1 设备 ID

19

5.10.2 Virtqueues

0 rx
1 tx
2 event

5.10.3 特性位

目前尚未为此设备定义任何特性位。

5.10.4 设备配置布局

套接字设备配置使用以下布局结构:

// virtio_vsock_config 结构体定义了 virtio-vsock 设备的配置参数。

struct virtio_vsock_config {
    le64 guest_cid;  // 客户机 ID
};

guest_cid 字段包含了来宾的上下文 ID,用于在其生命周期内唯一标识设备。CID 的高 32 位被保留并置零。
以下 CID 被保留,不能用作来宾的上下文 ID:
第三篇------Virtual I/O Device (VIRTIO) Version 1.1_第1张图片

5.10.5 设备初始化

  1. 来宾的 cid 从 guest_cid 中读取。
  2. 将缓冲区添加到事件 virtqueue 以接收来自设备的事件。
  3. 将缓冲区添加到 rx virtqueue 以开始接收数据包。

5.10.6 设备操作

传输或接收的数据包在有效载荷之前包含一个头部:

// virtio_vsock_hdr 结构体定义了 virtio-vsock 设备的头部信息。

struct virtio_vsock_hdr {
    le64 src_cid;     // 源端客户机 ID
    le64 dst_cid;     // 目标端客户机 ID
    le32 src_port;    // 源端端口号
    le32 dst_port;    // 目标端端口号
    le32 len;         // 数据长度
    le16 type;        // 类型
    le16 op;          // 操作
    le32 flags;       // 标志
    le32 buf_alloc;   // 缓冲区分配
    le32 fwd_cnt;     // 转发计数
};

src_cid 和 dst_cid 的高 32 位被保留并置零。
大多数数据包只是传输数据,但控制数据包也用于连接和缓冲区空间管理。op 是以下操作常量之一:

// virtio-vsock 操作类型的枚举

enum {
    VIRTIO_VSOCK_OP_INVALID = 0,            // 无效操作
    /* 连接操作 */
    VIRTIO_VSOCK_OP_REQUEST = 1,           // 请求连接
    VIRTIO_VSOCK_OP_RESPONSE = 2,          // 响应连接
    VIRTIO_VSOCK_OP_RST = 3,               // 连接重置
    VIRTIO_VSOCK_OP_SHUTDOWN = 4,          // 关闭连接
    /* 用于发送数据负载 */
    VIRTIO_VSOCK_OP_RW = 5,                // 读写数据
    /* 通知对等端我们的信用信息 */
    VIRTIO_VSOCK_OP_CREDIT_UPDATE = 6,     // 信用更新
    /* 请求对等端向我们发送信用信息 */
    VIRTIO_VSOCK_OP_CREDIT_REQUEST = 7,    // 信用请求
};

5.10.6.1 Virtqueue 流量控制

tx virtqueue 用于承载由应用程序发起的数据包以及对接收到的数据包的回复。rx virtqueue 用于承载由设备发起的数据包以及对先前传输的数据包的回复。
如果驱动程序和设备同时填充了 rx 和 tx virtqueues,那么可能会出现死锁的情况。驱动程序没有空闲的 tx 描述符来发送回复。设备也没有空闲的 rx 描述符来发送回复。因此,既设备也不可能处理 virtqueues,因为这可能涉及发送新的回复。
这个问题可以通过在 virtqueue 之外使用额外的资源来解决,以保存数据包。有了额外的资源,即使无法发送传出数据包,也可以处理传入数据包。
最终,即使额外的资源也会被耗尽,在另一方处理它忽略的 virtqueue 之前,进一步的处理是不可能的。这个处理停止防止一方导致另一方的无限资源消耗。

5.10.6.1.1 驱动程序要求:设备操作:Virtqueue 流量控制

即使 tx virtqueue 已满,只要外部 tx virtqueue 之外有额外的资源可用于保存数据包,就必须处理 rx virtqueue。

5.10.6.1.2 设备要求:设备操作:Virtqueue 流量控制

即使 rx virtqueue 已满,只要外部 rx virtqueue 之外有额外的资源可用于保存数据包,就必须处理 tx virtqueue。

5.10.6.2 寻址

流量通过(源、目的地)地址元组来标识。地址由(cid、端口号)元组组成。用于此目的的头字段包括 src_cid、src_port、dst_cid 和 dst_port。
目前只支持流套接字。流类型的 type 为 1。
流套接字提供按顺序、有保证、面向连接的传递,没有消息边界。

5.10.6.3 缓冲区空间管理

buf_alloc 和 fwd_cnt 用于流套接字的缓冲区空间管理。来宾和设备发布每个套接字可用的缓冲区空间大小。仅计算有效载荷字节,不包括头部字节。这有助于流控制,确保数据不会丢失。
buf_alloc 是该套接字的总接收缓冲区空间,以字节为单位。这包括空闲和正在使用的缓冲区。
fwd_cnt 是自由运行的字节接收计数器。发送方计算自由接收缓冲区空间的量如下:

/*
 * tx_cnt 是发送方的自由运行字节传输计数器
 * peer_free 表示对等端可用空间,计算方式为对等端缓冲分配大小减去(发送计数减去对等端转发计数)
 */
u32 peer_free = peer_buf_alloc - (tx_cnt - peer_fwd_cnt);

如果缓冲区空间不足,发送方将等待 virtqueue 缓冲区返回并重新检查 buf_alloc 和 fwd_cnt。发送 VIRTIO_VSOCK_OP_CREDIT_REQUEST 数据包查询可用的缓冲区空间大小。对此查询的回复是 VIRTIO_VSOCK_OP_CREDIT_UPDATE 数据包。
还可以在之前未收到 VIRTIO_VSOCK_OP_CREDIT_REQUEST 数据包的情况下发送 VIRTIO_VSOCK_OP_CREDIT_UPDATE 数据包。这允许在缓冲区空间发生更改时随时通信更新。

5.10.6.3.1 驱动程序要求:设备操作:缓冲区空间管理

仅当对等方具有足够的空闲缓冲区空间来容纳有效载荷时,才能传输 VIRTIO_VSOCK_OP_RW 数据包。
与流程相关的所有数据包都必须在 buf_alloc 和 fwd_cnt 字段中包含有效信息。

5.10.6.3.2 设备要求:设备操作:缓冲区空间管理

仅当对等方具有足够的空闲缓冲区空间来容纳有效载荷时,才能传输 VIRTIO_VSOCK_OP_RW 数据包。
与流程相关的所有数据包都必须在 buf_alloc 和 fwd_cnt 字段中包含有效信息。

5.10.6.4 接收和发送

驱动程序在 tx virtqueue 上排队传出数据包,并在 rx virtqueue 上排队传入数据包接收缓冲区。数据包的形式如下:

/*
 * virtio_vsock_packet 结构体用于表示 Virtio Vsock 数据包,
 * 包括数据包头部和可变长度的数据部分。
 */
struct virtio_vsock_packet {
    struct virtio_vsock_hdr hdr;  // 数据包头部
    u8 data[];                   // 可变长度的数据部分
};

Virtqueue 缓冲区用于传出数据包是只读的。Virtqueue 缓冲区用于传入数据包是只写的。

5.10.6.4.1 驱动程序要求:设备操作:接收和传输

在发送传出数据包时,必须使用 guest_cid 配置字段作为源 CID。
如果收到具有未知类型值的数据包,则必须发送 VIRTIO_VSOCK_OP_RST 回复。

5.10.6.4.2 设备要求:设备操作:接收和传输

guest_cid 配置字段不得包含 5.10.4 中列出的保留 CID。
如果收到具有未知类型值的数据包,则必须发送 VIRTIO_VSOCK_OP_RST 回复。

5.10.6.5 流套接字

通过发送 VIRTIO_VSOCK_OP_REQUEST 数据包建立连接。如果目标上存在一个监听套接字,则发送 VIRTIO_VSOCK_OP_RESPONSE 回复并建立连接。如果目标上不存在监听套接字或目标资源不足以建立连接,则发送 VIRTIO_VSOCK_OP_RST 回复。当连接的套接字接收到 VIRTIO_VSOCK_OP_SHUTDOWN 时,头部标志字段的位 0 表示对等方将不再接收任何数据,位 1 表示对等方将不再发送任何数据。这些提示一旦发送就是永久的,连续的带有清除位的数据包不会重置它们。VIRTIO_VSOCK_OP_RST 数据包中断连接过程或强制断开连接的已连接套接字。通过发送一个或多个 VIRTIO_VSOCK_OP_SHUTDOWN 数据包,指示不再发送和接收数据,然后由对等方回复一个 VIRTIO_VSOCK_OP_RST 来实现清除断开。如果在实现特定的时间内未收到 VIRTIO_VSOCK_OP_RST 回复,则会发送一个 VIRTIO_VSOCK_OP_RST 数据包,以强制断开套接字。清除断开过程确保在一个对等方仍在处理旧连接时,另一个对等方不会重新使用 (源、目标) 地址元组用于新连接。

5.10.6.6 设备事件

设备通过事件 virtqueue 向驱动程序传递某些事件。
事件缓冲区如下:

/*
 * virtio_vsock_event_id 是 Virtio Vsock 事件的枚举类型,
 * 用于标识不同类型的事件。
 */
enum virtio_vsock_event_id {
    VIRTIO_VSOCK_EVENT_TRANSPORT_RESET = 0,  // 传输重置事件
};

/*
 * virtio_vsock_event 结构体用于表示 Virtio Vsock 事件,
 * 它包括一个事件标识符 id。
 */
struct virtio_vsock_event {
    le32 id;  // 事件标识符
};

VIRTIO_VSOCK_EVENT_TRANSPORT_RESET 事件表示通信已中断。这通常发生在虚拟机已物理迁移的情况下。驱动程序会关闭已建立的连接,并重新获取 guest_cid 配置字段。现有的监听套接字保持不变,但它们的 CID 会更新以反映当前的 guest_cid。

5.10.6.6.1 驱动程序要求:设备操作:设备事件

事件 virtqueue 缓冲区应迅速补充,以确保不会错过任何事件。
在接收到 VIRTIO_VSOCK_EVENT_TRANSPORT_RESET 事件时,必须获取 guest_cid 配置字段以确定当前的 CID。
在接收到 VIRTIO_VSOCK_EVENT_TRANSPORT_RESET 事件时,必须关闭现有的连接。
在接收到 VIRTIO_VSOCK_EVENT_TRANSPORT_RESET 事件时,现有的监听连接必须保持可操作,且其当前的 CID 不得更改。

6、保留的功能位

目前,这些与设备无关的特性位已被定义:

  1. VIRTIO_F_RING_INDIRECT_DESC (28):协商此特性表示驱动程序可以使用带有 VIRTQ_DESC_F_INDIRECT 标志的描述符,如 2.6.5.3 Indirect Descriptors 和 2.7.7 Indirect Flag: Scatter-Gather Support 中所述。
  2. VIRTIO_F_RING_EVENT_IDX (29):此特性启用了已使用的 used_event 和 avail_event 字段,如 2.6.7、2.6.8 和 2.7.10 中所述。
  3. VIRTIO_F_VERSION_1 (32):这表示与本规范的兼容性,提供了一种检测传统设备或驱动程序的简单方法。
  4. VIRTIO_F_ACCESS_PLATFORM (33):此特性表示设备可以在设备访问内存中受限制和/或被转换的平台上使用。例如,如果设备可以位于在其中将来自设备的总线地址从设备转换为内存中的物理地址的 IOMMU 后面,或者如果设备可以限制只能访问某些内存地址,或者如果需要特殊命令(如缓存刷新)以将内存中的数据与设备同步,那么就会出现这种情况。实际上是否限制或转换访问由平台特定的手段描述。如果此特性位设置为 0,则设备对其提供的内存地址具有与驱动程序相同的访问权限。特别地,设备将始终使用与驱动程序使用的地址匹配的物理地址(通常意味着CPU使用的物理地址),而不会进一步转换,并且可以访问驱动程序提供的任何地址。当清除此标志时,这将覆盖任何平台特定的设备访问是否以任何方式受限制或转换的描述,例如,是否可能存在 IOMMU。
  5. VIRTIO_F_RING_PACKED (34):此特性表示支持如 2.7 Packed Virtqueues 中所述的紧凑型 virtqueue 布局。
  6. VIRTIO_F_IN_ORDER (35):此特性表示设备以它们提供的顺序使用所有缓冲区。
  7. VIRTIO_F_ORDER_PLATFORM (36):此特性表示由平台描述的驱动程序和设备的内存访问以某种方式进行排序。
    • 如果已协商此特性位,则需要按照与平台描述的设备适用的一种适合的方式来排序由驱动程序执行的任何需要以与设备的访问相关的特定方式排序的内存访问。这意味着驱动程序需要使用适合于平台描述的设备的内存栅栏;例如,对于硬件 PCI 设备的情况下,适用于 PCI 传输的内存栅栏。
    • 如果未协商此特性位,则假定设备和驱动程序是在软件中实现的,也就是可以假定它们在 SMP 配置中运行在相同的 CPU 上。因此,更弱的内存栅栏形式足以提供更好的性能。
  8. VIRTIO_F_SR_IOV (37):此特性表示设备支持单根 I/O 虚拟化。目前,只有 PCI 设备支持此特性。
  9. VIRTIO_F_NOTIFICATION_DATA (38):此特性表示驱动程序在其设备通知中传递额外的数据(除了识别 virtqueue)。请参阅 2.7.23 Driver notifications。

6.1 驱动程序要求:保留的特性位

  • 驱动程序必须在提供的情况下接受 VIRTIO_F_VERSION_1 特性。如果不提供 VIRTIO_F_VERSION_1,则驱动程序可能会在操作过程中失败。
  • 如果提供了 VIRTIO_F_ACCESS_PLATFORM 特性,则驱动程序应该接受它,并且必须要么禁用 IOMMU,要么配置 IOMMU 以将传递给设备的总线地址转换为内存中的物理地址。如果不接受 VIRTIO_F_ACCESS_PLATFORM,则驱动程序必须只向设备传递物理地址。
  • 如果提供了 VIRTIO_F_RING_PACKED 特性,则驱动程序应该接受它。
  • 如果提供了 VIRTIO_F_ORDER_PLATFORM 特性,则驱动程序应该接受它。如果已协商 VIRTIO_F_ORDER_PLATFORM,则驱动程序必须使用适合硬件设备的栅栏。
  • 如果已协商 VIRTIO_F_SR_IOV,则驱动程序可以通过设备的 PCI SR-IOV 能力结构启用虚拟功能。在启用虚拟功能之前,驱动程序不能协商 VIRTIO_F_SR_IOV 如果设备没有 PCI SR-IOV 能力结构或者不是 PCI 设备。一旦成功协商了 VIRTIO_F_SR_IOV,即使设备或系统已完全或部分复位,并且即使在复位后没有重新协商 VIRTIO_F_SR_IOV,驱动程序也可以通过设备的 PCI SR-IOV 能力结构启用虚拟功能。

6.2 设备要求:保留的特性位

  • 设备必须提供 VIRTIO_F_VERSION_1 特性。如果不接受 VIRTIO_F_VERSION_1,则设备可能会在操作过程中失败。
  • 如果设备的内存访问是通过与由驱动程序使用的物理地址不同并由平台转换为物理地址的总线地址进行的,和/或者只能访问由平台指定和/或授权的某些内存地址,则设备应该提供 VIRTIO_F_ACCESS_PLATFORM 特性。如果不接受 VIRTIO_F_ACCESS_PLATFORM,则设备可能会在操作过程中失败。
  • 如果已协商 VIRTIO_F_IN_ORDER,则设备必须以它们可用的顺序使用缓冲区。
  • 如果已协商 VIRTIO_F_ORDER_PLATFORM,则设备必须使用与平台描述的硬件设备适用的排序。
  • 对于基于插卡的 PCI 设备,建议同时提供 VIRTIO_F_ACCESS_PLATFORM 和 VIRTIO_F_ORDER_PLATFORM 以获得最大的可移植性。
  • 如果设备是 PCI 设备并具有 PCI SR-IOV 能力结构,则设备应该提供 VIRTIO_F_SR_IOV 特性;否则,不得提供 VIRTIO_F_SR_IOV。

6.3 Legacy Interface: Reserved Feature Bits

过渡性设备可以提供以下功能:

  • VIRTIO_F_NOTIFY_ON_EMPTY (24):如果驱动程序协商了此功能,即使使用了 VIRTQ_AVAIL_F_NO_INTERRUPT 标志或 used_event 字段来抑制通知,在 virtqueue 上的可用描述符用尽时,设备必须发出已使用缓冲区的通知。

    注意:使用此功能的一个示例是传统网络驱动程序:它不需要在每次传输数据包时都知道,但确实需要在它们传输后的有限时间内释放已传输的数据包。如果设备在所有数据包传输完成时通知它,它可以避免使用定时器。

过渡性设备必须提供以下功能,如果设备提供了,那么过渡性驱动程序必须接受:

  • VIRTIO_F_ANY_LAYOUT (27):此功能表示设备接受任意的描述符布局,正如第2.6.4.3节中所描述的“Legacy Interface: Message Framing”一样。
  • UNUSED (30):第30位由qemu的实现用于检查实验性的早期virtio版本,这些版本没有执行正确的功能协商,因此不应该进行协商。

7 符合性

本章列出了每个符合性目标和条款,这也形成了一个有用的检查清单,要求作者在实施时参考!

7.1 符合性目标

符合性目标:
驱动程序 驱动程序必须符合四个符合性条款:

  • 第7.2条款。
  • 第7.2.1、7.2.2或7.2.3中的一个。
  • 第7.2.4、7.2.5、7.2.6、7.2.7、7.2.8、7.2.9、7.2.10、7.2.11或7.2.12中的一个。
  • 第7.4条款。

设备 设备必须符合四个符合性条款:

  • 第7.3条款。
  • 第7.3.1、7.3.2或7.3.3中的一个。
  • 第7.3.4、7.3.5、7.3.6、7.3.7、7.3.8、7.3.9、7.3.10、7.3.11或7.3.12中的一个。
  • 第7.4条款。

7.2 条款1:驱动程序符合性

驱动程序必须符合以下规范性陈述:

  • 2.1.1
  • 2.2.1
  • 2.4.1
  • 2.6.1
  • 2.6.4.2
  • 2.6.5.2
  • 2.6.5.3.1
  • 2.6.7.1
  • 2.6.6.1
  • 2.6.8.3
  • 2.6.10.1
  • 2.6.13.3.1
  • 2.6.13.4.1
  • 3.1.1
  • 3.3.1
  • 6.1

7.2.1 条款2:PCI 驱动程序符合性

PCI 驱动程序必须符合以下规范性陈述:

  • 4.1.2.2
  • 4.1.3.1
  • 4.1.4.1
  • 4.1.4.3.2
  • 4.1.4.5.2
  • 4.1.4.7.2
  • 4.1.5.1.2.2
  • 4.1.5.4.2

7.2.2 条款3:MMIO 驱动程序符合性

MMIO 驱动程序必须符合以下规范性陈述:

  • 4.2.2.2
  • 4.2.3.1.1
  • 4.2.3.4.1

7.2.3 条款4:通道 I/O 驱动程序符合性

通道 I/O 驱动程序必须符合以下规范性陈述:

  • 4.3.1.4
  • 4.3.2.1.2
  • 4.3.2.3.1
  • 4.3.3.1.2.2
  • 4.3.3.2.2

7.2.4 条款5:网络驱动程序符合性

网络驱动程序必须符合以下规范性陈述:

  • 5.1.4.2
  • 5.1.6.2.1
  • 5.1.6.3.1
  • 5.1.6.4.2
  • 5.1.6.5.1.2
  • 5.1.6.5.2.2
  • 5.1.6.5.4.1
  • 5.1.6.5.5.1
  • 5.1.6.5.6.2

7.2.5 条款 6:块驱动程序符合性

块驱动程序必须符合以下规范性陈述:

  • 5.2.5.1
  • 5.2.6.1

7.2.6 条款 7:控制台驱动程序符合性

控制台驱动程序必须符合以下规范性陈述:

  • 5.3.6.1
  • 5.3.6.2.2

7.2.7 条款 8:熵驱动程序符合性

熵驱动程序必须符合以下规范性陈述:

  • 5.4.6.1

7.2.8 条款 9:传统内存气球驱动程序符合性

传统内存气球驱动程序必须符合以下规范性陈述:

  • 5.5.3.1
  • 5.5.6.1
  • 5.5.6.3.1

7.2.9 条款 10:SCSI 主机驱动程序符合性

SCSI 主机驱动程序必须符合以下规范性陈述:

  • 5.6.4.1
  • 5.6.6.1.2
  • 5.6.6.3.1

7.2.10 条款 11:输入驱动程序符合性

输入驱动程序必须符合以下规范性陈述:

  • 5.8.5.1
  • 5.8.6.1

7.2.11 条款 12:加密驱动程序符合性

加密驱动程序必须符合以下规范性陈述:

  • 5.9.5.2
  • 5.9.6.1
  • 5.9.7.2.1.5
  • 5.9.7.2.1.7
  • 5.9.7.4.1
  • 5.9.7.5.1
  • 5.9.7.6.1
  • 5.9.7.7.1

7.2.12 第13条款:套接字驱动程序一致性

套接字驱动程序必须符合以下规范性声明:

  • 5.10.6.3.1
  • 5.10.6.4.1
  • 5.10.6.6.1

7.3 第14条款:设备一致性

设备必须符合以下规范性声明:

  • 2.1.2
  • 2.2.2
  • 2.4.2
  • 2.6.4.1
  • 2.6.5.1
  • 2.6.5.3.2
  • 2.6.7.2
  • 2.6.8.2
  • 2.6.10.2
  • 6.2

7.3.1 第15条款:PCI设备一致性

PCI设备必须符合以下规范性声明:

  • 4.1.1
  • 4.1.2.1
  • 4.1.3.2
  • 4.1.4.2
  • 4.1.4.3.1
  • 4.1.4.4.1
  • 4.1.4.5.1
  • 4.1.4.6.1
  • 4.1.4.7.1
  • 4.1.4.9.0.1
  • 4.1.5.1.2.1
  • 4.1.5.3.1
  • 4.1.5.4.1

7.3.2 第16条款:MMIO设备一致性

MMIO设备必须符合以下规范性声明:

  • 4.2.2.1

7.3.3 第17条款:通道I/O设备一致性

通道I/O设备必须符合以下规范性声明:

  • 4.3.1.3
  • 4.3.2.1.1
  • 4.3.2.2.1
  • 4.3.2.3.2
  • 4.3.2.6.3.1
  • 4.3.3.1.2.1
  • 4.3.3.2.1

7.3.4 第18条款:网络设备一致性

网络设备必须符合以下规范性声明:

  • 5.1.4.1
  • 5.1.6.2.2
  • 5.1.6.3.2
  • 5.1.6.4.1
  • 5.1.6.5.1.1
  • 5.1.6.5.2.1
  • 5.1.6.5.4.2
  • 5.1.6.5.5.2

7.3.5 第19条款:块设备一致性

块设备必须符合以下规范性声明:

  • 5.2.5.2
  • 5.2.6.2

7.3.6 第20条款:控制台设备一致性

控制台设备必须符合以下规范性声明:

  • 5.3.5.1
  • 5.3.6.2.1

7.3.7 第21条款:熵设备一致性

熵设备必须符合以下规范性声明:

  • 5.4.6.2

7.3.8 第22条款:传统内存气球设备一致性

传统内存气球设备必须符合以下规范性声明:

  • 5.5.3.2
  • 5.5.6.2
  • 5.5.6.3.2

7.3.9 第23条款:SCSI主机设备一致性

SCSI主机设备必须符合以下规范性声明:

  • 5.6.4.2
  • 5.6.5
  • 5.6.6.1.1
  • 5.6.6.3.2

7.3.10 第24条款:输入设备一致性

输入设备必须符合以下规范性声明:

  • 5.8.5.2
  • 5.8.6.2

7.3.11 第25款:密码设备符合性

密码设备必须符合以下规范性陈述:

  • 5.9.5.1
  • 5.9.7.2.1.6
  • 5.9.7.2.1.8
  • 5.9.7.4.2
  • 5.9.7.5.2
  • 5.9.7.6.2
  • 5.9.7.7.2

7.3.12 第26款:套接字设备符合性

套接字设备必须符合以下规范性陈述:

  • 5.10.6.3.2
  • 5.10.6.4.2

7.4 第27款:遗留接口:过渡设备和过渡驱动程序符合性

符合性实现必须是过渡或非过渡的,请参阅1.3.1。
实现可以选择通过符合传统设备和驱动程序的遗留接口的所有MUST或REQUIRED级别要求来实现对遗留接口的可选支持,包括对遗留驱动程序或设备的支持。
过渡实现的遗留接口要求列在下面列出的名为“遗留接口”的部分中:

  • 第2.2.3节
  • 第2.4.3节
  • 第2.4.4节
  • 第2.6.2节
  • 第2.6.3节
  • 第2.6.4.3节
  • 第3.1.2节
  • 第4.1.2.3节
  • 第4.1.4.8节
  • 第4.1.5.1.1.1节
  • 第4.1.5.1.3.1节
  • 第4.2.4节
  • 第4.3.2.1.3节
  • 第4.3.2.2.2节
  • 第4.3.3.1.3节
  • 第4.3.2.6.4节
  • 第5.1.3.2节
  • 第5.1.4.3节
  • 第5.1.6.1节
  • 第5.1.6.5.2.3节
  • 第5.1.6.5.3.1节
  • 第5.1.6.5.5.3节
  • 第5.1.6.5.6.3节
  • 第5.2.3.1节
  • 第5.2.4.1节
  • 第5.2.5.3节
  • 第5.2.6.3节
  • 第5.3.4.1节
  • 第5.3.6.3节
  • 第5.5.3.2.0.1节
  • 第5.5.6.2.1节
  • 第5.5.6.3.3节
  • 第5.6.4.3节
  • 第5.6.6.0.1节
  • 第5.6.6.1.3节
  • 第5.6.6.2.1节
  • 第5.6.6.3.3节
  • 第6.3节
#ifndef VIRTQUEUE_H
#define VIRTQUEUE_H

/* 一个用于高效的virtio实现的接口。
 * 这个头文件采用BSD许可证,因此任何人都可以使用这些定义
 * 来实现兼容的驱动程序/服务器。
 *
 * 版权 2007, 2009, IBM Corporation
 * 版权 2011, Red Hat, Inc
 * 保留所有权利。
 *
 * 允许在源代码和二进制形式下重新分发和使用,无论是否经过修改,
 * 只要满足以下条件:
 * 1. 源代码的再分发必须保留上述版权声明、条件列表和以下免责声明。
 * 2. 在二进制形式中重新分发时,必须在分发的文档和/或其他材料中再现
 *    上述版权声明、条件列表和以下免责声明。
 * 3. 未经特定的事先书面许可,不得使用IBM或其贡献者的名称来认可或推广
 *    源自本软件的产品。
 * 本软件是由版权持有人和贡献者 "原样" 提供的,没有任何明示或暗示的保证,
 * 包括但不限于适销性和适用于特定目的的暗示保证。
 * 在任何情况下,无论是因合同、严格责任还是侵权行为产生的,
 * 与本软件有关的任何直接、间接、偶然、特殊、间接或继发性损害(包括,
 * 但不限于,替代商品或服务的采购;使用、数据或利润的损失;或业务中断)
 * 都是不承担责任的,即使事先通知了可能性。
 */

#include 

/* 下面是描述virtio描述符的一些标志 */

/* 这标记一个缓冲区为通过 "next" 字段继续的。 */
#define VIRTQ_DESC_F_NEXT 1

/* 这标记一个缓冲区为只写(否则只读)。 */
#define VIRTQ_DESC_F_WRITE 2

/* 这表示缓冲区包含缓冲区描述符的列表。 */
#define VIRTQ_DESC_F_INDIRECT 4

/* 设备使用这个标志在used->flags中告诉驱动程序:当你添加一个缓冲区时,不要唤醒我
 * 这是不可靠的,所以它只是一种优化。
 */
#define VIRTQ_USED_F_NO_NOTIFY 1

/* 驱动程序使用这个标志在avail->flags中告诉设备:当你消耗一个缓冲区时,不要中断我
 * 这是不可靠的,所以它只是一种优化。
 */
#define VIRTQ_AVAIL_F_NO_INTERRUPT 1

/* 支持间接描述符 */
#define VIRTIO_F_INDIRECT_DESC 28

/* 支持avail_event和used_event字段 */
#define VIRTIO_F_EVENT_IDX 29

/* 任意描述符布局的支持 */
#define VIRTIO_F_ANY_LAYOUT 27

/* Virtqueue描述符:16字节。
 * 这些可以通过 "next" 链接在一起。
 */
struct virtq_desc {
    /* 地址(客户机物理地址)。 */
    le64 addr;
    
    /* 长度。 */
    le32 len;
    
    /* 如上所示的标志。 */
    le16 flags;
    
    /* 我们也可以通过这个链未使用的描述符。 */
    le16 next;
};

struct virtq_avail {
    le16 flags;
    le16 idx;
    le16 ring[];
    
    /* 只有在 VIRTIO_F_EVENT_IDX 情况下才存在:le16 used_event; */
};

/* 由于填充原因,这里使用le32来表示id。 */
struct virtq_used_elem {
    /* 开始使用的描述符链的索引。 */
    le32 id;
    
    /* 写入的整个描述符链的总长度。 */
    le32 len;
};

struct virtq_used {
    le16 flags;
    le16 idx;
    struct virtq_used_elem ring[];
    
    /* 只有在 VIRTIO_F_EVENT_IDX 情况下才存在:le16 avail_event; */
};

struct virtq {
    unsigned int num;
    struct virtq_desc *desc;
    struct virtq_avail *avail;
    struct virtq_used *used;
};

/* 判断是否需要事件(仅当 VIRTIO_F_EVENT_IDX 存在时) */
static inline int virtq_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old_idx)
{
    return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx - old_idx);
}

/* 获取事件索引的位置(仅当 VIRTIO_F_EVENT_IDX 存在时) */
static inline le16 *virtq_used_event(struct virtq *vq)
{
    /* 为了向后兼容,已使用事件索引位于 avail ring 的 *末尾*。 */
    return &vq->avail->ring[vq->num];
}

static inline le16 *virtq_avail_event(struct virtq *vq)
{
    /* 为了向后兼容,可用事件索引位于已使用 ring 的 *末尾*。 */
    return (le16 *)&vq->used->ring[vq->num];
}

#endif /* VIRTQUEUE_H */

你可能感兴趣的:(网络虚拟化,信息与通信)