PCIE DOE(Data Object Exchange) 软件执行流程

【CXL协议-控制和状态寄存器(8)】 设备 0、功能 0 DVSEC 中的功能、状态和控制字段控制整个 CXL 设备的 CXL 功能。在此过程中应保持向后兼容性。Bit 7: CXL 复位功能:如果设置,表示设备支持 CXL 复位并在此寄存器中实现 CXL 复位超时字段、在 CXL Control2 寄存器中启动 CXL 复位位并在 CXL Status2 寄存器中实现 CXL 复位完成状态。此 DVSEC 还位于 CXL 2.0 根端口、上行交换机端口、下行交换机端口和 CXL 2.0 设备的主要功能(设备 0、功能 0)的配置空间中。 阅读详情

DOE(Data Object Exchange,数据对象交换)发现与数据发送逻辑总结(含完整缓存发送逻辑)

在PCIe 6.0规范中,DOE(Data Object Exchange)是实现设备与主机间安全数据交换的核心机制,其核心价值是通过标准化的“发现-配置-传输”流程,实现加密密钥、安全配置等敏感数据的可靠传输。以下从DOE发现逻辑、数据发送完整逻辑(含缓存机制) 两部分展开,结合PCIe 6.0规范细节与技术原理全面梳理:

在这里插入图片描述

1、DOE发现逻辑:定位通信资源的标准化流程

DOE的发现本质是通过PCIe配置空间枚举,找到DOE扩展能力的寄存器区域,为后续数据交换确定“通信地址”与“能力范围”,具体步骤遵循PCIe规范的配置空间枚举机制:

1.1. 前提:PCIe设备的基础枚举

所有PCIe设备在系统初始化阶段,会被根复合体(Root Complex)分配唯一的BDF(Bus:Device.Function)地址,用于定位其4KB标准配置空间(物理地址由根复合体动态分配,如0x80000000)。操作系统或BIOS通过BDF地址,可访问设备的配置空间寄存器,这是DOE发现的基础。

1.2. 枚举DOE扩展能力:从配置空间定位DOE入口

DOE功能以“扩展能力”形式存在于设备的扩展配置空间(偏移0x100开始,非标准配置空间),需通过以下步骤定位:

  • 遍历扩展能力链表:扩展配置空间中,每个扩展能力以“扩展能力头(Extended Capability Header)”标识,包含两个核心字段:
    • Capability ID:DOE的标准ID为0x0025(PCI-SIG定义,区分于其他扩展能力如IDE、SR-IOV);
    • Next Capability Offset:指向下一个扩展能力的偏移地址(若为0x00h则表示链表结束)。
  • 匹配DOE能力ID:从扩展能力起始偏移(0x100)开始,依次读取每个扩展能力头的Capability ID,当匹配到0x0025时,当前偏移地址即为DOE扩展能力的基地址(如0x120)。

1.3. 读取DOE核心信息:确定通信范围与能力

找到DOE扩展能力基地址后,通过访问其内部寄存器,获取数据交换所需的关键信息:

  • DOE能力寄存器(DOE Capabilities Register):定义设备支持的DOE功能上限,如“最大数据对象大小”(通常为2~256K DW,1 DW=4字节)、“是否支持多DOE实例”“是否支持中断通知”等;
  • DOE MMIO基地址寄存器(DOE Base Address Register):存储DOE专用寄存器区域的物理基地址(如0xA0000000)与区域大小(通常为4KB~64KB,含读写邮箱、控制/状态寄存器),这是后续数据发送的“硬件通信地址”;
  • DOE控制/状态寄存器(DOE Control/Status Register):标识DOE的使能状态、中断使能位、数据交换状态(如“忙”“空闲”“错误”)。

1.4. 映射虚拟地址:主机侧访问准备

由于主机CPU无法直接访问物理地址,需通过操作系统的MMIO(内存映射I/O)机制,将DOE的物理地址区域映射为主机虚拟地址(如0xFFFF0000)。映射完成后,主机软件(如SPDM安全栈、TEE驱动)可通过内存读写指令(如MOV)访问DOE寄存器,完成数据发送与接收。

2、DOE数据发送完整逻辑:含缓存机制的可靠传输流程

DOE数据发送遵循“请求-响应”模型,核心是通过“邮箱寄存器+缓存队列”实现大尺寸数据对象的分块传输,同时保障数据完整性与传输可靠性。以下结合PCIe 6.0规范,梳理从“数据准备”到“发送完成”的全流程:

2.1. 数据发送前准备:对象封装与缓存初始化

(1)数据对象封装:标准化格式定义

DOE传输的基本单元是“数据对象(Data Object)”,需按PCIe 6.0规范封装,格式要求:

  • 大小范围:2~256K DW(即8字节~1MB),需为2 DW的整数倍;
  • 头部结构(固定2 DW):
    字段位宽说明
    Data Object Type16位数据对象类型(如密钥、配置参数,PCI-SIG定义标准类型,厂商可自定义扩展)
    Vendor ID16位厂商标识(如PCI-SIG为0001h)
    Length16位数据对象总长度(单位:DW)
    Reserved16位保留字段,填0
  • 数据内容:紧跟头部,为实际传输的数据(如AES密钥、SPDM认证信息),需按小端字节序排列。
(2)发送缓存初始化:分块与队列管理

由于DOE的Write Data Mailbox Register(写数据邮箱寄存器) 位宽固定为32位(1 DW)或64位(2 DW,部分高性能设备),无法直接传输大尺寸数据对象,需通过“分块传输+缓存队列”实现:

  • 发送缓存队列:设备侧与主机侧均需维护“DOE发送缓存队列”,用于暂存待发送的分块数据(通常为FIFO结构,深度由设备硬件决定,如32级);
  • 分块规则:将封装后的完整数据对象,按“邮箱寄存器位宽”拆分为N个数据块(如32位邮箱则按1 DW/块拆分,64位则按2 DW/块拆分),并为每个块分配“块序号”(用于接收端重组);
  • 缓存状态标记:每个缓存块需标记“未发送”“发送中”“已确认”状态,避免重复发送或丢失。

2.2. 数据发送核心流程:分块传输与缓存控制

(1)步骤1:发送使能与状态检查

主机软件通过访问DOE控制寄存器(如DOE_CTRL):

  • 置位“发送使能位(Tx Enable)”,启动DOE发送流程;
  • 读取“发送状态位(Tx Status)”,确认DOE处于“空闲(Idle)”状态(无 ongoing 传输),避免冲突。
(2)步骤2:分块写入邮箱寄存器

主机软件按“块序号从小到大”的顺序,将缓存队列中的数据块逐块写入DOE Write Data Mailbox Register(偏移0x10h,32位/64位):

  • 每次写入前,需检查“邮箱空闲位(Mailbox Ready)”:仅当该位为1时,允许写入下一块数据(避免覆盖未发送的块);
  • 若设备支持“缓存预加载”,可一次性将多个块写入设备侧发送缓存队列(通过批量MMIO写指令),由设备硬件自动按序发送,减少主机CPU干预。
(3)步骤3:发送触发与硬件缓存调度
  • 主机触发:当一个数据对象的所有块写入完成后,主机置位“发送启动位(Tx Start)”,通知设备开始发送;
  • 设备侧缓存调度:设备硬件从“发送缓存队列”中读取数据块,按PCIe TLP格式封装为“Vendor-Defined Message”(协议ID01h,符合SPDM加密会话要求),并通过绑定的PCIe链路发送;
  • 流控制保障:设备发送前需检查PCIe链路的流控制信用(FC Credit),仅当有可用信用时才发送TLP,避免接收端缓存溢出(遵循PCIe 6.0的FC规则)。
(4)步骤4:发送状态反馈与缓存更新
  • 中断通知:若设备使能“发送完成中断”(通过DOE_CTRL的中断使能位),则发送完成后会触发中断,通知主机;
  • 状态寄存器查询:主机可轮询DOE_STATUS寄存器的“发送完成位(Tx Done)”,确认发送结果(成功/失败);
  • 缓存状态更新:发送成功后,主机与设备侧均将“发送缓存队列”中对应的块标记为“已发送”,并清空缓存(或移动队列指针),释放缓存空间。

2.3. 发送过程中的可靠性保障:重传与错误处理

(1)传输错误检测
  • 链路层错误:PCIe数据链路层通过LCRC(链路CRC)检测TLP传输错误,若发现错误,设备侧会触发“重传机制”(重传缓存中的未确认块);
  • 端到端错误:若启用ECRC(端到端CRC),接收端会检查数据对象的完整性,若错误则通过“DOE状态寄存器”反馈“数据错误位(Tx Error)”。
(2)重传机制:缓存块的重新发送
  • 重传触发条件:当主机检测到“发送超时”(超过规范定义的超时时间,如10ms)或“发送错误”时,触发重传;
  • 重传缓存依赖:由于发送缓存队列中暂存了未确认的分块数据,无需重新从主机内存读取,直接从缓存中取出对应块重新写入邮箱寄存器,触发发送;
  • 重传次数限制:规范建议重传次数不超过3次,若仍失败则上报“DOE传输错误”(通过PCIe AER机制)。

2.4. 发送完成:缓存清理与结果反馈

  • 缓存清理:发送成功后,主机侧清空“发送缓存队列”,设备侧释放硬件缓存资源(如FIFO复位);
  • 结果反馈:主机软件读取DOE_STATUS寄存器,确认“发送完成位(Tx Done)”置1,且“错误位(Tx Error)”为0,即表示整个数据对象发送完成;
  • 后续操作:若需传输下一个数据对象,重复“封装-分块-发送”流程;若传输结束,主机置位“DOE禁用位(Disable)”,关闭DOE功能以节省功耗。

3、关键补充:DOE发送缓存的核心设计要点

  1. 缓存一致性保障:主机侧缓存与设备侧缓存需通过“状态同步信号”(如Mailbox Ready位)确保一致性,避免“主机写入时设备正在读取”导致的数据冲突;
  2. 优先级调度:若设备支持多DOE实例或多数据流,发送缓存队列需支持“优先级调度”(如按数据对象类型分配优先级,密钥传输优先于配置参数);
  3. 安全隔离:发送缓存需与非DOE资源(如普通PCIe TLP缓存)物理隔离,避免敏感数据(如密钥)被非法访问,符合TEE(可信执行环境)的安全要求;
  4. 性能优化:部分高端设备支持“缓存预取”(Prefetch)功能,主机可提前将后续待发送块写入缓存,减少等待时间;同时支持“批量发送”(Burst Write),通过单次MMIO指令写入多个缓存块,提升传输效率。

4、总结:DOE发现与发送逻辑的核心特点

  1. 标准化发现:基于PCIe配置空间枚举,通过Capability ID=0x0025定位DOE资源,无需额外硬件信号,兼容性强;
  2. 分块缓存传输:通过“邮箱寄存器+缓存队列”解决大尺寸数据对象传输问题,平衡硬件复杂度与传输效率;
  3. 可靠性保障:结合PCIe链路层重传、ECRC校验、缓存状态标记,确保数据传输不丢失、不篡改;
  4. 安全适配:缓存隔离与标准化加密传输(如SPDM会话),满足PCIe 6.0的安全要求,适用于可信计算、密钥分发等敏感场景。

5. linux 内核6.6 关于doe的实现

你现在提供了Linux内核v6.6版本中doe.c的完整源码,核心需求是分析这段代码对PCIe DOE(Data Object Exchange,数据对象交换)机制的具体实现逻辑。我会从功能架构、核心流程、关键函数等维度,帮你拆解整个实现。

5.1、DOE核心定义与整体架构

PCIe设备与主机之间基于“协议+载荷”的标准化通信邮箱(Mailbox),用于设备固件升级、功能配置、协议发现等场景。

这段代码的核心架构分为三层:

  1. 基础数据结构:定义邮箱(pci_doe_mb)、任务(pci_doe_task)、协议(pci_doe_protocol)等核心对象;
  2. 硬件交互层:实现DOE寄存器读写、请求发送/响应接收、异常终止(Abort)等硬件操作;
  3. 任务调度层:通过工作队列(workqueue)异步处理DOE任务,提供同步/异步接口给上层调用。

5.2、核心数据结构解析

1. struct pci_doe_mb(DOE邮箱实例)
字段作用
pdev关联的PCI设备指针
cap_offsetDOE扩展能力在PCI配置空间的偏移地址
prots该邮箱支持的协议列表(XArray存储,key为索引,value为`(vid<<8)
wq等待队列,用于任务等待/唤醒
work_queue有序工作队列,确保DOE任务串行执行(避免硬件冲突)
flags状态标志(CANCEL:取消任务;DEAD:邮箱不可用)
2. struct pci_doe_task(DOE单次请求/响应任务)

封装单次DOE交互的所有信息:协议类型、请求/响应载荷、完成回调、工作项等,是任务调度的最小单元。

5.3、核心流程拆解

1. 邮箱初始化(pci_doe_init)

这是DOE的入口函数,流程如下:

pci_doe_init(pdev)
遍历PCI设备的DOE扩展能力
pci_find_next_ext_capability找PCI_EXT_CAP_ID_DOE
pci_doe_create_mb创建邮箱实例
分配内存+初始化等待队列/XArray
创建有序工作队列
执行Abort重置邮箱
pci_doe_cache_protocols发现并缓存支持的协议
将邮箱实例存入pdev->doe_mbs(XArray)

关键:初始化时会通过“协议发现流程”(pci_doe_discovery)主动查询设备支持的所有DOE协议,并缓存到prots中,方便后续校验。

2. 协议发现流程(pci_doe_discovery)

遵循PCIe规范的“协议发现协议”(VID=PCI_SIG,Type=0),流程:

  1. 构造“协议发现请求”(携带索引),调用pci_doe发送;
  2. 解析响应,获取该索引对应的协议(VID+Type);
  3. 循环直到索引为0(无更多协议),将所有协议存入prots。
3. 单次DOE交互(pci_doe核心接口)

pci_doe是对外导出的核心同步接口,流程如下:

pci_doe(doe_mb, vid, type, req, req_sz, resp, resp_sz)
构造pci_doe_task任务对象
pci_doe_submit_task提交任务
校验协议是否支持(pci_doe_supports_prot)
初始化栈上工作项,绑定状态机函数
将任务加入工作队列
wait_for_completion等待任务完成
返回任务结果(响应长度/错误码)
4. 任务状态机(doe_statemachine_work)

这是DOE交互的核心硬件逻辑,运行在工作队列中,流程:

  1. 发送请求(pci_doe_send_req):
    • 检查邮箱是否忙/出错;
    • 计算载荷长度(2个DWORD头部 + 载荷DWORD数,最大2^18 DWORD);
    • 写入DOE头部(VID+Type + 长度);
    • 逐DWORD写入请求载荷(支持非DWORD对齐,不足补0);
    • 写入PCI_DOE_CTRL_GO触发硬件处理。
  2. 等待响应:
    • 轮询PCI_DOE_STATUS寄存器,检查DATA_OBJECT_READY位;
    • 超时/出错时执行pci_doe_abort终止操作,标记邮箱不可用;
  3. 接收响应(pci_doe_recv_resp):
    • 读取响应头部,校验VID+Type是否匹配;
    • 读取响应长度,处理“0表示最大长度”的规范要求;
    • 逐DWORD读取响应载荷(支持非DWORD对齐,截断超出用户缓冲区的部分);
    • 清空多余数据,最终校验错误位。
  4. 异常处理:任何步骤出错都会调用signal_task_abort,执行Abort操作并标记邮箱状态。
5. 资源销毁(pci_doe_destroy)
  • 遍历设备的所有DOE邮箱,调用pci_doe_cancel_tasks取消所有待处理任务;
  • 销毁工作队列、XArray,释放内存。

5.4、关键细节与规范对齐

  1. 超时机制:严格遵循PCIe 6.0规范,超时时间1秒(PCI_DOE_TIMEOUT=HZ),轮询间隔1秒/128;

  2. 载荷处理:支持任意字节长度的载荷(无需用户对齐DWORD),底层自动处理部分DWORD的拷贝/补0;

  3. 字节序:载荷按原始字节流传输,用户需自行处理小端序转换(cpu_to_le32/le32_to_cpu);

  4. 并发安全:使用有序工作队列(alloc_ordered_workqueue)确保单个邮箱的任务串行执行,避免硬件冲突;

  5. 异常恢复:出错时执行Abort操作重置邮箱,Abort失败则标记邮箱“Dead”,拒绝后续请求。

  6. 核心功能:实现PCIe 6.0 DOE规范的标准化邮箱通信,提供同步的pci_doe接口,支持协议发现、请求发送/响应接收、异常处理;

  7. 设计亮点:通过工作队列异步处理硬件交互,有序队列保证并发安全,自动处理载荷对齐/长度转换,兼容规范的超时/Abort机制;

  8. 关键约束:单个DOE任务载荷最大2^18 DWORD,交互超时1秒,仅支持串行任务执行。

5.5 pci_doe_submit_task

pci_doe_submit_task函数本身并不直接负责把数据写入寄存器,它的核心作用是“任务提交与调度”——真正的寄存器读写逻辑在后续的工作队列处理函数中。我会帮你拆解清楚这个“任务提交→硬件交互”的完整链路,让你明白数据是如何最终写到寄存器的。

1. 先明确pci_doe_submit_task的定位

这个函数的核心工作只有3件事:

  • 校验协议是否支持、邮箱是否可用;
  • 把任务和邮箱绑定,初始化工作项;
  • 将任务加入工作队列(work_queue),等待异步执行。

它是“任务调度层”的入口,而寄存器读写属于“硬件交互层”,在工作项对应的处理函数doe_statemachine_work中完成。

2. 完整链路:从提交任务到写入寄存器
pci_doe_submit_task提交任务
将task加入work_queue有序工作队列
内核调度执行doe_statemachine_work
调用pci_doe_send_req发送请求
pci_doe_write_ctrl/直接pci_write_config_dword写寄存器
步骤1:任务提交(pci_doe_submit_task)
// 关键代码拆解
task->doe_mb = doe_mb; // 绑定任务到对应的邮箱(包含PCI设备、寄存器偏移)
INIT_WORK_ONSTACK(&task->work, doe_statemachine_work); // 绑定工作项到处理函数
queue_work(doe_mb->work_queue, &task->work); // 任务入队,等待执行

这一步只是“把任务放进待执行队列”,没有任何硬件操作。

步骤2:工作队列执行处理函数(doe_statemachine_work)

内核的工作队列机制会异步调用doe_statemachine_work,这个函数里会调用pci_doe_send_req——这才是真正写寄存器的入口。

步骤3:核心寄存器写入(pci_doe_send_req)

pci_doe_send_req函数中包含所有DOE寄存器的读写逻辑,核心代码如下(我标注关键寄存器操作):

static int pci_doe_send_req(struct pci_doe_mb *doe_mb, struct pci_doe_task *task)
{
    struct pci_dev *pdev = doe_mb->pdev;
    int offset = doe_mb->cap_offset; // DOE扩展能力在PCI配置空间的偏移
    u32 val;
    int i;

    // 1. 先检查状态寄存器(读操作)
    pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val); // 读状态寄存器
    if (FIELD_GET(PCI_DOE_STATUS_BUSY, val)) // 检查忙位
        return -EBUSY;

    // 2. 计算载荷长度(符合PCIe 6.0规范)
    size_t length = 2 + DIV_ROUND_UP(task->request_pl_sz, sizeof(__le32));
    if (length > PCI_DOE_MAX_LENGTH)
        return -EIO;

    // 3. 写DOE头部(第一个DWORD:VID+协议类型)
    val = FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_VID, task->prot.vid) |
          FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, task->prot.type);
    pci_write_config_dword(pdev, offset + PCI_DOE_WRITE, val); // 写WRITE寄存器

    // 4. 写DOE头部(第二个DWORD:长度)
    pci_write_config_dword(pdev, offset + PCI_DOE_WRITE,
                           FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH, length));

    // 5. 写请求载荷(逐DWORD写入WRITE寄存器)
    for (i = 0; i < task->request_pl_sz / sizeof(__le32); i++)
        pci_write_config_dword(pdev, offset + PCI_DOE_WRITE,
                               le32_to_cpu(task->request_pl[i])); // 写载荷数据

    // 6. 触发硬件处理(写控制寄存器的GO位)
    pci_doe_write_ctrl(doe_mb, PCI_DOE_CTRL_GO); // 最终触发硬件执行

    return 0;
}
3. 关键寄存器说明(PCIe DOE规范定义)
寄存器偏移(相对DOE cap)名称作用
PCI_DOE_CTRL控制寄存器写GO位触发请求,写ABORT终止
PCI_DOE_STATUS状态寄存器指示忙、错误、响应就绪等状态
PCI_DOE_WRITE写数据寄存器写入请求头部和载荷
PCI_DOE_READ读数据寄存器读取响应头部和载荷

pci_write_config_dword(pdev, offset + 寄存器偏移, 数值) 是Linux内核操作PCI配置空间寄存器的标准接口,最终会通过PCI总线把数据写入硬件寄存器。

4. 总结:核心逻辑梳理
  1. pci_doe_submit_task是“调度员”:只负责把任务放进队列,不碰硬件;
  2. doe_statemachine_work是“执行者”:调度到后调用真正的硬件交互函数;
  3. pci_doe_send_req是“操作工”:通过pci_write_config_dword直接写DOE的WRITE/CTRL寄存器,完成数据发送。

你希望我标注pci_doe_send_req中每一行寄存器操作对应的PCIe 6.0规范条款,我会结合PCIe r6.0规范§6.30(Data Object Exchange)的具体条款,逐行标注核心寄存器操作的规范依据,同时补充条款核心含义,让你清晰理解代码与规范的对应关系。

5.6 pci_doe_send_req

先明确PCIe 6.0规范核心参考范围
所有DOE相关操作均对应 PCIe Base Specification Revision 6.0, Section 6.30 Data Object Exchange (DOE),以下标注的“§6.30.x”均为此规范章节。

pci_doe_send_req 完整代码 + 规范条款标注
static int pci_doe_send_req(struct pci_doe_mb *doe_mb,
			    struct pci_doe_task *task)
{
	struct pci_dev *pdev = doe_mb->pdev;
	int offset = doe_mb->cap_offset; // DOE扩展能力在PCI配置空间的偏移(§6.30.1 DOE Capability Structure)
	size_t length, remainder;
	u32 val;
	int i;

	/*
	 * Check the DOE busy bit is not set. If it is set, this could indicate
	 * someone other than Linux (e.g. firmware) is using the mailbox. Note
	 * it is expected that firmware and OS will negotiate access rights via
	 * an, as yet to be defined, method.
	 */
	// 1. 读DOE状态寄存器,检查Busy位(§6.30.2 Operation, §6.30.1.3 DOE Status Register)
	// §6.30.1.3 定义:Status寄存器的Bit 0为Busy位,1表示邮箱忙,0表示空闲
	// §6.30.2 要求:发送请求前必须确保Busy位为0,否则不能发起新请求
	pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val);
	if (FIELD_GET(PCI_DOE_STATUS_BUSY, val))
		return -EBUSY;

	// 2. 检查Status寄存器的Error位(§6.30.1.3 DOE Status Register)
	// §6.30.1.3 定义:Status寄存器的Bit 1为Error位,1表示上一次操作出错
	// §6.30.2 要求:发起新请求前需确保Error位为0,否则需先处理错误
	if (FIELD_GET(PCI_DOE_STATUS_ERROR, val))
		return -EIO;

	/* Length is 2 DW of header + length of payload in DW */
	// 3. 计算数据对象总长度(§6.30.1.1 Data Object Format)
	// §6.30.1.1 定义:数据对象由2个DWORD头部 + N个DWORD载荷组成
	// 长度单位为DWORD,需将字节数的载荷转换为DWORD数(向上取整)
	length = 2 + DIV_ROUND_UP(task->request_pl_sz, sizeof(__le32));
	// 4. 校验最大长度(§6.30.1.1 Data Object Format)
	// §6.30.1.1 规定:数据对象最大长度为2^18 DWORD(即PCI_DOE_MAX_LENGTH)
	if (length > PCI_DOE_MAX_LENGTH)
		return -EIO;
	// 5. 长度为0的特殊处理(§6.30.1.1 Data Object Format)
	// §6.30.1.1 规定:若数据对象长度等于最大值(2^18),则Header 2的Length字段需填0
	if (length == PCI_DOE_MAX_LENGTH)
		length = 0;

	/* Write DOE Header */
	// 6. 写数据对象头部第1个DWORD(§6.30.1.1 Data Object Header 1)
	// §6.30.1.1 定义:Header 1格式为:
	// Bits 31:16 = Vendor ID(协议所属厂商ID)
	// Bits 15:8  = 保留
	// Bits 7:0   = Data Object Type(协议类型)
	val = FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_VID, task->prot.vid) |
		FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, task->prot.type);
	// 写DOE Write寄存器(§6.30.1.2 DOE Write Register)
	// §6.30.1.2 规定:所有请求数据(头部+载荷)需按顺序写入Write寄存器
	pci_write_config_dword(pdev, offset + PCI_DOE_WRITE, val);

	// 7. 写数据对象头部第2个DWORD(§6.30.1.1 Data Object Header 2)
	// §6.30.1.1 定义:Header 2格式为:
	// Bits 31:0  = Length(数据对象总DWORD数,0表示最大值)
	pci_write_config_dword(pdev, offset + PCI_DOE_WRITE,
			       FIELD_PREP(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH,
					  length));

	/* Write payload */
	// 8. 写完整DWORD的载荷数据(§6.30.1.1 Data Object Payload)
	// §6.30.1.1 规定:载荷为透明字节流,按DWORD为单位写入Write寄存器
	for (i = 0; i < task->request_pl_sz / sizeof(__le32); i++)
		pci_write_config_dword(pdev, offset + PCI_DOE_WRITE,
				       le32_to_cpu(task->request_pl[i]));

	// 9. 写最后一个不完整的DWORD载荷(规范未强制,但为兼容设计)
	// §6.30.1.1 虽要求载荷按DWORD对齐,但代码做了兼容:
	// 若载荷长度不是DWORD整数倍,剩余字节写入最后一个DWORD,其余位补0
	remainder = task->request_pl_sz % sizeof(__le32);
	if (remainder) {
		val = 0;
		memcpy(&val, &task->request_pl[i], remainder);
		le32_to_cpus(&val);
		pci_write_config_dword(pdev, offset + PCI_DOE_WRITE, val);
	}

	// 10. 触发请求执行(§6.30.2 Operation, §6.30.1.4 DOE Control Register)
	// §6.30.1.4 定义:Control寄存器的Bit 0为GO位,写1触发硬件处理请求
	// §6.30.2 规定:所有数据写入完成后,需写GO位启动DOE操作
	pci_doe_write_ctrl(doe_mb, PCI_DOE_CTRL_GO);

	return 0;
}
补充规范条款核心含义(快速查阅)
规范条款核心含义
§6.30.1.1定义DOE数据对象格式:2个DWORD头部 + 载荷,Header 1存VID+协议类型,Header 2存长度
§6.30.1.2定义Write寄存器:用于主机向设备发送数据,按顺序写入头部和载荷
§6.30.1.3定义Status寄存器:Busy位表示邮箱忙,Error位表示操作出错
§6.30.1.4定义Control寄存器:GO位触发请求执行,ABORT位终止当前操作
§6.30.2定义DOE操作流程:检查状态→写数据→触发GO→等待响应→读数据
总结
  1. pci_doe_send_req的每一步寄存器操作都严格遵循PCIe 6.0 §6.30的规范,核心是“先校验状态→写头部→写载荷→触发执行”;
  2. 关键规范约束:Busy位检查、最大长度2^18 DWORD、长度0表示最大值、GO位触发执行;
  3. 代码额外做了兼容优化:支持非DWORD对齐的载荷,无需用户提前处理字节对齐。

5.7 pci_doe_recv_resp

pci_doe_recv_resp是DOE响应接收的核心函数,所有操作仍对应 PCIe Base Specification Revision 6.0, Section 6.30 Data Object Exchange (DOE),以下标注的“§6.30.x”均为此规范章节。

pci_doe_recv_resp 完整代码 + 规范条款标注
static int pci_doe_recv_resp(struct pci_doe_mb *doe_mb, struct pci_doe_task *task)
{
	size_t length, payload_length, remainder, received;
	struct pci_dev *pdev = doe_mb->pdev;
	int offset = doe_mb->cap_offset; // DOE扩展能力偏移(§6.30.1 DOE Capability Structure)
	int i = 0;
	u32 val;

	/* Read the first dword to get the protocol */
	// 1. 读响应头部第1个DWORD(§6.30.1.1 Data Object Header 1、§6.30.1.5 DOE Read Register)
	// §6.30.1.5 定义:Read寄存器用于主机从设备读取响应数据,按顺序读取头部+载荷
	// §6.30.2 要求:响应读取需先读Header 1,校验协议匹配性
	pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val);
	// 2. 校验响应协议(§6.30.2 Operation)
	// §6.30.2 规定:设备返回的响应必须与主机发送的请求协议(VID+Type)一致,否则为无效响应
	if ((FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, val) != task->prot.vid) ||
	    (FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, val) != task->prot.type)) {
		dev_err_ratelimited(&pdev->dev, "[%x] expected [VID, Protocol] = [%04x, %02x], got [%04x, %02x]\n",
				    doe_mb->cap_offset, task->prot.vid, task->prot.type,
				    FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_VID, val),
				    FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_1_TYPE, val));
		return -EIO;
	}

	// 3. 读寄存器后写0确认(§6.30.1.5 DOE Read Register)
	// §6.30.1.5 规定:每次读取Read寄存器后,需写0到该寄存器,告知设备已读取当前DWORD
	pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0);

	/* Read the second dword to get the length */
	// 4. 读响应头部第2个DWORD(§6.30.1.1 Data Object Header 2)
	// §6.30.1.1 定义:Header 2的Bits 31:0为响应数据对象总长度(DWORD数)
	pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val);
	pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0); // 读后写0确认(§6.30.1.5)

	// 5. 处理长度字段的特殊值(§6.30.1.1 Data Object Format)
	// §6.30.1.1 规定:若Header 2的Length字段为0,表示响应长度为最大值(2^18 DWORD)
	length = FIELD_GET(PCI_DOE_DATA_OBJECT_HEADER_2_LENGTH, val);
	if (!length)
		length = PCI_DOE_MAX_LENGTH;
	// 6. 校验最小长度(§6.30.1.1 Data Object Format)
	// §6.30.1.1 规定:数据对象至少包含2个DWORD头部,因此长度不能小于2
	if (length < 2)
		return -EIO;

	/* First 2 dwords have already been read */
	// 7. 计算载荷长度(§6.30.1.1 Data Object Format)
	// 总长度减去2个DWORD头部,剩余为载荷的DWORD数
	length -= 2;
	received = task->response_pl_sz; // 用户提供的响应缓冲区长度(字节)
	payload_length = DIV_ROUND_UP(task->response_pl_sz, sizeof(__le32)); // 缓冲区能容纳的DWORD数
	remainder = task->response_pl_sz % sizeof(__le32); // 最后一个DWORD的有效字节数

	/* remainder signifies number of data bytes in last payload dword */
	// 8. 兼容非DWORD对齐的缓冲区(规范未强制,代码优化)
	// 若缓冲区长度是DWORD整数倍,最后一个DWORD的有效字节数为4
	if (!remainder)
		remainder = sizeof(__le32);

	// 9. 处理响应长度超过缓冲区的情况(§6.30.2 Operation)
	// §6.30.2 允许主机截断超出缓冲区的响应数据,只需读取并丢弃多余部分
	if (length < payload_length) {
		received = length * sizeof(__le32); // 实际接收的字节数
		payload_length = length; // 实际读取的DWORD数
		remainder = sizeof(__le32); // 最后一个DWORD全部有效
	}

	if (payload_length) {
		/* Read all payload dwords except the last */
		// 10. 读取完整DWORD的载荷数据(§6.30.1.1 Data Object Payload、§6.30.1.5)
		// §6.30.1.1 规定:载荷为透明字节流,按DWORD为单位从Read寄存器读取
		for (; i < payload_length - 1; i++) {
			pci_read_config_dword(pdev, offset + PCI_DOE_READ,
					      &val);
			task->response_pl[i] = cpu_to_le32(val); // 保留小端序(规范无强制,代码兼容)
			pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0); // 读后写0确认(§6.30.1.5)
		}

		/* Read last payload dword */
		// 11. 读取最后一个(可能不完整的)DWORD载荷(代码兼容优化)
		pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val);
		cpu_to_le32s(&val);
		memcpy(&task->response_pl[i], &val, remainder); // 仅拷贝有效字节
		/* Prior to the last ack, ensure Data Object Ready */
		// 12. 最后一次读取前校验Data Object Ready位(§6.30.1.3 DOE Status Register)
		// §6.30.1.3 定义:Status寄存器Bit 2为Data Object Ready位,1表示设备有响应数据可读
		// §6.30.2 要求:读取最后一个DWORD前需确认该位仍为1,避免数据未就绪
		if (!pci_doe_data_obj_ready(doe_mb))
			return -EIO;
		pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0); // 读后写0确认(§6.30.1.5)
		i++;
	}

	/* Flush excess length */
	// 13. 读取并丢弃超出缓冲区的剩余数据(§6.30.2 Operation)
	// §6.30.2 规定:主机必须读取设备返回的全部响应数据,即使超出缓冲区,否则邮箱会处于异常状态
	for (; i < length; i++) {
		pci_read_config_dword(pdev, offset + PCI_DOE_READ, &val);
		pci_write_config_dword(pdev, offset + PCI_DOE_READ, 0);
	}

	/* Final error check to pick up on any since Data Object Ready */
	// 14. 最终错误校验(§6.30.1.3 DOE Status Register、§6.30.2 Operation)
	// §6.30.2 要求:响应读取完成后,需再次检查Error位,确认整个操作无硬件错误
	pci_read_config_dword(pdev, offset + PCI_DOE_STATUS, &val);
	if (FIELD_GET(PCI_DOE_STATUS_ERROR, val))
		return -EIO;

	return received;
}
响应接收核心规范条款补充(与发送端对应)
规范条款核心含义(响应接收侧)
§6.30.1.5定义Read寄存器:用于读取响应数据,读后必须写0确认,否则设备不会推送下一个DWORD
§6.30.1.3Status寄存器的Data Object Ready位(Bit 2):1表示设备有响应数据可读,读取过程中需保持有效
§6.30.2响应读取流程:读Header 1校验协议→读Header 2获取长度→读载荷→读取并丢弃多余数据→最终校验错误
§6.30.1.1响应数据对象格式与请求一致,长度字段0表示最大值,必须读取全部响应数据(即使截断)
总结
  1. pci_doe_recv_resp的核心规范约束:读Read寄存器后必须写0确认、必须读取全部响应数据(即使截断)、全程校验Data Object Ready和Error位;
  2. 代码兼容优化点:支持非DWORD对齐的响应缓冲区,仅拷贝有效字节,无需用户提前处理对齐;
  3. 关键容错逻辑:最后一次读取前校验Data Object Ready位、读取完成后再次校验Error位,确保响应完整且无硬件错误。
保姆级教程:在QEMU虚拟环境中搭建与调试PCIe DOE功能 本文提供了一份详细的QEMU虚拟环境中搭建与调试PCIe DOE功能的保姆级教程。通过配置QEMU环境、准备内核与工具链、实现DOE功能发现与枚举,以及实战数据交换和高级调试技巧,帮助开发者掌握PCIe 6.0引入的Data Object Exchange(DOE)安全通信机制。 阅读详情

相关推荐

给硬件工程师的CXL寄存器实战指南:从PCIe配置空间到MMIO的完整寻址路径

本文为硬件工程师提供CXL寄存器操作的实战指南,详细解析从PCIe配置空间到MMIO的完整寻址路径。涵盖DVSEC结构定位、MMIO空间映射及动态分配区域处理,分享实际开发中的调试技巧与工具链使用建议,帮助工程师高效解决CXL设备开发中的寄存器访问问题。

dianzhuique8587的博客 375

PCIe6.0学习小记

从以上图4可知,PAM4的4个电平(Level0,1,2,3)进行格雷编码(Gray Coding),4个电平对应的格雷编码分别是00,01,11,10,格雷编码的方式是要求相邻符号,每次只能变化一位,且首尾符号也只是变化一位,这样的编码方式可以有效改善误码率。在将 PAM4 电压转换为 2 位对齐量后,如果适用,它会进行接收端预编码,然后解码格雷码,如果适用,则在单比特级别进行解扰。如上图所示的对比可以看出,NRZ只有0,1两个电平,形成一个眼,而PAM4会有0,1,2,3共4个电平,形成了3个眼。

Summer的博客 4585

IDE Key Management的密钥协商过程

IDE Key Management(IDE_KM)的密钥协商过程是实现的安全流程,核心是在主机(如Root Port)与PCIe设备(如Endpoint)之间建立信任关系,并生成/分发IDE流所需的加密/完整性密钥。以下是。

inquisiter 1321

pcie 6.0/7.0相对pcie 5.0的变化有哪些?

此外,还引入了新的接口,如LTSSM新增加了L0p低功耗状态,允许部分 Lane Electric Idle、部分 Lane Active,以进一步降低功耗并提高能效。PCIe 6.0可能会与新的CXL规范融合,提供更高效的解决方案,以支持更高端的服务器和工作站应用。pcie协议5.0还没完全普及,pcie 6.0 spec已经发布,pcie 7.0 spec也在路上了,这个世界都在狂奔。言归正传,为了追求高性能,PCIE SSD是数据中心的高性能标配,PCIE SSD在数据中心的占比还在继续攀升。

存储随笔 5148

【CXL】背景必修课:PCIe 与 Cache Coherence

本文聚焦CXL系统架构学习的两大基础背景知识:PCIe和Cache Coherence。作者指出理解CXL规范的关键在于掌握这两项核心技术: PCIe方面重点解析了与CXL.io直接相关的核心概念,包括协议分层、DVSEC寄存器结构、TLP报文格式等关键技术点,并推荐了权威学习资源。 Cache Coherence部分深入剖析了一致性协议原理,详细对比MESI/MOESI状态机与CXL.cache报文的对应关系,特别强调了内存顺序问题的重要性。 文章强调这些背景知识是理解CXL规范的必要前提,而非补充内容,

元直的博客 260

【信息科学与工程学】【通信工程】第八十六篇 通信网络设备及通信网络组网的所有学科知识01

编号001类型网络虚拟化与覆盖技术网络领域数据中心网络子领域大二层网络 / 网络虚拟化核心数学方程式/算法模型核心算法:VXLAN封装与解封装。关键在于24位VNI(虚拟网络标识符)到物理网络的三层IP地址的映射。封装:收到原始以太帧后,为其添加 VXLAN 头部(含 VNI)、外层 UDP 头(目的端口 4789)、外层 IP 头(源 IP 为本地 VTEP IP,目的 IP 为对端 VTEP IP)以及外层 MAC 头。解封装:对端 收到报文后,剥离外层头部,根据内层帧的目的 MAC 和 VNI 进行二

weixin_49199313的博客 840

【每日一题】一次讲明白SPDM/DOE/IDE,为啥大家都用SerialTek和SanBlaze测试该功能

本文介绍了企业级NVMe SSD测试中常见的三个关键技术:SPDM(安全协议与数据模型)、DOE(数据对象交换)和IDE(完整性及数据加密)。SPDM负责设备认证和安全会话建立,DOE作为PCIe传输通道承载安全消息,IDE则实现链路数据加密保护。三者协同工作:SPDM通过DOE传输认证信息,建立安全会话后生成密钥,最终由IDE实施链路加密。文章详细解析了Host与Device的初始化流程,包括PCIe枚举、SPDM认证、会话建立、密钥管理和IDE启动等步骤,并指出这些机制主要应用于数据中心和安全计算场景,

Saniffer的博客 612

SPDM安全协议详解:基于DSP0274_1.2.0的设备认证与固件测量实现

在服务器和数据中心环境中,硬件设备身份与运行状态的可信度是安全体系的基础。传统设备认证方案难以在驱动加载前验证固件真实性与完整性。SPDM(安全协议与数据模型)作为一种运行于PCIe DOE、MCTP等既有传输之上的标准化协议,通过能力协商、证书链验证、挑战-响应认证和运行时测量等机制,解决了设备身份伪造和固件篡改问题。其中,DSP0274_1.2.0定义了首个生产可用的完整状态机与消息格式,libspdm为设备端和主机端提供了可落地的参考实现。该技术适用于BMC固件、NVMe硬盘、网卡等设备的可信启动与持

weixin_29327977的博客 248

PCIe安全三件套:SPDM、DOE与IDE的分工及联调测试

PCIe链路安全常被误解为SPDM、DOE、IDE三个功能开关,实际上设备认证、消息承载与数据面加密是上下游关系。SPDM借助证书、挑战随机数和度量证明设备身份与固件状态;DOE在配置空间以窄带邮箱承载SPDM及密钥管理消息;IDE则在链路层对TLP做加密和完整性保护,并通过Stream、密钥集与防重放计数实现隔离。理解这套原理,才能把安全价值落到设备验证、固件可信、交换拓扑和合规测试中;联调时按认证、流使能、密钥更新、中止与超时逐层排查,可避免认证通过但数据未加密等典型问题。

weixin_29189003的博客 206

SPDM 1.2协议详解:从设备身份认证到固件测量与安全会话

在平台固件与设备管理场景中,如何确认对端设备身份可信、固件状态未被篡改,是构建安全基础设施的前提。SPDM(Security Protocol and Data Model)正是为此设计的开放标准,它通过版本协商、能力探测和算法互选建立信任基础,再以证书链、挑战响应和固件测量机制完成设备身份认证与完整性校验。SPDM 1.2进一步引入PSK预共享密钥会话、心跳保活和密钥更新能力,使主机能够在PCIe DOE、CXL、MCTP等链路上安全地管理设备。该协议的价值在于将设备证明与会话加密分离,让管理指令与敏感数

weixin_29315569的博客 73

使用Vivado对PCIe IP配置的详细步骤

使用Vivado对PCIe IP配置的详细步骤,图文解说。。。。

QEMU SPDM 支持实战指南:搭建 SPDM Responder 并为 NVMe 设备启用安全协议与数据模型(SPDM)认证

SPDM(Security Protocols and Data Models,安全协议与数据模型)由 DMTF 发布,用于设备与主机之间完成身份认证(Authentication)、可信证明(Attestation)与密钥交换(Key Exchange),是构建基础设施安全(如 PCIe 设备安全启动、固件信任链)的关键一环。QEMU 以 SPDM Requester 的角色接入外部 SPDM

gitblog_00299的博客 27

博士申请-考核制经验分享(2)——个人简历

申博过程中一般在联系导师时就将个人简历作为邮件附件,有些学校也会在提交材料或者复试阶段提交个人简历。

weixin_49282401的博客 5526

Linux Kernel 6.0 CXL Core pci.c 详解

Linux Kernel 6.0 CXL 驱动 pci.c 详细解析

这个地瓜真是甜啊 3468

使用C++编写一个可视化窗口

初学C++时,控制台会给我们提供一个黑窗口,而这篇文章所做的就是:创建一个属于图形界面窗口,而不是使用控制台黑窗口 如图,这是一个标题为XSZY的窗口 看完这篇文章,你想要的窗口大小,图标等都可以自己定义 甚至是在窗口上鼠标的样子,都是可以随意改的 所有有关视窗的基本函数和类型都定义在头文件 <windows.h> 中 我们只需要引用就好了 //引用头文件 windows.h #include<windows.h> 然后定义程序的入口函数main int __stdcall W

XSZY_的博客 6万+

云架构师进阶【SAP迁移方案设计】手把手教您玩转SAP上云

一,项目背景介绍 今天又接到了北京某制造企业SAP迁移的场景,先简单说明一下,好好的SAP在运行着为什么要做迁移这类大动作呢?众所周知SAP的ERP是企业的绝对核心系统,尤其对于制造业更是核心的系统,这样一套核心系统还是生产环境在迁移的过程中必然要面临比较大的挑战,如果有任何问题的话,都会对企业的绩效产生很大的影响。 经过跟客户沟通才得知,客户的SAP原来的系统是托管在运营商机房里,近期感染了勒索病毒,导致业务宕机,SAP应用服务器无法启动,仅有的数据库服务器可以运行。基于上述情况公司领导当机立断决定迅

bdsh818的博客 4220

【最新技术早知道】PCIe Gen5 还没用上,Gen6 就来了?PCIe 6(1)

FLIT 中有 6 B DLP 数据(非DLLP),其中前 2 B 是留给 FLIT ACK / NAK / Replay 用的,取代了 Gen5 中采用 DLLP 的 ACK / NAK 机制。之前是以 TLP 为单位进行事务传输,PCIe 6.0 以 FLIT 为单位进行事务传输,每个 FLIT 有 256 B 数据(格式如 式1),每 B 数据占用 4 UI。堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。图1 x8 FLIT 格式。

afagagagaa的博客 2033

Spring Boot 调用豆包大模型如何开启联网搜索?

场景推荐方案有权限创建 Bot + 需强集成✅ 使用bot-xxxx无 Bot 权限 / 追求通用性✅ 自行集成搜索引擎 + 普通豆包模型仅需基础对话❌ 无需考虑联网豆包大模型确实支持联网搜索,但前提是:你用的是“智能体 Bot”,且它被配置了“网页搜索”插件,并调用正确的 API 端点。希望本文能帮你少走弯路,快速构建出真正“知天下事”的智能助手!

lpfasd123的博客 1123

【最新技术早知道】PCIe Gen5 还没用上,Gen6 就来了?PCIe 6.0 系列文章之:《PCIe 6.0,到底 6 在哪?》

简单介绍了 PCIe 6.0 最新技术规范,PAM4、FLIT Mode、FEC、L0p 等

MangoPapa 1万+

别再只当调包侠了!YOLO检测头到损失函数全链路拆解,看完才算真懂

YOLO的精妙不在于某个单一创新,而在于各模块围绕“实时精准检测”这一目标的协同设计。Backbone追求高效特征压缩,Neck保障多尺度信息流通,Head解耦任务冲突,Loss动态引导优化方向。只有理解这套内在逻辑,才能跳出“调包-试错”的低效循环,在面对新场景、新问题时做出有理有据的改进决策。技术深度决定职业天花板。下次再调YOLO时,不妨先问自己:我改的这个参数,到底影响了系统中的哪一环?参考资料。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 186

TC3XX - MCAL知识点(三):MCU EB-tresos配置说明及代码浅析

AURIX 2G Tc3xx的配置相较AURIX 1G Tc2xx来讲简化了许多,但是依赖于数据手册的理解来讲整个MCU功能做了较大增强,此处先简单的介绍一下,本文前期先补充介绍一下英飞凌手册的阅读规则,因为后面的文章会基于此文的一些缩写进行截图寄存器之类的。MCU在AUTOSAR架构里面的图示如下MCU驱动程序负责配置SCU、GTM、CCU6、GPT12和STM外设。驱动程序提供由AUTOSAR指定的运行时服务。

梦想技术家 2874

【PCIe小知识】PCIe 5.0/6.0里面提到的IDE/DOE/SPDM 分别是什么?

本文解析了PCIe5.0/6.0中的三个关键技术:SPDM(安全协议与数据模型)、DOE(数据对象交换)和IDE(完整性与数据加密)。SPDM用于设备认证与安全会话建立,DOE为PCIe设备提供邮箱机制传输安全消息,IDE则实现链路级数据加密。三者协同工作:SPDM通过DOE通道进行认证和密钥协商,最终为IDE提供加密密钥,形成完整的PCIe安全体系。这些技术主要应用于数据中心等需要设备认证和链路安全的高安全性场景,如NVMe SSD、GPU设备验证及机密计算等。

Saniffer的博客 603

timing window file中的S L T指什么意思

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧?

m0_61544122的博客 774

OBEX(二)

三、Session Protocol OBEX操作由request-response对组成。Request由client发出的,response由server发出的。Client发送一个request后,需要在收到来自server的response之后才能发送新的request。但是当启动OBEX Single Response Mode (SRM)时执行Put或者Get操作的时候也有例外的情况,在这种情况下,为了提高OBEX的吞吐量会忽略标准的request-response序列。 1、Req...

sui1005316018的博客 1716

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

PCIe扫盲——PCIe总线事务层入门(一)

转载地址:http://blog.chinaaet.com/justlxy/p/5100053247 在介绍事务层之前,首先简单地了解一下PCIe总线的通信机制。假设某个设备要对另一个设备进行读取数据的操作,首先这个设备(称之为Requester)需要向另一个设备发送一个Request,然后另一个设备(称之为Completer)通过Completion Packet返回数据或者错误信息。在PCI...

kunkliu的博客 3190

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

上一篇: TDISP(TEE Device Interface Security Protocol)软件操作流程
下一篇: TEE Device Interface Security Protocol - TDISP翻译
inquisiter
博客等级 码龄10年 374粉丝 228原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值